org.jdom2.output.support.FormatStack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdom Show documentation
Show all versions of jdom Show documentation
A complete, Java-based solution for accessing, manipulating,
and outputting XML data
/*--
Copyright (C) 2000-2007 Jason Hunter & Brett McLaughlin.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the disclaimer that follows
these conditions in the documentation and/or other materials
provided with the distribution.
3. The name "JDOM" must not be used to endorse or promote products
derived from this software without prior written permission. For
written permission, please contact .
4. Products derived from this software may not be called "JDOM", nor
may "JDOM" appear in their name, without prior written permission
from the JDOM Project Management .
In addition, we request (but do not require) that you include in the
end-user documentation provided with the redistribution and/or in the
software itself an acknowledgement equivalent to the following:
"This product includes software developed by the
JDOM Project (http://www.jdom.org/)."
Alternatively, the acknowledgment may be graphical using the logos
available at http://www.jdom.org/images/logos.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
This software consists of voluntary contributions made by many
individuals on behalf of the JDOM Project and was originally
created by Jason Hunter and
Brett McLaughlin . For more information
on the JDOM Project, please see .
*/
package org.jdom2.output.support;
import org.jdom2.internal.ArrayCopy;
import org.jdom2.output.EscapeStrategy;
import org.jdom2.output.Format;
import org.jdom2.output.Format.TextMode;
/**
* FormatStack implements a mechanism where the formatting details can be
* changed mid-tree, but then get reverted when that tree segment is
* complete.
*
* This class is intended as a working-class for in the various outputter
* implementations. It is inly public so that people extending the
* Abstract*Processor classes can take advantage of it's functionality.
*
* The value this class adds is:
*
* - Fast -
*
*
* @since JDOM2
* @author Rolf Lear
*/
public final class FormatStack {
private int capacity = 16; // can grow if more than 16 levels in XML
private int depth = 0; // current level in XML
/*
* ====================================================================
* The following values cannot be changed mid-way through the output
* ====================================================================
*/
private final TextMode defaultMode; // the base/initial Text mode
/** The default indent is no spaces (as original document) */
private final String indent;
/** The encoding format */
private final String encoding;
/** New line separator */
private final String lineSeparator;
/**
* Whether or not to output the XML declaration - default is
* false
*/
private final boolean omitDeclaration;
/**
* Whether or not to output the encoding in the XML declaration -
* default is false
*/
private final boolean omitEncoding;
/**
* Whether or not to expand empty elements to
* <tagName></tagName> - default is false
*/
private final boolean expandEmptyElements;
/**
* Whether or not to output 'specified' Attributes only
*/
private final boolean specifiedAttributesOnly;
/** entity escape logic */
private final EscapeStrategy escapeStrategy;
/*
* ====================================================================
* The following values can be changed mid-way through the output, hence
* they are arrays.
* ====================================================================
*/
/** The 'current' accumulated indent */
private String[] levelIndent = new String[capacity];
/** The 'current' End-Of-Line */
private String[] levelEOL = new String[capacity];
/** The padding to put between content items */
private String[] levelEOLIndent = new String[capacity];
/** The padding to put after the last item (typically one less indent) */
private String[] termEOLIndent = new String[capacity];
/**
* Whether TrAX output escaping disabling/enabling PIs are ignored or
* processed - default is false
*/
private boolean[] ignoreTrAXEscapingPIs = new boolean[capacity];
/** text handling mode */
private TextMode[] mode = new TextMode[capacity];
/** escape Output logic - can be changed by */
private boolean[] escapeOutput = new boolean[capacity];
/**
* Creates a new FormatStack seeded with the specified Format
*
* @param format
* the Format instance to seed the stack with.
*/
public FormatStack(Format format) {
indent = format.getIndent();
lineSeparator = format.getLineSeparator();
encoding = format.getEncoding();
omitDeclaration = format.getOmitDeclaration();
omitEncoding = format.getOmitEncoding();
expandEmptyElements = format.getExpandEmptyElements();
escapeStrategy = format.getEscapeStrategy();
defaultMode = format.getTextMode();
specifiedAttributesOnly = format.isSpecifiedAttributesOnly();
levelIndent[depth] = format.getIndent() == null
? null : "";
levelEOL[depth] = format.getLineSeparator();
levelEOLIndent[depth] = levelIndent[depth] == null ?
null : levelEOL[depth];
termEOLIndent[depth] = levelEOLIndent[depth];
ignoreTrAXEscapingPIs[depth] = format.getIgnoreTrAXEscapingPIs();
mode[depth] = format.getTextMode();
escapeOutput[depth] = true;
}
/**
* If the indent strategy changes part way through a stack, we need to
* clear the previously calculated reusable 'lower' levels of the stack.
*/
private final void resetReusableIndents() {
int d = depth + 1;
while (d < levelIndent.length && levelIndent[d] != null) {
// all subsequent forays in to lower levels will need to be redone
levelIndent[d] = null;
d++;
}
}
/**
* @return the original {@link Format#getIndent()}, may be null
*/
public String getIndent() {
return indent;
}
/**
* @return the original {@link Format#getLineSeparator()}
*/
public String getLineSeparator() {
return lineSeparator;
}
/**
* @return the original {@link Format#getEncoding()}
*/
public String getEncoding() {
return encoding;
}
/**
* @return the original {@link Format#getOmitDeclaration()}
*/
public boolean isOmitDeclaration() {
return omitDeclaration;
}
/**
* Indicate whether only those Attributes specified in the XML
* should be output.
* @return true if only the specified Attributes should be output,
* false if those Attributes defaulted from the DTD or XML schema
* should be output too.
*/
public boolean isSpecifiedAttributesOnly() {
return specifiedAttributesOnly;
}
/**
* @return the original {@link Format#getOmitEncoding()}
*/
public boolean isOmitEncoding() {
return omitEncoding;
}
/**
* @return the original {@link Format#getExpandEmptyElements()}
*/
public boolean isExpandEmptyElements() {
return expandEmptyElements;
}
/**
* @return the original {@link Format#getEscapeStrategy()}
*/
public EscapeStrategy getEscapeStrategy() {
return escapeStrategy;
}
/**
* @return the current depth's {@link Format#getIgnoreTrAXEscapingPIs()}
*/
public boolean isIgnoreTrAXEscapingPIs() {
return ignoreTrAXEscapingPIs[depth];
}
/**
* Set the current depth's {@link Format#getIgnoreTrAXEscapingPIs()}
*
* @param ignoreTrAXEscapingPIs
* the boolean value to set.
*/
public void setIgnoreTrAXEscapingPIs(boolean ignoreTrAXEscapingPIs) {
this.ignoreTrAXEscapingPIs[depth] = ignoreTrAXEscapingPIs;
}
/**
* The escapeOutput flag can be set or unset. When set, Element text and
* Attribute values are 'escaped' so that the output is valid XML. When
* unset, the Element text and Attribute values are not escaped.
*
* @return the current depth's escapeOutput flag.
*/
public boolean getEscapeOutput() {
return escapeOutput[depth];
}
/**
* The escapeOutput flag can be set or unset. When set, Element text and
* Attribute values are 'escaped' so that the output is valid XML. When
* unset, the Element text and Attribute values are not escaped.
*
* @param escape
* what to set the current level's escapeOutput flag to.
*/
public void setEscapeOutput(boolean escape) {
escapeOutput[depth] = escape;
}
/**
* @return the TextMode that was originally set for this stack before
* any modifications.
*/
public TextMode getDefaultMode() {
return defaultMode;
}
/**
* @return the current depth's accumulated/maintained indent, may be null
*/
public String getLevelIndent() {
return levelIndent[depth];
}
/**
* Get the end-of-line indenting sequence for before the first item in an
* Element, as well as between subsequent items (but not after the last item)
* @return the String EOL sequence followed by an indent. Null if it should
* be ignored
*/
public String getPadBetween() {
return levelEOLIndent[depth];
}
/**
* Get the end-of-line indenting sequence for after the last item in an
* Element
* @return the String EOL sequence followed by an indent. Null if it should
* be ignored
*/
public String getPadLast() {
return termEOLIndent[depth];
}
/**
* Override the current depth's accumulated line indent.
*
* @param indent
* the indent to set.
*/
public void setLevelIndent(String indent) {
this.levelIndent[depth] = indent;
levelEOLIndent[depth] = (indent == null || levelEOL[depth] == null) ?
null : (levelEOL[depth] + indent);
resetReusableIndents();
}
/**
* @return the current depth's End-Of-Line sequence, may be null
*/
public String getLevelEOL() {
return levelEOL[depth];
}
/**
* Set the current depth's End-Of-Line sequence
*
* @param newline
* the new End-Of-Line sequence to set.
*/
public void setLevelEOL(String newline) {
this.levelEOL[depth] = newline;
resetReusableIndents();
}
/**
* @return the current depth's {@link Format#getTextMode()}
*/
public TextMode getTextMode() {
return mode[depth];
}
/**
* Change the current level's TextMode
*
* @param mode
* the new mode to set.
*/
public void setTextMode(TextMode mode) {
if (this.mode[depth] == mode) {
return;
}
this.mode[depth] = mode;
switch (mode) {
case PRESERVE:
levelEOL[depth] = null;
levelIndent[depth] = null;
levelEOLIndent[depth] = null;
termEOLIndent[depth] = null;
break;
default:
levelEOL[depth] = lineSeparator;
if (indent == null || lineSeparator == null) {
levelEOLIndent[depth] = null;
termEOLIndent[depth] = null;
} else {
if (depth > 0) {
final StringBuilder sb = new StringBuilder(indent.length() * depth);
for (int i = 1; i < depth; i++) {
sb.append(indent);
}
// the start point was '1', so we are one indent
// short, which is just right for the term....
termEOLIndent[depth] = lineSeparator + sb.toString();
// but we increase it once for the actual indent.
sb.append(indent);
levelIndent[depth] = sb.toString();
} else {
termEOLIndent[depth] = lineSeparator;
levelIndent[depth] = "";
}
levelEOLIndent[depth] = lineSeparator + levelIndent[depth];
}
}
resetReusableIndents();
}
/**
* Create a new depth level on the stack. The previous level's details
* are copied to this level, and the accumulated indent (if any) is
* indented further.
*/
public void push() {
final int prev = depth++;
if (depth >= capacity) {
capacity *= 2;
levelIndent = ArrayCopy.copyOf(levelIndent, capacity);
levelEOL = ArrayCopy.copyOf(levelEOL, capacity);
levelEOLIndent = ArrayCopy.copyOf(levelEOLIndent, capacity);
termEOLIndent = ArrayCopy.copyOf(termEOLIndent, capacity);
ignoreTrAXEscapingPIs = ArrayCopy.copyOf(ignoreTrAXEscapingPIs, capacity);
mode = ArrayCopy.copyOf(mode, capacity);
escapeOutput = ArrayCopy.copyOf(escapeOutput, capacity);
}
ignoreTrAXEscapingPIs[depth] = ignoreTrAXEscapingPIs[prev];
mode[depth] = mode[prev];
escapeOutput[depth] = escapeOutput[prev];
if (levelIndent[prev] == null || levelEOL[prev] == null) {
levelIndent[depth] = null;
levelEOL[depth] = null;
levelEOLIndent[depth] = null;
termEOLIndent[depth] = null;
} else if (levelIndent[depth] == null) {
// we need to build our level details ....
// cannot reuse previous ones.
levelEOL[depth] = levelEOL[prev];
termEOLIndent[depth] = levelEOL[depth] + levelIndent[prev];
levelIndent[depth] = levelIndent[prev] + indent;
levelEOLIndent[depth] = levelEOL[depth] + levelIndent[depth];
}
}
/**
* Move back a level on the stack.
*/
public void pop() {
// no need to clear previously used members in the stack.
// the stack is short-lived, and does not create new instances for
// the depth levels, in other words, it does not affect GC and does
// not save memory to clear the stack.
depth--;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy