package org.argeo.cms.gcr.xml; import java.util.HashSet; import java.util.Iterator; import java.util.Optional; import java.util.Set; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; import org.argeo.api.gcr.Content; import org.argeo.api.gcr.ContentName; import org.argeo.api.gcr.spi.AbstractContent; import org.argeo.api.gcr.spi.ProvidedContent; import org.argeo.api.gcr.spi.ProvidedSession; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; public class DomContent extends AbstractContent implements ProvidedContent { private final ProvidedSession session; private final DomContentProvider provider; private final Element element; // private String text = null; private Boolean hasText = null; public DomContent(ProvidedSession session, DomContentProvider contentProvider, Element element) { this.session = session; this.provider = contentProvider; this.element = element; } public DomContent(DomContent context, Element element) { this(context.getSession(), context.getProvider(), element); } @Override public QName getName() { return toQName(this.element); } protected QName toQName(Node node) { String prefix = node.getPrefix(); if (prefix == null) { String namespaceURI = node.getNamespaceURI(); if (namespaceURI == null) namespaceURI = node.getOwnerDocument().lookupNamespaceURI(null); if (namespaceURI == null) { return toQName(node, node.getLocalName()); } else { String contextPrefix = session.getPrefix(namespaceURI); if (contextPrefix == null) throw new IllegalStateException("Namespace " + namespaceURI + " is unbound"); return toQName(node, namespaceURI, node.getLocalName(), session); } } else { String namespaceURI = node.getNamespaceURI(); if (namespaceURI == null) namespaceURI = node.getOwnerDocument().lookupNamespaceURI(prefix); if (namespaceURI == null) { namespaceURI = session.getNamespaceURI(prefix); if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) throw new IllegalStateException("Prefix " + prefix + " is unbound"); // TODO bind the prefix in the document? } return toQName(node, namespaceURI, node.getLocalName(), session); } } protected QName toQName(Node source, String namespaceURI, String localName, NamespaceContext namespaceContext) { return new ContentName(namespaceURI, localName, session); } protected QName toQName(Node source, String localName) { return new ContentName(localName); } /* * ATTRIBUTES OPERATIONS */ @Override public Iterable keys() { // TODO implement an iterator? Set result = new HashSet<>(); NamedNodeMap attributes = element.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Attr attr = (Attr) attributes.item(i); QName key = toQName(attr); result.add(key); } return result; } @Override public Optional get(QName key, Class clss) { String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null : key.getNamespaceURI(); if (element.hasAttributeNS(namespaceUriOrNull, key.getLocalPart())) { String value = element.getAttributeNS(namespaceUriOrNull, key.getLocalPart()); if (clss.isAssignableFrom(String.class)) return Optional.of((A) value); else return Optional.empty(); } else return null; } @Override public Object put(QName key, Object value) { Object previous = get(key); String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null : key.getNamespaceURI(); element.setAttributeNS(namespaceUriOrNull, namespaceUriOrNull == null ? key.getLocalPart() : key.getPrefix() + ":" + key.getLocalPart(), value.toString()); return previous; } @Override public boolean hasText() { // return element instanceof Text; if (hasText != null) return hasText; NodeList nodeList = element.getChildNodes(); if (nodeList.getLength() > 1) { hasText = false; return hasText; } nodes: for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Text) { Text text = (Text) node; if (!text.isElementContentWhitespace()) { hasText = true; break nodes; } } } if (hasText == null) hasText = false; return hasText; // if (text != null) // return true; // text = element.getTextContent(); // return text != null; } @Override public String getText() { if (hasText()) return element.getTextContent(); else return null; } /* * CONTENT OPERATIONS */ @Override public Iterator iterator() { NodeList nodeList = element.getChildNodes(); return new ElementIterator(session, provider, nodeList); } @Override public Content getParent() { Node parent = element.getParentNode(); if (parent == null) return null; if (!(parent instanceof Element)) throw new IllegalStateException("Parent is not an element"); return new DomContent(this, (Element) parent); } @Override public Content add(QName name, QName... classes) { // TODO consider classes Document document = this.element.getOwnerDocument(); String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI()) ? null : name.getNamespaceURI(); Element child = document.createElementNS(namespaceUriOrNull, namespaceUriOrNull == null ? name.getLocalPart() : name.getPrefix() + ":" + name.getLocalPart()); element.appendChild(child); return new DomContent(this, child); } @Override public void remove() { // TODO make it more robust element.getParentNode().removeChild(element); } @Override protected void removeAttr(QName key) { String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null : key.getNamespaceURI(); element.removeAttributeNS(namespaceUriOrNull, namespaceUriOrNull == null ? key.getLocalPart() : key.getPrefix() + ":" + key.getLocalPart()); } public ProvidedSession getSession() { return session; } public DomContentProvider getProvider() { return provider; } }