jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From k...@apache.org
Subject svn commit: r1871734 [1/2] - in /jackrabbit/commons/filevault/trunk: parent/ vault-core/ vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/ vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ vault-core/src/main/java/org/apach...
Date Wed, 18 Dec 2019 09:28:26 GMT
Author: kwin
Date: Wed Dec 18 09:28:26 2019
New Revision: 1871734

URL: http://svn.apache.org/viewvc?rev=1871734&view=rev
Log:
JCRVLT-391 replace forked Xerces classes by StAX

Maximum line length is no longer available, apart from that the format
is the same as before.

Added:
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/QNameComparator.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/FormattingXmlStreamWriter.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/NormalizingSaxFilter.java
    jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/util/QNameComparatorTest.java
Removed:
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FilteredXMLSerializer.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/AttributeNameComparator.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/BaseMarkupSerializer.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/DOMSerializer.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/ElementState.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/EncodingInfo.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/Encodings.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/IndentPrinter.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/LineSeparator.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/Method.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/Printer.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/Serializer.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/XMLSerializer.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/dom/DOMErrorImpl.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/dom/DOMLocatorImpl.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/dom/DOMMessageFormatter.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/dom/package-info.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/util/EncodingMap.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/util/NamespaceSupport.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/util/SymbolTable.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/util/XMLChar.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/util/XMLSymbols.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/util/package-info.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/xni/NamespaceContext.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/xni/XMLLocator.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/xni/XNIException.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/xni/package-info.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/xni/parser/XMLParseException.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/xerces/xni/parser/package-info.java
Modified:
    jackrabbit/commons/filevault/trunk/parent/pom.xml
    jackrabbit/commons/filevault/trunk/vault-core/pom.xml
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/AbstractConfig.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/CredentialsConfig.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/DefaultWorkspaceFilter.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/SimpleCredentialsConfig.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultAuthConfig.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultSettings.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSerializer.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewFormat.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/ItemNameComparator.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/OutputFormat.java
    jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/DocViewFormatTest.java
    jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/TestFSInstallState.java
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/DocViewFormat/formatted.xml
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/DocViewFormat/malformed.xml
    jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntries.java
    jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntry.java
    jackrabbit/commons/filevault/trunk/vault-vlt/src/main/java/org/apache/jackrabbit/vault/vlt/meta/xml/XmlEntryInfo.java

Modified: jackrabbit/commons/filevault/trunk/parent/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/parent/pom.xml?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/parent/pom.xml (original)
+++ jackrabbit/commons/filevault/trunk/parent/pom.xml Wed Dec 18 09:28:26 2019
@@ -157,7 +157,7 @@ Apache Jackrabbit FileVault is a project
                 <plugin>
                     <groupId>org.apache.felix</groupId>
                     <artifactId>maven-bundle-plugin</artifactId>
-                    <version>3.3.0</version>
+                    <version>4.2.1</version>
                     <inherited>true</inherited>
                     <configuration>
                         <outputDirectory>${basedir}/target/classes</outputDirectory>
@@ -220,6 +220,7 @@ Apache Jackrabbit FileVault is a project
                                 <value>target/derby.log</value>
                             </property>
                         </systemProperties>
+                        <trimStackTrace>false</trimStackTrace>
                     </configuration>
                 </plugin>
                 <!-- ====================================================================== -->

