[Git][java-team/dom4j][upstream] New upstream version 2.1.1

Emmanuel Bourg gitlab at salsa.debian.org
Mon Jul 30 18:48:34 BST 2018


Emmanuel Bourg pushed to branch upstream at Debian Java Maintainers / dom4j


Commits:
fc1e5d4e by Emmanuel Bourg at 2018-07-30T16:20:03Z
New upstream version 2.1.1
- - - - -


15 changed files:

- .gitignore
- build.gradle
- src/main/java/org/dom4j/DocumentHelper.java
- src/main/java/org/dom4j/Namespace.java
- src/main/java/org/dom4j/QName.java
- src/main/java/org/dom4j/datatype/SchemaParser.java
- src/main/java/org/dom4j/io/SAXHelper.java
- src/main/java/org/dom4j/io/SAXReader.java
- src/main/java/org/dom4j/io/XMLWriter.java
- src/main/java/org/dom4j/tree/QNameCache.java
- + src/test/java/org/dom4j/AllowedCharsTest.java
- src/test/java/org/dom4j/IteratorTest.java
- src/test/java/org/dom4j/XMLWriterTest.java
- src/test/java/org/dom4j/dom/DOMTest.java
- src/test/java/org/dom4j/io/DTDTest.java


Changes:

=====================================
.gitignore
=====================================
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 /build
+/out
 /.gradle
 /gradle.properties
 /.idea/workspace.xml


=====================================
build.gradle
=====================================
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
-apply plugin: 'java'
+apply plugin: 'java-library'
 apply plugin: 'jacoco'
-apply plugin: 'maven'
+apply plugin: 'maven-publish'
 apply plugin: 'signing'
 
 group = 'org.dom4j'
@@ -9,120 +9,129 @@ archivesBaseName = 'dom4j'
 sourceCompatibility = 1.8
 
 tasks.withType(JavaCompile) + tasks.withType(Javadoc) {
-	options.encoding = 'UTF-8'
-	options.charSet = options.encoding
+    options.encoding = 'UTF-8'
+    options.charSet = options.encoding
 }
 
 repositories {
-	mavenCentral()
+    mavenCentral()
 }
 
 dependencies {
 
-	compile(
-			'jaxen:jaxen:1.1.6',
-	)
-
-	compileOnly(
-			'javax.xml.stream:stax-api:1.0-2',
-			'net.java.dev.msv:xsdlib:2013.6.1',
-			'xpp3:xpp3:1.1.4c',
-			'pull-parser:pull-parser:2',
-			'javax.xml.bind:jaxb-api:2.2.12',
-	)
-
-
-	testCompile(
-			'org.testng:testng:6.8.21',
-
-			'xerces:xercesImpl:2.11.0',
-			'xalan:xalan:2.7.2',
-	)
+    implementation(
+            'jaxen:jaxen:1.1.6',
+            'javax.xml.stream:stax-api:1.0-2',
+            'net.java.dev.msv:xsdlib:2013.6.1',
+            'javax.xml.bind:jaxb-api:2.2.12',
+            'pull-parser:pull-parser:2',
+            'xpp3:xpp3:1.1.4c',
+    )
+
+    testImplementation(
+            'org.testng:testng:6.8.21',
+
+            'xerces:xercesImpl:2.11.0',
+            'xalan:xalan:2.7.2',
+    )
 }
 
 sourceSets {
-	test {
-		compileClasspath += configurations.compileOnly
-		runtimeClasspath += configurations.compileOnly
-	}
+    test {
+        compileClasspath += configurations.compileOnly
+        runtimeClasspath += configurations.compileOnly
+    }
 }
 
 task sourcesJar(type: Jar, dependsOn: classes) {
-	classifier = 'sources'
-	from sourceSets.main.allSource
+    classifier = 'sources'
+    from sourceSets.main.allSource
 }
 
 task javadocJar(type: Jar, dependsOn: javadoc) {
-	classifier = 'javadoc'
-	from javadoc.destinationDir
+    classifier = 'javadoc'
+    from javadoc.destinationDir
 }
 
