Centralise configure script
[lgpl/argeo-commons.git] / jcr / org.argeo.cms.jcr / src / org / argeo / cms / jcr / acr / JcrContent.java
index 04c5d2d8c3b2f0d32960c52d05e64bc572e70b77..a4af35bc671437ad6b3d9d587817181a9ab7a480 100644 (file)
@@ -1,8 +1,19 @@
 package org.argeo.cms.jcr.acr;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ForkJoinPool;
 
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
@@ -13,93 +24,131 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeType;
 import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
 
 import org.argeo.api.acr.Content;
-import org.argeo.api.acr.spi.AbstractContent;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.spi.ContentProvider;
 import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.acr.AbstractContent;
+import org.argeo.cms.acr.ContentUtils;
 import org.argeo.jcr.Jcr;
 import org.argeo.jcr.JcrException;
+import org.argeo.jcr.JcrUtils;
 
+/** A JCR {@link Node} accessed as {@link Content}. */
 public class JcrContent extends AbstractContent {
-       private Node jcrNode;
+//     private Node jcrNode;
 
        private JcrContentProvider provider;
-       private ProvidedSession session;
 
-       protected JcrContent(ProvidedSession session, JcrContentProvider provider, Node node) {
-               this.session = session;
+       private String jcrWorkspace;
+       private String jcrPath;
+
+       protected JcrContent(ProvidedSession session, JcrContentProvider provider, String jcrWorkspace, String jcrPath) {
+               super(session);
                this.provider = provider;
-               this.jcrNode = node;
+               this.jcrWorkspace = jcrWorkspace;
+               this.jcrPath = jcrPath;
        }
 
        @Override
        public QName getName() {
-               return session.parsePrefixedName(Jcr.getName(jcrNode));
+               String name = Jcr.getName(getJcrNode());
+               if (name.equals("")) {// root
+                       String mountPath = provider.getMountPath();
+                       name = ContentUtils.getParentPath(mountPath)[1];
+                       // name = Jcr.getWorkspaceName(getJcrNode());
+               }
+               return NamespaceUtils.parsePrefixedName(provider, name);
        }
 
+       @SuppressWarnings("unchecked")
        @Override
        public <A> Optional<A> get(QName key, Class<A> clss) {
                if (isDefaultAttrTypeRequested(clss)) {
-                       return Optional.of((A) get(jcrNode, key.toString()));
+                       return Optional.of((A) get(getJcrNode(), key.toString()));
                }
-               return Optional.of((A) Jcr.get(jcrNode, key.toString()));
+               return Optional.of((A) Jcr.get(getJcrNode(), key.toString()));
        }
 
        @Override
        public Iterator<Content> iterator() {
                try {
-                       return new JcrContentIterator(jcrNode.getNodes());
+                       return new JcrContentIterator(getJcrNode().getNodes());
                } catch (RepositoryException e) {
-                       throw new JcrException("Cannot list children of " + jcrNode, e);
+                       throw new JcrException("Cannot list children of " + getJcrNode(), e);
                }
        }
 
        @Override
        protected Iterable<QName> keys() {
-               return new Iterable<QName>() {
-
-                       @Override
-                       public Iterator<QName> iterator() {
-                               try {
-                                       PropertyIterator propertyIterator = jcrNode.getProperties();
-                                       return new JcrKeyIterator(provider, propertyIterator);
-                               } catch (RepositoryException e) {
-                                       throw new JcrException("Cannot retrive properties from " + jcrNode, e);
-                               }
+               try {
+                       Set<QName> keys = new HashSet<>();
+                       for (PropertyIterator propertyIterator = getJcrNode().getProperties(); propertyIterator.hasNext();) {
+                               Property property = propertyIterator.nextProperty();
+                               // TODO convert standard names
+                               // TODO skip technical properties
+                               QName name = NamespaceUtils.parsePrefixedName(provider, property.getName());
+                               keys.add(name);
                        }
-               };
+                       return keys;
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot list properties of " + getJcrNode(), e);
+               }
        }
 
        public Node getJcrNode() {
-               return jcrNode;
+               try {
+                       // TODO caching?
+                       return provider.getJcrSession(getSession(), jcrWorkspace).getNode(jcrPath);
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot retrieve " + jcrPath + " from workspace " + jcrWorkspace, e);
+               }
        }
 
        /** Cast to a standard Java object. */
        static Object get(Node node, String property) {
                try {
-                       Value value = node.getProperty(property).getValue();
-                       switch (value.getType()) {
-                       case PropertyType.STRING:
-                               return value.getString();
-                       case PropertyType.DOUBLE:
-                               return (Double) value.getDouble();
-                       case PropertyType.LONG:
-                               return (Long) value.getLong();
-                       case PropertyType.BOOLEAN:
-                               return (Boolean) value.getBoolean();
-                       case PropertyType.DATE:
-                               Calendar calendar = value.getDate();
-                               return calendar.toInstant();
-                       case PropertyType.BINARY:
-                               throw new IllegalArgumentException("Binary is not supported as an attribute");
-                       default:
-                               return value.getString();
+                       Property p = node.getProperty(property);
+                       if (p.isMultiple()) {
+                               Value[] values = p.getValues();
+                               List<Object> lst = new ArrayList<>();
+                               for (Value value : values) {
+                                       lst.add(convertSingleValue(value));
+                               }
+                               return lst;
+                       } else {
+                               Value value = node.getProperty(property).getValue();
+                               return convertSingleValue(value);
                        }
                } catch (RepositoryException e) {
                        throw new JcrException("Cannot cast value from " + property + " of node " + node, e);
                }
        }
 
+       static Object convertSingleValue(Value value) throws RepositoryException {
+               switch (value.getType()) {
+               case PropertyType.STRING:
+                       return value.getString();
+               case PropertyType.DOUBLE:
+                       return (Double) value.getDouble();
+               case PropertyType.LONG:
+                       return (Long) value.getLong();
+               case PropertyType.BOOLEAN:
+                       return (Boolean) value.getBoolean();
+               case PropertyType.DATE:
+                       Calendar calendar = value.getDate();
+                       return calendar.toInstant();
+               case PropertyType.BINARY:
+                       throw new IllegalArgumentException("Binary is not supported as an attribute");
+               default:
+                       return value.getString();
+               }
+       }
+
        class JcrContentIterator implements Iterator<Content> {
                private final NodeIterator nodeIterator;
                // we keep track in order to be able to delete it
@@ -116,7 +165,7 @@ public class JcrContent extends AbstractContent {
 
                @Override
                public Content next() {
-                       current = new JcrContent(session, provider, nodeIterator.nextNode());
+                       current = new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getPath(nodeIterator.nextNode()));
                        return current;
                }
 
@@ -129,9 +178,31 @@ public class JcrContent extends AbstractContent {
 
        }
 
+       @Override
+       public String getPath() {
+               try {
+                       // Note: it is important to to use the default way (recursing through parents),
+                       // since the session may not have access to parent nodes
+                       return ContentUtils.ROOT_SLASH + jcrWorkspace + getJcrNode().getPath();
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot get depth of " + getJcrNode(), e);
+               }
+       }
+
+       @Override
+       public int getDepth() {
+               try {
+                       return getJcrNode().getDepth() + 1;
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot get depth of " + getJcrNode(), e);
+               }
+       }
+
        @Override
        public Content getParent() {
-               return new JcrContent(session, provider, Jcr.getParent(getJcrNode()));
+               if (Jcr.isRoot(getJcrNode())) // root
+                       return null;
+               return new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getParentPath(getJcrNode()));
        }
 
        @Override
@@ -171,31 +242,146 @@ public class JcrContent extends AbstractContent {
 
        }
 
-       class JcrKeyIterator implements Iterator<QName> {
-               private final JcrContentProvider contentSession;
-               private final PropertyIterator propertyIterator;
+       boolean exists() {
+               try {
+                       return provider.getJcrSession(getSession(), jcrWorkspace).itemExists(jcrPath);
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot check whether " + jcrPath + " exists", e);
+               }
+       }
+
+       /*
+        * ADAPTERS
+        */
+       @SuppressWarnings("unchecked")
+       public <A> A adapt(Class<A> clss) {
+               if (Source.class.isAssignableFrom(clss)) {
+//                     try {
+                       PipedInputStream in = new PipedInputStream();
+
+                       ForkJoinPool.commonPool().execute(() -> {
+                               try (PipedOutputStream out = new PipedOutputStream(in)) {
+                                       provider.getJcrSession(getSession(), jcrWorkspace).exportDocumentView(jcrPath, out, true, false);
+                                       out.flush();
+                               } catch (IOException | RepositoryException e) {
+                                       throw new RuntimeException("Cannot export " + jcrPath + " in workspace " + jcrWorkspace, e);
+                               }
+
+                       });
+                       return (A) new StreamSource(in);
+//                     } catch (IOException e) {
+//                             throw new RuntimeException("Cannot adapt " + JcrContent.this + " to " + clss, e);
+//                     }
+               } else
+
+                       return super.adapt(clss);
+       }
 
-               protected JcrKeyIterator(JcrContentProvider contentSession, PropertyIterator propertyIterator) {
-                       this.contentSession = contentSession;
-                       this.propertyIterator = propertyIterator;
+       @SuppressWarnings("unchecked")
+       @Override
+       public <C extends Closeable> C open(Class<C> clss) throws IOException, IllegalArgumentException {
+               if (InputStream.class.isAssignableFrom(clss)) {
+                       Node node = getJcrNode();
+                       if (Jcr.isNodeType(node, NodeType.NT_FILE)) {
+                               try {
+                                       return (C) JcrUtils.getFileAsStream(node);
+                               } catch (RepositoryException e) {
+                                       throw new JcrException("Cannot open " + jcrPath + " in workspace " + jcrWorkspace, e);
+                               }
+                       }
                }
+               return super.open(clss);
+       }
 
-               @Override
-               public boolean hasNext() {
-                       return propertyIterator.hasNext();
+       @Override
+       public ContentProvider getProvider() {
+               return provider;
+       }
+
+       @Override
+       public String getSessionLocalId() {
+               try {
+                       return getJcrNode().getIdentifier();
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot get identifier for " + getJcrNode(), e);
                }
+       }
 
-               @Override
-               public QName next() {
-                       Property property = null;
-                       try {
-                               property = propertyIterator.nextProperty();
-                               // TODO map standard property names
-                               return session.parsePrefixedName(property.getName());
-                       } catch (RepositoryException e) {
-                               throw new JcrException("Cannot retrieve property " + property, null);
+       /*
+        * TYPING
+        */
+       @Override
+       public List<QName> getContentClasses() {
+               try {
+//                     Node node = getJcrNode();
+//                     List<QName> res = new ArrayList<>();
+//                     res.add(nodeTypeToQName(node.getPrimaryNodeType()));
+//                     for (NodeType mixin : node.getMixinNodeTypes()) {
+//                             res.add(nodeTypeToQName(mixin));
+//                     }
+//                     return res;
+                       Node context = getJcrNode();
+
+                       List<QName> res = new ArrayList<>();
+                       // primary node type
+                       NodeType primaryType = context.getPrimaryNodeType();
+                       res.add(nodeTypeToQName(primaryType));
+
+                       Set<QName> secondaryTypes = new TreeSet<>(NamespaceUtils.QNAME_COMPARATOR);
+                       for (NodeType mixinType : context.getMixinNodeTypes()) {
+                               secondaryTypes.add(nodeTypeToQName(mixinType));
+                       }
+                       for (NodeType superType : primaryType.getDeclaredSupertypes()) {
+                               secondaryTypes.add(nodeTypeToQName(superType));
                        }
+                       // mixins
+                       for (NodeType mixinType : context.getMixinNodeTypes()) {
+                               for (NodeType superType : mixinType.getDeclaredSupertypes()) {
+                                       secondaryTypes.add(nodeTypeToQName(superType));
+                               }
+                       }
+//             // entity type
+//             if (context.isNodeType(EntityType.entity.get())) {
+//                     if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
+//                             String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
+//                             if (byType.containsKey(entityTypeName)) {
+//                                     types.add(entityTypeName);
+//                             }
+//                     }
+//             }
+                       res.addAll(secondaryTypes);
+                       return res;
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot list node types from " + getJcrNode(), e);
                }
+       }
 
+       private QName nodeTypeToQName(NodeType nodeType) {
+               String name = nodeType.getName();
+               return QName.valueOf(name);
        }
+
+       @Override
+       public int getSiblingIndex() {
+               return Jcr.getIndex(getJcrNode());
+       }
+
+       /*
+        * STATIC UTLITIES
+        */
+       public static Content nodeToContent(Node node) {
+               if (node == null)
+                       return null;
+               try {
+                       ProvidedSession contentSession = (ProvidedSession) node.getSession()
+                                       .getAttribute(ProvidedSession.class.getName());
+                       if (contentSession == null)
+                               throw new IllegalArgumentException(
+                                               "Cannot adapt " + node + " to content, because it was not loaded from a content session");
+                       return contentSession.get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + node.getPath());
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot adapt " + node + " to a content", e);
+               }
+       }
+
 }