1 package org.apache.maven.model.transform.pull;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.Reader;
25 import java.util.ArrayDeque;
26 import java.util.Deque;
27 import java.util.Objects;
28 import java.util.regex.Pattern;
29
30 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
31 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
32
33
34
35
36
37
38
39 public class BufferingParser implements XmlPullParser
40 {
41
42 private static final Pattern WHITESPACE_REGEX = Pattern.compile( "[ \r\t\n]+" );
43
44 protected XmlPullParser xmlPullParser;
45 protected Deque<Event> events;
46 protected Event current;
47 protected boolean bypass;
48
49 @SuppressWarnings( "checkstyle:VisibilityModifier" )
50 public static class Event
51 {
52 public int event;
53 public String name;
54 public String prefix;
55 public String namespace;
56 public boolean empty;
57 public String text;
58 public Attribute[] attributes;
59 public Namespace[] namespaces;
60 }
61
62 @SuppressWarnings( "checkstyle:VisibilityModifier" )
63 public static class Namespace
64 {
65 public String prefix;
66 public String uri;
67 }
68
69 @SuppressWarnings( "checkstyle:VisibilityModifier" )
70 public static class Attribute
71 {
72 public String name;
73 public String prefix;
74 public String namespace;
75 public String type;
76 public String value;
77 public boolean isDefault;
78 }
79
80
81 public BufferingParser( XmlPullParser xmlPullParser )
82 {
83 this.xmlPullParser = xmlPullParser;
84 }
85
86 @Override
87 public void setFeature( String name, boolean state ) throws XmlPullParserException
88 {
89 xmlPullParser.setFeature( name, state );
90 }
91
92 @Override
93 public boolean getFeature( String name )
94 {
95 return xmlPullParser.getFeature( name );
96 }
97
98 @Override
99 public void setProperty( String name, Object value ) throws XmlPullParserException
100 {
101 xmlPullParser.setProperty( name, value );
102 }
103
104 @Override
105 public Object getProperty( String name )
106 {
107 return xmlPullParser.getProperty( name );
108 }
109
110 @Override
111 public void setInput( Reader in ) throws XmlPullParserException
112 {
113 xmlPullParser.setInput( in );
114 }
115
116 @Override
117 public void setInput( InputStream inputStream, String inputEncoding ) throws XmlPullParserException
118 {
119 xmlPullParser.setInput( inputStream, inputEncoding );
120 }
121
122 @Override
123 public String getInputEncoding()
124 {
125 return xmlPullParser.getInputEncoding();
126 }
127
128 @Override
129 public void defineEntityReplacementText( String entityName, String replacementText ) throws XmlPullParserException
130 {
131 xmlPullParser.defineEntityReplacementText( entityName, replacementText );
132 }
133
134 @Override
135 public int getNamespaceCount( int depth ) throws XmlPullParserException
136 {
137
138 return xmlPullParser.getNamespaceCount( depth );
139 }
140
141 @Override
142 public String getNamespacePrefix( int pos ) throws XmlPullParserException
143 {
144
145 return xmlPullParser.getNamespacePrefix( pos );
146 }
147
148 @Override
149 public String getNamespaceUri( int pos ) throws XmlPullParserException
150 {
151
152 return xmlPullParser.getNamespaceUri( pos );
153 }
154
155 @Override
156 public String getNamespace( String prefix )
157 {
158
159 return xmlPullParser.getNamespace( prefix );
160 }
161
162 @Override
163 public int getDepth()
164 {
165
166 return xmlPullParser.getDepth();
167 }
168
169 @Override
170 public String getPositionDescription()
171 {
172 if ( current != null )
173 {
174 throw new IllegalStateException( "Not supported during events replay" );
175 }
176 return xmlPullParser.getPositionDescription();
177 }
178
179 @Override
180 public int getLineNumber()
181 {
182 if ( current != null )
183 {
184 throw new IllegalStateException( "Not supported during events replay" );
185 }
186 return xmlPullParser.getLineNumber();
187 }
188
189 @Override
190 public int getColumnNumber()
191 {
192 if ( current != null )
193 {
194 throw new IllegalStateException( "Not supported during events replay" );
195 }
196 return xmlPullParser.getColumnNumber();
197 }
198
199 @Override
200 public boolean isWhitespace() throws XmlPullParserException
201 {
202 if ( current != null )
203 {
204 if ( current.event == TEXT || current.event == CDSECT )
205 {
206 return WHITESPACE_REGEX.matcher( current.text ).matches();
207 }
208 else if ( current.event == IGNORABLE_WHITESPACE )
209 {
210 return true;
211 }
212 else
213 {
214 throw new XmlPullParserException( "no content available to check for whitespaces" );
215 }
216 }
217 return xmlPullParser.isWhitespace();
218 }
219
220 @Override
221 public String getText()
222 {
223 return current != null ? current.text : xmlPullParser.getText();
224 }
225
226 @Override
227 public char[] getTextCharacters( int[] holderForStartAndLength )
228 {
229 if ( current != null )
230 {
231 throw new IllegalStateException( "Not supported during events replay" );
232 }
233 return xmlPullParser.getTextCharacters( holderForStartAndLength );
234 }
235
236 @Override
237 public String getNamespace()
238 {
239 return current != null ? current.namespace : xmlPullParser.getNamespace();
240 }
241
242 @Override
243 public String getName()
244 {
245 return current != null ? current.name : xmlPullParser.getName();
246 }
247
248 @Override
249 public String getPrefix()
250 {
251 return current != null ? current.prefix : xmlPullParser.getPrefix();
252 }
253
254 @Override
255 public boolean isEmptyElementTag() throws XmlPullParserException
256 {
257 return current != null ? current.empty : xmlPullParser.isEmptyElementTag();
258 }
259
260 @Override
261 public int getAttributeCount()
262 {
263 if ( current != null )
264 {
265 return current.attributes != null ? current.attributes.length : 0;
266 }
267 else
268 {
269 return xmlPullParser.getAttributeCount();
270 }
271 }
272
273 @Override
274 public String getAttributeNamespace( int index )
275 {
276 if ( current != null )
277 {
278 return current.attributes[index].namespace;
279 }
280 else
281 {
282 return xmlPullParser.getAttributeNamespace( index );
283 }
284 }
285
286 @Override
287 public String getAttributeName( int index )
288 {
289 if ( current != null )
290 {
291 return current.attributes[index].name;
292 }
293 else
294 {
295 return xmlPullParser.getAttributeName( index );
296 }
297 }
298
299 @Override
300 public String getAttributePrefix( int index )
301 {
302 if ( current != null )
303 {
304 return current.attributes[index].prefix;
305 }
306 else
307 {
308 return xmlPullParser.getAttributePrefix( index );
309 }
310 }
311
312 @Override
313 public String getAttributeType( int index )
314 {
315 if ( current != null )
316 {
317 return current.attributes[index].type;
318 }
319 else
320 {
321 return xmlPullParser.getAttributeType( index );
322 }
323 }
324
325 @Override
326 public boolean isAttributeDefault( int index )
327 {
328 if ( current != null )
329 {
330 return current.attributes[index].isDefault;
331 }
332 else
333 {
334 return xmlPullParser.isAttributeDefault( index );
335 }
336 }
337
338 @Override
339 public String getAttributeValue( int index )
340 {
341 if ( current != null )
342 {
343 return current.attributes[index].value;
344 }
345 else
346 {
347 return xmlPullParser.getAttributeValue( index );
348 }
349 }
350
351 @Override
352 public String getAttributeValue( String namespace, String name )
353 {
354 if ( current != null )
355 {
356 if ( current.attributes != null )
357 {
358 for ( Attribute attr : current.attributes )
359 {
360 if ( Objects.equals( namespace, attr.namespace )
361 && Objects.equals( name, attr.name ) )
362 {
363 return attr.value;
364 }
365 }
366 }
367 return null;
368 }
369 else
370 {
371 return xmlPullParser.getAttributeValue( namespace, name );
372 }
373 }
374
375 @Override
376 public void require( int type, String namespace, String name ) throws XmlPullParserException, IOException
377 {
378 if ( current != null )
379 {
380 throw new IllegalStateException( "Not supported during events replay" );
381 }
382 xmlPullParser.require( type, namespace, name );
383 }
384
385 @Override
386 public int getEventType() throws XmlPullParserException
387 {
388 return current != null ? current.event : xmlPullParser.getEventType();
389 }
390
391 @Override
392 public int next() throws XmlPullParserException, IOException
393 {
394 while ( true )
395 {
396 if ( events != null && !events.isEmpty() )
397 {
398 current = events.removeFirst();
399 return current.event;
400 }
401 else
402 {
403 current = null;
404 }
405 if ( getEventType() == END_DOCUMENT )
406 {
407 throw new XmlPullParserException( "already reached end of XML input", this, null );
408 }
409 int currentEvent = xmlPullParser.next();
410 if ( bypass() || accept() )
411 {
412 return currentEvent;
413 }
414 }
415 }
416
417 @Override
418 public int nextToken() throws XmlPullParserException, IOException
419 {
420 while ( true )
421 {
422 if ( events != null && !events.isEmpty() )
423 {
424 current = events.removeFirst();
425 return current.event;
426 }
427 else
428 {
429 current = null;
430 }
431 if ( getEventType() == END_DOCUMENT )
432 {
433 throw new XmlPullParserException( "already reached end of XML input", this, null );
434 }
435 int currentEvent = xmlPullParser.nextToken();
436 if ( bypass() || accept() )
437 {
438 return currentEvent;
439 }
440 }
441 }
442
443 @Override
444 public int nextTag() throws XmlPullParserException, IOException
445 {
446 int eventType = next();
447 if ( eventType == TEXT && isWhitespace() )
448 {
449 eventType = next();
450 }
451 if ( eventType != START_TAG && eventType != END_TAG )
452 {
453 throw new XmlPullParserException( "expected START_TAG or END_TAG not "
454 + TYPES[getEventType()], this, null );
455 }
456 return eventType;
457 }
458
459 @Override
460 public String nextText() throws XmlPullParserException, IOException
461 {
462 int eventType = getEventType();
463 if ( eventType != START_TAG )
464 {
465 throw new XmlPullParserException( "parser must be on START_TAG to read next text", this, null );
466 }
467 eventType = next();
468 if ( eventType == TEXT )
469 {
470 final String result = getText();
471 eventType = next();
472 if ( eventType != END_TAG )
473 {
474 throw new XmlPullParserException( "TEXT must be immediately followed by END_TAG and not "
475 + TYPES[getEventType()], this, null );
476 }
477 return result;
478 }
479 else if ( eventType == END_TAG )
480 {
481 return "";
482 }
483 else
484 {
485 throw new XmlPullParserException( "parser must be on START_TAG or TEXT to read text", this, null );
486 }
487 }
488
489 protected Event bufferEvent() throws XmlPullParserException
490 {
491 Event event = new Event();
492 XmlPullParser pp = xmlPullParser;
493 event.event = xmlPullParser.getEventType();
494 switch ( event.event )
495 {
496 case START_DOCUMENT:
497 case END_DOCUMENT:
498 break;
499 case START_TAG:
500 event.name = pp.getName();
501 event.namespace = pp.getNamespace();
502 event.prefix = pp.getPrefix();
503 event.empty = pp.isEmptyElementTag();
504 event.text = pp.getText();
505 break;
506 case END_TAG:
507 event.name = pp.getName();
508 event.namespace = pp.getNamespace();
509 event.prefix = pp.getPrefix();
510 event.text = pp.getText();
511 break;
512 case TEXT:
513 case COMMENT:
514 case IGNORABLE_WHITESPACE:
515 event.text = pp.getText();
516 break;
517 default:
518 break;
519 }
520 return event;
521 }
522
523 protected void pushEvent( Event event )
524 {
525 if ( events == null )
526 {
527 events = new ArrayDeque<>();
528 }
529 events.add( event );
530 }
531
532 protected boolean accept() throws XmlPullParserException, IOException
533 {
534 return true;
535 }
536
537 public void bypass( boolean bypass )
538 {
539 if ( bypass && events != null && !events.isEmpty() )
540 {
541 throw new IllegalStateException( "Can not disable filter while processing" );
542 }
543 this.bypass = bypass;
544 }
545
546 public boolean bypass()
547 {
548 return bypass
549 || ( xmlPullParser instanceof BufferingParser
550 && ( (BufferingParser) xmlPullParser ).bypass() );
551 }
552
553 protected static String nullSafeAppend( String originalValue, String charSegment )
554 {
555 if ( originalValue == null )
556 {
557 return charSegment;
558 }
559 else
560 {
561 return originalValue + charSegment;
562 }
563 }
564 }