-artifacts {
-	archives sourcesJar
-	archives javadocJar
+publishing {
+    publications {
+        mavenJava(MavenPublication) {
+            from components.java
+
+            artifact sourcesJar {
+                classifier "sources"
+            }
+
+            artifact javadocJar {
+                classifier "javadoc"
+            }
+
+            pom {
+                name = 'dom4j'
+                description = 'flexible XML framework for Java'
+                url = 'http://dom4j.github.io/'
+                licenses {
+                    license {
+                        name = 'BSD 3-clause New License'
+                        url = 'https://github.com/dom4j/dom4j/blob/master/LICENSE'
+                    }
+                }
+                developers {
+                    developer {
+                        name = 'Filip Jirsák'
+                        email = 'filip at jirsak.org'
+                        url = 'https://github.com/FilipJirsak'
+                    }
+                }
+                scm {
+                    connection = 'scm:git:git at github.com:dom4j/dom4j.git'
+                    developerConnection = 'scm:git:git at github.com:dom4j/dom4j.git'
+                    url = 'git at github.com:dom4j/dom4j.git'
+                }
+
+                withXml {
+                    asNode().dependencies.dependency.findAll { xmlDep ->
+                        xmlDep.appendNode('optional').value = 'true'
+                    }
+                }
+            }
+        }
+    }
 }
 
 test {
-	useTestNG()
+    useTestNG()
 }
 
 jacocoTestReport {
-	reports {
-		xml.enabled true
-	}
+    reports {
+        xml.enabled true
+    }
 }
 
 check.dependsOn 'jacocoTestReport'
 
 if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) {
-	signing {
-		sign configurations.archives
-	}
-
-	uploadArchives {
-		repositories {
-			mavenDeployer {
-				beforeDeployment {
-					deployment -> signing.signPom(deployment)
-				}
-
-				repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
-					authentication(userName: ossrhUsername, password: ossrhPassword)
-				}
-
-				snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
-					authentication(userName: ossrhUsername, password: ossrhPassword)
-				}
-
-				pom.project {
-					name 'dom4j'
-					packaging 'jar'
-					description 'flexible XML framework for Java'
-					url 'http://dom4j.github.io/'
-					licenses {
-						license {
-							name 'BSD 3-clause New License'
-							url 'https://github.com/dom4j/dom4j/blob/master/LICENSE'
-						}
-					}
-					developers {
-						developer {
-							name 'Filip Jirsák'
-							email 'filip at jirsak.org'
-							url 'https://github.com/FilipJirsak'
-						}
-					}
-					scm {
-						connection 'scm:git:git at github.com:dom4j/dom4j.git'
-						developerConnection 'scm:git:git at github.com:dom4j/dom4j.git'
-						url 'git at github.com:dom4j/dom4j.git'
-					}
-				}
-			}
-		}
-	}
+    publishing {
+        repositories {
+            maven {
+                url 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
+                credentials {
+                    username = ossrhUsername
+                    password = ossrhPassword
+                }
+            }
+
+//		mavenSnapshot{
+//		url "https://oss.sonatype.org/content/repositories/snapshots/"
+//			authentication(userName: ossrhUsername, password: ossrhPassword)
+//		}
+        }
+    }
 }
 
-task wrapper(type: Wrapper) {
-  gradleVersion = '4.1'
-}
+if (project.hasProperty('signing.keyId')) {
+    signing {
+        sign configurations.archives
+    }
+}
\ No newline at end of file


