]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.api.acr/src/org/argeo/api/acr/Content.java
Start improving single-user login
[lgpl/argeo-commons.git] / org.argeo.api.acr / src / org / argeo / api / 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 /** AND */
132 default boolean isContentClass(QNamed... contentClass) {
133 List<QName> lst = new ArrayList<>();
134 for (QNamed qNamed : contentClass)
135 lst.add(qNamed.qName());
136 return isContentClass(lst.toArray(new QName[lst.size()]));
137 }
138
139 /** OR */
140 default boolean hasContentClass(QName... contentClass) {
141 List<QName> contentClasses = getContentClasses();
142 for (QName cClass : contentClass) {
143 if (contentClasses.contains(cClass))
144 return true;
145 }
146 return false;
147 }
148
149 /** OR */
150 default boolean hasContentClass(QNamed... contentClass) {
151 List<QName> lst = new ArrayList<>();
152 for (QNamed qNamed : contentClass)
153 lst.add(qNamed.qName());
154 return hasContentClass(lst.toArray(new QName[lst.size()]));
155 }
156
157 /*
158 * SIBLINGS
159 */
160
161 default int getSiblingIndex() {
162 return 1;
163 }
164
165 /*
166 * DEFAULT METHODS
167 */
168 default <A> A adapt(Class<A> clss) {
169 throw new UnsupportedOperationException("Cannot adapt content " + this + " to " + clss.getName());
170 }
171
172 default <C extends Closeable> C open(Class<C> clss) throws IOException {
173 throw new UnsupportedOperationException("Cannot open content " + this + " as " + clss.getName());
174 }
175
176 default <A> CompletableFuture<A> write(Class<A> clss) {
177 throw new UnsupportedOperationException("Cannot write content " + this + " as " + clss.getName());
178 }
179
180 /*
181 * CHILDREN
182 */
183
184 default boolean hasChild(QName name) {
185 for (Content child : this) {
186 if (child.getName().equals(name))
187 return true;
188 }
189 return false;
190 }
191
192 default boolean hasChild(QNamed name) {
193 return hasChild(name.qName());
194 }
195
196 default Content anyOrAddChild(QName name, QName... classes) {
197 Content child = anyChild(name);
198 if (child != null)
199 return child;
200 return this.add(name, classes);
201 }
202
203 default Content anyOrAddChild(String name, QName... classes) {
204 return anyOrAddChild(unqualified(name), classes);
205 }
206
207 /** Any child with this name, or null if there is none */
208 default Content anyChild(QName name) {
209 for (Content child : this) {
210 if (child.getName().equals(name))
211 return child;
212 }
213 return null;
214 }
215
216 default List<Content> children(QName name) {
217 List<Content> res = new ArrayList<>();
218 for (Content child : this) {
219 if (child.getName().equals(name))
220 res.add(child);
221 }
222 return res;
223 }
224
225 default Optional<Content> soleChild(QName name) {
226 List<Content> res = children(name);
227 if (res.isEmpty())
228 return Optional.empty();
229 if (res.size() > 1)
230 throw new IllegalStateException(this + " has multiple children with name " + name);
231 return Optional.of(res.get(0));
232 }
233
234 default Content child(QName name) {
235 return soleChild(name).orElseThrow();
236 }
237
238 default Content child(QNamed name) {
239 return child(name.qName());
240 }
241
242 /*
243 * ATTR AS STRING
244 */
245 default String attr(QName key) {
246 // TODO check String type?
247 Object obj = get(key);
248 if (obj == null)
249 return null;
250 return obj.toString();
251 }
252
253 default String attr(QNamed key) {
254 return attr(key.qName());
255 }
256
257 default String attr(String key) {
258 return attr(unqualified(key));
259 }
260 //
261 // default String attr(Object key) {
262 // return key != null ? attr(key.toString()) : attr(null);
263 // }
264 //
265 // default <A> A get(Object key, Class<A> clss) {
266 // return key != null ? get(key.toString(), clss) : get(null, clss);
267 // }
268
269 /*
270 * EXPERIMENTAL UNSUPPORTED
271 */
272 default boolean hasText() {
273 return false;
274 }
275
276 default String getText() {
277 throw new UnsupportedOperationException();
278 }
279
280 }