]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.api.acr/src/org/argeo/api/acr/Content.java
Add cr:path name
[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 /** The base of a repository path. */
20 String ROOT_PATH = "/";
21
22 QName getName();
23
24 String getPath();
25
26 Content getParent();
27
28 /*
29 * ATTRIBUTES OPERATIONS
30 */
31
32 <A> Optional<A> get(QName key, Class<A> clss);
33
34 Class<?> getType(QName key);
35
36 boolean isMultiple(QName key);
37
38 <A> List<A> getMultiple(QName key, Class<A> clss);
39
40 /*
41 * ATTRIBUTES OPERATION HELPERS
42 */
43 default boolean containsKey(QNamed key) {
44 return containsKey(key.qName());
45 }
46
47 default <A> Optional<A> get(QNamed key, Class<A> clss) {
48 return get(key.qName(), clss);
49 }
50
51 default Object get(QNamed key) {
52 return get(key.qName());
53 }
54
55 default Object put(QNamed key, Object value) {
56 return put(key.qName(), value);
57 }
58
59 default Object remove(QNamed key) {
60 return remove(key.qName());
61 }
62
63 // TODO do we really need the helpers below?
64
65 default Object get(String key) {
66 return get(unqualified(key));
67 }
68
69 default Object put(String key, Object value) {
70 return put(unqualified(key), value);
71 }
72
73 default Object remove(String key) {
74 return remove(unqualified(key));
75 }
76
77 @SuppressWarnings("unchecked")
78 default <A> List<A> getMultiple(QName key) {
79 Class<A> type;
80 try {
81 type = (Class<A>) getType(key);
82 } catch (ClassCastException e) {
83 throw new IllegalArgumentException("Requested type is not the default type");
84 }
85 List<A> res = getMultiple(key, type);
86 return res;
87 }
88
89 /*
90 * CONTENT OPERATIONS
91 */
92 /** Adds a new empty {@link Content} to this {@link Content}. */
93 Content add(QName name, QName... classes);
94
95 /**
96 * Adds a new {@link Content} to this {@link Content}, setting the provided
97 * attributes. The provided attributes can be used as hints by the
98 * implementation. In particular, setting {@link DName#getcontenttype} will
99 * imply that this content has a file semantic.
100 */
101 default Content add(QName name, Map<QName, Object> attrs, QName... classes) {
102 Content child = add(name, classes);
103 putAll(attrs);
104 return child;
105 }
106
107 default Content add(String name, QName... classes) {
108 return add(unqualified(name), classes);
109 }
110
111 default Content add(String name, Map<QName, Object> attrs, QName... classes) {
112 return add(unqualified(name), attrs, classes);
113 }
114
115 void remove();
116
117 /*
118 * TYPING
119 */
120 List<QName> getContentClasses();
121
122 default void addContentClasses(QName... contentClass) {
123 throw new UnsupportedOperationException("Adding content classes to " + getPath() + " is not supported");
124 }
125
126 /** AND */
127 default boolean isContentClass(QName... contentClass) {
128 List<QName> contentClasses = getContentClasses();
129 for (QName cClass : contentClass) {
130 if (!contentClasses.contains(cClass))
131 return false;
132 }
133 return true;
134 }
135
136 /** AND */
137 default boolean isContentClass(QNamed... contentClass) {
138 List<QName> lst = new ArrayList<>();
139 for (QNamed qNamed : contentClass)
140 lst.add(qNamed.qName());
141 return isContentClass(lst.toArray(new QName[lst.size()]));
142 }
143
144 /** OR */
145 default boolean hasContentClass(QName... contentClass) {
146 List<QName> contentClasses = getContentClasses();
147 for (QName cClass : contentClass) {
148 if (contentClasses.contains(cClass))
149 return true;
150 }
151 return false;
152 }
153
154 /** OR */
155 default boolean hasContentClass(QNamed... contentClass) {
156 List<QName> lst = new ArrayList<>();
157 for (QNamed qNamed : contentClass)
158 lst.add(qNamed.qName());
159 return hasContentClass(lst.toArray(new QName[lst.size()]));
160 }
161
162 /*
163 * SIBLINGS
164 */
165
166 default int getSiblingIndex() {
167 return 1;
168 }
169
170 /*
171 * DEFAULT METHODS
172 */
173 default <A> A adapt(Class<A> clss) {
174 throw new UnsupportedOperationException("Cannot adapt content " + this + " to " + clss.getName());
175 }
176
177 default <C extends Closeable> C open(Class<C> clss) throws IOException {
178 throw new UnsupportedOperationException("Cannot open content " + this + " as " + clss.getName());
179 }
180
181 default <A> CompletableFuture<A> write(Class<A> clss) {
182 throw new UnsupportedOperationException("Cannot write content " + this + " as " + clss.getName());
183 }
184
185 /*
186 * CHILDREN
187 */
188
189 default boolean hasChild(QName name) {
190 for (Content child : this) {
191 if (child.getName().equals(name))
192 return true;
193 }
194 return false;
195 }
196
197 default boolean hasChild(QNamed name) {
198 return hasChild(name.qName());
199 }
200
201 default Content anyOrAddChild(QName name, QName... classes) {
202 Content child = anyChild(name);
203 if (child != null)
204 return child;
205 return this.add(name, classes);
206 }
207
208 default Content anyOrAddChild(String name, QName... classes) {
209 return anyOrAddChild(unqualified(name), classes);
210 }
211
212 /** Any child with this name, or null if there is none */
213 default Content anyChild(QName name) {
214 for (Content child : this) {
215 if (child.getName().equals(name))
216 return child;
217 }
218 return null;
219 }
220
221 default List<Content> children(QName name) {
222 List<Content> res = new ArrayList<>();
223 for (Content child : this) {
224 if (child.getName().equals(name))
225 res.add(child);
226 }
227 return res;
228 }
229
230 default List<Content> children(QNamed name) {
231 return children(name.qName());
232 }
233
234 default Optional<Content> soleChild(QNamed name) {
235 return soleChild(name.qName());
236 }
237
238 default Optional<Content> soleChild(QName name) {
239 List<Content> res = children(name);
240 if (res.isEmpty())
241 return Optional.empty();
242 if (res.size() > 1)
243 throw new IllegalStateException(this + " has multiple children with name " + name);
244 return Optional.of(res.get(0));
245 }
246
247 default Content soleOrAddChild(QName name, QName... classes) {
248 return soleChild(name).orElseGet(() -> this.add(name, classes));
249 }
250
251 default Content child(QName name) {
252 return soleChild(name).orElseThrow();
253 }
254
255 default Content child(QNamed name) {
256 return child(name.qName());
257 }
258
259 /*
260 * ATTR AS STRING
261 */
262 /**
263 * Convenience method returning an attribute as a {@link String}.
264 *
265 * @param key the attribute name
266 * @return the attribute value as a {@link String} or <code>null</code>.
267 *
268 * @see Object#toString()
269 */
270 default String attr(QName key) {
271 return get(key, String.class).orElse(null);
272 }
273
274 /**
275 * Convenience method returning an attribute as a {@link String}.
276 *
277 * @param key the attribute name
278 * @return the attribute value as a {@link String} or <code>null</code>.
279 *
280 * @see Object#toString()
281 */
282 default String attr(QNamed key) {
283 return attr(key.qName());
284 }
285
286 /**
287 * Convenience method returning an attribute as a {@link String}.
288 *
289 * @param key the attribute name
290 * @return the attribute value as a {@link String} or <code>null</code>.
291 *
292 * @see Object#toString()
293 */
294 default String attr(String key) {
295 return attr(unqualified(key));
296 }
297
298 /*
299 * CONTEXT
300 */
301 /**
302 * A content within this repository
303 *
304 * @param path either an absolute path or a path relative to this content
305 */
306 Optional<Content> getContent(String path);
307
308 /*
309 * EXPERIMENTAL UNSUPPORTED
310 */
311 default boolean hasText() {
312 return false;
313 }
314
315 default String getText() {
316 throw new UnsupportedOperationException();
317 }
318
319 }