You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2022/04/04 18:51:18 UTC

[jackrabbit-filevault] branch feature/JCRVLT-624-add-write-capability-to-docviewnode2 created (now c5c993d9)

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a change to branch feature/JCRVLT-624-add-write-capability-to-docviewnode2
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git


      at c5c993d9 JCRVLT-624 add write methods to DocViewNode2

This branch includes the following new commits:

     new c5c993d9 JCRVLT-624 add write methods to DocViewNode2

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[jackrabbit-filevault] 01/01: JCRVLT-624 add write methods to DocViewNode2

Posted by kw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/JCRVLT-624-add-write-capability-to-docviewnode2
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git

commit c5c993d9a2bd27f3206e404805ae210d4829d352
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Mon Apr 4 20:51:08 2022 +0200

    JCRVLT-624 add write methods to DocViewNode2
    
    This allows to write enhanced docview XML without an underlying
    repository.
---
 .../vault/fs/impl/io/DocViewSAXFormatter.java      | 104 ++++-----------------
 .../apache/jackrabbit/vault/util/DocViewNode2.java |  89 +++++++++++++++++-
 .../vault/util/DocViewProperty2Comparator.java     |  51 ++++++++++
 .../apache/jackrabbit/vault/util/package-info.java |   2 +-
 .../vault/fs/impl/io/DocViewSaxFormatterIT.java    |   2 +
 5 files changed, 159 insertions(+), 89 deletions(-)

diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java
index b5e40a7b..5153fc4c 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java
@@ -18,8 +18,10 @@
 package org.apache.jackrabbit.vault.fs.impl.io;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -33,20 +35,13 @@ import javax.xml.stream.XMLStreamWriter;
 
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
-import org.apache.jackrabbit.spi.commons.conversion.NameException;
-import org.apache.jackrabbit.spi.commons.conversion.NameParser;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
-import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
 import org.apache.jackrabbit.spi.commons.namespace.SessionNamespaceResolver;
-import org.apache.jackrabbit.util.ISO9075;
-import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.vault.fs.api.Aggregate;
 import org.apache.jackrabbit.vault.fs.api.VaultFsConfig;
-import org.apache.jackrabbit.vault.util.DocViewProperty2;
-import org.apache.jackrabbit.vault.util.ItemNameComparator2;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
 import org.apache.jackrabbit.vault.util.JcrConstants;
-import org.xml.sax.SAXException;
 
 /**
  * The docview sax formatter generates SAX events to a given ContentHandler based on the aggregate tree.
@@ -125,15 +120,12 @@ public class DocViewSAXFormatter implements AggregateWalkListener {
         IGNORED_PROTECTED_PROPERTIES = Collections.unmodifiableSet(props);
     }
 
-    private ItemNameComparator2 itemNameComparator;
-
     protected DocViewSAXFormatter(Aggregate aggregate, XMLStreamWriter writer)
             throws RepositoryException {
 
         this.aggregate = aggregate;
         this.session = aggregate.getNode().getSession();
         nsResolver = new SessionNamespaceResolver(session);
-        itemNameComparator = new ItemNameComparator2(nsResolver);
         this.writer = writer;
 
         DefaultNamePathResolver npResolver = new DefaultNamePathResolver(nsResolver);
@@ -155,39 +147,6 @@ public class DocViewSAXFormatter implements AggregateWalkListener {
         useBinaryReferences = "true".equals(aggregate.getManager().getConfig().getProperty(VaultFsConfig.NAME_USE_BINARY_REFERENCES));
     }
 
-    /**
-     * Starts namespace declarations
-     *
-     * @throws RepositoryException if a repository error occurs
-     * @throws SAXException if the underlying content handler throws a sax exception
-     */
-    private void startNamespaceDeclarations() throws RepositoryException, XMLStreamException {
-        // always include jcr namespace (see JCRVLT-266)
-        
-        writer.writeNamespace(Name.NS_JCR_PREFIX, Name.NS_JCR_URI);
-
-        for (String prefix: aggregate.getNamespacePrefixes()) {
-            if (Name.NS_XML_PREFIX.equals(prefix)) {
-                // skip 'xml' prefix as this would be an illegal namespace declaration
-                continue;
-            }
-            if (Name.NS_JCR_PREFIX.equals(prefix)) {
-                continue;
-            }
-            writer.writeNamespace(prefix, aggregate.getNamespaceURI(prefix));
-        }
-    }
-
-    private Name getQName(String rawName) throws RepositoryException {
-        try {
-            return NameParser.parse(rawName, nsResolver, NameFactoryImpl.getInstance());
-        } catch (NameException e) {
-            // should never get here...
-            String msg = "internal error: failed to resolve namespace mappings";
-            throw new RepositoryException(msg, e);
-        }
-    }
-
     /**
      * {@inheritDoc}
      */
