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.LineSeparator; 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> 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, "></" ); write( out, element.getQualifiedName() ); 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, "></" ); write( out, element.getQualifiedName() ); 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, "</" ); write( out, element.getQualifiedName() ); write( out, ">" ); } } finally { fstack.pop(); } } finally { nstack.pop(); } // Technically, nstack.push(), super.printElement(), nstack.pop() // would be enough 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. * * The encoding argument here only sets an attribute in the * XML declaration. It's up to the caller to ensure the writer * is encoding bytes to match. If encoding is null, the default * is "UTF-8". * * LineEndings will be CR-LF. Except for comments!? */ public static void sloppyPrint( Document doc, Writer writer, String encoding ) throws IOException { Format format = Format.getPrettyFormat(); format.setExpandEmptyElements( false ); format.setOmitDeclaration( false ); format.setIndent( "\t" ); format.setLineSeparator( LineSeparator.CRNL ); if ( encoding != null ) format.setEncoding( encoding ); XMLOutputter outputter = new XMLOutputter( format, new SloppyXMLOutputProcessor() ); outputter.output( doc, writer ); } }