Modified: jackrabbit/commons/filevault/trunk/vault-core/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/pom.xml?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/pom.xml (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/pom.xml Wed Dec 18 09:28:26 2019
@@ -83,7 +83,10 @@
                         </Import-Package>
                         <!-- to load hook classes through this bundle's classloader -->
                         <DynamicImport-Package>*</DynamicImport-Package>
-                        
+                        <!-- embed classes from jaxb-impl (https://bnd.bndtools.org/instructions/conditionalpackage.html) -->
+                        <Conditional-Package>com.sun.xml.txw2.output</Conditional-Package>
+                        <Embed-Dependency>woodstox-core,stax2-api</Embed-Dependency>
+                        <Embed-Transitive>true</Embed-Transitive>
                     </instructions>
                 </configuration>
             </plugin>
@@ -156,7 +159,20 @@
             <version>4.3.0</version>
             <scope>provided</scope>
         </dependency>
-
+    
+        <!-- StaX implementation for indentation -->
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>txw2</artifactId>
+            <version>2.3.2</version>
+        </dependency>
+        <!-- embed -->
+        <dependency>
+            <groupId>com.fasterxml.woodstox</groupId>
+            <artifactId>woodstox-core</artifactId>
+            <version>6.0.2</version>
+        </dependency>
+        
         <!-- SLF4j / Log4j -->
         <dependency>
             <groupId>org.slf4j</groupId>

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/AbstractConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/AbstractConfig.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/AbstractConfig.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/AbstractConfig.java Wed Dec 18 09:28:26 2019
@@ -25,20 +25,20 @@ import java.io.OutputStream;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.vault.util.RejectingEntityResolver;
+import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
 import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
-import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
-import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
 
 /**
  * {@code VaultUserConfig}...
@@ -123,14 +123,11 @@ abstract public class AbstractConfig {
     }
     
     public void save(OutputStream out) throws IOException {
-        OutputFormat fmt = new OutputFormat("xml", "UTF-8", true);
-        fmt.setLineWidth(0);
-        fmt.setIndent(2);
-        XMLSerializer ser = new XMLSerializer(out, fmt);
-        try {
-            write(ser);
-        } catch (SAXException e) {
-            throw new IOException(e.toString());
+        OutputFormat fmt = new OutputFormat(2, false);
+        try (FormattingXmlStreamWriter writer = FormattingXmlStreamWriter.create(out, fmt)){
+            write(writer);
+        } catch (XMLStreamException e) {
+            throw new IOException(e.toString(), e);
         }
     }
 
@@ -146,15 +143,14 @@ abstract public class AbstractConfig {
         return configDir;
     }
 
-    protected void write(ContentHandler handler) throws SAXException {
-        handler.startDocument();
-        AttributesImpl attrs = new AttributesImpl();
-        attrs.addAttribute("", ATTR_VERSION, "", "CDATA", String.valueOf(version));
-        handler.startElement("", getRootElemName(), "", attrs);
-        doWrite(handler);
-        handler.endElement("", getRootElemName(), "");
-        handler.endDocument();
+    protected void write(XMLStreamWriter writer) throws XMLStreamException {
+        writer.writeStartDocument();
+        writer.writeStartElement(getRootElemName());
+        writer.writeAttribute(ATTR_VERSION, String.valueOf(version));
+        doWrite(writer);
+        writer.writeEndElement();
+        writer.writeEndDocument();
     }
 
-    abstract protected void doWrite(ContentHandler handler) throws SAXException;
+    abstract protected void doWrite(XMLStreamWriter writer) throws XMLStreamException;
 }
\ No newline at end of file

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/CredentialsConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/CredentialsConfig.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/CredentialsConfig.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/CredentialsConfig.java Wed Dec 18 09:28:26 2019
@@ -18,6 +18,8 @@
 package org.apache.jackrabbit.vault.fs.config;
 
 import javax.jcr.Credentials;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
 import org.w3c.dom.Element;
 import org.xml.sax.ContentHandler;
@@ -50,13 +52,12 @@ public abstract class CredentialsConfig
 
     public abstract Credentials getCredentials();
 
-    public void write(ContentHandler handler) throws SAXException {
-        AttributesImpl attrs = new AttributesImpl();
-        attrs.addAttribute("", ATTR_TYPE, "", "CDATA", type);
-        handler.startElement("", ELEM_CREDETIALS, "", attrs);
-        writeInner(handler);
-        handler.endElement("", ELEM_CREDETIALS, "");
+    public void write(XMLStreamWriter writer) throws XMLStreamException {
+        writer.writeStartElement(ELEM_CREDETIALS);
+        writer.writeAttribute(ATTR_TYPE, type);
+        writeInner(writer);
+        writer.writeEndElement();
     }
 
-    protected abstract void writeInner(ContentHandler handler) throws SAXException;
+    protected abstract void writeInner(XMLStreamWriter writer) throws XMLStreamException;
 }
\ No newline at end of file

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/DefaultWorkspaceFilter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/DefaultWorkspaceFilter.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/DefaultWorkspaceFilter.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/DefaultWorkspaceFilter.java Wed Dec 18 09:28:26 2019
@@ -37,6 +37,7 @@ import javax.jcr.Session;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLStreamException;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.util.Text;
@@ -53,8 +54,8 @@ import org.apache.jackrabbit.vault.fs.fi
 import org.apache.jackrabbit.vault.fs.spi.ProgressTracker;
 import org.apache.jackrabbit.vault.util.RejectingEntityResolver;
 import org.apache.jackrabbit.vault.util.Tree;
+import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
 import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
-import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -62,7 +63,6 @@ import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
 
 /**
  * Holds a list of {@link PathFilterSet}s.
@@ -488,53 +488,51 @@ public class DefaultWorkspaceFilter impl
 
 
     private void generateSource() {
-        try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            XMLSerializer ser = new XMLSerializer(out, new OutputFormat("xml", "UTF-8", true));
-            ser.startDocument();
-            AttributesImpl attrs = new AttributesImpl();
-            attrs.addAttribute(null, null, ATTR_VERSION, "CDATA", String.valueOf(version));
-            ser.startElement(null, null, "workspaceFilter", attrs);
+        try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+             FormattingXmlStreamWriter writer = FormattingXmlStreamWriter.create(out, new OutputFormat(4, false))) {
+            
+            writer.writeStartDocument();
+            writer.writeStartElement("workspaceFilter");
+            writer.writeAttribute(ATTR_VERSION, String.valueOf(version));
 
             if (referenceFilterSets == null) {
                 referenceFilterSets = new LinkedList<>(nodesFilterSets);
             }
             for (PathFilterSet set: referenceFilterSets) {
-                attrs = new AttributesImpl();
-                attrs.addAttribute(null, null, "root", "CDATA", set.getRoot());
+                writer.writeStartElement("filter");
+                writer.writeAttribute("root", set.getRoot());
                 if (set.getImportMode() != ImportMode.REPLACE) {
-                    attrs.addAttribute(null, null, "mode", "CDATA", set.getImportMode().name().toLowerCase());
+                    writer.writeAttribute("mode", set.getImportMode().name().toLowerCase());
                 }
                 if (set.getType() != null) {
-                    attrs.addAttribute(null, null, "type", "CDATA", set.getType());
+                    writer.writeAttribute("type", set.getType());
                 }
-                ser.startElement(null, null, "filter", attrs);
                 for (PathFilterSet.Entry<PathFilter> entry: set.getEntries()) {
                     // only handle path filters
                     PathFilter filter = entry.getFilter();
                     if (filter instanceof DefaultPathFilter) {
-                        attrs = new AttributesImpl();
-                        attrs.addAttribute(null, null, "pattern", "CDATA", ((DefaultPathFilter) filter).getPattern());
-                        if (filter instanceof DefaultPropertyPathFilter) {
-                            attrs.addAttribute(null, null, "matchProperties", "CDATA", "true");
-                        }
                         if (entry.isInclude()) {
-                            ser.startElement(null, null, "include", attrs);
-                            ser.endElement("include");
+                            writer.writeStartElement("include");
                         } else {
-                            ser.startElement(null, null, "exclude", attrs);
-                            ser.endElement("exclude");
+                            writer.writeStartElement("exclude");
                         }
+                        writer.writeAttribute("pattern", ((DefaultPathFilter) filter).getPattern());
+                        if (filter instanceof DefaultPropertyPathFilter) {
+                            writer.writeAttribute("matchProperties", "true");
+                        }
+                        writer.writeEndElement();
                     } else {
                         throw new IllegalArgumentException("Can only export default path filters, yet.");
                     }
                 }
-                ser.endElement("filter");
+                writer.writeEndElement();
             }
-            ser.endElement("workspaceFilter");
-            ser.endDocument();
+            writer.writeEndElement();
+            writer.writeEndDocument();
             source = out.toByteArray();
-        } catch (SAXException e) {
+        } catch (XMLStreamException e) {
+            throw new IllegalStateException(e);
+        } catch (IOException e) {
             throw new IllegalStateException(e);
         }
     }

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/SimpleCredentialsConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/SimpleCredentialsConfig.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/SimpleCredentialsConfig.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/SimpleCredentialsConfig.java Wed Dec 18 09:28:26 2019
@@ -25,6 +25,8 @@ import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 import javax.jcr.Credentials;
 import javax.jcr.SimpleCredentials;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
 import org.apache.jackrabbit.vault.util.Text;
 import org.slf4j.Logger;
@@ -92,13 +94,13 @@ public class SimpleCredentialsConfig ext
         throw new ConfigurationException("mandatory element <user> missing.");
     }
 
-    public void writeInner(ContentHandler handler) throws SAXException {
+    @Override
+    protected void writeInner(XMLStreamWriter writer) throws XMLStreamException {
         if (creds != null) {
-            AttributesImpl attrs = new AttributesImpl();
-            attrs.addAttribute("", ATTR_NAME, "", "CDATA", creds.getUserID());
-            attrs.addAttribute("", ATTR_PASSWORD, "", "CDATA", encrypt(new String(creds.getPassword())));
-            handler.startElement("", ELEM_USER, "", attrs);
-            handler.endElement("", ELEM_USER, "");
+            writer.writeStartElement(ELEM_USER);
+            writer.writeAttribute(ATTR_NAME, creds.getUserID());
+            writer.writeAttribute(ATTR_PASSWORD, encrypt(new String(creds.getPassword())));
+            writer.writeEndElement();
         }
     }
 

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultAuthConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultAuthConfig.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultAuthConfig.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultAuthConfig.java Wed Dec 18 09:28:26 2019
@@ -22,6 +22,9 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
 import org.apache.jackrabbit.vault.util.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -71,9 +74,9 @@ public class VaultAuthConfig extends Abs
         repoConfigs.put(cfg.uri, cfg);
     }
 
-    protected void doWrite(ContentHandler handler) throws SAXException {
+    protected void doWrite(XMLStreamWriter writer) throws XMLStreamException {
         for (RepositoryConfig cfg: repoConfigs.values()) {
-            cfg.write(handler);
+            cfg.write(writer);
         }
     }
 
@@ -132,12 +135,11 @@ public class VaultAuthConfig extends Abs
             return cfg;
         }
 
-        public void write(ContentHandler handler) throws SAXException {
-            AttributesImpl attrs = new AttributesImpl();
-            attrs.addAttribute("", ATTR_URI, "", "CDATA", uri);
-            handler.startElement("", ELEM_REPOSITORY, "", attrs);
-            creds.write(handler);
-            handler.endElement("", ELEM_REPOSITORY, "");
+        public void write(XMLStreamWriter writer) throws XMLStreamException {
+            writer.writeStartElement(ELEM_REPOSITORY);
+            writer.writeAttribute(ATTR_URI, uri);
+            creds.write(writer);
+            writer.writeEndElement();
         }
     }
 

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultSettings.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultSettings.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultSettings.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/VaultSettings.java Wed Dec 18 09:28:26 2019
@@ -20,6 +20,9 @@ package org.apache.jackrabbit.vault.fs.c
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
 import org.w3c.dom.Element;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -68,12 +71,11 @@ public class VaultSettings extends Abstr
         return 1.0;
     }
 
-    protected void doWrite(ContentHandler handler) throws SAXException {
+    protected void doWrite(XMLStreamWriter writer) throws XMLStreamException {
         for (String ignore: ignores) {
-            AttributesImpl attrs = new AttributesImpl();
-            attrs.addAttribute("", ATTR_IGNORE_NAME, "", "CDATA", ignore);
-            handler.startElement("", ELEM_IGNORE, "", attrs);
-            handler.endElement("", ELEM_IGNORE, "");
+            writer.writeStartElement(ELEM_IGNORE);
+            writer.writeAttribute(ATTR_IGNORE_NAME, ignore);
+            writer.writeEndElement();
         }
     }
 

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXFormatter.java Wed Dec 18 09:28:26 2019
@@ -28,6 +28,8 @@ import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
@@ -44,9 +46,6 @@ import org.apache.jackrabbit.vault.util.
 import org.apache.jackrabbit.vault.util.ItemNameComparator;
 import org.apache.jackrabbit.vault.util.JcrConstants;
 import org.apache.jackrabbit.vault.util.Text;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
 
 /**
  * The docview sax formatter generates SAX events to a given ContentHandler based on the aggregate tree.
@@ -69,9 +68,9 @@ public class DocViewSAXFormatter impleme
     protected final NamespaceResolver nsResolver;
 
     /**
-     * the content handler to feed the SAX events to
+     * the writer for outputting the xml
      */
-    protected final ContentHandler contentHandler;
+    protected final XMLStreamWriter writer;
 
     /**
      * The jcr:primaryType property name (allowed for session-local prefix mappings)
@@ -116,14 +115,16 @@ public class DocViewSAXFormatter impleme
      */
     private final Set<String> ignored = new HashSet<String>();
 
-    protected DocViewSAXFormatter(Aggregate aggregate, ContentHandler contentHandler)
+    private ItemNameComparator itemNameComparator;
+
+    protected DocViewSAXFormatter(Aggregate aggregate, XMLStreamWriter writer)
             throws RepositoryException {
 
         this.aggregate = aggregate;
         this.session = aggregate.getNode().getSession();
         nsResolver = new SessionNamespaceResolver(session);
-
-        this.contentHandler = contentHandler;
+        itemNameComparator = new ItemNameComparator(nsResolver);
+        this.writer = writer;
 
         DefaultNamePathResolver npResolver = new DefaultNamePathResolver(nsResolver);
 
@@ -150,9 +151,10 @@ public class DocViewSAXFormatter impleme
      * @throws RepositoryException if a repository error occurs
      * @throws SAXException if the underlying content handler throws a sax exception
      */
-    private void startNamespaceDeclarations() throws RepositoryException, SAXException {
+    private void startNamespaceDeclarations() throws RepositoryException, XMLStreamException {
         // always include jcr namespace (see JCRVLT-266)
-        contentHandler.startPrefixMapping(Name.NS_JCR_PREFIX, Name.NS_JCR_URI);
+        
+        writer.writeNamespace(Name.NS_JCR_PREFIX, Name.NS_JCR_URI);
 
         for (String prefix: aggregate.getNamespacePrefixes()) {
             if (Name.NS_XML_PREFIX.equals(prefix)) {
@@ -162,29 +164,7 @@ public class DocViewSAXFormatter impleme
             if (Name.NS_JCR_PREFIX.equals(prefix)) {
                 continue;
             }
-            contentHandler.startPrefixMapping(prefix, aggregate.getNamespaceURI(prefix));
-        }
-    }
-
-    /**
-     * Ends namespace declarations
-     *
-     * @throws RepositoryException if a repository error occurs
-     * @throws SAXException if the underlying content handler throws a sax exception
-      */
-    private void endNamespaceDeclarations() throws RepositoryException, SAXException {
-        // always include jcr namespace (see JCRVLT-266)
-        contentHandler.endPrefixMapping(Name.NS_JCR_PREFIX);
-
-        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;
-            }
-            contentHandler.endPrefixMapping(prefix);
+            writer.writeNamespace(prefix, aggregate.getNamespaceURI(prefix));
         }
     }
 
@@ -212,9 +192,8 @@ public class DocViewSAXFormatter impleme
         ignored.add(JcrConstants.JCR_PREDECESSORS);
 
         try {
-            contentHandler.startDocument();
-            startNamespaceDeclarations();
-        } catch (SAXException e) {
+            writer.writeStartDocument();
+        } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
     }
@@ -225,10 +204,8 @@ public class DocViewSAXFormatter impleme
     @Override
     public void onWalkEnd(Node root) throws RepositoryException {
         try {
-            // clear namespace declarations and end document
-            endNamespaceDeclarations();
-            contentHandler.endDocument();
-        } catch (SAXException e) {
+            writer.writeEndDocument();
+        } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
     }
@@ -259,24 +236,36 @@ public class DocViewSAXFormatter impleme
             elemName = ISO9075.encode(label);
         }
 
-        // attributes (properties)
-        AttributesImpl attrs = new AttributesImpl();
-        Collections.sort(props, ItemNameComparator.INSTANCE);
-        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 qName = getQName(attrName);
-            boolean sort = qName.equals(NameConstants.JCR_MIXINTYPES);
-            attrs.addAttribute(qName.getNamespaceURI(), qName.getLocalName(),
-                    attrName, CDATA_TYPE, DocViewProperty.format(prop, sort, useBinaryReferences));
-        }
+        Collections.sort(props, itemNameComparator);
 
         // start element (node)
         Name qName = getQName(elemName);
         try {
-            contentHandler.startElement(qName.getNamespaceURI(),
-                    qName.getLocalName(), elemName, attrs);
-        } catch (SAXException e) {
+            // with namespace?
+            String namespaceUri = qName.getNamespaceURI();
+            if (namespaceUri.length()>0) {
+                writer.writeStartElement(nsResolver.getPrefix(namespaceUri), qName.getLocalName(), namespaceUri);
+            } else {
+                writer.writeStartElement(qName.getLocalName());
+            }
+            if (elemName == jcrRoot) {
+                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(), 
+                            DocViewProperty.format(prop, sort, useBinaryReferences));
+                } else {
+                    writer.writeAttribute(qAttributeName.getLocalName(), DocViewProperty.format(prop, sort, useBinaryReferences));
+                }
+               
+            }
+        } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
     }