=====================================
src/main/java/org/dom4j/DocumentHelper.java
=====================================
--- a/src/main/java/org/dom4j/DocumentHelper.java
+++ b/src/main/java/org/dom4j/DocumentHelper.java
@@ -18,6 +18,7 @@ import org.dom4j.rule.Pattern;
 import org.jaxen.VariableContext;
 
 import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
 /**
  * <code>DocumentHelper</code> is a collection of helper methods for using
@@ -256,6 +257,8 @@ public final class DocumentHelper {
      * <code>parseText</code> parses the given text as an XML document and
      * returns the newly created Document.
      * </p>
+     *
+     * Loading external DTD and entities is disabled (if it is possible) for security reasons.
      * 
      * @param text
      *            the XML text to be parsed
@@ -267,6 +270,14 @@ public final class DocumentHelper {
      */
     public static Document parseText(String text) throws DocumentException {
         SAXReader reader = new SAXReader();
+        try {
+            reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+            reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        } catch (SAXException e) {
+            //Parse with external resources downloading allowed.
+        }
+
         String encoding = getEncoding(text);
 
         InputSource source = new InputSource(new StringReader(text));


=====================================
src/main/java/org/dom4j/Namespace.java
=====================================
--- a/src/main/java/org/dom4j/Namespace.java
+++ b/src/main/java/org/dom4j/Namespace.java
@@ -49,6 +49,10 @@ public class Namespace extends AbstractNode {
     public Namespace(String prefix, String uri) {
         this.prefix = (prefix != null) ? prefix : "";
         this.uri = (uri != null) ? uri : "";
+
+        if (!this.prefix.isEmpty()) {
+            QName.validateNCName(this.prefix);
+        }
     }
 
     /**


=====================================
src/main/java/org/dom4j/QName.java
=====================================
--- a/src/main/java/org/dom4j/QName.java
+++ b/src/main/java/org/dom4j/QName.java
@@ -11,6 +11,7 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.util.regex.Pattern;
 
 import org.dom4j.tree.QNameCache;
 import org.dom4j.util.SingletonStrategy;
@@ -21,11 +22,86 @@ import org.dom4j.util.SingletonStrategy;
  * object is immutable.
  *
  * @author <a href="mailto:jstrachan at apache.org">James Strachan </a>
+ * @author Filip Jirsák
  */
 public class QName implements Serializable {
     /** The Singleton instance */
     private static SingletonStrategy<QNameCache> singleton = null;
 
+    /**
+     * {@code NameStartChar} without colon.
+     *
+     * <pre>NameStartChar	::=	":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]</pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml/#sec-common-syn">XML 1.0 – 2.3 Common Syntactic Constructs</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn">XML 1.1 – 2.3 Common Syntactic Constructs</a>
+     */
+    private static final String NAME_START_CHAR = "_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
+
+    /**
+     * {@code NameChar} without colon.
+     *
+     * <pre>NameChar	::=	NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]</pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml/#sec-common-syn">XML 1.0 – 2.3 Common Syntactic Constructs</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn">XML 1.1 – 2.3 Common Syntactic Constructs</a>
+     */
+    private static final String NAME_CHAR = NAME_START_CHAR + "-.0-9\u00B7\u0300-\u036F\u203F-\u2040";
+
+    /**
+     * {@code NCName}
+     *
+     * <pre>
+     * NCName		::=	NCNameStartChar NCNameChar*	(An XML Name, minus the ":")
+     * NCNameChar	::=	NameChar -':'
+     * NCNameStartChar	::=	NameStartChar -':'
+     * </pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml-names/#ns-qualnames">Namespaces in XML 1.0 – 4 Qualified Names</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-qualnames">Namespaces in XML 1.1 – 4 Qualified Names</a>
+     */
+    private static final String NCNAME = "["+NAME_START_CHAR+"]["+NAME_CHAR+"]*";
+
+    /**
+     * Regular expression for {@code Name} (with colon).
+     *
+     * <pre>Name	::=	NameStartChar (NameChar)*</pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml/#sec-common-syn">XML 1.0 – 2.3 Common Syntactic Constructs</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-common-syn">XML 1.1 – 2.3 Common Syntactic Constructs</a>
+     */
+    private static final Pattern RE_NAME = Pattern.compile("[:"+NAME_START_CHAR+"][:"+NAME_CHAR+"]*");
+
+    /**
+     * Regular expression for {@code NCName}.
+     *
+     * <pre>
+     * NCName		::=	NCNameStartChar NCNameChar*	(An XML Name, minus the ":")
+     * NCNameChar	::=	NameChar -':'
+     * NCNameStartChar	::=	NameStartChar -':'
+     * </pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml-names/#ns-qualnames">Namespaces in XML 1.0 – 4 Qualified Names</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-qualnames">Namespaces in XML 1.1 – 4 Qualified Names</a>
+     */
+    private static final Pattern RE_NCNAME = Pattern.compile(NCNAME);
+
+    /**
+     * Regular expression for {@code QName}.
+     *
+     * <pre>
+     * QName		::=	PrefixedName | UnprefixedName
+     * PrefixedName	::=	Prefix ':' LocalPart
+     * UnprefixedName	::=	LocalPart
+     * Prefix		::=	NCName
+     * LocalPart	::=	NCName
+     * </pre>
+     *
+     * @see <a href="https://www.w3.org/TR/xml-names/#ns-qualnames">Namespaces in XML 1.0 – 4 Qualified Names</a>
+     * @see <a href="https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-qualnames">Namespaces in XML 1.1 – 4 Qualified Names</a>
+     */
+    private static final Pattern RE_QNAME = Pattern.compile("(?:"+NCNAME+":)?"+NCNAME);
+
     static {
         try {
             String defaultSingletonClass = "org.dom4j.util.SimpleSingleton";
@@ -71,6 +147,11 @@ public class QName implements Serializable {
         this.name = (name == null) ? "" : name;
         this.namespace = (namespace == null) ? Namespace.NO_NAMESPACE
                 : namespace;
+        if (this.namespace.equals(Namespace.NO_NAMESPACE)) {
+            validateName(this.name);
+        } else {
+            validateNCName(this.name);
+        }
     }
 
     public QName(String name, Namespace namespace, String qualifiedName) {
@@ -78,6 +159,8 @@ public class QName implements Serializable {
         this.qualifiedName = qualifiedName;
         this.namespace = (namespace == null) ? Namespace.NO_NAMESPACE
                 : namespace;
+        validateNCName(this.name);
+        validateQName(this.qualifiedName);
     }
 
     public static QName get(String name) {
@@ -251,6 +334,24 @@ public class QName implements Serializable {
         QNameCache cache = singleton.instance();
         return cache;
     }
+
+    private static void validateName(String name) {
+        if (!RE_NAME.matcher(name).matches()) {
+            throw new IllegalArgumentException(String.format("Illegal character in name: '%s'.", name));
+        }
+    }
+
+    protected static void validateNCName(String ncname) {
+        if (!RE_NCNAME.matcher(ncname).matches()) {
+            throw new IllegalArgumentException(String.format("Illegal character in local name: '%s'.", ncname));
+        }
+    }
+
+    private static void validateQName(String qname) {
+        if (!RE_QNAME.matcher(qname).matches()) {
+            throw new IllegalArgumentException(String.format("Illegal character in qualified name: '%s'.", qname));
+        }
+    }
 }
 
 


=====================================
src/main/java/org/dom4j/datatype/SchemaParser.java
=====================================
--- a/src/main/java/org/dom4j/datatype/SchemaParser.java
+++ b/src/main/java/org/dom4j/datatype/SchemaParser.java
@@ -180,15 +180,19 @@ public class SchemaParser {
             DocumentFactory parentFactory) {
         String name = xsdElement.attributeValue("name");
         String type = xsdElement.attributeValue("type");
-        QName qname = getQName(name);
 
-        DatatypeElementFactory factory = getDatatypeElementFactory(qname);
+        QName qname = null;
+        DatatypeElementFactory factory = null;
+        if (name != null) {
+            qname = getQName(name);
+            factory = getDatatypeElementFactory(qname);
+        }
 
         if (type != null) {
             // register type with this element name
             XSDatatype dataType = getTypeByName(type);
 
-            if (dataType != null) {
+            if (dataType != null && factory != null) {
                 factory.setChildElementXSDatatype(qname, dataType);
             } else {
                 QName typeQName = getQName(type);
@@ -205,24 +209,25 @@ public class SchemaParser {
         if (xsdSimpleType != null) {
             XSDatatype dataType = loadXSDatatypeFromSimpleType(xsdSimpleType);
 
-            if (dataType != null) {
+            if (dataType != null && factory != null) {
                 factory.setChildElementXSDatatype(qname, dataType);
             }
         }
 
         Element schemaComplexType = xsdElement.element(XSD_COMPLEXTYPE);
 
-        if (schemaComplexType != null) {
+        if (schemaComplexType != null && factory != null) {
             onSchemaComplexType(schemaComplexType, factory);
         }
 
-        Iterator<Element> iter = xsdElement.elementIterator(XSD_ATTRIBUTE);
-
-        if (iter.hasNext()) {
-            do {
-                onDatatypeAttribute(xsdElement, factory, iter
-                        .next());
-            } while (iter.hasNext());
+        if (factory != null) {
+            Iterator<Element> iter = xsdElement.elementIterator(XSD_ATTRIBUTE);
+            if (iter.hasNext()) {
+                do {
+                    onDatatypeAttribute(xsdElement, factory, iter
+                            .next());
+                } while (iter.hasNext());
+            }
         }
     }
 


=====================================
src/main/java/org/dom4j/io/SAXHelper.java
=====================================
--- a/src/main/java/org/dom4j/io/SAXHelper.java
+++ b/src/main/java/org/dom4j/io/SAXHelper.java
@@ -103,6 +103,21 @@ class SAXHelper {
             throw new SAXException("Couldn't create SAX reader");
         }
 
+        // configure namespace support
+        SAXHelper.setParserFeature(reader, "http://xml.org/sax/features/namespaces", true);
+        SAXHelper.setParserFeature(reader, "http://xml.org/sax/features/namespace-prefixes", false);
+
+        // external entites
+        SAXHelper.setParserFeature(reader, "http://xml.org/sax/properties/external-general-entities", false);
+        SAXHelper.setParserFeature(reader, "http://xml.org/sax/properties/external-parameter-entities", false);
+
+        // external DTD
+        SAXHelper.setParserFeature(reader,"http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+
+
+        // use Locator2 if possible
+        SAXHelper.setParserFeature(reader,"http://xml.org/sax/features/use-locator2", true);
+
         return reader;
     }
 


=====================================
src/main/java/org/dom4j/io/SAXReader.java
=====================================
--- a/src/main/java/org/dom4j/io/SAXReader.java
+++ b/src/main/java/org/dom4j/io/SAXReader.java
@@ -65,11 +65,7 @@ import org.xml.sax.helpers.XMLReaderFactory;
 public class SAXReader {
     private static final String SAX_STRING_INTERNING = 
             "http://xml.org/sax/features/string-interning";
-    private static final String SAX_NAMESPACE_PREFIXES = 
-            "http://xml.org/sax/features/namespace-prefixes";
-    private static final String SAX_NAMESPACES = 
-            "http://xml.org/sax/features/namespaces";
-    private static final String SAX_DECL_HANDLER = 
+    private static final String SAX_DECL_HANDLER =
             "http://xml.org/sax/properties/declaration-handler";
     private static final String SAX_LEXICAL_HANDLER = 
             "http://xml.org/sax/properties/lexical-handler";
@@ -902,27 +898,10 @@ public class SAXReader {
             SAXHelper.setParserProperty(reader, SAX_DECL_HANDLER, handler);
         }
 
-        // configure namespace support
-        SAXHelper.setParserFeature(reader, SAX_NAMESPACES, true);
-
-        SAXHelper.setParserFeature(reader, SAX_NAMESPACE_PREFIXES, false);
-
         // string interning
         SAXHelper.setParserFeature(reader, SAX_STRING_INTERNING,
                 isStringInternEnabled());
 
-        // external entites
-        /*
-         * SAXHelper.setParserFeature( reader,
-         * "http://xml.org/sax/properties/external-general-entities",
-         * includeExternalGeneralEntities ); SAXHelper.setParserFeature( reader,
-         * "http://xml.org/sax/properties/external-parameter-entities",
-         * includeExternalParameterEntities );
-         */
-        // use Locator2 if possible
-        SAXHelper.setParserFeature(reader,
-                "http://xml.org/sax/features/use-locator2", true);
-
         try {
             // configure validation support
             reader.setFeature("http://xml.org/sax/features/validation",


=====================================
src/main/java/org/dom4j/io/XMLWriter.java
=====================================
--- a/src/main/java/org/dom4j/io/XMLWriter.java
+++ b/src/main/java/org/dom4j/io/XMLWriter.java
@@ -574,6 +574,7 @@ public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
     public void writeOpen(Element element) throws IOException {
         writer.write("<");
         writer.write(element.getQualifiedName());
+        writeNamespaces(element);
         writeAttributes(element);
         writer.write(">");
     }
@@ -1206,6 +1207,19 @@ public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
         writer.write("\"");
     }
 
+    /**
+     * Writes all namespaces declared directly on element.
+     *
+     * @throws IOException
+     */
+    protected void writeNamespaces(Element element) throws IOException {
+        assert element != null;
+        for (Namespace ns : element.declaredNamespaces()) {
+            writeNamespace(ns);
+            namespaceStack.push(ns);
+        }
+    }
+
     protected void writeProcessingInstruction(ProcessingInstruction pi)
             throws IOException {
         // indent();
@@ -1631,44 +1645,37 @@ public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
 
         for (i = 0; i < size; i++) {
             String entity = null;
-            char c = text.charAt(i);
 
+            int c = text.codePointAt(i);
             switch (c) {
                 case '<':
                     entity = "<";
-
                     break;
-
                 case '>':
                     entity = ">";
-
                     break;
-
                 case '&':
                     entity = "&";
-
                     break;
-
                 case '\t':
                 case '\n':
                 case '\r':
-
                     // don't encode standard whitespace characters
                     if (preserve) {
-                        entity = String.valueOf(c);
+                        entity = String.valueOf((char) c);
                     }
-
                     break;
 
                 default:
 
                     if ((c < 32) || shouldEncodeChar(c)) {
-                        entity = "&#" + (int) c + ";";
+                        entity = "&#" + c + ";";
                     }
 
                     break;
             }
 
+
             if (entity != null) {
                 if (block == null) {
                     block = text.toCharArray();
@@ -1677,6 +1684,12 @@ public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
                 buffer.append(block, last, i - last);
                 buffer.append(entity);
                 last = i + 1;
+                if (Character.isSupplementaryCodePoint(c)) {
+                    last++;
+                }
+            }
+            if (Character.isSupplementaryCodePoint(c)) {
+                i++;
             }
         }
 
@@ -1725,53 +1738,37 @@ public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
 
         for (i = 0; i < size; i++) {
             String entity = null;
-            char c = text.charAt(i);
+            int c = text.codePointAt(i);
 
             switch (c) {
                 case '<':
                     entity = "<";
-
                     break;
-
                 case '>':
                     entity = ">";
-
                     break;
-
                 case '\'':
-
                     if (quote == '\'') {
                         entity = "'";
                     }
-
                     break;
-
                 case '\"':
-
                     if (quote == '\"') {
                         entity = """;
                     }
-
                     break;
-
                 case '&':
                     entity = "&";
-
                     break;
-
                 case '\t':
                 case '\n':
                 case '\r':
-
                     // don't encode standard whitespace characters
                     break;
-
                 default:
-
                     if ((c < 32) || shouldEncodeChar(c)) {
-                        entity = "&#" + (int) c + ";";
+                        entity = "&#" + c + ";";
                     }
-
                     break;
             }
 
@@ -1783,6 +1780,12 @@ public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
                 buffer.append(block, last, i - last);
                 buffer.append(entity);
                 last = i + 1;
+                if(Character.isSupplementaryCodePoint(c)) {
+                    last++;
+                }
+            }
+            if(Character.isSupplementaryCodePoint(c)) {
+                i++;
             }
         }
 
@@ -1808,15 +1811,15 @@ public class XMLWriter extends XMLFilterImpl implements LexicalHandler {
      * Should the given character be escaped. This depends on the encoding of
      * the document.
      * 
-     * @param c
+     * @param codepoint Unicode codepoint.
      *            DOCUMENT ME!
      * 
      * @return boolean
      */
-    protected boolean shouldEncodeChar(char c) {
+    protected boolean shouldEncodeChar(int codepoint) {
         int max = getMaximumAllowedCharacter();
 
-        return (max > 0) && (c > max);
+        return (max > 0) && (codepoint > max);
     }
 
     /**


=====================================
src/main/java/org/dom4j/tree/QNameCache.java
=====================================
--- a/src/main/java/org/dom4j/tree/QNameCache.java
+++ b/src/main/java/org/dom4j/tree/QNameCache.java
@@ -52,11 +52,15 @@ public class QNameCache {
      */
     public List<QName> getQNames() {
         List<QName> answer = new ArrayList<QName>();
-        answer.addAll(noNamespaceCache.values());
+	synchronized(noNamespaceCache) {
+            answer.addAll(noNamespaceCache.values());
+	}
 
-        for (Map<String, QName> map : namespaceCache.values()) {
-            answer.addAll(map.values());
-        }
+	synchronized(namespaceCache) {
+            for (Map<String, QName> map : namespaceCache.values()) {
+                answer.addAll(map.values());
+            }
+	}
 
         return answer;
     }
@@ -152,6 +156,8 @@ public class QNameCache {
 
         if (index < 0) {
             return get(qualifiedName, Namespace.get(uri));
+        } else if (index == 0){
+            throw new IllegalArgumentException("Qualified name cannot start with ':'.");
         } else {
             String name = qualifiedName.substring(index + 1);
             String prefix = qualifiedName.substring(0, index);
@@ -295,4 +301,4 @@ public class QNameCache {
  * POSSIBILITY OF SUCH DAMAGE.
  * 
  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
- */
\ No newline at end of file
+ */


=====================================
src/test/java/org/dom4j/AllowedCharsTest.java
=====================================
--- /dev/null
+++ b/src/test/java/org/dom4j/AllowedCharsTest.java
@@ -0,0 +1,78 @@
+package org.dom4j;
+
+import org.testng.annotations.Test;
+
+/**
+ * @author Filip Jirsák
+ */
+public class AllowedCharsTest {
+    @Test
+    public void localName() {
+        QName.get("element");
+        QName.get(":element");
+        QName.get("elem:ent");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void localNameFail() {
+        QName.get("!element");
+    }
+
+    @Test
+    public void qname() {
+        QName.get("element", "http://example.com/namespace");
+        QName.get("ns:element", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void qnameFail1() {
+        QName.get("ns:elem:ent", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void qnameFail2() {
+        QName.get(":nselement", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void createElementLT() {
+        DocumentHelper.createElement("element<name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void createElementGT() {
+        DocumentHelper.createElement("element>name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void createElementAmpersand() {
+        DocumentHelper.createElement("element&name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElement() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("element>name");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElementQualified() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("element>name", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElementQualifiedPrefix() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("ns:element>name", "http://example.com/namespace");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void addElementPrefix() {
+        Element root = DocumentHelper.createElement("root");
+        root.addElement("ns>:element", "http://example.com/namespace");
+    }
+
+    //TODO It is illegal to create element or attribute with namespace prefix and empty namespace IRI.
+    //See https://www.w3.org/TR/2006/REC-xml-names11-20060816/#scoping
+}


=====================================
src/test/java/org/dom4j/IteratorTest.java
=====================================
--- a/src/test/java/org/dom4j/IteratorTest.java
+++ b/src/test/java/org/dom4j/IteratorTest.java
@@ -31,7 +31,7 @@ public class IteratorTest extends AbstractTestCase {
         Element root = iterDocument.addElement("root");
 
         for (int i = 0; i < NUMELE; i++) {
-            root.addElement("iterator test").addAttribute("instance",
+            root.addElement("iterator-test").addAttribute("instance",
                     Integer.toString(i));
         }
     }
@@ -42,7 +42,7 @@ public class IteratorTest extends AbstractTestCase {
         Element root = iterDocument.getRootElement();
         assertTrue("Has root element", root != null);
 
-        List elements = root.elements("iterator test");
+        List elements = root.elements("iterator-test");
         int elementSize = elements.size();
         assertTrue("Root has " + elementSize + " children", (elements != null)
                 && (elementSize == NUMELE));
@@ -50,8 +50,8 @@ public class IteratorTest extends AbstractTestCase {
 
     public void testPlainIteration() throws Exception {
         Element root = iterDocument.getRootElement();
-        List elements = root.elements("iterator test");
-        Iterator iter = root.elementIterator("iterator test");
+        List elements = root.elements("iterator-test");
+        Iterator iter = root.elementIterator("iterator-test");
         int elementSize = elements.size();
 
         int count = 0;
@@ -69,8 +69,8 @@ public class IteratorTest extends AbstractTestCase {
 
     public void testSkipAlternates() throws Exception {
         Element root = iterDocument.getRootElement();
-        List elements = root.elements("iterator test");
-        Iterator iter = root.elementIterator("iterator test");
+        List elements = root.elements("iterator-test");
+        Iterator iter = root.elementIterator("iterator-test");
         int elementSize = elements.size();
         int count = 0;
 
@@ -89,8 +89,8 @@ public class IteratorTest extends AbstractTestCase {
 
     public void testNoHasNext() throws Exception {
         Element root = iterDocument.getRootElement();
-        List elements = root.elements("iterator test");
-        Iterator iter = root.elementIterator("iterator test");
+        List elements = root.elements("iterator-test");
+        Iterator iter = root.elementIterator("iterator-test");
         int elementSize = elements.size();
         int count = 0;
         Element e = null;
@@ -121,8 +121,8 @@ public class IteratorTest extends AbstractTestCase {
 
     public void testExtraHasNexts() throws Exception {
         Element root = iterDocument.getRootElement();
-        List elements = root.elements("iterator test");
-        Iterator iter = root.elementIterator("iterator test");
+        List elements = root.elements("iterator-test");
+        Iterator iter = root.elementIterator("iterator-test");
         int elementSize = elements.size();
         int count = 0;
 


=====================================
src/test/java/org/dom4j/XMLWriterTest.java
=====================================
--- a/src/test/java/org/dom4j/XMLWriterTest.java
+++ b/src/test/java/org/dom4j/XMLWriterTest.java
@@ -14,6 +14,7 @@ import org.dom4j.io.SAXReader;
 import org.dom4j.io.XMLWriter;
 import org.dom4j.tree.BaseElement;
 import org.dom4j.tree.DefaultDocument;
+import org.testng.Assert;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
@@ -577,6 +578,115 @@ public class XMLWriterTest extends AbstractTestCase {
         new XMLWriter(new CharArrayWriter(128), format).write(element);
     }
 
+    public void testElementNamespaceWriteDocument() throws IOException {
+        Document document = DocumentHelper.createDocument();
+        Element root = document.addElement("rss")
+                .addNamespace("g", "http://base.google.com/ns/1.0")
+                .addNamespace("c", "http://base.google.com/cns/1.0");
+
+        OutputFormat outputFormat = OutputFormat.createCompactFormat();
+        outputFormat.setSuppressDeclaration(true);
+
+        StringWriter stringWriter = new StringWriter();
+        XMLWriter writer = new XMLWriter(stringWriter, outputFormat);
+        writer.write(document);
+        writer.close();
+
+        Assert.assertEquals(stringWriter.toString(), "<rss xmlns:g=\"http://base.google.com/ns/1.0\" xmlns:c=\"http://base.google.com/cns/1.0\"></rss>");
+    }
+
+    public void testElementNamespaceWriteOpen() throws IOException {
+        Document document = DocumentHelper.createDocument();
+        Element root = document.addElement("rss")
+                .addNamespace("g", "http://base.google.com/ns/1.0")
+                .addNamespace("c", "http://base.google.com/cns/1.0");
+
+        OutputFormat outputFormat = OutputFormat.createCompactFormat();
+        outputFormat.setSuppressDeclaration(true);
+
+        StringWriter stringWriter = new StringWriter();
+        XMLWriter writer = new XMLWriter(stringWriter, outputFormat);
+        writer.writeOpen(root);
+        writer.close();
+
+        Assert.assertEquals(stringWriter.toString(), "<rss xmlns:g=\"http://base.google.com/ns/1.0\" xmlns:c=\"http://base.google.com/cns/1.0\">");
+    }
+
+    public void testElementNamespaceAttributesWriteOpen() throws IOException {
+        Document document = DocumentHelper.createDocument();
+        Element root = document.addElement("rss")
+                .addNamespace("g", "http://base.google.com/ns/1.0")
+                .addNamespace("c", "http://base.google.com/cns/1.0");
+        root.addAttribute("nons", "value");
+        root.addAttribute(QName.get("g:ns"), "value");
+
+        OutputFormat outputFormat = OutputFormat.createCompactFormat();
+        outputFormat.setSuppressDeclaration(true);
+
+        StringWriter stringWriter = new StringWriter();
+        XMLWriter writer = new XMLWriter(stringWriter, outputFormat);
+        writer.writeOpen(root);
+        writer.close();
+
+        Assert.assertEquals(stringWriter.toString(), "<rss xmlns:g=\"http://base.google.com/ns/1.0\" xmlns:c=\"http://base.google.com/cns/1.0\" nons=\"value\" g:ns=\"value\">");
+    }
+
+    public void testPenguin() throws IOException {
+        // U+1F427 PENGUIN
+        final String penguin = "\ud83d\udc27";
+
+        Document document = DocumentHelper.createDocument();
+        document.addElement("doc").setText(penguin);
+
+        OutputFormat outputFormat = OutputFormat.createCompactFormat();
+        outputFormat.setSuppressDeclaration(true);
+
+        StringWriter stringWriter = new StringWriter();
+        XMLWriter writer = new XMLWriter(stringWriter, outputFormat);
+        writer.write(document);
+        writer.close();
+
+        Assert.assertEquals(stringWriter.toString(), "<doc>"+penguin+"</doc>");
+    }
+
+    public void testSurrogatePairElement() throws IOException {
+        // U+1F427 PENGUIN
+        final String penguin = "\ud83d\udc27";
+
+        Document document = DocumentHelper.createDocument();
+        document.addElement("doc").setText(penguin);
+
+        OutputFormat outputFormat = OutputFormat.createCompactFormat();
+        outputFormat.setSuppressDeclaration(true);
+        outputFormat.setEncoding("US-ASCII");
+
+        StringWriter stringWriter = new StringWriter();
+        XMLWriter writer = new XMLWriter(stringWriter, outputFormat);
+        writer.write(document);
+        writer.close();
+
+        Assert.assertEquals(stringWriter.toString(), "<doc>🐧</doc>");
+    }
+
+    public void testSurrogatePairAttribute() throws IOException {
+        // U+1F427 PENGUIN
+        final String penguin = "\ud83d\udc27";
+
+        Document document = DocumentHelper.createDocument();
+        document.addElement("doc").addAttribute("penguin", penguin);
+
+        OutputFormat outputFormat = OutputFormat.createCompactFormat();
+        outputFormat.setSuppressDeclaration(true);
+        outputFormat.setEncoding("US-ASCII");
+
+        StringWriter stringWriter = new StringWriter();
+        XMLWriter writer = new XMLWriter(stringWriter, outputFormat);
+        writer.write(document);
+        writer.close();
+
+        Assert.assertEquals(stringWriter.toString(), "<doc penguin=\"🐧\"/>");
+    }
+
     protected void generateXML(ContentHandler handler) throws SAXException {
         handler.startDocument();
 


=====================================
src/test/java/org/dom4j/dom/DOMTest.java
=====================================
--- a/src/test/java/org/dom4j/dom/DOMTest.java
+++ b/src/test/java/org/dom4j/dom/DOMTest.java
@@ -109,7 +109,7 @@ public class DOMTest extends AbstractTestCase {
         assertEquals(newFirst, firstChild);
 
         /* try to replace a node that doesn't exist */
-        org.w3c.dom.Element badNode = document.createElement("No Child");
+        org.w3c.dom.Element badNode = document.createElement("No-Child");
 
         try {
             parent.replaceChild(newFirst, badNode);


=====================================
src/test/java/org/dom4j/io/DTDTest.java
=====================================
--- a/src/test/java/org/dom4j/io/DTDTest.java
+++ b/src/test/java/org/dom4j/io/DTDTest.java
@@ -445,6 +445,8 @@ public class DTDTest extends AbstractTestCase {
         reader.setEntityResolver(new MyEntityResolver(DTD_FILE,
                 DTD_PUBLICID, DTD_SYSTEM_ID));
 
+        reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", true);
+
         return getDocument(resourceName, reader);
     }
 



View it on GitLab: https://salsa.debian.org/java-team/dom4j/commit/fc1e5d4e924148668e94a9d48263a180b7231875

-- 
View it on GitLab: https://salsa.debian.org/java-team/dom4j/commit/fc1e5d4e924148668e94a9d48263a180b7231875
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20180730/eeb00786/attachment.html>


More information about the pkg-java-commits mailing list