1 package org.apache.maven.shared.utils.xml;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.shared.utils.Os;
23
24 import java.io.PrintWriter;
25 import java.io.Writer;
26 import java.util.ArrayList;
27
28
29
30
31 public class PrettyPrintXMLWriter
32 implements XMLWriter
33 {
34 private static final char[] CLOSE_1 = "/>".toCharArray();
35
36 private static final char[] CLOSE_2 = "</".toCharArray();
37
38 private static final char[] DEFAULT_LINE_INDENT = new char[]{ ' ', ' ' };
39
40 private PrintWriter writer;
41
42 private ArrayList<String> elementStack = new ArrayList<String>();
43
44 private boolean processingElement = false;
45
46 private boolean documentStarted = false;
47
48 private boolean endOnSameLine = false;
49
50 private int depth = 0;
51
52 private char[] lineIndent;
53
54 private char[] lineSeparator;
55
56 private String encoding;
57
58 private String docType;
59
60
61
62
63
64 public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent )
65 {
66 this( writer, lineIndent, null, null );
67 }
68
69
70
71
72
73 public PrettyPrintXMLWriter( Writer writer, String lineIndent )
74 {
75 this( new PrintWriter( writer ), lineIndent );
76 }
77
78
79
80
81 public PrettyPrintXMLWriter( PrintWriter writer )
82 {
83 this( writer, null, null );
84 }
85
86
87
88
89 public PrettyPrintXMLWriter( Writer writer )
90 {
91 this( new PrintWriter( writer ) );
92 }
93
94
95
96
97
98
99
100 public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String encoding, String doctype )
101 {
102 this( writer, lineIndent.toCharArray(), Os.LINE_SEP.toCharArray(), encoding, doctype );
103 }
104
105
106
107
108
109
110
111 public PrettyPrintXMLWriter( Writer writer, String lineIndent, String encoding, String doctype )
112 {
113 this( new PrintWriter( writer ), lineIndent, encoding, doctype );
114 }
115
116
117
118
119
120
121 public PrettyPrintXMLWriter( PrintWriter writer, String encoding, String doctype )
122 {
123 this( writer, DEFAULT_LINE_INDENT, Os.LINE_SEP.toCharArray(), encoding, doctype );
124 }
125
126
127
128
129
130
131 public PrettyPrintXMLWriter( Writer writer, String encoding, String doctype )
132 {
133 this( new PrintWriter( writer ), encoding, doctype );
134 }
135
136
137
138
139
140
141
142
143 public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String lineSeparator, String encoding,
144 String doctype )
145 {
146 this( writer, lineIndent.toCharArray(), lineSeparator.toCharArray(), encoding, doctype );
147 }
148
149
150
151
152
153
154
155
156 private PrettyPrintXMLWriter( PrintWriter writer, char[] lineIndent, char[] lineSeparator, String encoding,
157 String doctype )
158 {
159 this.writer = writer;
160 this.lineIndent = lineIndent;
161 this.lineSeparator = lineSeparator;
162 this.encoding = encoding;
163 this.docType = doctype;
164
165 depth = 0;
166 }
167
168 public void addAttribute( String key, String value )
169 {
170 if ( !processingElement )
171 {
172 throw new IllegalStateException( "currently processing no element" );
173 }
174
175 writer.write( ' ' );
176 writer.write( key );
177 writer.write( '=' );
178 XMLEncode.xmlEncodeTextAsPCDATA( value, true, '"', writer );
179 }
180
181 public void setEncoding( String encoding )
182 {
183 if ( documentStarted )
184 {
185 throw new IllegalStateException( "Document headers already written!" );
186 }
187
188 this.encoding = encoding;
189 }
190
191 public void setDocType( String docType )
192 {
193 if ( documentStarted )
194 {
195 throw new IllegalStateException( "Document headers already written!" );
196 }
197
198 this.docType = docType;
199 }
200
201 public void setLineSeparator( String lineSeparator )
202 {
203 if ( documentStarted )
204 {
205 throw new IllegalStateException( "Document headers already written!" );
206 }
207
208 this.lineSeparator = lineSeparator.toCharArray();
209 }
210
211 public void setLineIndenter( String lineIndent )
212 {
213 if ( documentStarted )
214 {
215 throw new IllegalStateException( "Document headers already written!" );
216 }
217
218 this.lineIndent = lineIndent.toCharArray();
219 }
220
221 public void startElement( String elementName )
222 {
223 boolean firstLine = ensureDocumentStarted();
224
225 completePreviouslyOpenedElement();
226
227 if ( !firstLine )
228 {
229 newLine();
230 }
231
232 writer.write( '<' );
233 writer.write( elementName );
234
235 processingElement = true;
236
237 elementStack.add( depth++, elementName );
238 }
239
240 public void writeText( String text )
241 {
242 ensureDocumentStarted();
243
244 completePreviouslyOpenedElement();
245
246 XMLEncode.xmlEncodeText( text, writer );
247
248 endOnSameLine = true;
249 }
250
251 public void writeMarkup( String markup )
252 {
253 ensureDocumentStarted();
254
255 completePreviouslyOpenedElement();
256
257 writer.write( markup );
258 }
259
260 public void endElement()
261 {
262 String chars = elementStack.get( --depth );
263 if ( processingElement )
264 {
265
266 writer.write( CLOSE_1 );
267
268 processingElement = false;
269 }
270 else
271 {
272 if ( !endOnSameLine )
273 {
274 newLine();
275 }
276
277
278 writer.write( CLOSE_2 );
279 writer.write( chars );
280 writer.write( '>' );
281 }
282
283 endOnSameLine = false;
284 }
285
286
287
288
289
290
291 private boolean ensureDocumentStarted()
292 {
293 if ( !documentStarted )
294 {
295 if ( docType != null || encoding != null )
296 {
297 writeDocumentHeader();
298 }
299
300 documentStarted = true;
301
302 return true;
303 }
304
305 return false;
306 }
307
308 private void writeDocumentHeader()
309 {
310 writer.write( "<?xml version=\"1.0\"" );
311
312 if ( encoding != null )
313 {
314 writer.write( " encoding=\"" );
315 writer.write( encoding );
316 writer.write( '\"' );
317 }
318
319 writer.write( "?>" );
320
321 newLine();
322
323 if ( docType != null )
324 {
325 newLine();
326 writer.write( "<!DOCTYPE " );
327 writer.write( docType );
328 writer.write( '>' );
329 }
330 }
331
332 private void newLine()
333 {
334 writer.write( lineSeparator );
335
336 for ( int i = 0; i < depth; i++ )
337 {
338 writer.write( lineIndent );
339 }
340 }
341
342 private void completePreviouslyOpenedElement()
343 {
344 if ( processingElement )
345 {
346 writer.write( '>' );
347 processingElement = false;
348 }
349 }
350
351 }