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