@@ -286,21 +275,10 @@ public class DocViewSAXFormatter impleme
      */
     @Override
     public void onNodeEnd(Node node, boolean included, int level) throws RepositoryException {
-        String label = Text.getName(node.getPath());
-        String elemName;
-        if (node.getDepth() == 0) {
-            // root node needs a name
-            elemName = jcrRoot;
-        } else {
-            // encode node name to make sure it's a valid xml name
-            elemName = ISO9075.encode(label);
-        }
-
         // end element (node)
-        Name qName = getQName(elemName);
         try {
-            contentHandler.endElement(qName.getNamespaceURI(), qName.getLocalName(), elemName);
-        } catch (SAXException e) {
+            writer.writeEndElement();
+        } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
     }
@@ -327,9 +305,9 @@ public class DocViewSAXFormatter impleme
         String elemName = ISO9075.encode(label);
         Name qName = getQName(elemName);
         try {
-            contentHandler.startElement(qName.getNamespaceURI(), qName.getLocalName(), elemName, null);
-            contentHandler.endElement(qName.getNamespaceURI(), qName.getLocalName(), elemName);
-        } catch (SAXException e) {
+            writer.writeStartElement(nsResolver.getPrefix(qName.getNamespaceURI()), qName.getLocalName(), qName.getNamespaceURI());
+            writer.writeEndElement();
+        } catch (XMLStreamException e) {
             throw new RepositoryException(e);
         }
     }

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSerializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSerializer.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSerializer.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSerializer.java Wed Dec 18 09:28:26 2019
@@ -21,13 +21,15 @@ import java.io.IOException;
 import java.io.OutputStream;
 
 import javax.jcr.RepositoryException;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLStreamException;
 
 import org.apache.jackrabbit.vault.fs.api.Aggregate;
 import org.apache.jackrabbit.vault.fs.api.SerializationType;
 import org.apache.jackrabbit.vault.fs.impl.AggregateImpl;
 import org.apache.jackrabbit.vault.fs.io.DocViewFormat;
 import org.apache.jackrabbit.vault.fs.io.Serializer;
