]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java
Fix anonymous login
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / xml / DomContent.java
1 package org.argeo.cms.acr.xml;
2
3 import java.nio.CharBuffer;
4 import java.util.HashSet;
5 import java.util.Iterator;
6 import java.util.Optional;
7 import java.util.Set;
8 import java.util.concurrent.CompletableFuture;
9
10 import javax.xml.XMLConstants;
11 import javax.xml.namespace.NamespaceContext;
12 import javax.xml.namespace.QName;
13
14 import org.argeo.api.acr.Content;
15 import org.argeo.api.acr.ContentName;
16 import org.argeo.api.acr.spi.AbstractContent;
17 import org.argeo.api.acr.spi.ProvidedContent;
18 import org.argeo.api.acr.spi.ProvidedSession;
19 import org.argeo.cms.acr.ContentUtils;
20 import org.w3c.dom.Attr;
21 import org.w3c.dom.Document;
22 import org.w3c.dom.Element;
23 import org.w3c.dom.NamedNodeMap;
24 import org.w3c.dom.Node;
25 import org.w3c.dom.NodeList;
26 import org.w3c.dom.Text;
27
28 public class DomContent extends AbstractContent implements ProvidedContent {
29
30 private final ProvidedSession session;
31 private final DomContentProvider provider;
32 private final Element element;
33
34 // private String text = null;
35 private Boolean hasText = null;
36
37 public DomContent(ProvidedSession session, DomContentProvider contentProvider, Element element) {
38 this.session = session;
39 this.provider = contentProvider;
40 this.element = element;
41 }
42
43 public DomContent(DomContent context, Element element) {
44 this(context.getSession(), context.getProvider(), element);
45 }
46
47 @Override
48 public QName getName() {
49 if (element.getParentNode() == null) {// root
50 String mountPath = provider.getMountPath();
51 if (mountPath != null) {
52 Content mountPoint = session.getMountPoint(mountPath);
53 return mountPoint.getName();
54 }
55 }
56 return toQName(this.element);
57 }
58
59 protected QName toQName(Node node) {
60 String prefix = node.getPrefix();
61 if (prefix == null) {
62 String namespaceURI = node.getNamespaceURI();
63 if (namespaceURI == null)
64 namespaceURI = node.getOwnerDocument().lookupNamespaceURI(null);
65 if (namespaceURI == null) {
66 return toQName(node, node.getLocalName());
67 } else {
68 String contextPrefix = provider.getPrefix(namespaceURI);
69 if (contextPrefix == null)
70 throw new IllegalStateException("Namespace " + namespaceURI + " is unbound");
71 return toQName(node, namespaceURI, node.getLocalName(), provider);
72 }
73 } else {
74 String namespaceURI = node.getNamespaceURI();
75 if (namespaceURI == null)
76 namespaceURI = node.getOwnerDocument().lookupNamespaceURI(prefix);
77 if (namespaceURI == null) {
78 namespaceURI = provider.getNamespaceURI(prefix);
79 if (XMLConstants.NULL_NS_URI.equals(namespaceURI))
80 throw new IllegalStateException("Prefix " + prefix + " is unbound");
81 // TODO bind the prefix in the document?
82 }
83 return toQName(node, namespaceURI, node.getLocalName(), provider);
84 }
85 }
86
87 protected QName toQName(Node source, String namespaceURI, String localName, NamespaceContext namespaceContext) {
88 return new ContentName(namespaceURI, localName, namespaceContext);
89 }
90
91 protected QName toQName(Node source, String localName) {
92 return new ContentName(localName);
93 }
94 /*
95 * ATTRIBUTES OPERATIONS
96 */
97
98 @Override
99 public Iterable<QName> keys() {
100 // TODO implement an iterator?
101 Set<QName> result = new HashSet<>();
102 NamedNodeMap attributes = element.getAttributes();
103 for (int i = 0; i < attributes.getLength(); i++) {
104 Attr attr = (Attr) attributes.item(i);
105 QName key = toQName(attr);
106 result.add(key);
107 }
108 return result;
109 }
110
111 @SuppressWarnings("unchecked")
112 @Override
113 public <A> Optional<A> get(QName key, Class<A> clss) {
114 String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null
115 : key.getNamespaceURI();
116 if (element.hasAttributeNS(namespaceUriOrNull, key.getLocalPart())) {
117 String value = element.getAttributeNS(namespaceUriOrNull, key.getLocalPart());
118 if (clss.isAssignableFrom(String.class))
119 return Optional.of((A) value);
120 else
121 return Optional.empty();
122 } else
123 return null;
124 }
125
126 @Override
127 public Object put(QName key, Object value) {
128 Object previous = get(key);
129 String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null
130 : key.getNamespaceURI();
131 element.setAttributeNS(namespaceUriOrNull,
132 namespaceUriOrNull == null ? key.getLocalPart() : key.getPrefix() + ":" + key.getLocalPart(),
133 value.toString());
134 return previous;
135 }
136
137 @Override
138 public boolean hasText() {
139 // return element instanceof Text;
140 if (hasText != null)
141 return hasText;
142 NodeList nodeList = element.getChildNodes();
143 if (nodeList.getLength() > 1) {
144 hasText = false;
145 return hasText;
146 }
147 nodes: for (int i = 0; i < nodeList.getLength(); i++) {
148 Node node = nodeList.item(i);
149 if (node instanceof Text) {
150 Text text = (Text) node;
151 if (!text.isElementContentWhitespace()) {
152 hasText = true;
153 break nodes;
154 }
155 }
156 }
157 if (hasText == null)
158 hasText = false;
159 return hasText;
160 // if (text != null)
161 // return true;
162 // text = element.getTextContent();
163 // return text != null;
164 }
165
166 @Override
167 public String getText() {
168 if (hasText())
169 return element.getTextContent();
170 else
171 return null;
172 }
173
174 /*
175 * CONTENT OPERATIONS
176 */
177
178 @Override
179 public Iterator<Content> iterator() {
180 NodeList nodeList = element.getChildNodes();
181 return new ElementIterator(this, session, provider, nodeList);
182 }
183
184 @Override
185 public Content getParent() {
186 Node parentNode = element.getParentNode();
187 if (parentNode == null) {
188 String mountPath = provider.getMountPath();
189 if (mountPath == null)
190 return null;
191 String[] parent = ContentUtils.getParentPath(mountPath);
192 return session.get(parent[0]);
193 }
194 if (parentNode instanceof Document)
195 return null;
196 if (!(parentNode instanceof Element))
197 throw new IllegalStateException("Parent is not an element");
198 return new DomContent(this, (Element) parentNode);
199 }
200
201 @Override
202 public Content add(QName name, QName... classes) {
203 // TODO consider classes
204 Document document = this.element.getOwnerDocument();
205 String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI()) ? null
206 : name.getNamespaceURI();
207 Element child = document.createElementNS(namespaceUriOrNull,
208 namespaceUriOrNull == null ? name.getLocalPart() : name.getPrefix() + ":" + name.getLocalPart());
209 element.appendChild(child);
210 return new DomContent(this, child);
211 }
212
213 @Override
214 public void remove() {
215 // TODO make it more robust
216 element.getParentNode().removeChild(element);
217
218 }
219
220 @Override
221 protected void removeAttr(QName key) {
222 String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null
223 : key.getNamespaceURI();
224 element.removeAttributeNS(namespaceUriOrNull,
225 namespaceUriOrNull == null ? key.getLocalPart() : key.getPrefix() + ":" + key.getLocalPart());
226
227 }
228
229 @Override
230 public <A> A adapt(Class<A> clss) throws IllegalArgumentException {
231 if (CharBuffer.class.isAssignableFrom(clss)) {
232 String textContent = element.getTextContent();
233 CharBuffer buf = CharBuffer.wrap(textContent);
234 return (A) buf;
235 }
236 return super.adapt(clss);
237 }
238
239 public <A> CompletableFuture<A> write(Class<A> clss) {
240 if (String.class.isAssignableFrom(clss)) {
241 CompletableFuture<String> res = new CompletableFuture<>();
242 res.thenAccept((s) -> {
243 session.notifyModification(this);
244 element.setTextContent(s);
245 });
246 return (CompletableFuture<A>) res;
247 }
248 return super.write(clss);
249 }
250
251 /*
252 * MOUNT MANAGEMENT
253 */
254 @Override
255 public ProvidedContent getMountPoint(String relativePath) {
256 // FIXME use qualified names
257 Element childElement = (Element) element.getElementsByTagName(relativePath).item(0);
258 // TODO check that it is a mount
259 return new DomContent(this, childElement);
260 }
261
262 public ProvidedSession getSession() {
263 return session;
264 }
265
266 public DomContentProvider getProvider() {
267 return provider;
268 }
269
270 }