package folioxml.xml; public class XmlFormatter { public XmlFormatter(int startingIndent) { defaultIndentDepth = startingIndent; } public XmlFormatter(int startingIndent, String indentString) { defaultIndentDepth = startingIndent; this.indentString = indentString; } public String indentString = "\t"; public int optimalLineLength = 80; public String inlineElementsRegex = "span|link|a"; public int defaultIndentDepth = 0; public String format(NodeList nl) { StringBuilder sb = new StringBuilder(); writeNodes(nl, sb, defaultIndentDepth); return sb.toString(); } public String format(Node n) { StringBuilder sb = new StringBuilder(); writeNode(n, sb, defaultIndentDepth); return sb.toString(); } private void writeNodes(NodeList nl, StringBuilder sb, int indentDepth) { if (nl == null) return; for (Node n : nl.list()) writeNode(n, sb, indentDepth); } private void writeNode(Node n, StringBuilder sb, int indentDepth) { boolean onNewLine = sb.length() > 0 ? (sb.charAt(sb.length() - 1) == '\n') : true; //We want // * non-ghost tags to appear by themselves on their own lines (and their closing tags) // * ghost tags, text, entities, and comments are all the same, except text and comments are wrapped. if (n.isTag() && !n.matches(inlineElementsRegex)) { if (!onNewLine) sb.append('\n'); for (int i = 0; i < indentDepth; i++) sb.append(indentString); n.writeTokenTo(sb); //write a newline after sb.append('\n'); if (n.isOpening()) { //Do children writeNodes(n.children, sb, indentDepth + 1);//Do children. Indent the depth. //Write closing tag onNewLine = sb.length() > 0 ? (sb.charAt(sb.length() - 1) == '\n') : true; if (!onNewLine) sb.append('\n'); //Write tabs for (int i = 0; i < indentDepth; i++) sb.append(indentString); //Closing tag sb.append(n.getClosingTagString()); sb.append('\n'); } } else { if (onNewLine) { for (int i = 0; i < indentDepth; i++) sb.append(indentString); } if (n.isTag()) { n.writeTokenTo(sb); //Opening tag if (n.isOpening()) { writeNodes(n.children, sb, indentDepth);//Do children. Don't indent the depth. sb.append(n.getClosingTagString()); //Closing tag } } else if (n.isEntity()) { sb.append(n.toTokenString());//Write inline - append to the previous line. } else { //text and comments //Build indent string StringBuilder indentStr = new StringBuilder(indentDepth + 1); for (int i = 0; i < indentDepth; i++) indentStr.append(indentString); //Write text wrapped writeText(n.toTokenString(), sb, optimalLineLength, indentStr.toString()); } } } private void writeText(String s, StringBuilder sb, int wrapChars, String indentString) { //Keeps track of how many characters are on the current line. int currentChars = sb.length(); //Find the last newline if it exists. int lastNl = sb.lastIndexOf("\n"); if (lastNl > -1) currentChars = sb.length() - lastNl - 1; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); sb.append(c); currentChars++; //Break lines after whitespace if (currentChars > wrapChars) { if (c == '\t' || c == ' ') { sb.append('\n'); sb.append(indentString); currentChars = indentString.length(); } } } } }