1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.struts2.interceptor.debugging;
23
24 import java.io.PrintWriter;
25 import java.io.Writer;
26 import java.util.Stack;
27
28 public class PrettyPrintWriter {
29
30 private final PrintWriter writer;
31 private final Stack<String> elementStack = new Stack<String>();
32 private final char[] lineIndenter;
33
34 private boolean tagInProgress;
35 private int depth;
36 private boolean readyForNewLine;
37 private boolean tagIsEmpty;
38 private String newLine;
39 private boolean escape = true;
40
41 private static final char[] NULL = "�".toCharArray();
42 private static final char[] AMP = "&".toCharArray();
43 private static final char[] LT = "<".toCharArray();
44 private static final char[] GT = ">".toCharArray();
45 private static final char[] SLASH_R = "
".toCharArray();
46 private static final char[] QUOT = """.toCharArray();
47 private static final char[] APOS = "'".toCharArray();
48 private static final char[] CLOSE = "</".toCharArray();
49
50 public PrettyPrintWriter(Writer writer, char[] lineIndenter, String newLine) {
51 this.writer = new PrintWriter(writer);
52 this.lineIndenter = lineIndenter;
53 this.newLine = newLine;
54 }
55
56 public PrettyPrintWriter(Writer writer, char[] lineIndenter) {
57 this(writer, lineIndenter, "\n");
58 }
59
60 public PrettyPrintWriter(Writer writer, String lineIndenter, String newLine) {
61 this(writer, lineIndenter.toCharArray(), newLine);
62 }
63
64 public PrettyPrintWriter(Writer writer, String lineIndenter) {
65 this(writer, lineIndenter.toCharArray());
66 }
67
68 public PrettyPrintWriter(Writer writer) {
69 this(writer, new char[]{' ', ' '});
70 }
71
72 public void startNode(String name) {
73 tagIsEmpty = false;
74 finishTag();
75 writer.write('<');
76 writer.write(name);
77 elementStack.push(name);
78 tagInProgress = true;
79 depth++;
80 readyForNewLine = true;
81 tagIsEmpty = true;
82 }
83
84 public void setValue(String text) {
85 readyForNewLine = false;
86 tagIsEmpty = false;
87 finishTag();
88
89 writeText(writer, text);
90 }
91
92 public void addAttribute(String key, String value) {
93 writer.write(' ');
94 writer.write(key);
95 writer.write('=');
96 writer.write('\"');
97 writeAttributeValue(writer, value);
98 writer.write('\"');
99 }
100
101 protected void writeAttributeValue(PrintWriter writer, String text) {
102 writeText(text);
103 }
104
105 protected void writeText(PrintWriter writer, String text) {
106 writeText(text);
107 }
108
109 private void writeText(String text) {
110 int length = text.length();
111 for (int i = 0; i < length; i++) {
112 char c = text.charAt(i);
113 switch (c) {
114 case '\0':
115 this.writer.write(NULL);
116 break;
117 case '&':
118 this.writer.write(AMP);
119 break;
120 case '<':
121 this.writer.write(LT);
122 break;
123 case '>':
124 this.writer.write(GT);
125 break;
126 case '"':
127 this.writer.write(QUOT);
128 break;
129 case '\'':
130
131
132 if (escape)
133 this.writer.write(APOS);
134 else
135 this.writer.write(c);
136 break;
137 case '\r':
138 this.writer.write(SLASH_R);
139 break;
140 default:
141 this.writer.write(c);
142 }
143 }
144 }
145
146 public void endNode() {
147 depth--;
148 if (tagIsEmpty) {
149 writer.write('/');
150 readyForNewLine = false;
151 finishTag();
152 elementStack.pop();
153 } else {
154 finishTag();
155 writer.write(CLOSE);
156 writer.write((String)elementStack.pop());
157 writer.write('>');
158 }
159 readyForNewLine = true;
160 if (depth == 0 ) {
161 writer.flush();
162 }
163 }
164
165 private void finishTag() {
166 if (tagInProgress) {
167 writer.write('>');
168 }
169 tagInProgress = false;
170 if (readyForNewLine) {
171 endOfLine();
172 }
173 readyForNewLine = false;
174 tagIsEmpty = false;
175 }
176
177 protected void endOfLine() {
178 writer.write(newLine);
179 for (int i = 0; i < depth; i++) {
180 writer.write(lineIndenter);
181 }
182 }
183
184 public void flush() {
185 writer.flush();
186 }
187
188 public void close() {
189 writer.close();
190 }
191
192 public boolean isEscape() {
193 return escape;
194 }
195
196 public void setEscape(boolean escape) {
197 this.escape = escape;
198 }
199 }