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/05 15:15:09 UTC
[jackrabbit-filevault] branch master updated: JCRVLT-624 add write methods to DocViewNode2 (#218)
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
The following commit(s) were added to refs/heads/master by this push:
new 415a858c JCRVLT-624 add write methods to DocViewNode2 (#218)
415a858c is described below
commit 415a858cc5816f58b8010172360a69703e58b953
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Tue Apr 5 17:15:04 2022 +0200
JCRVLT-624 add write methods to DocViewNode2 (#218)
This allows to write enhanced docview XML without an underlying
repository.
---
.../vault/fs/impl/io/DocViewSAXFormatter.java | 104 ++++----------------
.../apache/jackrabbit/vault/util/DocViewNode2.java | 108 ++++++++++++++++++++-
.../vault/util/DocViewProperty2Comparator.java | 51 ++++++++++
.../apache/jackrabbit/vault/util/package-info.java | 2 +-
4 files changed, 174 insertions(+), 91 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 5b441b46..166f8766 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;
/**
* Writes the enhanced docview XML based on the aggregate tree to a given {@link XMLStreamWriter}.
@@ -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 = qAttributeName.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 ec35aeaf..e065dabe 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,20 +16,35 @@
************************************************************************/
package org.apache.jackrabbit.vault.util;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
+import java.util.Iterator;
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;
/**
@@ -68,6 +83,32 @@ public class DocViewNode2 {
return new DocViewNode2(name, index, properties);
}
+ @SuppressWarnings("unchecked")
+ public static @NotNull DocViewNode2 fromNode(@NotNull Node node, boolean isRoot, boolean useBinaryReferences) throws RepositoryException {
+ return fromNode(node, isRoot, JcrUtils.in((Iterator<Property>)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));
+ }
+ int index = node.getIndex();
+ if (index == 1) {
+ index = 0; // we use 0 here, if no index necessary
+ }
+ return new DocViewNode2(nodeName, index, docViewProps);
+ }
+
/**
*
* @return the name of the {@link Node} represented by this class
@@ -78,8 +119,8 @@ public class DocViewNode2 {
/**
*
- * @return 0, except if there is a same-name sibling in the docview. In that case the index gives the order of the SNS nodes.
- * @see <a href="https://s.apache.org/jcr-2.0-spec/22_Same-Name_Siblings.html#22.2%20Addressing%20Same-Name%20Siblings%20by%20Path">Same-Name Siblings</a>
+ * @return 0, except if there is a same-name sibling in the docview. In that case the index gives the 1-based order of the SNS nodes.
+ * @see <a href="https://s.apache.org/jcr-2.0-spec/22_Same-Name_Siblings.html#22.2%20Addressing%20Same-Name%20Siblings%20by%20Path">Same-Name Siblings</a>
*/
public int getIndex() {
return index;
@@ -162,7 +203,68 @@ 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}.
+ * Use the following writer for properly formatting the output according to FileVault standards:
+ * {@code FormattingXmlStreamWriter.create(out, new DocViewFormat().getXmlOutputFormat())}.
+ *
+ * @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 = getName().getLocalName();
+ if (getIndex() > 1) {
+ localName += "[" + getIndex() + "]";
+ }
+ String encodedLocalName = ISO9075.encode(localName);
+
+ if (namespaceUri.length()>0) {
+ writer.writeStartElement(nsResolver.getPrefix(namespaceUri), encodedLocalName, namespaceUri);
+ } else {
+ writer.writeStartElement(encodedLocalName);
+ }
+ 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}.
+ * Use the following writer for properly formatting the output according to FileVault standards:
+ * {@code FormattingXmlStreamWriter.create(out, new DocViewFormat().getXmlOutputFormat())}.
+ * @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