]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.api.acr/src/org/argeo/api/acr/Content.java
Make JCR to ACR more flexible
[lgpl/argeo-commons.git] / org.argeo.api.acr / src / org / argeo / api / acr / Content.java
1 package org.argeo.api.acr;
2
3 import java.io.Closeable;
4 import java.io.IOException;
5 import java.util.Collection;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Optional;
9 import java.util.concurrent.CompletableFuture;
10
11 import javax.xml.XMLConstants;
12 import javax.xml.namespace.QName;
13
14 /**
15 * A semi-structured content, with attributes, within a hierarchical structure.
16 */
17 public interface Content extends Iterable<Content>, Map<QName, Object> {
18
19 QName getName();
20
21 String getPath();
22
23 Content getParent();
24
25 /*
26 * ATTRIBUTES OPERATIONS
27 */
28
29 <A> Optional<A> get(QName key, Class<A> clss);
30
31 default Object get(String key) {
32 if (key.indexOf(':') >= 0)
33 throw new IllegalArgumentException("Name " + key + " has a prefix");
34 return get(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX));
35 }
36
37 default Object put(String key, Object value) {
38 if (key.indexOf(':') >= 0)
39 throw new IllegalArgumentException("Name " + key + " has a prefix");
40 return put(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX), value);
41 }
42
43 default Object remove(String key) {
44 if (key.indexOf(':') >= 0)
45 throw new IllegalArgumentException("Name " + key + " has a prefix");
46 return remove(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX));
47 }
48
49 Class<?> getType(QName key);
50
51 boolean isMultiple(QName key);
52
53 <A> Optional<List<A>> getMultiple(QName key, Class<A> clss);
54
55 @SuppressWarnings("unchecked")
56 default <A> List<A> getMultiple(QName key) {
57 Class<A> type;
58 try {
59 type = (Class<A>) getType(key);
60 } catch (ClassCastException e) {
61 throw new IllegalArgumentException("Requested type is not the default type");
62 }
63 Optional<List<A>> res = getMultiple(key, type);
64 if (res == null)
65 return null;
66 else {
67 if (res.isEmpty())
68 throw new IllegalStateException("Metadata " + key + " is not availabel as list of type " + type);
69 return res.get();
70 }
71 }
72
73 /*
74 * CONTENT OPERATIONS
75 */
76 // default CompletionStage<Content> edit(Consumer<Content> work) {
77 // return CompletableFuture.supplyAsync(() -> {
78 // work.accept(this);
79 // return this;
80 // }).minimalCompletionStage();
81 // }
82
83 Content add(QName name, QName... classes);
84
85 default Content add(String name, QName... classes) {
86 if (name.indexOf(':') >= 0)
87 throw new IllegalArgumentException("Name " + name + " has a prefix");
88 return add(new QName(XMLConstants.NULL_NS_URI, name, XMLConstants.DEFAULT_NS_PREFIX), classes);
89 }
90
91 void remove();
92
93 /*
94 * TYPING
95 */
96 List<QName> getContentClasses();
97
98 /** AND */
99 default boolean isContentClass(QName... contentClass) {
100 List<QName> contentClasses = getContentClasses();
101 for (QName cClass : contentClass) {
102 if (!contentClasses.contains(cClass))
103 return false;
104 }
105 return true;
106 }
107
108 /** OR */
109 default boolean hasContentClass(QName... contentClass) {
110 List<QName> contentClasses = getContentClasses();
111 for (QName cClass : contentClass) {
112 if (contentClasses.contains(cClass))
113 return true;
114 }
115 return false;
116 }
117
118 /*
119 * DEFAULT METHODS
120 */
121 default <A> A adapt(Class<A> clss) {
122 throw new UnsupportedOperationException("Cannot adapt content " + this + " to " + clss.getName());
123 }
124
125 default <C extends Closeable> C open(Class<C> clss) throws IOException {
126 throw new UnsupportedOperationException("Cannot open content " + this + " as " + clss.getName());
127 }
128
129 default <A> CompletableFuture<A> write(Class<A> clss) {
130 throw new UnsupportedOperationException("Cannot write content " + this + " as " + clss.getName());
131 }
132
133 /*
134 * CHILDREN
135 */
136
137 default boolean hasChild(QName name) {
138 for (Content child : this) {
139 if (child.getName().equals(name))
140 return true;
141 }
142 return false;
143 }
144
145 default Content anyOrAddChild(QName name, QName... classes) {
146 Content child = anyChild(name);
147 if (child != null)
148 return child;
149 return this.add(name, classes);
150 }
151
152 /** Any child with this name, or null if there is none */
153 default Content anyChild(QName name) {
154 for (Content child : this) {
155 if (child.getName().equals(name))
156 return child;
157 }
158 return null;
159 }
160
161 /*
162 * CONVENIENCE METHODS
163 */
164 default String attr(String key) {
165 Object obj = get(key);
166 if (obj == null)
167 return null;
168 return obj.toString();
169
170 }
171
172 default String attr(QName key) {
173 Object obj = get(key);
174 if (obj == null)
175 return null;
176 return obj.toString();
177
178 }
179 //
180 // default String attr(Object key) {
181 // return key != null ? attr(key.toString()) : attr(null);
182 // }
183 //
184 // default <A> A get(Object key, Class<A> clss) {
185 // return key != null ? get(key.toString(), clss) : get(null, clss);
186 // }
187
188 /*
189 * EXPERIMENTAL UNSUPPORTED
190 */
191 default boolean hasText() {
192 return false;
193 }
194
195 default String getText() {
196 throw new UnsupportedOperationException();
197 }
198
199 }