@@ -228,45 +187,18 @@ public class DocViewSAXFormatter implements AggregateWalkListener {
      */
     @Override
     public void onChildren(Node node, int level) throws RepositoryException {
-        String label = Text.getName(node.getPath());
-        final String elemName;
-        if (level == 0) {
-            // root node needs a special name
-            elemName = jcrRoot;
-        } else {
-            // encode node name to make sure it's a valid xml name
-            elemName = ISO9075.encode(label);
-        }
-
-        Collections.sort(props, itemNameComparator);
-
-        // start element (node)
-        Name qName = getQName(elemName);
         try {
-            // with namespace?
-            String namespaceUri = qName.getNamespaceURI();
-            if (namespaceUri.length()>0) {
-                writer.writeStartElement(nsResolver.getPrefix(namespaceUri), qName.getLocalName(), namespaceUri);
-            } else {
-                writer.writeStartElement(qName.getLocalName());
-            }
+            DocViewNode2 docViewNode = DocViewNode2.fromNode(node, level == 0, props, useBinaryReferences);
+            final Set<String> namespacePrefixes;
             if (level == 0) {
-                startNamespaceDeclarations();
-            }
-            for (Property prop: props) {
-                // attribute name (encode property name to make sure it's a valid xml name)
-                String attrName = ISO9075.encode(prop.getName());
-                Name qAttributeName = getQName(attrName);
-                boolean sort = qName.equals(NameConstants.JCR_MIXINTYPES);
-                String attributeNamespaceUri = qAttributeName.getNamespaceURI();
-                if (attributeNamespaceUri.length()>0) {
-                    writer.writeAttribute(nsResolver.getPrefix(attributeNamespaceUri), attributeNamespaceUri, qAttributeName.getLocalName(), 
-                            DocViewProperty2.format(prop, sort, useBinaryReferences));
-                } else {
-                    writer.writeAttribute(qAttributeName.getLocalName(), DocViewProperty2.format(prop, sort, useBinaryReferences));
-                }
-               
+                namespacePrefixes = new LinkedHashSet<>();
+                // always include jcr namespace (see JCRVLT-266)
+                namespacePrefixes.add(Name.NS_JCR_PREFIX);
+                namespacePrefixes.addAll(Arrays.asList(aggregate.getNamespacePrefixes()));
+            } else {
+                namespacePrefixes = Collections.emptySet();
             }
+            docViewNode.writeStart(writer, nsResolver, namespacePrefixes);
         } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
@@ -279,7 +211,7 @@ public class DocViewSAXFormatter implements AggregateWalkListener {
     public void onNodeEnd(Node node, boolean included, int level) throws RepositoryException {
         // end element (node)
         try {
-            writer.writeEndElement();
+            DocViewNode2.writeEnd(writer);
         } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
@@ -302,13 +234,11 @@ public class DocViewSAXFormatter implements AggregateWalkListener {
      */
     @Override
     public void onNodeIgnored(Node node, int depth) throws RepositoryException {
-        // just add an empty node. used for ordering
-        String label = Text.getName(node.getPath());
-        String elemName = ISO9075.encode(label);
-        Name qName = getQName(elemName);
         try {
-            writer.writeStartElement(nsResolver.getPrefix(qName.getNamespaceURI()), qName.getLocalName(), qName.getNamespaceURI());
-            writer.writeEndElement();
+            // just add an empty node. used for ordering
+            DocViewNode2 docViewNode = DocViewNode2.fromNode(node, false, Collections.emptyList(), useBinaryReferences);
+            docViewNode.writeStart(writer, nsResolver, Collections.emptyList());
+            DocViewNode2.writeEnd(writer);
         } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode2.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode2.java
index 30963929..22221b29 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode2.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode2.java
@@ -16,19 +16,33 @@
  ************************************************************************/
 package org.apache.jackrabbit.vault.util;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import javax.jcr.NamespaceException;
 import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
+import org.apache.jackrabbit.commons.JcrUtils;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
+import org.apache.jackrabbit.spi.commons.conversion.ParsingNameResolver;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.namespace.SessionNamespaceResolver;
+import org.apache.jackrabbit.util.ISO9075;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -62,6 +76,28 @@ public class DocViewNode2 {
         return new DocViewNode2(name, index, properties);
     }
 
+    public static @NotNull DocViewNode2 fromNode(@NotNull Node node, boolean isRoot, boolean useBinaryReferences) throws RepositoryException {
+        return fromNode(node, isRoot, JcrUtils.in(node.getProperties()), useBinaryReferences);
+    }
+
+    public static @NotNull DocViewNode2 fromNode(@NotNull Node node, boolean isRoot, @NotNull Iterable<Property> properties, boolean useBinaryReferences) throws RepositoryException {
+        NameResolver nameResolver = new ParsingNameResolver(NameFactoryImpl.getInstance(), new SessionNamespaceResolver(node.getSession()));
+        final Name nodeName;
+        if (isRoot) {
+            nodeName = NameConstants.JCR_ROOT;
+        } else {
+            nodeName = nameResolver.getQName(node.getName());
+        }
+        Collection<DocViewProperty2> docViewProps = new ArrayList<>();
+        for (Property property : properties) {
+            Name propertyName =  nameResolver.getQName(property.getName());
+            boolean sort = propertyName.equals(NameConstants.JCR_MIXINTYPES);
+            docViewProps.add(DocViewProperty2.fromProperty(property, sort, useBinaryReferences));
+        }
+        return new DocViewNode2(nodeName, 0, docViewProps);
+    }
+    
+
     /** 
      * 
      * @return the name of the {@link Node} represented by this class
@@ -156,7 +192,58 @@ public class DocViewNode2 {
         return index == other.index && areNamesEqual(name, other.name) && Objects.equals(properties, other.properties);
     }
 
-    static boolean areNamesEqual(@NotNull Name name,  @NotNull Name otherName ) {
+    static boolean areNamesEqual(@NotNull Name name, @NotNull Name otherName) {
         return Objects.equals(name.getLocalName(), otherName.getLocalName()) && Objects.equals(name.getNamespaceURI(), otherName.getNamespaceURI());
     }
+
+    /**
+     * Writes the node's start tag (including the attributes for the properties and optionally the namespace declarations) to the given {@link XMLStreamWriter}.
+     * @param writer the XMLStreamWriter to write to
+     * @param nsResolver the namespace resolver to use for retrieving prefixes for namespace URIs of {@link #getName()} and {@link DocViewProperty2#getName()}
+     * @param namespacePrefixes the namespace prefixes for which to emit namespace declarations in this node
+     * @throws NamespaceException in case no prefix is defined for the namespace URI of a name (either node's or property's)
+     * @throws XMLStreamException
+     * @since 3.6.2
+     */
+    public void writeStart(@NotNull XMLStreamWriter writer, @NotNull NamespaceResolver nsResolver, @NotNull Iterable<String> namespacePrefixes) throws NamespaceException, XMLStreamException {
+        // sort properties
+        Set<DocViewProperty2> props = new TreeSet<>(new DocViewProperty2Comparator(nsResolver));
+        props.addAll(properties.values());
+        // start element (node)
+        // with namespace?
+        String namespaceUri = getName().getNamespaceURI();
+        String localName = ISO9075.encode(getName().getLocalName());
+        if (namespaceUri.length()>0) {
+            writer.writeStartElement(nsResolver.getPrefix(namespaceUri), localName, namespaceUri);
+        } else {
+            writer.writeStartElement(localName);
+        }
+        for (String namespacePrefix : namespacePrefixes) {
+            if (Name.NS_XML_PREFIX.equals(namespacePrefix)) {
+                // skip 'xml' prefix as this would be an illegal namespace declaration
+                continue;
+            }
+            writer.writeNamespace(namespacePrefix, nsResolver.getURI(namespacePrefix));
+        }
+        for (DocViewProperty2 prop: props) {
+            String attributeLocalName = ISO9075.encode(prop.getName().getLocalName());
+            String attributeNamespaceUri = prop.getName().getNamespaceURI();
+            if (attributeNamespaceUri.length()>0) {
+                writer.writeAttribute(nsResolver.getPrefix(attributeNamespaceUri), attributeNamespaceUri, attributeLocalName, 
+                        prop.formatValue());
+            } else {
+                writer.writeAttribute(attributeLocalName, prop.formatValue());
+            }
+        }
+    }
+
+    /**
+     * Writes the node's end tag to the given {@link XMLStreamWriter}.
+     * @param writer the XMLStreamWriter to write to
+     * @throws XMLStreamException
+     * @since 3.6.2
+     */
+    public static void writeEnd(@NotNull XMLStreamWriter writer) throws XMLStreamException {
+        writer.writeEndElement();
+    }
 }
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2Comparator.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2Comparator.java
new file mode 100644
index 00000000..29bba1ca
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2Comparator.java
@@ -0,0 +1,51 @@
+/*************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ************************************************************************/
+package org.apache.jackrabbit.vault.util;
+
+import java.util.Comparator;
+
+import javax.jcr.RepositoryException;
+import javax.xml.namespace.QName;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+
+/** 
+ * Similar to {@link QNameComparator} but acting on {@link DocViewProperty2} leveraging its name and a given namespace resolver
+ */
+public final class DocViewProperty2Comparator implements Comparator<DocViewProperty2> {
+
+    private final NamespaceResolver nsResolver;
+
+    public DocViewProperty2Comparator(NamespaceResolver nsResolver) {
+        this.nsResolver = nsResolver;
+    }
+
+    private QName getQName(Name name) throws RepositoryException {
+        return new QName(name.getNamespaceURI(), name.getLocalName(), nsResolver.getPrefix(name.getNamespaceURI()));
+    }
+
+    @Override
+    public int compare(DocViewProperty2 o1, DocViewProperty2 o2) {
+        try {
+            return QNameComparator.INSTANCE.compare(getQName(o1.getName()), getQName(o2.getName()));
+        } catch (RepositoryException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java
index 82c0e8b0..27fff62d 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-@Version("2.9.0")
+@Version("2.10.0")
 package org.apache.jackrabbit.vault.util;
 
 import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSaxFormatterIT.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSaxFormatterIT.java
index 98b3cc37..ee8c8901 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSaxFormatterIT.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSaxFormatterIT.java
@@ -143,4 +143,6 @@ public class DocViewSaxFormatterIT extends IntegrationTestBase {
                 "    testproperty=\"lowercase\"/>\n", out.toString("utf-8"));
     }
 
+    // TODO: Test with complex node names and property names and non-string properties (binary references)
+    
 }