From: <dal...@us...> - 2006-07-28 00:35:14
|
Revision: 6441 Author: daleanson Date: 2006-07-27 17:35:08 -0700 (Thu, 27 Jul 2006) ViewCVS: http://svn.sourceforge.net/jedit/?rev=6441&view=rev Log Message: ----------- fix for [ 1520219 ] XML Indenter losing attributes Modified Paths: -------------- plugins/XmlIndenter/trunk/xmlindenter/IndentingTransformer.java Modified: plugins/XmlIndenter/trunk/xmlindenter/IndentingTransformer.java =================================================================== --- plugins/XmlIndenter/trunk/xmlindenter/IndentingTransformer.java 2006-07-27 23:11:41 UTC (rev 6440) +++ plugins/XmlIndenter/trunk/xmlindenter/IndentingTransformer.java 2006-07-28 00:35:08 UTC (rev 6441) @@ -1,24 +1,24 @@ /* - * AbstractIndentingTransformer.java - Indents XML elements, by adding whitespace where appropriate. - * - * Copyright (c) 2002, 2003 Robert McKinnon - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * email: rob...@us... - */ +* AbstractIndentingTransformer.java - Indents XML elements, by adding whitespace where appropriate. +* +* Copyright (c) 2002, 2003 Robert McKinnon +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* email: rob...@us... +*/ package xmlindenter; @@ -30,6 +30,7 @@ import javax.xml.transform.sax.TransformerHandler; import java.io.IOException; import java.io.Writer; +import java.util.regex.*; /** @@ -42,464 +43,497 @@ */ public abstract class IndentingTransformer implements TransformerHandler, DeclHandler { - private static final String ON_NEW_LINE = "onNewLine"; + private static final String ON_NEW_LINE = "onNewLine"; - /** buffer to hold character data */ - private Writer writer; + /** buffer to hold character data */ + private Writer writer; - private String xml; - private char[] chars; - private boolean okToContinue = true; - private boolean isClosingTag = false; - private boolean isEmptyElement = false; - private boolean isDocType = false; + private String xml; + private char[] chars; + private boolean okToContinue = true; + private boolean isClosingTag = false; + private boolean isEmptyElement = false; + private boolean isDocType = false; - public void characters(char ch[], int start, int length) throws SAXException { - try { - if(isDocType) { - isDocType = false; - } else { - writer.write(ch, start, length); - } - } catch(IOException e) { - throw new SAXException(e); + public void characters( char ch[], int start, int length ) throws SAXException { + try { + if ( isDocType ) { + isDocType = false; + } + else { + writer.write( ch, start, length ); + } + } + catch ( IOException e ) { + throw new SAXException( e ); + } } - } - public void comment(char ch[], int start, int length) throws SAXException { - try { - writer.write("<!--"); - writer.write(ch, start, length); - writer.write("-->"); - } catch(IOException e) { - throw new SAXException(e); + public void comment( char ch[], int start, int length ) throws SAXException { + try { + writer.write( "<!--" ); + writer.write( ch, start, length ); + writer.write( "-->" ); + } + catch ( IOException e ) { + throw new SAXException( e ); + } } - } - public void processingInstruction(String target, String data) throws SAXException { - try { - writer.write("<?"); - writer.write(target); - writer.write(" "); - writer.write(data); - writer.write("?>"); - } catch(IOException e) { - throw new SAXException(e); + public void processingInstruction( String target, String data ) throws SAXException { + try { + writer.write( "<?" ); + writer.write( target ); + writer.write( " " ); + writer.write( data ); + writer.write( "?>" ); + } + catch ( IOException e ) { + throw new SAXException( e ); + } } - } - public void endElement(String namespaceURI, String localName, String qName) throws SAXException { - try { - if(isClosingTag) { - writer.write("</"); - writer.write(qName); - writer.write(">"); - isClosingTag = false; - } - } catch(IOException e) { - throw new SAXException(e); + public void endElement( String namespaceURI, String localName, String qName ) throws SAXException { + try { + if ( isClosingTag ) { + writer.write( "</" ); + writer.write( qName ); + writer.write( ">" ); + isClosingTag = false; + } + } + catch ( IOException e ) { + throw new SAXException( e ); + } } - } - public void startElement(String namespaceURI, String localName, String qName, Attributes atts) - throws SAXException { + public void startElement( String namespaceURI, String localName, String qName, Attributes atts ) + throws SAXException { - try { - writer.write("<"); + try { + writer.write( "<" ); - boolean spaceAtEnd = qName.charAt(qName.length() - 1) == ' '; - if(spaceAtEnd) { - writer.write(qName.substring(0, qName.length() - 1)); - } else { - writer.write(qName); - } + boolean spaceAtEnd = qName.charAt( qName.length() - 1 ) == ' '; + if ( spaceAtEnd ) { + writer.write( qName.substring( 0, qName.length() - 1 ) ); + } + else { + writer.write( qName ); + } - for(int i = 0; i < atts.getLength(); i++) { - String attributeQName = atts.getQName(i); - String attributeValue = atts.getValue(i); - boolean onNewLine = (atts.getType(i) == ON_NEW_LINE); + for ( int i = 0; i < atts.getLength(); i++ ) { + String attributeQName = atts.getQName( i ); + String attributeValue = atts.getValue( i ); + boolean onNewLine = ( atts.getType( i ) == ON_NEW_LINE ); - boolean containsDoubleQuote = (attributeValue.indexOf('"') != -1); - char quote = containsDoubleQuote ? '\'' : '\"'; + boolean containsDoubleQuote = ( attributeValue.indexOf( '"' ) != -1 ); + char quote = containsDoubleQuote ? '\'' : '\"'; - if(onNewLine) { - indent(2); - } else { - writer.write(' '); - } + if ( onNewLine ) { + indent( 2 ); + } + else { + writer.write( ' ' ); + } - writer.write(attributeQName); - writer.write('='); - writer.write(quote); - writer.write(attributeValue); - writer.write(quote); - } + writer.write( attributeQName ); + writer.write( '=' ); + writer.write( quote ); + writer.write( attributeValue ); + writer.write( quote ); + } - if(isEmptyElement) { - if(spaceAtEnd) { - writer.write(" />"); // to cater for <br /> elements - } else { - writer.write("/>"); + if ( isEmptyElement ) { + if ( spaceAtEnd ) { + writer.write( " />" ); // to cater for <br /> elements + } + else { + writer.write( "/>" ); + } + } + else { + writer.write( ">" ); + } } - } else { - writer.write(">"); - } - } catch(IOException e) { - throw new SAXException(e); + catch ( IOException e ) { + throw new SAXException( e ); + } + } - } + protected abstract void indent( int levelAdjustment ) throws SAXException; - protected abstract void indent(int levelAdjustment) throws SAXException; + protected String indentXml( final String xmlString, final Writer outputWriter ) throws IOException, SAXException { + this.okToContinue = true; + this.isClosingTag = false; + this.isEmptyElement = false; + this.isDocType = false; + this.writer = outputWriter; + this.xml = xmlString; + this.chars = xml.toCharArray(); - protected String indentXml(final String xmlString, final Writer outputWriter) throws IOException, SAXException { - this.okToContinue = true; - this.isClosingTag = false; - this.isEmptyElement = false; - this.isDocType = false; - this.writer = outputWriter; - this.xml = xmlString; - this.chars = xml.toCharArray(); + int start = 0; + int end = 0; - int start = 0; - int end = 0; + while ( okToContinue ) { + end = xml.indexOf( '<', start ); + writeTextPrecedingLessThan( start, end ); - while(okToContinue) { - end = xml.indexOf('<', start); - writeTextPrecedingLessThan(start, end); + if ( okToContinue ) { + start = end; - if(okToContinue) { - start = end; + if ( xml.startsWith( "<!--", start ) ) { + end = writeComment( start ); - if(xml.startsWith("<!--", start)) { - end = writeComment(start); + } + else if ( xml.startsWith( "<?", start ) ) { + end = writeXmlDeclarationOrProcessingInstruction( start ); - } else if(xml.startsWith("<?", start)) { - end = writeXmlDeclarationOrProcessingInstruction(start); + } + else if ( xml.startsWith( "<!", start ) ) { + if ( xml.startsWith( "<![CDATA[", start ) ) { + end = writeCData( start ); + } + else { + end = writeDocType( start ); + } + } + else if ( xml.startsWith( "</", start ) ) { + end = writeClosingTag( start ); - } else if(xml.startsWith("<!", start)) { - if(xml.startsWith("<![CDATA[", start)) { - end = writeCData(start); - } else { - end = writeDocType(start); - } - } else if(xml.startsWith("</", start)) { - end = writeClosingTag(start); + } + else if ( Character.isWhitespace( chars[ start + 1 ] ) ) { + throw new SAXException( "The content of elements must consist of well-formed character data or markup." ); - } else if(Character.isWhitespace(chars[start+1])) { - throw new SAXException("The content of elements must consist of well-formed character data or markup."); + } + else { + end = writeElement( start ); + } - } else { - end = writeElement(start); + start = end; + } } - start = end; - } + return outputWriter.toString(); } - return outputWriter.toString(); - } + private int writeCData( int start ) throws IOException, SAXException { + int end = xml.indexOf( "]]>", start ); + writeRemaining( start, end ); + if ( okToContinue ) { + startCDATA(); + end = end + 3; + writer.write( xml, start, end - start ); + endCDATA(); + } - private int writeCData(int start) throws IOException, SAXException { - int end = xml.indexOf("]]>", start); - writeRemaining(start, end); - if(okToContinue) { - startCDATA(); - end = end + 3; - writer.write(xml, start, end - start); - endCDATA(); + return end; } - return end; - } + private int writeElement( int start ) throws IOException, SAXException { + int end = getStartTagEnd( start ); + writeRemaining( start, end ); - private int writeElement(int start) throws IOException, SAXException { - int end = getStartTagEnd(start); - writeRemaining(start, end); + if ( okToContinue ) { + int offset = 1; + while ( Character.isWhitespace( chars[ end - offset ] ) ) { + offset++; + } + isEmptyElement = ( chars[ end - offset ] == '/' ); - if(okToContinue) { - int offset = 1; - while(Character.isWhitespace(chars[end - offset])) { - offset++; - } - isEmptyElement = (chars[end - offset] == '/'); + if ( isEmptyElement ) { + end = end - offset; + } - if(isEmptyElement) { - end = end - offset; - } + AttributesImpl attributes = new AttributesImpl(); + String elementName = getElementNameAndPopulateAttributes( start, end, attributes ); - AttributesImpl attributes = new AttributesImpl(); - String elementName = getElementNameAndPopulateAttributes(start, end, attributes); + if ( isEmptyElement && chars[ end - 1 ] == ' ' ) { + elementName += ' '; // to cater for <br /> elements + } - if(isEmptyElement && chars[end - 1] == ' ') { - elementName += ' '; // to cater for <br /> elements - } + startElement( "", "", elementName, attributes ); - startElement("", "", elementName, attributes); + if ( isEmptyElement ) { + endElement( "", "", elementName ); + end = end + offset + 1; + isEmptyElement = false; + } + else { + end = end + 1; + } + } - if(isEmptyElement) { - endElement("", "", elementName); - end = end + offset + 1; - isEmptyElement = false; - } else { - end = end + 1; - } + return end; } - return end; - } + /** + * Ignores '>' characters that are inside of attribute values. + */ + private int getStartTagEnd( int start ) { + int end = -1; + int index = start; - /** - * Ignores '>' characters that are inside of attribute values. - */ - private int getStartTagEnd(int start) { - int end = -1; - int index = start; + while ( index < chars.length && end == -1 ) { + char aChar = chars[ index ]; + index++; - while(index < chars.length && end == -1) { - char aChar = chars[index]; - index++; - - if(aChar == '\"') { - while(chars[index] != '\"') { - index++; + if ( aChar == '\"' ) { + while ( chars[ index ] != '\"' ) { + index++; + } + index++; + } + else if ( aChar == '\'' ) { + while ( chars[ index ] != '\'' ) { + index++; + } + index++; + // } else if(aChar == '/') { + // while(chars[index] != '>') { + // index++; + // } + } + else if ( aChar == '>' ) { + end = index - 1; + // end = index; + } } - index++; - } else if(aChar == '\'') { - while(chars[index] != '\'') { - index++; - } - index++; -// } else if(aChar == '/') { -// while(chars[index] != '>') { -// index++; -// } - } else if(aChar == '>') { - end = index -1; -// end = index; - } + return end; } - return end; - } - private String getElementNameAndPopulateAttributes(int start, int end, AttributesImpl attributes) throws SAXException { - int nameEnd = xml.indexOf(' ', start); + Pattern p = Pattern.compile( "([<].*?)\\s", Pattern.DOTALL ); + private String getElementNameAndPopulateAttributes( int start, int end, AttributesImpl attributes ) throws SAXException { + // element name ends at _first_ white space char + int nameEnd = -1; + Matcher m = p.matcher(xml.substring(start, end)); + boolean found = m.find(); + if (found) { + nameEnd = start + m.end(); + } + else { + nameEnd = xml.indexOf( ' ', start ); + + if ( nameEnd == -1 || nameEnd > end ) { + nameEnd = xml.indexOf( '\n', start ); + } + + if ( nameEnd == -1 || nameEnd > end ) { + nameEnd = xml.indexOf( '\r', start ); + } + } + if ( nameEnd == -1 || nameEnd > end ) { + nameEnd = end; + } + else if ( nameEnd + 1 != end ) { + while ( Character.isWhitespace( chars[ nameEnd - 1 ] ) ) { + nameEnd--; // want to check if char at nameEnd is a new line char + } + char[] elementChars = xml.substring( nameEnd, end ).toCharArray(); + populateAttributes( elementChars, attributes ); + } - if(nameEnd == -1 || nameEnd > end) { - nameEnd = xml.indexOf('\n', start); - } + StringBuffer elementName = new StringBuffer(); + int index = start + 1; - if(nameEnd == -1 || nameEnd > end) { - nameEnd = xml.indexOf('\r', start); - } + while ( !Character.isWhitespace( chars[ index ] ) && chars[ index ] != '>' && chars[ index ] != '/' ) { + elementName.append( chars[ index++ ] ); + } - if(nameEnd == -1 || nameEnd > end) { - nameEnd = end; - } else if(nameEnd + 1 != end) { - while(Character.isWhitespace(chars[nameEnd - 1])) { - nameEnd--; // want to check if char at nameEnd is a new line char - } - char[] elementChars = xml.substring(nameEnd, end).toCharArray(); - populateAttributes(elementChars, attributes); + return elementName.toString(); } - StringBuffer elementName = new StringBuffer(); - int index = start + 1; - while(!Character.isWhitespace(chars[index]) && chars[index] != '>' && chars[index] != '/') { - elementName.append(chars[index++]); - } + private void populateAttributes( char[] chars, AttributesImpl attributes ) throws SAXException { + StringBuffer qName = new StringBuffer(); + StringBuffer value = new StringBuffer(); + boolean isLastSpace = false; + char quote = '\"'; + int i = 0; + boolean attributeOnNewLine; - return elementName.toString(); - } + while ( i < chars.length ) { + attributeOnNewLine = false; + qName.setLength( 0 ); + value.setLength( 0 ); + while ( i < chars.length && Character.isWhitespace( chars[ i ] ) ) { + if ( chars[ i ] == '\r' || chars[ i ] == '\n' ) { + attributeOnNewLine = true; + } + i++; + } - private void populateAttributes(char[] chars, AttributesImpl attributes) throws SAXException { - StringBuffer qName = new StringBuffer(); - StringBuffer value = new StringBuffer(); - boolean isLastSpace = false; - char quote = '\"'; - int i = 0; - boolean attributeOnNewLine; + if ( i < chars.length ) { + while ( i < chars.length && chars[ i ] != '=' ) { + qName.append( chars[ i ] ); + i++; + } + i++; // get past equals - while(i < chars.length) { - attributeOnNewLine = false; - qName.setLength(0); - value.setLength(0); + while ( i < chars.length && Character.isWhitespace( chars[ i ] ) ) { + i++; // get past whitespace before first quote + } - while(i < chars.length && Character.isWhitespace(chars[i])) { - if(chars[i] == '\r' || chars[i] == '\n') { - attributeOnNewLine = true; - } - i++; - } + if ( i < chars.length ) { + if ( chars[ i ] != '\"' && chars[ i ] != '\'' ) { + throw new SAXException( "value for attribute " + qName.toString().trim() + " must be in quotes" ); + } + else { + quote = chars[ i ]; + i++; // get past first quote + } + } - if(i < chars.length) { - while(i < chars.length && chars[i] != '=') { - qName.append(chars[i]); - i++; - } - i++; // get past equals + while ( i < chars.length && chars[ i ] != quote ) { + if ( Character.isWhitespace( chars[ i ] ) ) { + if ( !isLastSpace ) { + if ( chars[ i ] == '\f' || chars[ i ] == '\n' || chars[ i ] == '\r' || chars[ i ] == '\t' || chars[ i ] == ' ' ) { + value.append( ' ' ); + } + isLastSpace = true; + } + else { + // don't add consecutive space + } + } + else { + value.append( chars[ i ] ); + isLastSpace = false; + } - while(i < chars.length && Character.isWhitespace(chars[i])) { - i++; // get past whitespace before first quote - } + i++; + } - if(i < chars.length) { - if(chars[i] != '\"' && chars[i] != '\'') { - throw new SAXException("value for attribute " + qName.toString().trim() + " must be in quotes"); - } else { - quote = chars[i]; - i++; // get past first quote - } - } + i++; // get past quote - while(i < chars.length && chars[i] != quote) { - if(Character.isWhitespace(chars[i])) { - if(!isLastSpace) { - if(chars[i] == '\f' || chars[i] == '\n' || chars[i] == '\r' || chars[i] == '\t' || chars[i] == ' ') { - value.append(' '); - } - isLastSpace = true; - } else { - // don't add consecutive space + String type = attributeOnNewLine ? ON_NEW_LINE : ""; + attributes.addAttribute( "", "", qName.toString().trim(), type, value.toString() ); } - } else { - value.append(chars[i]); - isLastSpace = false; - } - - i++; } - - i++; // get past quote - - String type = attributeOnNewLine ? ON_NEW_LINE : ""; - attributes.addAttribute("", "", qName.toString().trim(), type, value.toString()); - } } - } - private int writeClosingTag(int start) throws IOException, SAXException { - int end = xml.indexOf('>', start); - writeRemaining(start, end); + private int writeClosingTag( int start ) throws IOException, SAXException { + int end = xml.indexOf( '>', start ); + writeRemaining( start, end ); - if(okToContinue) { - isClosingTag = true; - endElement("", "", xml.substring(start + 2, end).trim()); - end = end + 1; + if ( okToContinue ) { + isClosingTag = true; + endElement( "", "", xml.substring( start + 2, end ).trim() ); + end = end + 1; + } + return end; } - return end; - } - private int writeDocType(int start) throws IOException { - int end = xml.indexOf('>', start); - int bracketStart = xml.indexOf('[', start); + private int writeDocType( int start ) throws IOException { + int end = xml.indexOf( '>', start ); + int bracketStart = xml.indexOf( '[', start ); - if(bracketStart != -1 && bracketStart < end) { - int bracketEnd = xml.indexOf(']', bracketStart); - end = xml.indexOf('>', bracketEnd); - } + if ( bracketStart != -1 && bracketStart < end ) { + int bracketEnd = xml.indexOf( ']', bracketStart ); + end = xml.indexOf( '>', bracketEnd ); + } - writeRemaining(start, end); + writeRemaining( start, end ); - if(okToContinue) { - end = end + 1; - writer.write("\n"); - int length = end - start; - writer.write(xml, start, length); - isDocType = true; + if ( okToContinue ) { + end = end + 1; + writer.write( "\n" ); + int length = end - start; + writer.write( xml, start, length ); + isDocType = true; + } + + return end; } - return end; - } + private int writeXmlDeclarationOrProcessingInstruction( int start ) throws IOException, SAXException { + int end; - private int writeXmlDeclarationOrProcessingInstruction(int start) throws IOException, SAXException { - int end; + if ( xml.startsWith( "<?xml ", start ) ) { + end = writeXmlDeclaration( start ); + } + else { + end = writeProcessingInstruction( start ); + } - if(xml.startsWith("<?xml ", start)) { - end = writeXmlDeclaration(start); - } else { - end = writeProcessingInstruction(start); + return end; } - return end; - } + private int writeProcessingInstruction( int start ) throws IOException, SAXException { + int end = xml.indexOf( "?>", start ); + writeRemaining( start, end ); - private int writeProcessingInstruction(int start) throws IOException, SAXException { - int end = xml.indexOf("?>", start); - writeRemaining(start, end); + if ( okToContinue ) { + int targetEnd = xml.indexOf( " ", start ); + String target = xml.substring( start + "<?".length(), targetEnd ); + String data = xml.substring( targetEnd + 1, end ); + processingInstruction( target, data ); + end = end + "?>".length(); + } - if(okToContinue) { - int targetEnd = xml.indexOf(" ", start); - String target = xml.substring(start + "<?".length(), targetEnd); - String data = xml.substring(targetEnd + 1, end); - processingInstruction(target, data); - end = end + "?>".length(); + return end; } - return end; - } + private int writeXmlDeclaration( int start ) throws IOException { + int end = xml.indexOf( "?>", start ); + writeRemaining( start, end ); - private int writeXmlDeclaration(int start) throws IOException { - int end = xml.indexOf("?>", start); - writeRemaining(start, end); + if ( okToContinue ) { + end = end + "?>".length(); + int length = end - start; + writer.write( xml, start, length ); + } - if(okToContinue) { - end = end + "?>".length(); - int length = end - start; - writer.write(xml, start, length); + return end; } - return end; - } + private int writeComment( int start ) throws IOException, SAXException { + int end = xml.indexOf( "-->", start ); + writeRemaining( start, end ); - private int writeComment(int start) throws IOException, SAXException { - int end = xml.indexOf("-->", start); - writeRemaining(start, end); + if ( okToContinue ) { + int commentTextStart = start + "<!--".length(); + int commentTextLength = end - commentTextStart; + comment( chars, commentTextStart, commentTextLength ); + end = end + "-->".length(); + } - if(okToContinue) { - int commentTextStart = start + "<!--".length(); - int commentTextLength = end - commentTextStart; - comment(chars, commentTextStart, commentTextLength); - end = end + "-->".length(); + return end; } - return end; - } + private void writeTextPrecedingLessThan( int start, int end ) throws IOException, SAXException { + writeRemaining( start, end ); - private void writeTextPrecedingLessThan(int start, int end) throws IOException, SAXException { - writeRemaining(start, end); - - if(okToContinue && end > start) { - int length = end - start; - characters(chars, start, length); + if ( okToContinue && end > start ) { + int length = end - start; + characters( chars, start, length ); + } } - } - private void writeRemaining(int start, int end) throws IOException { - if(end == -1) { - int length = xml.length() - start; - writer.write(xml, start, length); - okToContinue = false; + private void writeRemaining( int start, int end ) throws IOException { + if ( end == -1 ) { + int length = xml.length() - start; + writer.write( xml, start, length ); + okToContinue = false; + } } - } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |