]> git.argeo.org Git - lgpl/argeo-commons.git/blob - acr/Content.java
Prepare next development cycle
[lgpl/argeo-commons.git] / acr / Content.java
1 package org.argeo.api.acr;
2
3 import static org.argeo.api.acr.NamespaceUtils.unqualified;
4
5 import java.io.Closeable;
6 import java.io.IOException;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Optional;
11 import java.util.concurrent.CompletableFuture;
12
13 import javax.xml.namespace.QName;
14
15 /**
16 * A semi-structured content, with attributes, within a hierarchical structure.
17 */
18 public interface Content extends Iterable<Content>, Map<QName, Object> {
19
20 QName getName();
21
22 String getPath();
23
24 Content getParent();
25
26 /*
27 * ATTRIBUTES OPERATIONS
28 */
29
30 <A> Optional<A> get(QName key, Class<A> clss);
31
32 Class<?> getType(QName key);
33
34 boolean isMultiple(QName key);
35
36 <A> List<A> getMultiple(QName key, Class<A> clss);
37
38 /*
39 * ATTRIBUTES OPERATION HELPERS
40 */
41 default boolean containsKey(QNamed key) {
42 return containsKey(key.qName());
43 }
44
45 default <A> Optional<A> get(QNamed key, Class<A> clss) {
46 return get(key.qName(), clss);
47 }
48
49 default Object get(QNamed key) {
50 return get(key.qName());
51 }
52
53 default Object put(QNamed key, Object value) {
54 return put(key.qName(), value);
55 }
56
57 default Object remove(QNamed key) {
58 return remove(key.qName());
59 }
60
61 // TODO do we really need the helpers below?
62
63 default Object get(String key) {
64 return get(unqualified(key));
65 }
66
67 default Object put(String key, Object value) {
68 return put(unqualified(key), value);
69 }
70
71 default Object remove(String key) {
72 return remove(unqualified(key));
73 }
74
75 @SuppressWarnings("unchecked")
76 default <A> List<A> getMultiple(QName key) {
77 Class<A> type;
78 try {
79 type = (Class<A>) getType(key);
80 } catch (ClassCastException e) {
81 throw new IllegalArgumentException("Requested type is not the default type");
82 }
83 List<A> res = getMultiple(key, type);
84 return res;
85 // if (res == null)
86 // return null;
87 // else {
88 // if (res.isEmpty())
89 // throw new IllegalStateException("Metadata " + key + " is not availabel as list of type " + type);
90 // return res.get();
91 // }
92 }
93
94 /*
95 * CONTENT OPERATIONS
96 */
97 // default CompletionStage<Content> edit(Consumer<Content> work) {
98 // return CompletableFuture.supplyAsync(() -> {
99 // work.accept(this);
100 // return this;
101 // }).minimalCompletionStage();
102 // }
103
104 Content add(QName name, QName... classes);
105
106 default Content add(String name, QName... classes) {
107 return add(unqualified(name), classes);
108 }
109
110 void remove();
111
112 /*
113 * TYPING
114 */
115 List<QName> getContentClasses();
116
117 default void addContentClasses(QName... contentClass) {
118 throw new UnsupportedOperationException("Adding content classes to " + getPath() + " is not supported");
119 }
120
121 /** AND */
122 default boolean isContentClass(QName... contentClass) {
123 List<QName> contentClasses = getContentClasses();
124 for (QName cClass : contentClass) {
125 if (!contentClasses.contains(cClass))
126 return false;
127 }
128 return true;
129 }
130
131 /** OR */
132 default boolean hasContentClass(QName... contentClass) {
133 List<QName> contentClasses = getContentClasses();
134 for (QName cClass : contentClass) {
135 if (contentClasses.contains(cClass))
136 return true;
137 }
138 return false;
139 }
140
141 default boolean hasContentClass(QNamed contentClass) {
142 return hasContentClass(contentClass.qName());
143 }
144
145 /*
146 * SIBLINGS
147 */
148
149 default int getSiblingIndex() {
150 return 1;
151 }
152
153 /*
154 * DEFAULT METHODS
155 */
156 default <A> A adapt(Class<A> clss) {
157 throw new UnsupportedOperationException("Cannot adapt content " + this + " to " + clss.getName());
158 }
159
160 default <C extends Closeable> C open(Class<C> clss) throws IOException {
161 throw new UnsupportedOperationException("Cannot open content " + this + " as " + clss.getName());
162 }
163
164 default <A> CompletableFuture<A> write(Class<A> clss) {
165 throw new UnsupportedOperationException("Cannot write content " + this + " as " + clss.getName());
166 }
167
168 /*
169 * CHILDREN
170 */
171
172 default boolean hasChild(QName name) {
173 for (Content child : this) {
174 if (child.getName().equals(name))
175 return true;
176 }
177 return false;
178 }
179
180 default boolean hasChild(QNamed name) {
181 return hasChild(name.qName());
182 }
183
184 default Content anyOrAddChild(QName name, QName... classes) {
185 Content child = anyChild(name);
186 if (child != null)
187 return child;
188 return this.add(name, classes);
189 }
190
191 default Content anyOrAddChild(String name, QName... classes) {
192 return anyOrAddChild(unqualified(name), classes);
193 }
194
195 /** Any child with this name, or null if there is none */
196 default Content anyChild(QName name) {
197 for (Content child : this) {
198 if (child.getName().equals(name))
199 return child;
200 }
201 return null;
202 }
203
204 default List<Content> children(QName name) {
205 List<Content> res = new ArrayList<>();
206 for (Content child : this) {
207 if (child.getName().equals(name))
208 res.add(child);
209 }
210 return res;
211 }
212
213 default Optional<Content> soleChild(QName name) {
214 List<Content> res = children(name);
215 if (res.isEmpty())
216 return Optional.empty();
217 if (res.size() > 1)
218 throw new IllegalStateException(this + " has multiple children with name " + name);
219 return Optional.of(res.get(0));
220 }
221
222 default Content child(QName name) {
223 return soleChild(name).orElseThrow();
224 }
225
226 default Content child(QNamed name) {
227 return child(name.qName());
228 }
229
230 /*
231 * ATTR AS STRING
232 */
233 default String attr(QName key) {
234 // TODO check String type?
235 Object obj = get(key);
236 if (obj == null)
237 return null;
238 return obj.toString();
239 }
240
241 default String attr(QNamed key) {
242 return attr(key.qName());
243 }
244
245 default String attr(String key) {
246 return attr(unqualified(key));
247 }
248 //
249 // default String attr(Object key) {
250 // return key != null ? attr(key.toString()) : attr(null);
251 // }
252 //
253 // default <A> A get(Object key, Class<A> clss) {
254 // return key != null ? get(key.toString(), clss) : get(null, clss);
255 // }
256
257 /*
258 * EXPERIMENTAL UNSUPPORTED
259 */
260 default boolean hasText() {
261 return false;
262 }
263
264 default String getText() {
265 throw new UnsupportedOperationException();
266 }
267
268 }