Clarify ACR API
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / xml / DomContent.java
index 30ecd8e82b649be7251473c204062937f5a3af8c..1d751149101caaabad4e17b697b4bb699de94e9e 100644 (file)
@@ -1,29 +1,40 @@
 package org.argeo.cms.acr.xml;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
 import java.nio.CharBuffer;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ForkJoinPool;
 
 import javax.xml.XMLConstants;
 import javax.xml.namespace.NamespaceContext;
 import javax.xml.namespace.QName;
+import javax.xml.transform.Result;
 import javax.xml.transform.Source;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
 
 import org.argeo.api.acr.Content;
 import org.argeo.api.acr.ContentName;
+import org.argeo.api.acr.CrAttributeType;
+import org.argeo.api.acr.CrName;
 import org.argeo.api.acr.spi.ProvidedContent;
 import org.argeo.api.acr.spi.ProvidedSession;
 import org.argeo.cms.acr.AbstractContent;
-import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.acr.CmsContent;
 import org.w3c.dom.Attr;
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
@@ -55,16 +66,24 @@ public class DomContent extends AbstractContent implements ProvidedContent {
 
        @Override
        public QName getName() {
-               if (element.getParentNode() == null) {// root
+               if (isLocalRoot()) {// root
                        String mountPath = provider.getMountPath();
                        if (mountPath != null) {
+                               if (Content.ROOT_PATH.equals(mountPath)) {
+                                       return CrName.root.qName();
+                               }
                                Content mountPoint = getSession().getMountPoint(mountPath);
-                               return mountPoint.getName();
+                               QName mountPointName = mountPoint.getName();
+                               return mountPointName;
                        }
                }
                return toQName(this.element);
        }
 
+       protected boolean isLocalRoot() {
+               return element.getParentNode() == null || element.getParentNode() instanceof Document;
+       }
+
        protected QName toQName(Node node) {
                String prefix = node.getPrefix();
                if (prefix == null) {
@@ -112,24 +131,25 @@ public class DomContent extends AbstractContent implements ProvidedContent {
                for (int i = 0; i < attributes.getLength(); i++) {
                        Attr attr = (Attr) attributes.item(i);
                        QName key = toQName(attr);
+                       if (key.getNamespaceURI().equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI))
+                               continue;// skip prefix mapping
                        result.add(key);
                }
                return result;
        }
 
-       @SuppressWarnings("unchecked")
+//     @SuppressWarnings("unchecked")
        @Override
        public <A> Optional<A> get(QName key, Class<A> 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();
+//                     if (isDefaultAttrTypeRequested(clss))
+//                             return Optional.of((A) CrAttributeType.parse(value));
+                       return CrAttributeType.cast(clss, value);
                } else
-                       return null;
+                       return Optional.empty();
        }
 
        @Override
@@ -212,15 +232,18 @@ public class DomContent extends AbstractContent implements ProvidedContent {
        @Override
        public Content getParent() {
                Node parentNode = element.getParentNode();
-               if (parentNode == null) {
+               if (isLocalRoot()) {
                        String mountPath = provider.getMountPath();
                        if (mountPath == null)
                                return null;
-                       String[] parent = ContentUtils.getParentPath(mountPath);
+                       if (Content.ROOT_PATH.equals(mountPath)) {
+                               return null;
+                       }
+                       String[] parent = CmsContent.getParentPath(mountPath);
+                       if ("".equals(parent[0]))
+                               return null;
                        return getSession().get(parent[0]);
                }
-               if (parentNode instanceof Document)
-                       return null;
                if (!(parentNode instanceof Element))
                        throw new IllegalStateException("Parent is not an element");
                return new DomContent(this, (Element) parentNode);
@@ -308,7 +331,7 @@ public class DomContent extends AbstractContent implements ProvidedContent {
                                        }
 //                                     parentNode.replaceChild(resultNode, element);
 //                                     element = (Element)resultNode;
-                                       
+
                                } catch (DOMException | TransformerException e) {
                                        throw new RuntimeException("Cannot write to element", e);
                                }
@@ -318,16 +341,78 @@ public class DomContent extends AbstractContent implements ProvidedContent {
                return super.write(clss);
        }
 
+       @SuppressWarnings("unchecked")
+       @Override
+       public <C extends Closeable> C open(Class<C> clss) throws IOException, IllegalArgumentException {
+               if (InputStream.class.isAssignableFrom(clss)) {
+                       PipedOutputStream out = new PipedOutputStream();
+                       ForkJoinPool.commonPool().execute(() -> {
+                               try {
+                                       Source source = new DOMSource(element);
+                                       Result result = new StreamResult(out);
+                                       provider.getTransformerFactory().newTransformer().transform(source, result);
+                                       out.flush();
+                                       out.close();
+                               } catch (TransformerException | IOException e) {
+                                       throw new RuntimeException("Cannot read " + getPath(), e);
+                               }
+                       });
+                       return (C) new PipedInputStream(out);
+               }
+               return super.open(clss);
+       }
+
+       @Override
+       public int getSiblingIndex() {
+               Node curr = element.getPreviousSibling();
+               int count = 1;
+               while (curr != null) {
+                       if (curr instanceof Element) {
+                               if (Objects.equals(curr.getNamespaceURI(), element.getNamespaceURI())
+                                               && Objects.equals(curr.getLocalName(), element.getLocalName())) {
+                                       count++;
+                               }
+                       }
+                       curr = curr.getPreviousSibling();
+               }
+               return count;
+       }
+
        /*
         * TYPING
         */
        @Override
        public List<QName> getContentClasses() {
                List<QName> res = new ArrayList<>();
-               res.add(getName());
+               if (isLocalRoot()) {
+                       String mountPath = provider.getMountPath();
+                       if (Content.ROOT_PATH.equals(mountPath)) {// repository root
+                               res.add(CrName.root.qName());
+                       } else {
+                               Content mountPoint = getSession().getMountPoint(mountPath);
+                               res.addAll(mountPoint.getContentClasses());
+                       }
+               } else {
+                       res.add(getName());
+               }
                return res;
        }
 
+       @Override
+       public void addContentClasses(QName... contentClass) {
+               if (isLocalRoot()) {
+                       String mountPath = provider.getMountPath();
+                       if (Content.ROOT_PATH.equals(mountPath)) {// repository root
+                               throw new IllegalArgumentException("Cannot add content classes to repository root");
+                       } else {
+                               Content mountPoint = getSession().getMountPoint(mountPath);
+                               mountPoint.addContentClasses(contentClass);
+                       }
+               } else {
+                       super.addContentClasses(contentClass);
+               }
+       }
+
        /*
         * MOUNT MANAGEMENT
         */