diff --git a/src/main/java/net/vhati/modmanager/core/SloppyXMLOutputProcessor.java b/src/main/java/net/vhati/modmanager/core/SloppyXMLOutputProcessor.java new file mode 100644 index 0000000..8bb278f --- /dev/null +++ b/src/main/java/net/vhati/modmanager/core/SloppyXMLOutputProcessor.java @@ -0,0 +1,157 @@ +package net.vhati.modmanager.core; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +import org.jdom2.Attribute; +import org.jdom2.Content; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.jdom2.output.support.AbstractXMLOutputProcessor; +import org.jdom2.output.support.FormatStack; +import org.jdom2.output.support.Walker; +import org.jdom2.util.NamespaceStack; + + +/** + * An XMLOutputProcessor that omits the root element. + * + * The root element will be invisible. + * The root's namespace declarations will still count. + * The root's immediate child content will not be indented. + * + * Adding namespace declarations to the root will prevent + * descendents from having an xmlns:prefix="uri" attribute. + * + * To use, construct an XMLOutputter with this as an arg. + * Then call outputter.output( doc, writer ). + * + * Or just call the static sloppyPrint() method below. + * + * @see org.jdom2.output.XMLOutputter + */ +public class SloppyXMLOutputProcessor extends AbstractXMLOutputProcessor { + + // Copied from AbstractXMLOutputProcessor in JDOM 2.0.5, with modification. + @Override + protected void printElement( Writer out, FormatStack fstack, NamespaceStack nstack, Element element ) throws IOException { + nstack.push( element ); + try { + final List content = element.getContent(); + + if ( !element.isRootElement() ) { + write( out, "<" ); + write( out, element.getQualifiedName() ); + for ( final Namespace ns : nstack.addedForward() ) { + printNamespace( out, fstack, ns ); + } + if ( element.hasAttributes() ) { + for ( final Attribute attribute : element.getAttributes() ) { + printAttribute( out, fstack, attribute ); + } + } + + if ( content.isEmpty() ) { + if ( fstack.isExpandEmptyElements() ) { + write( out, ">" ); + } else { + write( out, " />" ); + } + return; + } + } + + if ( element.isRootElement() ) { + // Undo indention that comes with push(). + String prevIndent = fstack.getLevelIndent(); + fstack.push(); + fstack.setLevelIndent( prevIndent ); + } + else { + fstack.push(); + } + + try { + final String space = element.getAttributeValue( "space", Namespace.XML_NAMESPACE ); + if ( "default".equals(space) ) { + fstack.setTextMode( fstack.getDefaultMode() ); + } + else if ( "preserve".equals(space) ) { + fstack.setTextMode( Format.TextMode.PRESERVE ); + } + + Walker walker = buildWalker( fstack, content, true ); + if ( !walker.hasNext() ) { + // The walker has formatted away whatever content we had. + // But there WAS content, so expand the tag. + // Omitted: /> + if ( !element.isRootElement() ) { + write( out, ">" ); + } + return; + } + // We have some content. + + if ( !element.isRootElement() ) { + write( out, ">" ); + } + + if ( !walker.isAllText() ) { + // We need to newline/indent. + textRaw( out, fstack.getPadBetween() ); + } + + printContent( out, fstack, nstack, walker ); + + if ( !walker.isAllText() ) { + // We need to newline/indent. + textRaw( out, fstack.getPadLast() ); + } + + if ( !element.isRootElement() ) { + write( out, "" ); + } + } + finally { + fstack.pop(); + } + } + finally { + nstack.pop(); + } + + // Technically, nstack.push(), super.printElement(), nstack.pop() + // would do to make namespaces look already declared. + // But to avoid having to loop over root's child content to feed + // XMLOutputter means writing most of this method just for + // root (omitting the tag printing parts). + // And to expand tags that have blank content while still + // trimming means writing the whole method (excluding root with + // if blocks). + } + + + /** + * Creates an outputter and writes an XML tree. + */ + public static void sloppyPrint( Document doc, Writer writer, String encoding ) throws IOException { + Format format = Format.getPrettyFormat(); + format.setExpandEmptyElements( false ); + format.setOmitDeclaration( false ); + + if ( encoding != null ) format.setEncoding( encoding ); + + XMLOutputter outputter = new XMLOutputter( format, new SloppyXMLOutputProcessor() ); + outputter.output( doc, writer ); + } +}