
fi.evolver.utils.format.XmlPrettyPrinter Maven / Gradle / Ivy
package fi.evolver.utils.format;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import fi.evolver.utils.parse.CharStream;
public class XmlPrettyPrinter {
public static final XmlPrettyPrinter INSTANT = new XmlPrettyPrinter(" ");
private enum State {
TEXT,
START_TAG,
END_TAG,
ATTRIBUTE,
CONTENT_START,
;
public boolean is(State... states) {
for (State s: states) {
if (s == this)
return true;
}
return false;
}
}
private final String indentString;
public XmlPrettyPrinter(String indentString) {
this.indentString = indentString;
}
public static String format(String xml) {
StringReader reader = new StringReader(xml);
xml = null;
StringWriter writer = new StringWriter();
try {
new XmlPrettyPrinter("\t").format(reader, writer);
return writer.toString();
} catch (IOException e) {
return "";
}
}
public void format(Reader input, Writer output) throws IOException {
format(new CharStream(input, 4), output, 0, false);
}
public void format(CharStream stream, Writer output, int baseIndentation, boolean inComment) throws IOException {
int indentation = baseIndentation;
State state = State.TEXT;
if (stream.accept('<', '?')) {
state = State.START_TAG;
output.write("");
}
boolean containsXml = false;
StringBuilder whitespace = new StringBuilder();
while (stream.hasNext()) {
if (state.is(State.CONTENT_START) && stream.acceptWhitespace())
continue;
if (!inComment && stream.accept('<', '!', '-', '-')) {
while (stream.acceptWhitespace()) { /* skip */ }
boolean xml = state.is(State.CONTENT_START) && stream.has('<');
if (xml)
indent(output, indentation);
output.append("");
if (xml) {
containsXml = true;
while (stream.acceptWhitespace()) { /* skip */ }
if (!stream.has('<'))
indent(output, indentation);
}
}
else if (stream.accept('-', '-', '>')) {
if (inComment)
return;
else
output.append("-->");
}
else if (state.is(State.TEXT, State.CONTENT_START) && stream.accept('<', '/')) {
--indentation;
if (containsXml)
indent(output, indentation);
state = State.END_TAG;
debug(output, "[END]");
output.append("");
containsXml = true;
}
else if (state.is(State.TEXT, State.CONTENT_START) && stream.accept('<')) {
if (state == State.CONTENT_START)
indent(output, indentation);
debug(output, "[START]");
state = State.START_TAG;
++indentation;
output.append('<');
containsXml = false;
}
else if (state.is(State.START_TAG) && stream.accept('/', '>')) {
debug(output, "[CONTENT]");
output.append("/>");
--indentation;
state = State.CONTENT_START;
containsXml = true;
}
else if (state.is(State.START_TAG, State.END_TAG) && stream.accept('>')) {
output.append('>');
debug(output, "[CONTENT]");
containsXml = state.is(State.END_TAG);
state = State.CONTENT_START;
}
else if (state.is(State.START_TAG, State.END_TAG) && stream.accept('"')) {
debug(output, "[ATTRIBUTE]");
state = State.ATTRIBUTE;
output.append('"');
}
else if (state.is(State.ATTRIBUTE) && stream.accept('"')) {
state = State.START_TAG;
output.append('"');
debug(output, "[START]");
}
else {
if (state.is(State.CONTENT_START)) {
debug(output, "[TEXT]");
state = State.TEXT;
}
char c = stream.next();
if (state.is(State.TEXT) && Character.isWhitespace(c))
whitespace.append(c);
else {
flushWhitespace(output, whitespace);
output.append(c);
}
}
if (!state.is(State.TEXT))
whitespace.setLength(0);
}
output.append('\n');
}
private static void flushWhitespace(Writer output, StringBuilder whitespace) throws IOException {
if (whitespace.length() > 0) {
output.append(whitespace);
whitespace.setLength(0);
}
}
private void indent(Writer writer, int amount) throws IOException {
writer.append('\n');
for (int i = 0; i < amount; ++i)
writer.append(indentString);
}
@SuppressWarnings("unused")
private void debug(Writer output, String text) throws IOException {
//output.append(text);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy