]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java
Make DOM ACR more robust when used as repository root.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / AbstractContent.java
1 package org.argeo.cms.acr;
2
3 import java.util.AbstractMap;
4 import java.util.AbstractSet;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Optional;
11 import java.util.Set;
12
13 import javax.xml.namespace.QName;
14
15 import org.argeo.api.acr.Content;
16 import org.argeo.api.acr.CrName;
17 import org.argeo.api.acr.NamespaceUtils;
18 import org.argeo.api.acr.spi.ProvidedContent;
19 import org.argeo.api.acr.spi.ProvidedSession;
20 import org.argeo.cms.util.LangUtils;
21
22 /** Partial reference implementation of a {@link ProvidedContent}. */
23 public abstract class AbstractContent extends AbstractMap<QName, Object> implements ProvidedContent {
24 private final ProvidedSession session;
25
26 // cache
27 // private String _path = null;
28
29 public AbstractContent(ProvidedSession session) {
30 this.session = session;
31 }
32
33 /*
34 * ATTRIBUTES OPERATIONS
35 */
36 // protected abstract Iterable<QName> keys();
37 //
38 // protected abstract void removeAttr(QName key);
39
40 @Override
41 public Set<Entry<QName, Object>> entrySet() {
42 Set<Entry<QName, Object>> result = new AttrSet();
43 return result;
44 }
45
46 @Override
47 public Class<?> getType(QName key) {
48 return String.class;
49 }
50
51 @Override
52 public boolean isMultiple(QName key) {
53 return false;
54 }
55
56 @SuppressWarnings("unchecked")
57 @Override
58 public <A> List<A> getMultiple(QName key, Class<A> clss) {
59 Object value = get(key);
60 if (value == null)
61 return new ArrayList<>();
62 if (value instanceof List) {
63 if (isDefaultAttrTypeRequested(clss))
64 return (List<A>) value;
65 List<A> res = new ArrayList<>();
66 List<?> lst = (List<?>) value;
67 for (Object o : lst) {
68 A item = clss.isAssignableFrom(String.class) ? (A) o.toString() : (A) o;
69 res.add(item);
70 }
71 return res;
72 } else {// singleton
73 // try {
74 A res = (A) value;
75 return Collections.singletonList(res);
76 // } catch (ClassCastException e) {
77 // return Optional.empty();
78 // }
79 }
80 }
81
82 /*
83 * CONTENT OPERATIONS
84 */
85
86 @Override
87 public String getPath() {
88 // if (_path != null)
89 // return _path;
90 List<Content> ancestors = new ArrayList<>();
91 collectAncestors(ancestors, this);
92 StringBuilder path = new StringBuilder();
93 ancestors: for (Content c : ancestors) {
94 QName name = c.getName();
95 if (CrName.root.qName().equals(name))
96 continue ancestors;
97
98 path.append('/');
99 path.append(NamespaceUtils.toPrefixedName(name));
100 int siblingIndex = c.getSiblingIndex();
101 if (siblingIndex != 1)
102 path.append('[').append(siblingIndex).append(']');
103 }
104 // _path = path.toString();
105 // return _path;
106 return path.toString();
107 }
108
109 private void collectAncestors(List<Content> ancestors, Content content) {
110 if (content == null)
111 return;
112 ancestors.add(0, content);
113 collectAncestors(ancestors, content.getParent());
114 }
115
116 @Override
117 public int getDepth() {
118 List<Content> ancestors = new ArrayList<>();
119 collectAncestors(ancestors, this);
120 return ancestors.size();
121 }
122
123 @Override
124 public boolean isRoot() {
125 return CrName.root.qName().equals(getName());
126 }
127
128 @Override
129 public String getSessionLocalId() {
130 return getPath();
131 }
132
133 /*
134 * SESSION
135 */
136
137 @Override
138 public ProvidedSession getSession() {
139 return session;
140 }
141
142 /*
143 * TYPING
144 */
145
146 @Override
147 public List<QName> getContentClasses() {
148 return new ArrayList<>();
149 }
150
151 /*
152 * UTILITIES
153 */
154 protected boolean isDefaultAttrTypeRequested(Class<?> clss) {
155 // check whether clss is Object.class
156 return clss.isAssignableFrom(Object.class);
157 }
158
159 // @Override
160 // public String toString() {
161 // return "content " + getPath();
162 // }
163
164 /*
165 * DEFAULTS
166 */
167 // - no children
168 // - no attributes
169 // - cannot be modified
170 @Override
171 public Iterator<Content> iterator() {
172 return Collections.emptyIterator();
173 }
174
175 @Override
176 public Content add(QName name, QName... classes) {
177 throw new UnsupportedOperationException("Content cannot be added.");
178 }
179
180 @Override
181 public void remove() {
182 throw new UnsupportedOperationException("Content cannot be removed.");
183 }
184
185 protected Iterable<QName> keys() {
186 return Collections.emptySet();
187 }
188
189 @Override
190 public <A> Optional<A> get(QName key, Class<A> clss) {
191 return null;
192 }
193
194 protected void removeAttr(QName key) {
195 throw new UnsupportedOperationException("Attributes cannot be removed.");
196 }
197
198 /*
199 * SUB CLASSES
200 */
201
202 class AttrSet extends AbstractSet<Entry<QName, Object>> {
203
204 @Override
205 public Iterator<Entry<QName, Object>> iterator() {
206 final Iterator<QName> keys = keys().iterator();
207 Iterator<Entry<QName, Object>> it = new Iterator<Map.Entry<QName, Object>>() {
208
209 QName key = null;
210
211 @Override
212 public boolean hasNext() {
213 return keys.hasNext();
214 }
215
216 @Override
217 public Entry<QName, Object> next() {
218 key = keys.next();
219 // TODO check type
220 Optional<?> value = get(key, Object.class);
221 assert !value.isEmpty();
222 AbstractMap.SimpleEntry<QName, Object> entry = new SimpleEntry<>(key, value.get());
223 return entry;
224 }
225
226 @Override
227 public void remove() {
228 if (key != null) {
229 AbstractContent.this.removeAttr(key);
230 } else {
231 throw new IllegalStateException("Iteration has not started");
232 }
233 }
234
235 };
236 return it;
237 }
238
239 @Override
240 public int size() {
241 return LangUtils.size(keys());
242 }
243
244 }
245
246 /*
247 * OBJECT METHODS
248 */
249 @Override
250 public String toString() {
251 return "content " + getPath();
252 }
253 }