Author: krosenvold Date: Sat Sep 12 21:30:18 2015 New Revision: 1702682 URL: http://svn.apache.org/r1702682 Log: Improved performance of PrettyPrintXMLWriter by approx 45%. Reduced garbage production by 80%
Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java?rev=1702682&r1=1702681&r2=1702682&view=diff ============================================================================== --- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java (original) +++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java Sat Sep 12 21:30:18 2015 @@ -19,11 +19,11 @@ package org.apache.maven.shared.utils.xm * under the License. */ +import org.apache.maven.shared.utils.Os; + import java.io.PrintWriter; import java.io.Writer; -import java.util.LinkedList; - -import org.apache.maven.shared.utils.Os; +import java.util.ArrayList; /** * XMLWriter with nice indentation @@ -31,9 +31,15 @@ import org.apache.maven.shared.utils.Os; public class PrettyPrintXMLWriter implements XMLWriter { + private static final char[] CLOSE_1 = "/>".toCharArray(); + + private static final char[] CLOSE_2 = "</".toCharArray(); + + private static final char[] DEFAULT_LINE_INDENT = new char[]{ ' ', ' ' }; + private PrintWriter writer; - private LinkedList<String> elementStack = new LinkedList<String>(); + private ArrayList<String> elementStack = new ArrayList<String>(); private boolean processingElement = false; @@ -43,9 +49,9 @@ public class PrettyPrintXMLWriter private int depth = 0; - private String lineIndent; + private char[] lineIndent; - private String lineSeparator; + private char[] lineSeparator; private String encoding; @@ -93,7 +99,7 @@ public class PrettyPrintXMLWriter */ public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String encoding, String doctype ) { - this( writer, lineIndent, Os.LINE_SEP, encoding, doctype ); + this( writer, lineIndent.toCharArray(), Os.LINE_SEP.toCharArray(), encoding, doctype ); } /** @@ -114,7 +120,7 @@ public class PrettyPrintXMLWriter */ public PrettyPrintXMLWriter( PrintWriter writer, String encoding, String doctype ) { - this( writer, " ", encoding, doctype ); + this( writer, DEFAULT_LINE_INDENT, Os.LINE_SEP.toCharArray(), encoding, doctype ); } /** @@ -137,6 +143,19 @@ public class PrettyPrintXMLWriter public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String lineSeparator, String encoding, String doctype ) { + this( writer, lineIndent.toCharArray(), lineSeparator.toCharArray(), encoding, doctype ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param lineSeparator could be null, but the normal way is valid line separator + * @param encoding could be null or the encoding to use. + * @param doctype could be null. + */ + private PrettyPrintXMLWriter( PrintWriter writer, char[] lineIndent, char[] lineSeparator, String encoding, + String doctype ) + { this.writer = writer; this.lineIndent = lineIndent; this.lineSeparator = lineSeparator; @@ -156,7 +175,7 @@ public class PrettyPrintXMLWriter writer.write( ' ' ); writer.write( key ); writer.write( '=' ); - writer.write( XMLEncode.xmlEncodeTextForAttribute( value, '"' ) ); + XMLEncode.xmlEncodeTextAsPCDATA( value, true, '"', writer ); } public void setEncoding( String encoding ) @@ -186,7 +205,7 @@ public class PrettyPrintXMLWriter throw new IllegalStateException( "Document headers already written!" ); } - this.lineSeparator = lineSeparator; + this.lineSeparator = lineSeparator.toCharArray(); } public void setLineIndenter( String lineIndent ) @@ -196,7 +215,7 @@ public class PrettyPrintXMLWriter throw new IllegalStateException( "Document headers already written!" ); } - this.lineIndent = lineIndent; + this.lineIndent = lineIndent.toCharArray(); } public void startElement( String elementName ) @@ -214,9 +233,8 @@ public class PrettyPrintXMLWriter writer.write( elementName ); processingElement = true; - depth++; - elementStack.addLast( elementName ); + elementStack.add( depth++, elementName ); } public void writeText( String text ) @@ -225,7 +243,7 @@ public class PrettyPrintXMLWriter completePreviouslyOpenedElement(); - writer.write( XMLEncode.xmlEncodeText( text ) ); + XMLEncode.xmlEncodeText( text, writer ); endOnSameLine = true; } @@ -241,14 +259,12 @@ public class PrettyPrintXMLWriter public void endElement() { - depth--; - + String chars = elementStack.get( --depth ); if ( processingElement ) { // this means we don't have any content yet so we just add a /> - writer.write( "/>" ); + writer.write( CLOSE_1 ); - elementStack.removeLast(); processingElement = false; } else @@ -259,7 +275,9 @@ public class PrettyPrintXMLWriter } // otherwise we need a full closing tag for that element - writer.write( "</" + elementStack.removeLast() + ">" ); + writer.write( CLOSE_2 ); + writer.write( chars ); + writer.write( '>' ); } endOnSameLine = false; @@ -267,7 +285,7 @@ public class PrettyPrintXMLWriter /** * Write the documents if not already done. - * + * * @return <code>true</code> if the document headers have freshly been written. */ private boolean ensureDocumentStarted() @@ -293,7 +311,9 @@ public class PrettyPrintXMLWriter if ( encoding != null ) { - writer.write( " encoding=\"" + encoding + "\"" ); + writer.write( " encoding=\"" ); + writer.write( encoding ); + writer.write( '\"' ); } writer.write( "?>" ); @@ -303,7 +323,9 @@ public class PrettyPrintXMLWriter if ( docType != null ) { newLine(); - writer.write( "<!DOCTYPE " + docType + ">" ); + writer.write( "<!DOCTYPE " ); + writer.write( docType ); + writer.write( '>' ); } } Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java?rev=1702682&r1=1702681&r2=1702682&view=diff ============================================================================== --- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java (original) +++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java Sat Sep 12 21:30:18 2015 @@ -19,6 +19,10 @@ package org.apache.maven.shared.utils.xm * under the License. */ +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; + /** * Collection of XML encoding/decoding helpers. <br> * This is all about the special characters & and <, and for attributes @@ -28,6 +32,7 @@ final class XMLEncode { private static final int CDATA_BLOCK_THRESHOLD_LENGTH = 12; + private static final char DEFAULT_QUOTE_CHAR = '"'; /** @@ -68,24 +73,44 @@ final class XMLEncode { return null; } - if ( !needsEncoding( text ) ) + StringWriter writer = new StringWriter( text.length() * 2 ); + xmlEncodeText( text, writer ); + return writer.toString(); + } + + public static void xmlEncodeText( String text, Writer writer ) + { + if ( text == null ) { - return text; + return; } - else + try { - // only encode as cdata if is is longer than CDATA block overhead: - if ( text.length() > CDATA_BLOCK_THRESHOLD_LENGTH ) + if ( !needsEncoding( text ) ) + { + writer.write( text ); + return; + } + else { - String cdata = xmlEncodeTextAsCDATABlock( text ); - if ( cdata != null ) + // only encode as cdata if is is longer than CDATA block overhead: + if ( text.length() > CDATA_BLOCK_THRESHOLD_LENGTH ) { - return cdata; + String cdata = xmlEncodeTextAsCDATABlock( text ); + if ( cdata != null ) + { + writer.write( cdata ); + return; + } } } } + catch ( IOException e ) + { + throw new RuntimeException( e ); + } // if every thing else fails, do it the save way... - return xmlEncodeTextAsPCDATA( text ); + xmlEncodeTextAsPCDATA( text, false, DEFAULT_QUOTE_CHAR, writer ); } /** @@ -124,83 +149,98 @@ final class XMLEncode { return null; } - char c; - int length = text.length(); - StringBuilder n = new StringBuilder( length * 2 ); - if ( forAttribute ) - { - n.append( quoteChar ); - } - - for ( int i = 0; i < length; i++ ) - { - c = text.charAt( i ); - switch ( c ) - { - case '&': - n.append( "&" ); - break; - case '<': - n.append( "<" ); - break; - case '>': // FIX for sourceforge bug #802520 ("]]>" needs encoding) - n.append( ">" ); - break; - case '"': - if ( forAttribute ) - { - n.append( """ ); - } - else - { - n.append( c ); - } - break; - case '\'': - if ( forAttribute ) - { - n.append( "'" ); - } - else - { - n.append( c ); - } - break; - case '\r': - if ( forAttribute ) - { - if ( i == ( length - 1 ) || text.charAt( i + 1 ) != '\n' ) + StringWriter writer = new StringWriter( text.length() * 2 ); + xmlEncodeTextAsPCDATA( text, forAttribute, quoteChar, writer ); + return writer.toString(); + } + + public static void xmlEncodeTextAsPCDATA( String text, boolean forAttribute, char quoteChar, Writer n ) + { + if ( text == null ) + { + return; + } + try + { + char c; + int length = text.length(); + if ( forAttribute ) + { + n.append( quoteChar ); + } + + for ( int i = 0; i < length; i++ ) + { + c = text.charAt( i ); + switch ( c ) + { + case '&': + n.append( "&" ); + break; + case '<': + n.append( "<" ); + break; + case '>': // FIX for sourceforge bug #802520 ("]]>" needs encoding) + n.append( ">" ); + break; + case '"': + if ( forAttribute ) { - n.append( " " ); + n.append( """ ); } - } - else - { - n.append( c ); - } - // but skip the \r in \r\n + else + { + n.append( c ); + } + break; + case '\'': + if ( forAttribute ) + { + n.append( "'" ); + } + else + { + n.append( c ); + } + break; + case '\r': + if ( forAttribute ) + { + if ( i == ( length - 1 ) || text.charAt( i + 1 ) != '\n' ) + { + n.append( " " ); + } + } + else + { + n.append( c ); + } + // but skip the \r in \r\n + break; + case '\n': + if ( forAttribute ) + { + n.append( " " ); + } + break; - break; - case '\n': - if ( forAttribute ) - { - n.append( " " ); - } - break; + default: + n.append( c ); + break; + } + } - default: - n.append( c ); - break; + if ( forAttribute ) + { + n.append( quoteChar ); } } - - if ( forAttribute ) + catch ( IOException e ) { - n.append( quoteChar ); + throw new RuntimeException( e ); } - return n.toString(); } /**