-import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
+import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
 
 /**
  * {@code DocViewSerializer}...
@@ -53,9 +55,12 @@ public class DocViewSerializer implement
      */
     public void writeContent(OutputStream out) throws IOException, RepositoryException {
         // build content handler and add filter in case of original xml files
-        XMLSerializer ser = new XMLSerializer(out, new DocViewFormat().getXmlOutputFormat());
-        DocViewSAXFormatter fmt = new DocViewSAXFormatter(aggregate, ser);
-        aggregate.walk(fmt);
+        try (FormattingXmlStreamWriter writer = FormattingXmlStreamWriter.create(out, new DocViewFormat().getXmlOutputFormat())){
+            DocViewSAXFormatter fmt = new DocViewSAXFormatter(aggregate, writer);
+            aggregate.walk(fmt);
+        } catch (XMLStreamException | FactoryConfigurationError e) {
+            throw new IOException(e);
+        }
     }
 
     /**

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewFormat.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewFormat.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewFormat.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewFormat.java Wed Dec 18 09:28:26 2019
@@ -41,51 +41,49 @@ import java.util.zip.CheckedInputStream;
 import java.util.zip.CheckedOutputStream;
 import java.util.zip.Checksum;
 
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stax.StAXResult;
+
 import org.apache.commons.io.IOUtils;
-import org.apache.jackrabbit.vault.util.xml.serialize.AttributeNameComparator;
+import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
+import org.apache.jackrabbit.vault.util.xml.serialize.NormalizingSaxFilter;
 import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
-import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
 
-/**
- * This class provides access to the commonly used doc view xml format and functionality that checks files for the format or reformats
- * them accordingly.
- */
+/** This class provides access to the commonly used doc view xml format and functionality that checks files for the format or reformats them
+ * accordingly. */
 public class DocViewFormat {
 
     private final OutputFormat format;
     private WeakReference<ByteArrayOutputStream> formattingBuffer;
 
     public DocViewFormat() {
-        format = new OutputFormat("xml", "UTF-8", true);
-        format.setIndent(4);
-        format.setLineWidth(0);
-        format.setBreakEachAttribute(true);
-        format.setSortAttributeNamesBy(AttributeNameComparator.INSTANCE);
+        format = new OutputFormat(4, true);
     }
 
-    /**
-     * Returns the {@link OutputFormat} used by {@link org.apache.jackrabbit.vault.fs.impl.io.DocViewSerializer} when writing doc view xml
+    /** Returns the {@link OutputFormat} used by {@link org.apache.jackrabbit.vault.fs.impl.io.DocViewSerializer} when writing doc view xml
      * files.
      *
-     * @return the output format
-     */
+     * @return the output format */
     public OutputFormat getXmlOutputFormat() {
         return format;
     }
 
-    /**
-     * Formats a given file using the {@link OutputFormat} returned by {@link DocViewFormat#getXmlOutputFormat()}.
-     * The file is replaced on disk but only if wasn't already formatted correctly and if {@code dryRun} is {@code false}.
+    /** Formats a given file using the {@link OutputFormat} returned by {@link DocViewFormat#getXmlOutputFormat()}. The file is replaced on
+     * disk but only if wasn't already formatted correctly and if {@code dryRun} is {@code false}.
      *
      * @param file the file to format
      * @param dryRun If {@code true}, then the file is never replaced on disk.
      * @return {@code true} if the formatted version differs from the original.
-     * @throws IOException if an I/O error occurs
-     */
+     * @throws IOException if an I/O error occurs */
     public boolean format(File file, boolean dryRun) throws IOException {
         CRC32 originalCrc32 = new CRC32();
         CRC32 formattedCrc32 = new CRC32();
@@ -100,21 +98,21 @@ public class DocViewFormat {
         return changed;
     }
 
-    /**
-     * Formats given files using the {@link OutputFormat} returned by {@link DocViewFormat#getXmlOutputFormat()} by traversing the directory
-     * tree given as file. Only those files will be formatted, that have a filename matching at least one of the given filenamePatterns,
-     * and only if {@code dryRun} is {@code false}.
+    /** Formats given files using the {@link OutputFormat} returned by {@link DocViewFormat#getXmlOutputFormat()} by traversing the
+     * directory tree given as file. Only those files will be formatted, that have a filename matching at least one of the given
+     * filenamePatterns, and only if {@code dryRun} is {@code false}.
      *
      * @param directory the start directory
      * @param filenamePatterns list of regexp patterns
      * @param dryRun If {@code true}, then the file is never replaced on disk.
      * @return a list of relative paths of those files which are not formatted correctly according to {@link #format(File, boolean)}
-     * @throws IOException in case there is an exception during traversal or formatting. That means formatting will fail on the first error that appeared
-     */
+     * @throws IOException in case there is an exception during traversal or formatting. That means formatting will fail on the first error
+     *             that appeared */
     public List<String> format(File directory, List<Pattern> filenamePatterns, final boolean dryRun) throws IOException {
         final List<String> changed = new LinkedList<>();
         Files.walkFileTree(directory.toPath(), new AbstractFormattingVisitor(filenamePatterns) {
-            @Override protected void process(File file) throws IOException {
+            @Override
+            protected void process(File file) throws IOException {
                 if (format(file, dryRun)) {
                     changed.add(file.getPath());
                 }
@@ -123,14 +121,13 @@ public class DocViewFormat {
         return changed;
     }
 
-    /**
-     * internally formats the given file and computes their checksum
+    /** internally formats the given file and computes their checksum
+     * 
      * @param file the file
      * @param original checksum of the original file
      * @param formatted checksum of the formatted file
      * @return the formatted bytes
-     * @throws IOException if an error occurs
-     */
+     * @throws IOException if an error occurs */
     private byte[] format(File file, Checksum original, Checksum formatted) throws IOException {
         try (InputStream in = new CheckedInputStream(new BufferedInputStream(new FileInputStream(file)), original)) {
             @SuppressWarnings("resource")
@@ -142,14 +139,24 @@ public class DocViewFormat {
                 buffer.reset();
             }
 
-            XMLSerializer serializer = new XMLSerializer(new CheckedOutputStream(buffer, formatted), format);
-            XMLReader reader = XMLReaderFactory.createXMLReader();
-            reader.setContentHandler(serializer);
-            reader.setDTDHandler(serializer);
-            reader.parse(new InputSource(in));
-
+            try (OutputStream out = new CheckedOutputStream(buffer, formatted);
+                 FormattingXmlStreamWriter writer = FormattingXmlStreamWriter.create(out, format)) {
+                // cannot use XMlStreamReader due to comment handling:
+                // https://stackoverflow.com/questions/15792007/why-does-xmlstreamreader-staxsource-strip-comments-from-xml
+                TransformerFactory tf = TransformerFactory.newInstance();
+                tf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+                SAXSource saxSource = new SAXSource(new InputSource(in));
+                SAXParserFactory sf = SAXParserFactory.newInstance();
+                sf.setNamespaceAware(true);
+                sf.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+                sf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+                saxSource.setXMLReader(new NormalizingSaxFilter(sf.newSAXParser().getXMLReader()));
+                Transformer t = tf.newTransformer();
+                StAXResult result = new StAXResult(writer);
+                t.transform(saxSource, result);
+            }
             return buffer.toByteArray();
-        } catch (SAXException ex) {
+        } catch (TransformerException | XMLStreamException | FactoryConfigurationError | ParserConfigurationException | SAXException ex) {
             throw new IOException(ex);
         }
     }

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/ExecutionPlanBuilderImpl.java Wed Dec 18 09:28:26 2019
@@ -33,6 +33,7 @@ import javax.jcr.Session;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLStreamException;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
@@ -50,8 +51,8 @@ import org.apache.jackrabbit.vault.packa
 import org.apache.jackrabbit.vault.packaging.registry.PackageTaskBuilder;
 import org.apache.jackrabbit.vault.packaging.registry.RegisteredPackage;
 import org.apache.jackrabbit.vault.util.RejectingEntityResolver;
+import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
 import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
-import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -59,7 +60,6 @@ import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
 
 /**
  * {@code ExecutionPlanBuilderImpl}...
@@ -101,23 +101,19 @@ public class ExecutionPlanBuilderImpl im
     @Override
     public ExecutionPlanBuilder save(@Nonnull OutputStream out) throws IOException, PackageException {
         validate();
-        try {
-            XMLSerializer ser = new XMLSerializer(out, new OutputFormat("xml", "UTF-8", true));
-            ser.startDocument();
-            AttributesImpl attrs = new AttributesImpl();
-            attrs.addAttribute(null, null, ATTR_VERSION, "CDATA", String.valueOf(version));
-            ser.startElement(null, null, TAG_EXECUTION_PLAN, attrs);
-
+        try (FormattingXmlStreamWriter writer = FormattingXmlStreamWriter.create(out, new OutputFormat(4, false))) {
+            writer.writeStartDocument();
+            writer.writeStartElement(TAG_EXECUTION_PLAN);
+            writer.writeAttribute(ATTR_VERSION, String.valueOf(version));
             for (PackageTask task: plan.getTasks()) {
-                attrs = new AttributesImpl();
-                attrs.addAttribute(null, null, ATTR_CMD, "CDATA", task.getType().name().toLowerCase());
-                attrs.addAttribute(null, null, ATTR_PACKAGE_ID, "CDATA", task.getPackageId().toString());
-                ser.startElement(null, null, TAG_TASK, attrs);
-                ser.endElement(TAG_TASK);
+                writer.writeStartElement(TAG_TASK);
+                writer.writeAttribute(ATTR_CMD, task.getType().name().toLowerCase());
+                writer.writeAttribute(ATTR_PACKAGE_ID, task.getPackageId().toString());
+                writer.writeEndElement();
             }
-            ser.endElement(TAG_EXECUTION_PLAN);
-            ser.endDocument();
-        } catch (SAXException e) {
+            writer.writeEndElement();
+            writer.writeEndDocument();
+        } catch (XMLStreamException e) {
             throw new IllegalStateException(e);
         }
         return this;

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java Wed Dec 18 09:28:26 2019
@@ -34,6 +34,7 @@ import javax.annotation.Nullable;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLStreamException;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.vault.fs.api.FilterSet.Entry;
@@ -46,9 +47,10 @@ import org.apache.jackrabbit.vault.fs.fi
 import org.apache.jackrabbit.vault.packaging.Dependency;
 import org.apache.jackrabbit.vault.packaging.PackageId;
 import org.apache.jackrabbit.vault.packaging.SubPackageHandling;
+import org.apache.jackrabbit.vault.packaging.SubPackageHandling.Option;
 import org.apache.jackrabbit.vault.util.RejectingEntityResolver;
+import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter;
 import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
-import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -56,7 +58,6 @@ import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
 
 /**
  * Internal (immutable) State object to cache and pass the relevant metadata around.
@@ -317,69 +318,62 @@ public class FSInstallState {
      * @throws IOException if an error occurs.
      */
     public void save(OutputStream out) throws IOException {
-        try {
-            XMLSerializer ser = new XMLSerializer(out, new OutputFormat("xml", "UTF-8", true));
-            ser.startDocument();
-            AttributesImpl attrs = new AttributesImpl();
-            attrs.addAttribute(null, null, ATTR_PACKAGE_ID, "CDATA", packageId.toString());
-            attrs.addAttribute(null, null, ATTR_SIZE, "CDATA", Long.toString(size));
+        try (FormattingXmlStreamWriter writer = FormattingXmlStreamWriter.create(out, new OutputFormat(4, false))) {
+            writer.writeStartDocument();
+            writer.writeStartElement(TAG_REGISTRY_METADATA);
+            writer.writeAttribute(ATTR_PACKAGE_ID, packageId.toString());
+            writer.writeAttribute(ATTR_SIZE, Long.toString(size));
             if (installTime != null) {
-                attrs.addAttribute(null, null, ATTR_INSTALLATION_TIME, "CDATA", Long.toString(installTime));
+                writer.writeAttribute(ATTR_INSTALLATION_TIME, Long.toString(installTime));
             }
-            attrs.addAttribute(null, null, ATTR_FILE_PATH, "CDATA", filePath.toString());
-            attrs.addAttribute(null, null, ATTR_EXTERNAL, "CDATA", Boolean.toString(external));
-            attrs.addAttribute(null, null, ATTR_PACKAGE_STATUS, "CDATA", status.name().toLowerCase());
-            ser.startElement(null, null, TAG_REGISTRY_METADATA, attrs);
+            writer.writeAttribute(ATTR_FILE_PATH, filePath.toString());
+            writer.writeAttribute(ATTR_EXTERNAL, Boolean.toString(external));
+            writer.writeAttribute(ATTR_PACKAGE_STATUS, status.name().toLowerCase());
 
             if (filter != null && !filter.getFilterSets().isEmpty()) {
-                ser.startElement(null, null, TAG_WORKSPACEFILTER, null);
+                writer.writeStartElement(TAG_WORKSPACEFILTER);
                 for (PathFilterSet pfs : filter.getFilterSets()) {
-                    attrs = new AttributesImpl();
-                    attrs.addAttribute(null, null, ATTR_ROOT, "CDATA", pfs.getRoot());
-                    ser.startElement(null, null, TAG_FILTER, attrs);
+                    writer.writeStartElement(TAG_FILTER);
+                    writer.writeAttribute(ATTR_ROOT, pfs.getRoot());
                     for (Entry<PathFilter> pf : pfs.getEntries()) {
-                        attrs = new AttributesImpl();
+                        writer.writeStartElement(TAG_RULE);
                         DefaultPathFilter dpf = (DefaultPathFilter) pf.getFilter();
                         if (pf.isInclude()) {
-                            attrs.addAttribute(null, null, ATTR_INCLUDE, "CDATA", dpf.getPattern());
+                            writer.writeAttribute(ATTR_INCLUDE, dpf.getPattern());
                         } else {
-                            attrs.addAttribute(null, null, ATTR_EXCLUDE, "CDATA", dpf.getPattern());
+                            writer.writeAttribute(ATTR_EXCLUDE, dpf.getPattern());
                         }
-                        ser.startElement(null, null, TAG_RULE, attrs);
-                        ser.endElement(TAG_RULE);
+                        writer.writeEndElement();
                     }
-                    ser.endElement(TAG_FILTER);
+                    writer.writeEndElement();
                 }
-                ser.endElement(TAG_WORKSPACEFILTER);
+                writer.writeEndElement();
             }
             if (dependencies != null) {
                 for (Dependency dependency : dependencies) {
-                    attrs = new AttributesImpl();
-                    attrs.addAttribute(null, null, ATTR_PACKAGE_ID, "CDATA", dependency.toString());
-                    ser.startElement(null, null, TAG_DEPENDENCY, attrs);
-                    ser.endElement(TAG_DEPENDENCY);
+                    writer.writeStartElement(TAG_DEPENDENCY);
+                    writer.writeAttribute(ATTR_PACKAGE_ID, dependency.toString());
+                    writer.writeEndElement();
                 }
             }
             if (subPackages != null) {
-                for (PackageId subPackId : subPackages.keySet()) {
-                    attrs = new AttributesImpl();
-                    attrs.addAttribute(null, null, ATTR_PACKAGE_ID, "CDATA", subPackId.toString());
-                    attrs.addAttribute(null, null, ATTR_SUBPACKAGE_HANDLING_OPTION, "CDATA", subPackages.get(subPackId).toString());
-                    ser.startElement(null, null, TAG_SUBPACKAGE, attrs);
-                    ser.endElement(TAG_SUBPACKAGE);
+                for (java.util.Map.Entry<PackageId, Option> entry : subPackages.entrySet()) {
+                    writer.writeStartElement(TAG_SUBPACKAGE);
+                    writer.writeAttribute(ATTR_PACKAGE_ID, entry.getKey().toString());
+                    writer.writeAttribute(ATTR_SUBPACKAGE_HANDLING_OPTION, entry.getValue().toString());
+                    writer.writeEndElement();
                 }
             }
-            attrs = new AttributesImpl();
-            for (String key : properties.stringPropertyNames()) {
-                attrs.addAttribute(null, null, key, "CDATA", properties.getProperty(key));
-            }
-            if (attrs.getLength() > 0) {
-                ser.startElement(null, null, TAG_PACKAGEPROPERTIES, attrs);
-                ser.endElement(TAG_PACKAGEPROPERTIES);
+            if (properties.size() > 0) {
+                writer.writeStartElement(TAG_PACKAGEPROPERTIES);
+                for (String key : properties.stringPropertyNames()) {
+                    writer.writeAttribute(key, properties.getProperty(key));
+                }
+                writer.writeEndElement();
             }
-            ser.endElement(TAG_REGISTRY_METADATA);
-            ser.endDocument();
-        } catch (SAXException e) {
+            writer.writeEndElement();
+            writer.writeEndDocument();
+        } catch (XMLStreamException e) {
             throw new IllegalStateException(e);
         }
     }

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/ItemNameComparator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/ItemNameComparator.java?rev=1871734&r1=1871733&r2=1871734&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/ItemNameComparator.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/ItemNameComparator.java Wed Dec 18 09:28:26 2019
@@ -21,22 +21,44 @@ import java.util.Comparator;
 
 import javax.jcr.Item;
 import javax.jcr.RepositoryException;
+import javax.xml.namespace.QName;
 
-import org.apache.jackrabbit.vault.util.xml.serialize.AttributeNameComparator;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.conversion.NameException;
+import org.apache.jackrabbit.spi.commons.conversion.NameParser;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
 
 /**
  * {@code ItemNameComparator}...
  */
 public class ItemNameComparator implements Comparator<Item> {
 
-    public static final ItemNameComparator INSTANCE = new ItemNameComparator();
+    
+    /**
+     * the session's namespace resolver
+     */
+    private final NamespaceResolver nsResolver;
+    
+    public ItemNameComparator(NamespaceResolver nsResolver) {
+        super();
+        this.nsResolver = nsResolver;
+    }
 
+    private QName getQName(String rawName) throws RepositoryException {
+        try {
+            Name name = NameParser.parse(rawName, nsResolver, NameFactoryImpl.getInstance());
+            return new QName(name.getNamespaceURI(), name.getLocalName(), nsResolver.getPrefix(name.getNamespaceURI()));
+        } catch (NameException e) {
+            // should never get here...
+            String msg = "internal error: failed to resolve namespace mappings";
+            throw new RepositoryException(msg, e);
+        }
+    }
+    
     public int compare(Item o1, Item o2) {
         try {
-            // sort namespaced first
-            String n1 = o1.getName();
-            String n2 = o2.getName();
-            return AttributeNameComparator.INSTANCE.compare(n1, n2);
+            return QNameComparator.INSTANCE.compare(getQName(o1.getName()), getQName(o2.getName()));
         } catch (RepositoryException e) {
             throw new IllegalStateException(e);
         }

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/QNameComparator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/QNameComparator.java?rev=1871734&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/QNameComparator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/QNameComparator.java Wed Dec 18 09:28:26 2019
@@ -0,0 +1,73 @@
+/*
+ * 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 java.util.Locale;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+
+/** 
+ * Compares based on prefixes and local names as follows:
+ * <ol>
+ * <li>first ns prefixes</li>
+ * <li>then prefixed attributes</li>
+ * <li>only afterwards non-prefixed attributes</li>
+ * </ol>
+ * The letter case does not matter for the order, except when two names are equal. Only then the case will be considered as well.
+ * 
+ */
+public class QNameComparator implements Comparator<QName> {
+
+    public static final QNameComparator INSTANCE = new QNameComparator();
+
+    private static final int LESS_THAN = -1;
+    private static final int EQUAL = 0;
+    private static final int GREATER_THAN = 1;
+    
+    @Override
+    public int compare(final QName o1, final QName o2) {
+        // first namespace declarations
+        final boolean isXmlNs1 = o1.getPrefix().equalsIgnoreCase(XMLConstants.XML_NS_PREFIX);
+        final boolean isXmlNs2 = o2.getPrefix().equalsIgnoreCase(XMLConstants.XML_NS_PREFIX);
+        if (isXmlNs1 && !isXmlNs2) {
+            return LESS_THAN;
+        } else if (!isXmlNs1 && isXmlNs2) {
+            return GREATER_THAN;
+        }
+        // prefixed attributes before non-prefixed ones...
+        if (!o1.getPrefix().equalsIgnoreCase(XMLConstants.DEFAULT_NS_PREFIX) && o2.getPrefix().equalsIgnoreCase(XMLConstants.DEFAULT_NS_PREFIX)) {
+            return LESS_THAN;
+        } else if (o1.getPrefix().equalsIgnoreCase(XMLConstants.DEFAULT_NS_PREFIX) && !o2.getPrefix().equalsIgnoreCase(XMLConstants.DEFAULT_NS_PREFIX)) {
+            return GREATER_THAN;
+        } else {
+            // order first by prefix, then by local name
+            String lowerCaseQName1 = o1.getPrefix().toLowerCase(Locale.ROOT) + o1.getLocalPart().toLowerCase(Locale.ROOT);
+            String lowerCaseQName2 = o2.getPrefix().toLowerCase(Locale.ROOT) + o2.getLocalPart().toLowerCase(Locale.ROOT);
+            final int c = lowerCaseQName1.compareTo(lowerCaseQName2);
+            // if the lowercase versions are equal, they could differ in case (see JCRVLT-334)
+            if (c == EQUAL) {
+                String qName1 = o1.getPrefix() + o1.getLocalPart();
+                String qName2 = o2.getPrefix() + o2.getLocalPart();
+                return qName1.compareTo(qName2);
+            }
+            return c;
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/FormattingXmlStreamWriter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/FormattingXmlStreamWriter.java?rev=1871734&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/FormattingXmlStreamWriter.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/FormattingXmlStreamWriter.java Wed Dec 18 09:28:26 2019
@@ -0,0 +1,244 @@
+/*
+ * 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.xml.serialize;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import com.ctc.wstx.api.WstxOutputProperties;
+import com.ctc.wstx.stax.WstxOutputFactory;
+
+/** StAX XML Stream Writer filter. Adds the following functionality:
+ * <ul>
+ * <li>optional line break before each attribute</li>
+ * <li>new line at end</li>
+ * <li>indentation for elements and comments</li>
+ * </ul> 
+ */
+public class FormattingXmlStreamWriter extends com.sun.xml.txw2.output.IndentingXMLStreamWriter implements AutoCloseable {
+
+    private final Writer rawWriter;
+    private final XMLStreamWriter writer;
+    private final OutputFormat output;
+
+    int numNamespaceDeclarations = 0;
+    int numAttributes = 0;
+    private int depth = 0;
+    private Attribute bufferedAttribute;
+
+    public static FormattingXmlStreamWriter create(OutputStream output, OutputFormat format)
+            throws XMLStreamException, FactoryConfigurationError {
+        // always use WoodstoX
+        XMLOutputFactory factory = new WstxOutputFactory();
+        factory.setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true);
+        return new FormattingXmlStreamWriter(factory, output, format);
+    }
+
+    private FormattingXmlStreamWriter(XMLOutputFactory factory, OutputStream output, OutputFormat format)
+            throws XMLStreamException, FactoryConfigurationError {
+        this(factory.createXMLStreamWriter(output, StandardCharsets.UTF_8.name()), format);
+    }
+
+    private FormattingXmlStreamWriter(XMLStreamWriter writer, OutputFormat output) {
+        super(writer);
+        super.setIndentStep(output.getIndent());
+        this.output = output;
+        this.writer = writer;
+        this.rawWriter = (Writer) writer.getProperty(WstxOutputProperties.P_OUTPUT_UNDERLYING_WRITER);
+        if (this.rawWriter == null) {
+            throw new IllegalStateException("Could not get underlying writer!");
+        }
+    }
+
+    @Override
+    public void writeEndDocument() throws XMLStreamException {
+        // nothing can be written after writeEndDocument() has been called, therefore call the additional new line before
+        super.writeEndDocument();
+        addLineBreak(true);
+    }
+
+    @Override
+    public void writeStartElement(String localName) throws XMLStreamException {
+        onStartElement();
+        super.writeStartElement(localName);
+    }
+
+    @Override
+    public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
+        onStartElement();
+        super.writeStartElement(namespaceURI, localName);
+    }
+
+    @Override
+    public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
+        onStartElement();
+        super.writeStartElement(prefix, localName, namespaceURI);
+    }
+
+    @Override
+    public void writeEndElement() throws XMLStreamException {
+        // is it new element or 
+        flushBufferedAttribute();
+        depth--;
+        super.writeEndElement();
+    }
+
+    private void onStartElement() throws XMLStreamException {
+        flushBufferedAttribute();
+        numNamespaceDeclarations = 0;
+        numAttributes = 0;
+        depth++;
+    }
+
+    @Override
+    public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
+        numNamespaceDeclarations++;
+        super.writeNamespace(prefix, namespaceURI);
+    }
+
+    @Override
+    public void writeAttribute(String localName, String value) throws XMLStreamException {
+        if (onAttribute(null, null, localName, value)) {
+            super.writeAttribute(localName, value);
+        }
+    }
+
+    @Override
+    public void writeAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException {
+        if (onAttribute(prefix, namespaceURI, localName, value)) {
+            super.writeAttribute(prefix, namespaceURI, localName, value);
+        }
+    }
+
+    @Override
+    public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException {
+        if (onAttribute(null, namespaceURI, localName, value)) {
+            super.writeAttribute(namespaceURI, localName, value);
+        }
+    }
+
+    private final class Attribute {
+        private final String prefix;
+        private final String namespaceURI;
+        private final String localName;
+        private final String value;
+
+        public Attribute(String prefix, String namespaceURI, String localName, String value) {
+            super();
+            this.prefix = prefix;
+            this.namespaceURI = namespaceURI;
+            this.localName = localName;
+            this.value = value;
+        }
+
+        public void write(XMLStreamWriter writer) throws XMLStreamException {
+            if (prefix == null) {
+                if (namespaceURI == null) {
+                    writer.writeAttribute(localName, value);
+                } else {
+                    writer.writeAttribute(namespaceURI, localName, value);
+                }
+            } else {
+                writer.writeAttribute(prefix, namespaceURI, localName, value);
+            }
+        }
+    }
+
+    private boolean onAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException {
+        numAttributes++;
+        if (output.isSplitAttributesByLineBreaks()) {
+            // if the amount of namespace declarations + attributes is bigger than 1
+            if (numNamespaceDeclarations + numAttributes > 1) {
+                if (bufferedAttribute != null) {
+                    addLineBreak(true);
+                    indent(true);
+                    flushBufferedAttribute();
+                }
+                addLineBreak(true);
+                indent(true);
+            } else {
+                bufferedAttribute = new Attribute(prefix, namespaceURI, localName, value);
+                // buffer attributes to wait for the next ones
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean flushBufferedAttribute() throws XMLStreamException {
+        if (bufferedAttribute != null) {
+            bufferedAttribute.write(writer);
+            bufferedAttribute = null;
+            return true;
+        }
+        return false;
+    }
+
+    private void indent(boolean isAttribute) throws XMLStreamException {
+        // writeCharacters does close the current element and changes the state!
+        // Stax2.writeSpace cannot be used either due to https://github.com/FasterXML/woodstox/issues/95
+        // instead write directly to underlying writer
+        try {
+            writer.flush();
+            if (depth > 0) {
+                for (int i = 0; i < depth; i++) {
+                    final String indent;
+                    if (isAttribute && i == depth - 1) {
+                        // leave out one space as that is automatically added by any XMLStreamWriter between any two attributes
+                        indent = output.getIndent().substring(0, output.getIndent().length() - 1);
+                    } else {
+                        indent = output.getIndent();
+                    }
+                    rawWriter.write(indent);
+                }
+            }
+            rawWriter.flush();
+        } catch (IOException e) {
+            throw new XMLStreamException("Could not indent attribute", e);
+        }
+    }
+    
+    private void addLineBreak(boolean keepState) throws XMLStreamException {
+        if (keepState) {
+            try {
+                writer.flush();
+                rawWriter.write('\n');
+                rawWriter.flush();
+            } catch (IOException e) {
+                throw new XMLStreamException("Could not add line break", e);
+            }
+        } else {
+            writeCharacters("\n");
+        }
+    }
+
+    @Override
+    public void writeComment(String data) throws XMLStreamException {
+        flushBufferedAttribute();
+        addLineBreak(false);
+        indent(false);
+        super.writeComment(data);
+        //addLineBreak(false);
+    }
+}

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/NormalizingSaxFilter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/NormalizingSaxFilter.java?rev=1871734&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/NormalizingSaxFilter.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/xml/serialize/NormalizingSaxFilter.java Wed Dec 18 09:28:26 2019
@@ -0,0 +1,223 @@
+/*
+ * 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.xml.serialize;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+
+import org.apache.jackrabbit.vault.util.QNameComparator;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+/** 
+ * SAX filter which
+ * <ol>
+ * <li>orders attributes alphabetically</li>
+ * <li>strips ignorable whitespace (works even without schema/DTD: all characters in FileVault can be stripped because all elements are supposed to be empty)</li>
+ * <li>removes leading comments (prior to root element) due to bug in SAX2StAXStreamWriter in Java</li>
+ * </ol>
+ */
+public class NormalizingSaxFilter extends XMLFilterImpl implements LexicalHandler {
+
+    private final class AttributeComparator implements Comparator<Attribute> {
+
+        private final QNameComparator nameComparator;
+
+        public AttributeComparator() {
+            nameComparator = new QNameComparator();
+        }
+
+        @Override
+        public int compare(Attribute o1, Attribute o2) {
+            return nameComparator.compare(o1.getName(), o2.getName());
+        }
+    }
+
+    private static final class Attribute {
+        private final QName name;
+        private final String value;
+        private final String type;
+
+        public Attribute(Attributes attributes, int i) {
+            this(getQNameFromAttribute(attributes, i), attributes.getValue(i), attributes.getType(i));
+        }
+
+        private static QName getQNameFromAttribute(Attributes attributes, int i) {
+            String qName = attributes.getQName(i);
+            String prefix = XMLConstants.DEFAULT_NS_PREFIX;
+            final String localPart;
+            String nameParts[] = qName.split(":", 2);
+            if (nameParts.length > 1) {
+                prefix = nameParts[0];
+                localPart = nameParts[1];
+            } else {
+                localPart = nameParts[0];
+            }
+            return new QName(attributes.getURI(i), localPart, prefix);
+        }
+
+        public Attribute(QName name, String value, String type) {
+            super();
+            this.name = name;
+            this.value = value;
+            this.type = type;
+        }
+
+        public QName getName() {
+            return name;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public void addToAttributes(AttributesImpl attributes) {
+            StringBuilder qName = new StringBuilder();
+            if (getName().getPrefix().length() > 0) {
+                qName.append(getName().getPrefix()).append(":");
+            }
+            qName.append(getName().getLocalPart());
+            attributes.addAttribute(getName().getNamespaceURI(), getName().getLocalPart(),
+                    qName.toString(), getType(), getValue());
+        }
+    }
+
+    private static final String SAX_PROPERTY_LEXICAL_HANDLER = "http://xml.org/sax/properties/lexical-handler";
+    private LexicalHandler lexicalHandler;
+    boolean hasReachedRootElement;
+
+    public NormalizingSaxFilter(XMLReader parent) {
+        super(parent);
+        lexicalHandler = null;
+    }
+
+    @Override
+    public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
+        if (name.equals(SAX_PROPERTY_LEXICAL_HANDLER)) {
+            this.lexicalHandler = (LexicalHandler) value;
+            super.setProperty(name, this);
+        } else {
+            super.setProperty(name, value);
+        }
+    }
+
+    @Override
+    public void parse(InputSource input) throws SAXException, IOException {
+        hasReachedRootElement = false;
+        super.parse(input);
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+        hasReachedRootElement = true;
+        List<Attribute> attributeList = new LinkedList<>();
+        for (int i = 0; i < atts.getLength(); i++) {
+            Attribute attribute = new Attribute(atts, i);
+            attributeList.add(attribute);
+        }
+        Collections.sort(attributeList, new AttributeComparator());
+        super.startElement(uri, localName, qName, getAttributesFromList(attributeList));
+    }
+
+    /**
+     * Filter out ignorable whitespace (for FileVault all elements are empty, i.e. every character is ignorable)
+     */
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        // filter out everything
+    }
+
+    AttributesImpl getAttributesFromList(List<Attribute> attributeList) {
+        AttributesImpl attributes = new AttributesImpl();
+        for (Attribute attribute : attributeList) {
+            attribute.addToAttributes(attributes);
+        }
+        return attributes;
+    }
+
+    @Override
+    public void startDTD(String name, String publicId, String systemId) throws SAXException {
+        if (lexicalHandler != null) {
+            lexicalHandler.startDTD(name, publicId, systemId);
+        }
+    }
+
+    /********* Start Lexical Handler *********/
+    @Override
+    public void endDTD() throws SAXException {
+        if (lexicalHandler != null) {
+            lexicalHandler.endDTD();
+        }
+    }
+
+    @Override
+    public void startEntity(String name) throws SAXException {
+        if (lexicalHandler != null) {
+            lexicalHandler.startEntity(name);
+        }
+    }
+
+    @Override
+    public void endEntity(String name) throws SAXException {
+        if (lexicalHandler != null) {
+            lexicalHandler.endEntity(name);
+        }
+    }
+
+    @Override
+    public void startCDATA() throws SAXException {
+        if (lexicalHandler != null) {
+            lexicalHandler.startCDATA();
+        }
+    }
+
+    @Override
+    public void endCDATA() throws SAXException {
+        if (lexicalHandler != null) {
+            lexicalHandler.endCDATA();
+        }
+    }
+
+    @Override
+    public void comment(char[] ch, int start, int length) throws SAXException {
+        // prevent comment before first element due to a bug in Java
+        if (!hasReachedRootElement) {
+            return;
+        }
+        if (lexicalHandler != null) {
+            lexicalHandler.comment(ch, start, length);
+        }
+    }
+    /********* End Lexical Handler *********/
+}



Mime
View raw message