package org.jdom.output; import java.io.*; import java.util.*; import org.jdom.*; /** *
XMLOutputter
takes a JDOM tree and formats it to a
* stream as XML. The outputter can manage many styles of document
* formatting, from untouched to pretty printed. The default constructor
* creates an outputter to output the document exactly as created.
* Constructor parameters control the indent amount and whether new lines
* are printed between elements. The other parameters are configurable
* through the set*
methods.
* The XML declaration is always printed on its own line. Empty elements
* are by default printed as <empty/> but that can be configured.
* Text-only contents are printed as <tag>content</tag> on a
* single line.
For compact machine-readable output create a default * XMLOutputter and call setTextNormalize(true) to normalize any whitespace * that was preserved from the source.
* *For pretty output, set the indent to " ", set the new lines feature * to true, and set text trimming to true.
* * There are output(...)
methods to print any of the
* standard JDOM classes, including Document
and
* Element
, to either a Writer
or an
* OutputStream
. Warning: When outputting to a Writer, make
* sure the writer's encoding matches the encoding setting in the
* XMLOutputter. This ensures the encoding in which the content is written
* (controlled by the Writer configuration) matches the encoding placed in
* the document's XML declaration (controlled by the XMLOutputter). Because a
* Writer cannot be queried for its encoding, the information must be passed
* to the XMLOutputter manually in its constructor or via the setEncoding()
* method. The default XMLOutputter encoding is UTF-8.
*
The methods outputString(...)
are for convenience
* only; for top performance you should call output(...)
* and pass in your own Writer
or
* OutputStream
to if possible.
false
*/
private boolean omitDeclaration = false;
/** The encoding format */
private String encoding = "UTF-8";
/** Whether or not to output the encoding in the XML declaration
* - default is false
*/
private boolean omitEncoding = false;
/** The default indent is no spaces (as original document) */
private String indent = null;
/** Whether or not to expand empty elements to
* <tagName></tagName> - default is false
*/
private boolean expandEmptyElements = false;
/** The default new line flag, set to do new lines only as in
* original document */
private boolean newlines = false;
/** New line separator */
private String lineSeparator = STANDARD_LINE_SEPARATOR;
/** Should we preserve whitespace or not in text nodes */
private boolean textNormalize = false;
/**
* Our own null subclass of NamespaceStack. This plays a little
* trick with Java access protection. We want subclasses of
* XMLOutputter to be able to override protected methods that
* declare a NamespaceStack parameter, but we don't want to
* declare the parent NamespaceStack class as public.
**/
protected class NamespaceStack
extends org.jdom.output.NamespaceStack
{
}
/**
*
* This will create an XMLOutputter
with
* no additional whitespace (indent or new lines) added;
* the whitespace from the element text content is fully preserved.
*
* This will create an XMLOutputter
with
* the given indent added but no new lines added;
* all whitespace from the element text content is included as well.
*
* This will create an XMLOutputter
with
* the given indent that prints newlines only if newlines
is
* true
;
* all whitespace from the element text content is included as well.
*
String
, usually some number
* of spaces
* @param newlines true
indicates new lines should be
* printed, else new lines are ignored (compacted).
*/
public XMLOutputter(String indent, boolean newlines) {
this.indent = indent;
this.newlines = newlines;
}
/**
*
* This will create an XMLOutputter
with
* the given indent and new lines printing only if newlines is
* true
, and encoding format encoding
.
*
String
, usually some number
* of spaces
* @param newlines true
indicates new lines should be
* printed, else new lines are ignored (compacted).
* @param encoding set encoding format. Use XML-style names like
* "UTF-8" or "ISO-8859-1" or "US-ASCII"
*/
public XMLOutputter(String indent, boolean newlines, String encoding) {
this.indent = indent;
this.newlines = newlines;
this.encoding = encoding;
}
/**
* This will create an XMLOutputter
with all the
* options as set in the given XMLOutputter
. Note
* that XMLOutputter two = (XMLOutputter)one.clone();
* would work equally well.
* This will take the pre-defined entities in XML 1.0 and * convert their character representation to the appropriate * entity reference, suitable for XML attributes. It does * no converstion for ' because it's not necessary as the outputter * writes attributes surrounded by double-quotes. *
* * @param stString
input to escape.
* @return String
with escaped content.
*/
protected String escapeAttributeEntities(String st) {
StringBuffer buff = new StringBuffer();
char[] block = st.toCharArray();
String stEntity = null;
int i, last;
for (i=0, last=0; i < block.length; i++) {
switch(block[i]) {
case '<' :
stEntity = "<";
break;
case '>' :
stEntity = ">";
break;
/*
case '\'' :
stEntity = "'";
break;
*/
case '\"' :
stEntity = """;
break;
case '&' :
stEntity = "&";
break;
default :
/* no-op */ ;
}
if (stEntity != null) {
buff.append(block, last, i - last);
buff.append(stEntity);
stEntity = null;
last = i + 1;
}
}
if(last < block.length) {
buff.append(block, last, i - last);
}
return buff.toString();
}
/**
* * This will take the three pre-defined entities in XML 1.0 * (used specifically in XML elements) and * convert their character representation to the appropriate * entity reference, suitable for XML element. *
* * @param stString
input to escape.
* @return String
with escaped content.
*/
protected String escapeElementEntities(String st) {
StringBuffer buff = new StringBuffer();
char[] block = st.toCharArray();
String stEntity = null;
int i, last;
for (i=0, last=0; i < block.length; i++) {
switch(block[i]) {
case '<' :
stEntity = "<";
break;
case '>' :
stEntity = ">";
break;
case '&' :
stEntity = "&";
break;
default :
/* no-op */ ;
}
if (stEntity != null) {
buff.append(block, last, i - last);
buff.append(stEntity);
stEntity = null;
last = i + 1;
}
}
if (last < block.length) {
buff.append(block, last, i - last);
}
return buff.toString();
}
// returns the text of the element, taking "textNormalize" into account
private String getElementText(Element element) {
return textNormalize ? element.getTextNormalize() : element.getText();
}
/**
* * This will print the proper indent characters for the given indent level. *
* * @param outWriter
to write to
* @param level int
indentation level
*/
protected void indent(Writer out, int level) throws IOException {
if (indent != null && !indent.equals("")) {
for (int i = 0; i < level; i++) {
out.write(indent);
}
}
}
// returns true if this is an empty element (meaning no content, or only
// whitespace content with textNormalize true)
private boolean isEmpty(Element element) {
// Calculate if the content is empty
// We can handle "" string same as empty (CDATA has no effect here)
List eltContent = element.getContent();
// An element with absolutely no content is empty
if (eltContent.size() == 0) {
return true;
}
// An element with only string content, whose string content
// adds up to nothing, is empty
if (isStringOnly(eltContent)) {
String elementText = getElementText(element);
if (elementText == null || elementText.equals("")) {
return true;
}
}
// anything else is not empty
return false;
}
// Return true if the element's content list consists only of
// String or CDATA nodes (or is empty)
private boolean isStringOnly(List eltContent) {
// Calculate if the contents are String/CDATA only
Iterator itr = eltContent.iterator();
while (itr.hasNext()) {
Object o = itr.next();
if (!(o instanceof String) && !(o instanceof CDATA)) {
return false;
}
}
return true;
}
// true if string is all whitespace (space, tab, cr, lf only)
private boolean isWhitespace(String s) {
char[] c = s.toCharArray();
for (int i=0; iWriter
to write to
*/
protected void maybePrintln(Writer out) throws IOException {
maybePrintln(out, 0);
}
/**
* * This will print a new line only if the newlines flag was set to * true, and then print indents (only if indent is non-null) *
* * @param outWriter
to write to
* @param indentLevel current indent level (number of tabs)
*/
protected void maybePrintln(Writer out, int indentLevel) throws IOException {
if (newlines) {
out.write(lineSeparator);
}
indent(out, indentLevel);
}
/**
* *
Print out a {@link java.lang.String}
. Perfoms
* the necessary entity escaping and whitespace stripping.
String
to output.
* @param out OutputStream
to write to.
**/
public void output(String string, OutputStream out) throws IOException {
Writer writer = makeWriter(out);
output(string, writer); // output() flushes
}
// * * * * * String * * * * *
/**
* Print out a {@link java.lang.String}
. Perfoms
* the necessary entity escaping and whitespace stripping.
String
to output.
* @param out Writer
to write to.
**/
public void output(String string, Writer out) throws IOException {
printString(string, out);
out.flush();
}
/**
*
* Print out a {@link CDATA}
*
CDATA
to output.
* @param out OutputStream
to write to.
**/
public void output(CDATA cdata, OutputStream out) throws IOException {
Writer writer = makeWriter(out);
output(cdata, writer); // output() flushes
}
// * * * * * CDATA * * * * *
/**
*
* Print out a {@link CDATA}
*
CDATA
to output.
* @param out Writer
to write to.
**/
public void output(CDATA cdata, Writer out) throws IOException {
printCDATA(cdata, out);
out.flush();
}
/**
*
* Print out a {@link Comment}
*
Comment
to output.
* @param out OutputStream
to write to.
**/
public void output(Comment comment, OutputStream out) throws IOException {
Writer writer = makeWriter(out);
output(comment, writer); // output() flushes
}
// * * * * * Comment * * * * *
/**
*
* Print out a {@link Comment}
*
Comment
to output.
* @param out Writer
to write to.
**/
public void output(Comment comment, Writer out) throws IOException {
printComment(comment, out);
out.flush();
}
/**
*
* Print out a {@link DocType}
*
DocType
to output.
* @param out OutputStream
to write to.
**/
public void output(DocType doctype, OutputStream out) throws IOException {
Writer writer = makeWriter(out);
output(doctype, writer); // output() flushes
}
/**
*
* Print out a {@link DocType}
*
DocType
to output.
* @param out Writer
to write to.
**/
public void output(DocType doctype, Writer out) throws IOException {
printDocType(doctype, out);
out.flush();
}
/**
*
* This will print the Document
to the given output stream.
* The characters are printed using the encoding specified in the
* constructor, or a default of UTF-8.
*
Document
to format.
* @param out OutputStream
to write to.
* @throws IOException - if there's any problem writing.
*/
public void output(Document doc, OutputStream out)
throws IOException {
Writer writer = makeWriter(out);
output(doc, writer); // output() flushes
}
/**
* This will print the Document
to the given
* Writer.
*
Warning: using your own Writer may cause the outputter's * preferred character encoding to be ignored. If you use * encodings other than UTF-8, we recommend using the method that * takes an OutputStream instead.
* * @param docDocument
to format.
* @param out Writer
to write to.
* @throws IOException - if there's any problem writing.
**/
public void output(Document doc, Writer out) throws IOException {
printDeclaration(doc, out, encoding);
if (doc.getDocType() != null) {
printDocType(doc.getDocType(), out);
}
// Print out root element, as well as any root level
// comments and processing instructions,
// starting with no indentation
Iterator i = doc.getContent().iterator();
while (i.hasNext()) {
Object obj = i.next();
if (obj instanceof Element) {
printElement(doc.getRootElement(), out, 0,
createNamespaceStack());
}
else if (obj instanceof Comment) {
printComment((Comment) obj, out);
}
else if (obj instanceof ProcessingInstruction) {
printProcessingInstruction((ProcessingInstruction) obj, out);
}
maybePrintln(out);
}
// Output final line separator
out.write(lineSeparator);
out.flush();
}
/**
*
* Print out an {@link Element}
, including
* its {@link Attribute}
s, and its value, and all
* contained (child) elements etc.
*
Element
to output.
* @param out Writer
to write to.
**/
public void output(Element element, OutputStream out) throws IOException {
Writer writer = makeWriter(out);
output(element, writer); // output() flushes
}
// * * * * * Element * * * * *
/**
*
* Print out an {@link Element}
, including
* its {@link Attribute}
s, and its value, and all
* contained (child) elements etc.
*
Element
to output.
* @param out Writer
to write to.
**/
public void output(Element element, Writer out) throws IOException {
// If this is the root element we could pre-initialize the
// namespace stack with the namespaces
printElement(element, out, 0, createNamespaceStack());
out.flush();
}
/**
*
* Print out an {@link EntityRef}
.
*
EntityRef
to output.
* @param out OutputStream
to write to.
**/
public void output(EntityRef entity, OutputStream out) throws IOException {
Writer writer = makeWriter(out);
output(entity, writer); // output() flushes
}
// * * * * * EntityRef * * * * *
/**
* Print out an {@link EntityRef}
.
*
EntityRef
to output.
* @param out Writer
to write to.
**/
public void output(EntityRef entity, Writer out) throws IOException {
printEntityRef(entity, out);
out.flush();
}
/**
*
* Print out a {@link ProcessingInstruction}
*
ProcessingInstruction
* to output.
* @param out OutputStream
to write to.
**/
public void output(ProcessingInstruction pi, OutputStream out)
throws IOException {
Writer writer = makeWriter(out);
output(pi, writer); // output() flushes
}
// * * * * * ProcessingInstruction * * * * *
/**
*
* Print out a {@link ProcessingInstruction}
*
ProcessingInstruction
to output.
* @param out Writer
to write to.
**/
public void output(ProcessingInstruction pi, Writer out)
throws IOException {
printProcessingInstruction(pi, out);
out.flush();
}
/**
* *
Print out a {@link Text}
. Perfoms
* the necessary entity escaping and whitespace stripping.
Text
to output.
* @param out OutputStream
to write to.
**/
public void output(Text text, OutputStream out) throws IOException {
Writer writer = makeWriter(out);
output(text, writer); // output() flushes
}
// * * * * * Text * * * * *
/**
* Print out a {@link Text}
. Perfoms
* the necessary entity escaping and whitespace stripping.
Text
to output.
* @param out Writer
to write to.
**/
public void output(Text text, Writer out) throws IOException {
printString(text.getValue(), out);
out.flush();
}
/**
* This will handle printing out an {@link
* Element}
's content only, not including its tag, and
* attributes. This can be useful for printing the content of an
* element that contains HTML, like "<description>JDOM is
* <b>fun>!</description>".
Element
to output.
* @param out OutputStream
to write to.
**/
public void outputElementContent(Element element, OutputStream out)
throws IOException {
Writer writer = makeWriter(out);
outputElementContent(element, writer); // output() flushes
}
/**
* This will handle printing out an {@link
* Element}
's content only, not including its tag, and
* attributes. This can be useful for printing the content of an
* element that contains HTML, like "<description>JDOM is
* <b>fun>!</description>".
Element
to output.
* @param out Writer
to write to.
**/
public void outputElementContent(Element element, Writer out)
throws IOException {
List eltContent = element.getContent();
printElementContent(element, out, 0, createNamespaceStack(),
eltContent);
out.flush();
}
/**
* Return a string representing a CDATA section. Warning: a String is
* Unicode, which may not match the outputter's specified
* encoding.
*
* @param cdata CDATA
to format.
**/
public String outputString(CDATA cdata) {
StringWriter out = new StringWriter();
try {
output(cdata, out); // output() flushes
} catch (IOException e) { }
return out.toString();
}
/**
* Return a string representing a comment. Warning: a String is
* Unicode, which may not match the outputter's specified
* encoding.
*
* @param comment Comment
to format.
**/
public String outputString(Comment comment) {
StringWriter out = new StringWriter();
try {
output(comment, out); // output() flushes
} catch (IOException e) { }
return out.toString();
}
/**
* Return a string representing a DocType. Warning: a String is
* Unicode, which may not match the outputter's specified
* encoding.
*
* @param doc DocType
to format.
**/
public String outputString(DocType doctype) {
StringWriter out = new StringWriter();
try {
output(doctype, out); // output() flushes
} catch (IOException e) { }
return out.toString();
}
// * * * * * String * * * * *
/**
* Return a string representing a document. Uses an internal
* StringWriter. Warning: a String is Unicode, which may not match
* the outputter's specified encoding.
*
* @param doc Document
to format.
**/
public String outputString(Document doc) {
StringWriter out = new StringWriter();
try {
output(doc, out); // output() flushes
} catch (IOException e) { }
return out.toString();
}
/**
* Return a string representing an element. Warning: a String is
* Unicode, which may not match the outputter's specified
* encoding.
*
* @param doc Element
to format.
**/
public String outputString(Element element) {
StringWriter out = new StringWriter();
try {
output(element, out); // output() flushes
} catch (IOException e) { }
return out.toString();
}
/**
* Return a string representing an entity. Warning: a String is
* Unicode, which may not match the outputter's specified
* encoding.
*
* @param doc EntityRef
to format.
**/
public String outputString(EntityRef entity) {
StringWriter out = new StringWriter();
try {
output(entity, out); // output() flushes
} catch (IOException e) { }
return out.toString();
}
/**
* Return a string representing a PI. Warning: a String is
* Unicode, which may not match the outputter's specified
* encoding.
*
* @param doc ProcessingInstruction
to format.
**/
public String outputString(ProcessingInstruction pi) {
StringWriter out = new StringWriter();
try {
output(pi, out); // output() flushes
} catch (IOException e) { }
return out.toString();
}
/**
* parse command-line arguments of the form -omitEncoding
* -indentSize 3 ...
* @return int index of first parameter that we didn't understand
**/
public int parseArgs(String[] args, int i) {
for (; i{@link Attribute}
list.
*
*
* @param attributes List
of Attribute objcts
* @param out Writer
to write to
*/
protected void printAttributes(List attributes, Element parent,
Writer out, NamespaceStack namespaces)
throws IOException {
// I do not yet handle the case where the same prefix maps to
// two different URIs. For attributes on the same element
// this is illegal; but as yet we don't throw an exception
// if someone tries to do this
Set prefixes = new HashSet();
Iterator itr = attributes.iterator();
while (itr.hasNext()) {
Attribute attribute = (Attribute)itr.next();
Namespace ns = attribute.getNamespace();
if (ns != Namespace.NO_NAMESPACE && ns != Namespace.XML_NAMESPACE) {
String prefix = ns.getPrefix();
String uri = namespaces.getURI(prefix);
if (!ns.getURI().equals(uri)) { // output a new namespace decl
printNamespace(ns, out);
namespaces.push(ns);
}
}
out.write(" ");
out.write(attribute.getQualifiedName());
out.write("=");
out.write("\"");
out.write(escapeAttributeEntities(attribute.getValue()));
out.write("\"");
}
}
/**
*
* This will handle printing out an {@link CDATA}
,
* and its value.
*
CDATA
to output.
* @param out Writer
to write to.
*/
protected void printCDATA(CDATA cdata, Writer out)
throws IOException {
out.write("");
}
/**
* * This will write the comment to the specified writer. *
* * @param commentComment
to write.
* @param out Writer
to write to.
*/
protected void printComment(Comment comment, Writer out)
throws IOException {
out.write("");
}
// * * * * * Internal printing methods * * * * *
/**
* * This will write the declaration to the given Writer. * Assumes XML version 1.0 since we don't directly know. *
* * @param docDocument
whose declaration to write.
* @param out Writer
to write to.
* @param encoding The encoding to add to the declaration
*/
protected void printDeclaration(Document doc,
Writer out,
String encoding) throws IOException {
// Only print the declaration if it's not being omitted
if (!omitDeclaration) {
// Assume 1.0 version
out.write("");
// Print new line after decl always, even if no other new lines
// Helps the output look better and is semantically
// inconsequential
out.write(lineSeparator);
}
}
/**
* * This will write the DOCTYPE declaration if one exists. *
* * @param docDocument
whose declaration to write.
* @param out Writer
to write to.
*/
protected void printDocType(DocType docType, Writer out)
throws IOException {
if (docType == null) {
return;
}
String publicID = docType.getPublicID();
String systemID = docType.getSystemID();
boolean hasPublic = false;
out.write("");
// Print new line after decl always, even if no other new lines
// Helps the output look better and is semantically
// inconsequential
out.write(lineSeparator);
}
/**
*
* This will handle printing out an {@link Element}
,
* its {@link Attribute}
s, and its value.
*
Element
to output.
* @param out Writer
to write to.
* @param indent int
level of indention.
* @param namespaces List
stack of Namespaces in scope.
*/
protected void printElement(Element element, Writer out,
int indentLevel, NamespaceStack namespaces)
throws IOException {
// This method prints from the beginning < to the trailing >.
// (no leading or trailing whitespace!)
List eltContent = element.getContent();
// Print the beginning of the tag plus attributes and any
// necessary namespace declarations
out.write("<");
out.write(element.getQualifiedName());
// Mark our namespace starting point
int previouslyDeclaredNamespaces = namespaces.size();
// Print the element's namespace, if appropriate
printElementNamespace(element, out, namespaces);
// Print out additional namespace declarations
printAdditionalNamespaces(element, out, namespaces);
printAttributes(element.getAttributes(), element, out, namespaces);
// Calculate if the contents are String/CDATA only
// This helps later with the "empty" check
boolean stringOnly = true;
Iterator itr = eltContent.iterator();
while (itr.hasNext()) {
Object o = itr.next();
if (!(o instanceof String) && !(o instanceof CDATA)) {
stringOnly = false;
break;
}
}
// Calculate if the content is empty
// We can handle "" string same as empty (CDATA has no effect here)
boolean empty = false;
if (stringOnly) {
String elementText =
textNormalize ? element.getTextNormalize() : element.getText();
if (elementText == null || elementText.equals("")) {
empty = true;
}
}
// If empty, print closing; if not empty, print content
if (empty) {
// Simply close up
if (!expandEmptyElements) {
out.write(" />");
}
else {
out.write(">");
out.write(element.getQualifiedName());
out.write(">");
}
}
else {
// We know it's not null or empty from above
out.write(">");
printElementContent(element, out, indentLevel + 1,
namespaces, eltContent);
out.write("");
out.write(element.getQualifiedName());
out.write(">");
}
// remove declared namespaces from stack
while (namespaces.size() > previouslyDeclaredNamespaces) {
namespaces.pop();
}
}
/**
* This will handle printing out an {@link
* Element}
's content only, not including its tag,
* attributes, and namespace info.
Element
to output.
* @param out Writer
to write to.
* @param indent int
level of indentation.
**/
protected void printElementContent(Element element, Writer out,
int indentLevel,
NamespaceStack namespaces,
List eltContent) throws IOException {
// get same local flags as printElement does
// a little redundant code-wise, but not performance-wise
boolean empty = eltContent.size() == 0;
// Calculate if the content is String/CDATA only
boolean stringOnly = true;
if (!empty) {
stringOnly = isStringOnly(eltContent);
}
if (stringOnly) {
Class justOutput = null;
boolean endedWithWhite = false;
Iterator itr = eltContent.iterator();
while (itr.hasNext()) {
Object content = itr.next();
if (content instanceof String) {
String scontent = (String) content;
if ((justOutput == CDATA.class) &&
(textNormalize) &&
(startsWithWhite(scontent))) {
out.write(" ");
}
printString(scontent, out);
endedWithWhite = endsWithWhite(scontent);
justOutput = String.class;
} else {
// We're in a CDATA section
if ((justOutput == String.class) &&
(textNormalize) &&
(endedWithWhite)) {
out.write(" "); // padding
}
printCDATA((CDATA)content, out);
justOutput = CDATA.class;
}
}
} else {
// Iterate through children
Object content = null;
Class justOutput = null;
boolean endedWithWhite = false;
boolean wasFullyWhite = false;
Iterator itr = eltContent.iterator();
while (itr.hasNext()) {
content = itr.next();
// See if text, an element, a PI or a comment
if (content instanceof Comment) {
if (!((justOutput == String.class) && (wasFullyWhite))) {
maybePrintln(out, indentLevel);
}
printComment((Comment) content, out);
justOutput = Comment.class;
} else if (content instanceof String) {
String scontent = (String) content;
// bugfix: don't print lines for whitespace that's
// only sticking between close tags
if (textNormalize && isWhitespace(scontent)) {
wasFullyWhite = true;
continue;
}
if ((justOutput == CDATA.class) &&
(textNormalize) &&
(startsWithWhite(scontent))) {
out.write(" ");
} else if ((justOutput != CDATA.class) &&
(justOutput != String.class) &&
(justOutput != null)
// (justOutput != Element.class)
)
{
maybePrintln(out, indentLevel);
}
// if scontent is not a single-character newline
if (!((scontent.length() == 1) &&
("\r\n".indexOf(scontent.charAt(0)) != -1))) {
printString(scontent, out);
endedWithWhite = endsWithWhite(scontent);
justOutput = String.class;
wasFullyWhite = (scontent.trim().length() == 0);
}
} else if (content instanceof Element) {
if (!((justOutput == String.class) && (wasFullyWhite))) {
maybePrintln(out, indentLevel);
}
printElement((Element) content, out,
indentLevel, namespaces);
justOutput = Element.class;
} else if (content instanceof EntityRef) {
if (!((justOutput == String.class) && (wasFullyWhite))) {
maybePrintln(out, indentLevel);
}
printEntityRef((EntityRef) content, out);
justOutput = EntityRef.class;
} else if (content instanceof ProcessingInstruction) {
if (!((justOutput == String.class) && (wasFullyWhite))) {
maybePrintln(out, indentLevel);
}
printProcessingInstruction((ProcessingInstruction) content,
out);
justOutput = ProcessingInstruction.class;
} else if (content instanceof CDATA) {
if ((justOutput == String.class) &&
(textNormalize) &&
(endedWithWhite)) {
out.write(" "); // padding
}
else if (justOutput != String.class &&
justOutput != CDATA.class) {
maybePrintln(out, indentLevel);
}
printCDATA((CDATA)content, out);
justOutput = CDATA.class;
}
// Unsupported types are *not* printed, nor should they exist
}
maybePrintln(out, indentLevel - 1);
}
} // printElementContent
private void printElementNamespace(Element element, Writer out,
NamespaceStack namespaces)
throws IOException {
// Add namespace decl only if it's not the XML namespace and it's
// not the NO_NAMESPACE with the prefix "" not yet mapped
// (we do output xmlns="" if the "" prefix was already used and we
// need to reclaim it for the NO_NAMESPACE)
Namespace ns = element.getNamespace();
if (ns != Namespace.XML_NAMESPACE &&
!(ns == Namespace.NO_NAMESPACE && namespaces.getURI("") == null)) {
String prefix = ns.getPrefix();
String uri = namespaces.getURI(prefix);
if (!ns.getURI().equals(uri)) { // output a new namespace decl
namespaces.push(ns);
printNamespace(ns, out);
}
}
}
/**
*
* This will handle printing out an {@link EntityRef}
.
* Only the entity reference such as &entity;
* will be printed. However, subclasses are free to override
* this method to print the contents of the entity instead.
*
EntityRef
to output.
* @param out Writer
to write to. */
protected void printEntityRef(EntityRef entity, Writer out)
throws IOException {
out.write(new StringBuffer()
.append("&")
.append(entity.getName())
.append(";")
.toString());
}
/**
*
* This will handle printing out any needed {@link Namespace}
* declarations.
*
Namespace
to print definition of
* @param out Writer
to write to.
*/
private void printNamespace(Namespace ns, Writer out) throws IOException {
out.write(" xmlns");
String prefix = ns.getPrefix();
if (!prefix.equals("")) {
out.write(":");
out.write(prefix);
}
out.write("=\"");
out.write(ns.getURI());
out.write("\"");
}
/**
* * This will write the processing instruction to the specified writer. *
* * @param commentProcessingInstruction
to write.
* @param out Writer
to write to.
*/
protected void printProcessingInstruction(ProcessingInstruction pi,
Writer out) throws IOException {
String target = pi.getTarget();
String rawData = pi.getData();
// Write or if no data then just
if (!"".equals(rawData)) {
out.write("");
out.write(target);
out.write(" ");
out.write(rawData);
out.write("?>");
}
else {
out.write("");
out.write(target);
out.write("?>");
}
}
/**
* Print a string. Escapes the element entities, trims interior
* whitespace if necessary.
**/
protected void printString(String s, Writer out) throws IOException {
s = escapeElementEntities(s);
// patch by Brad Morgan to strip interior whitespace
// (Brad.Morgan@e-pubcorp.com)
if (textNormalize) {
StringTokenizer tokenizer = new StringTokenizer(s);
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
out.write(token);
if (tokenizer.hasMoreTokens()) {
out.write(" ");
}
}
}
else {
out.write(s);
}
}
/**
* Sets the output encoding. The name should be an accepted XML
* encoding.
*
* @param encoding the encoding format. Use XML-style names like
* "UTF-8" or "ISO-8859-1" or "US-ASCII"
**/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
*
* This will set whether empty elements are expanded from
* <tagName>
to
* <tagName></tagName>
.
*
boolean
indicating whether or not
* empty elements should be expanded.
*/
public void setExpandEmptyElements(boolean expandEmptyElements) {
this.expandEmptyElements = expandEmptyElements;
}
/**
* This will set the indent String
to use; this
* is usually a String
of empty spaces. If you pass
* null, or the empty string (""), then no indentation will
* happen.
String
to use for indentation.
**/
public void setIndent(String indent) {
// if passed the empty string, change it to null, for marginal
// performance gains later (can compare to null first instead
// of calling equals())
if ("".equals(indent))
indent = null;
this.indent = indent;
}
/**
* Set the indent on or off. If setting on, will use the value of
* STANDARD_INDENT, which is usually two spaces.
*
* @param doIndent if true, set indenting on; if false, set indenting off
**/
public void setIndent(boolean doIndent) {
if (doIndent) {
this.indent = STANDARD_INDENT;
}
else {
this.indent = null;
}
}
/**
* Set the initial indentation level.
*
* @deprecated Deprecated in beta7, because this is better done with a
* stacked FilterOutputStream
*/
public void setIndentLevel(int indentLevel) { }
/**
*
* This will set the indent String
's size; an indentSize
* of 4 would result in the indentation being equivalent to the
* String
" " (four space chars).
*
int
number of spaces in indentation.
*/
public void setIndentSize(int indentSize) {
StringBuffer indentBuffer = new StringBuffer();
for (int i=0; i\r\n
. Note that if the "newlines" property is
* false, this value is irrelevant. To make it output the system
* default line ending string, call
* setLineSeparator(System.getProperty("line.separator"))
*
*
*
* To output "UNIX-style" documents, call
* setLineSeparator("\n")
. To output "Mac-style"
* documents, call setLineSeparator("\r")
. DOS-style
* documents use CR-LF ("\r\n"), which is the default.
*
* Note that this only applies to newlines generated by the
* outputter. If you parse an XML document that contains newlines
* embedded inside a text node, and you do not call
* setTextNormalize
, then the newlines will be output
* verbatim, as "\n" which is how parsers normalize them.
*
String
line separator to use.
**/
public void setLineSeparator(String separator) {
lineSeparator = separator;
}
/**
* @see #setLineSeparator(String)
* @param newlines true
indicates new lines should be
* printed, else new lines are ignored (compacted).
**/
public void setNewlines(boolean newlines) {
this.newlines = newlines;
}
/**
*
* This will set whether the XML declaration
* (<?xml version="1.0"?>
)
* will be omitted or not. It is common to omit this in uses such
* as SOAP and XML-RPC calls.
*
boolean
indicating whether or not
* the XML declaration should be omitted.
*/
public void setOmitDeclaration(boolean omitDeclaration) {
this.omitDeclaration = omitDeclaration;
}
/**
*
* This will set whether the XML declaration
* (<?xml version="1.0" encoding="UTF-8"?>
)
* includes the encoding of the document. It is common to omit
* this in uses such as WML and other wireless device protocols.
*
boolean
indicating whether or not
* the XML declaration should indicate the document encoding.
*/
public void setOmitEncoding(boolean omitEncoding) {
this.omitEncoding = omitEncoding;
}
/**
* Ensure that text immediately preceded by or followed by an * element will be "padded" with a single space.
* * @deprecated Deprecated in beta7, because this is no longer necessary */ public void setPadText(boolean padText) { } /** *
* This will set whether the XML declaration
* (<?xml version="1.0"?>
)
* will be suppressed or not. It is common to suppress this in uses such
* as SOAP and XML-RPC calls.
*
boolean
indicating whether or not
* the XML declaration should be suppressed.
* @deprecated Deprecated in beta7, use setOmitDeclaration() instead
*/
public void setSuppressDeclaration(boolean suppressDeclaration) {
this.omitDeclaration = suppressDeclaration;
}
/**
* This will set whether the text is output verbatim (false)
* or with whitespace normalized as per {@link
* org.jdom.Element#getTextNormalize()}
.
* *
Default: false
* * @param textNormalizeboolean
true=>normalize the
* whitespace, false=>use text verbatim
**/
public void setTextNormalize(boolean textNormalize) {
this.textNormalize = textNormalize;
}
/**
* This will set whether the text is output verbatim (false) * or with whitespace stripped.
* *
Default: false
* * @param trimTextboolean
true=>trim the whitespace,
* false=>use text verbatim
*
* @deprecated Deprecated in beta7, use setTextNormalize() instead
**/
public void setTrimText(boolean textTrim) {
this.textNormalize = textTrim;
}
private boolean startsWithWhite(String s) {
return (s.length() > 0 && s.charAt(0) <= ' ');
}
private String toByte(char ch) {
switch (ch) {
case '\r':
return "\\r";
case '\n':
return "\\n";
case '\t':
return "\\t";
default:
return ("[" + ((int)ch) + "]");
}
}
private String toBytes(String x) {
StringBuffer buf = new StringBuffer();
for (int i=0; i