1 package org.apache.struts2.interceptor.debugging; 2 3 import java.io.PrintWriter; 4 import java.io.Writer; 5 import java.util.Stack; 6 7 /*** 8 * A simple writer that outputs XML in a pretty-printed indented stream. 9 * 10 * <p>By default, the chars <code><xmp>& < > " ' \r</xmp></code> are escaped and replaced with a suitable XML entity. 11 * To alter this behavior, override the the {@link #writeText(com.thoughtworks.xstream.core.util.QuickWriter, String)} 12 * and {@link #writeAttributeValue(com.thoughtworks.xstream.core.util.QuickWriter, String)} methods.</p> 13 * 14 * <p>This code was taken from the XStream project under the BSD license.</p> 15 * 16 */ 17 public class PrettyPrintWriter { 18 19 private final PrintWriter writer; 20 private final Stack<String> elementStack = new Stack<String>(); 21 private final char[] lineIndenter; 22 23 private boolean tagInProgress; 24 private int depth; 25 private boolean readyForNewLine; 26 private boolean tagIsEmpty; 27 private String newLine; 28 29 private static final char[] NULL = "�".toCharArray(); 30 private static final char[] AMP = "&".toCharArray(); 31 private static final char[] LT = "<".toCharArray(); 32 private static final char[] GT = ">".toCharArray(); 33 private static final char[] SLASH_R = "
".toCharArray(); 34 private static final char[] QUOT = """.toCharArray(); 35 private static final char[] APOS = "'".toCharArray(); 36 private static final char[] CLOSE = "</".toCharArray(); 37 38 public PrettyPrintWriter(Writer writer, char[] lineIndenter, String newLine) { 39 this.writer = new PrintWriter(writer); 40 this.lineIndenter = lineIndenter; 41 this.newLine = newLine; 42 } 43 44 public PrettyPrintWriter(Writer writer, char[] lineIndenter) { 45 this(writer, lineIndenter, "\n"); 46 } 47 48 public PrettyPrintWriter(Writer writer, String lineIndenter, String newLine) { 49 this(writer, lineIndenter.toCharArray(), newLine); 50 } 51 52 public PrettyPrintWriter(Writer writer, String lineIndenter) { 53 this(writer, lineIndenter.toCharArray()); 54 } 55 56 public PrettyPrintWriter(Writer writer) { 57 this(writer, new char[]{' ', ' '}); 58 } 59 60 public void startNode(String name) { 61 tagIsEmpty = false; 62 finishTag(); 63 writer.write('<'); 64 writer.write(name); 65 elementStack.push(name); 66 tagInProgress = true; 67 depth++; 68 readyForNewLine = true; 69 tagIsEmpty = true; 70 } 71 72 public void setValue(String text) { 73 readyForNewLine = false; 74 tagIsEmpty = false; 75 finishTag(); 76 77 writeText(writer, text); 78 } 79 80 public void addAttribute(String key, String value) { 81 writer.write(' '); 82 writer.write(key); 83 writer.write('='); 84 writer.write('\"'); 85 writeAttributeValue(writer, value); 86 writer.write('\"'); 87 } 88 89 protected void writeAttributeValue(PrintWriter writer, String text) { 90 writeText(text); 91 } 92 93 protected void writeText(PrintWriter writer, String text) { 94 writeText(text); 95 } 96 97 private void writeText(String text) { 98 int length = text.length(); 99 for (int i = 0; i < length; i++) { 100 char c = text.charAt(i); 101 switch (c) { 102 case '\0': 103 this.writer.write(NULL); 104 break; 105 case '&': 106 this.writer.write(AMP); 107 break; 108 case '<': 109 this.writer.write(LT); 110 break; 111 case '>': 112 this.writer.write(GT); 113 break; 114 case '"': 115 this.writer.write(QUOT); 116 break; 117 case '\'': 118 this.writer.write(APOS); 119 break; 120 case '\r': 121 this.writer.write(SLASH_R); 122 break; 123 default: 124 this.writer.write(c); 125 } 126 } 127 } 128 129 public void endNode() { 130 depth--; 131 if (tagIsEmpty) { 132 writer.write('/'); 133 readyForNewLine = false; 134 finishTag(); 135 elementStack.pop(); 136 } else { 137 finishTag(); 138 writer.write(CLOSE); 139 writer.write((String)elementStack.pop()); 140 writer.write('>'); 141 } 142 readyForNewLine = true; 143 if (depth == 0 ) { 144 writer.flush(); 145 } 146 } 147 148 private void finishTag() { 149 if (tagInProgress) { 150 writer.write('>'); 151 } 152 tagInProgress = false; 153 if (readyForNewLine) { 154 endOfLine(); 155 } 156 readyForNewLine = false; 157 tagIsEmpty = false; 158 } 159 160 protected void endOfLine() { 161 writer.write(newLine); 162 for (int i = 0; i < depth; i++) { 163 writer.write(lineIndenter); 164 } 165 } 166 167 public void flush() { 168 writer.flush(); 169 } 170 171 public void close() { 172 writer.close(); 173 } 174 }