1
2
3
4
5
6 package org.apache.maven.archetype.model.io.xpp3;
7
8
9
10
11
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.Reader;
15 import java.text.DateFormat;
16 import org.apache.maven.archetype.model.ArchetypeModel;
17 import org.apache.maven.archetype.model.Resource;
18 import org.apache.maven.archetype.model.Source;
19 import org.codehaus.plexus.util.ReaderFactory;
20 import org.codehaus.plexus.util.xml.pull.EntityReplacementMap;
21 import org.codehaus.plexus.util.xml.pull.MXParser;
22 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
23 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
24
25
26
27
28
29
30 @SuppressWarnings( "all" )
31 public class ArchetypeXpp3Reader
32 {
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 private boolean addDefaultEntities = true;
49
50
51
52
53 public final ContentTransformer contentTransformer;
54
55
56
57
58
59
60 public ArchetypeXpp3Reader()
61 {
62 this( new ContentTransformer()
63 {
64 public String transform( String source, String fieldName )
65 {
66 return source;
67 }
68 } );
69 }
70
71 public ArchetypeXpp3Reader(ContentTransformer contentTransformer)
72 {
73 this.contentTransformer = contentTransformer;
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 private boolean checkFieldWithDuplicate( XmlPullParser parser, String tagName, String alias, java.util.Set parsed )
92 throws XmlPullParserException
93 {
94 if ( !( parser.getName().equals( tagName ) || parser.getName().equals( alias ) ) )
95 {
96 return false;
97 }
98 if ( !parsed.add( tagName ) )
99 {
100 throw new XmlPullParserException( "Duplicated tag: '" + tagName + "'", parser, null );
101 }
102 return true;
103 }
104
105
106
107
108
109
110
111
112
113
114
115 private void checkUnknownAttribute( XmlPullParser parser, String attribute, String tagName, boolean strict )
116 throws XmlPullParserException, IOException
117 {
118
119 if ( strict )
120 {
121 throw new XmlPullParserException( "Unknown attribute '" + attribute + "' for tag '" + tagName + "'", parser, null );
122 }
123 }
124
125
126
127
128
129
130
131
132
133 private void checkUnknownElement( XmlPullParser parser, boolean strict )
134 throws XmlPullParserException, IOException
135 {
136 if ( strict )
137 {
138 throw new XmlPullParserException( "Unrecognised tag: '" + parser.getName() + "'", parser, null );
139 }
140
141 for ( int unrecognizedTagCount = 1; unrecognizedTagCount > 0; )
142 {
143 int eventType = parser.next();
144 if ( eventType == XmlPullParser.START_TAG )
145 {
146 unrecognizedTagCount++;
147 }
148 else if ( eventType == XmlPullParser.END_TAG )
149 {
150 unrecognizedTagCount--;
151 }
152 }
153 }
154
155
156
157
158
159
160 public boolean getAddDefaultEntities()
161 {
162 return addDefaultEntities;
163 }
164
165
166
167
168
169
170
171
172
173
174 private boolean getBooleanValue( String s, String attribute, XmlPullParser parser )
175 throws XmlPullParserException
176 {
177 return getBooleanValue( s, attribute, parser, null );
178 }
179
180
181
182
183
184
185
186
187
188
189
190 private boolean getBooleanValue( String s, String attribute, XmlPullParser parser, String defaultValue )
191 throws XmlPullParserException
192 {
193 if ( s != null && s.length() != 0 )
194 {
195 return Boolean.valueOf( s ).booleanValue();
196 }
197 if ( defaultValue != null )
198 {
199 return Boolean.valueOf( defaultValue ).booleanValue();
200 }
201 return false;
202 }
203
204
205
206
207
208
209
210
211
212
213
214 private byte getByteValue( String s, String attribute, XmlPullParser parser, boolean strict )
215 throws XmlPullParserException
216 {
217 if ( s != null )
218 {
219 try
220 {
221 return Byte.valueOf( s ).byteValue();
222 }
223 catch ( NumberFormatException nfe )
224 {
225 if ( strict )
226 {
227 throw new XmlPullParserException( "Unable to parse element '" + attribute + "', must be a byte", parser, nfe );
228 }
229 }
230 }
231 return 0;
232 }
233
234
235
236
237
238
239
240
241
242
243 private char getCharacterValue( String s, String attribute, XmlPullParser parser )
244 throws XmlPullParserException
245 {
246 if ( s != null )
247 {
248 return s.charAt( 0 );
249 }
250 return 0;
251 }
252
253
254
255
256
257
258
259
260
261
262 private java.util.Date getDateValue( String s, String attribute, XmlPullParser parser )
263 throws XmlPullParserException
264 {
265 return getDateValue( s, attribute, null, parser );
266 }
267
268
269
270
271
272
273
274
275
276
277
278 private java.util.Date getDateValue( String s, String attribute, String dateFormat, XmlPullParser parser )
279 throws XmlPullParserException
280 {
281 if ( s != null )
282 {
283 String effectiveDateFormat = dateFormat;
284 if ( dateFormat == null )
285 {
286 effectiveDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS";
287 }
288 if ( "long".equals( effectiveDateFormat ) )
289 {
290 try
291 {
292 return new java.util.Date( Long.parseLong( s ) );
293 }
294 catch ( NumberFormatException e )
295 {
296 throw new XmlPullParserException( e.getMessage(), parser, e );
297 }
298 }
299 else
300 {
301 try
302 {
303 DateFormat dateParser = new java.text.SimpleDateFormat( effectiveDateFormat, java.util.Locale.US );
304 return dateParser.parse( s );
305 }
306 catch ( java.text.ParseException e )
307 {
308 throw new XmlPullParserException( e.getMessage(), parser, e );
309 }
310 }
311 }
312 return null;
313 }
314
315
316
317
318
319
320
321
322
323
324
325 private double getDoubleValue( String s, String attribute, XmlPullParser parser, boolean strict )
326 throws XmlPullParserException
327 {
328 if ( s != null )
329 {
330 try
331 {
332 return Double.valueOf( s ).doubleValue();
333 }
334 catch ( NumberFormatException nfe )
335 {
336 if ( strict )
337 {
338 throw new XmlPullParserException( "Unable to parse element '" + attribute + "', must be a floating point number", parser, nfe );
339 }
340 }
341 }
342 return 0;
343 }
344
345
346
347
348
349
350
351
352
353
354
355 private float getFloatValue( String s, String attribute, XmlPullParser parser, boolean strict )
356 throws XmlPullParserException
357 {
358 if ( s != null )
359 {
360 try
361 {
362 return Float.valueOf( s ).floatValue();
363 }
364 catch ( NumberFormatException nfe )
365 {
366 if ( strict )
367 {
368 throw new XmlPullParserException( "Unable to parse element '" + attribute + "', must be a floating point number", parser, nfe );
369 }
370 }
371 }
372 return 0;
373 }
374
375
376
377
378
379
380
381
382
383
384
385 private int getIntegerValue( String s, String attribute, XmlPullParser parser, boolean strict )
386 throws XmlPullParserException
387 {
388 if ( s != null )
389 {
390 try
391 {
392 return Integer.valueOf( s ).intValue();
393 }
394 catch ( NumberFormatException nfe )
395 {
396 if ( strict )
397 {
398 throw new XmlPullParserException( "Unable to parse element '" + attribute + "', must be an integer", parser, nfe );
399 }
400 }
401 }
402 return 0;
403 }
404
405
406
407
408
409
410
411
412
413
414
415 private long getLongValue( String s, String attribute, XmlPullParser parser, boolean strict )
416 throws XmlPullParserException
417 {
418 if ( s != null )
419 {
420 try
421 {
422 return Long.valueOf( s ).longValue();
423 }
424 catch ( NumberFormatException nfe )
425 {
426 if ( strict )
427 {
428 throw new XmlPullParserException( "Unable to parse element '" + attribute + "', must be a long integer", parser, nfe );
429 }
430 }
431 }
432 return 0;
433 }
434
435
436
437
438
439
440
441
442
443
444
445 private String getRequiredAttributeValue( String s, String attribute, XmlPullParser parser, boolean strict )
446 throws XmlPullParserException
447 {
448 if ( s == null )
449 {
450 if ( strict )
451 {
452 throw new XmlPullParserException( "Missing required value for attribute '" + attribute + "'", parser, null );
453 }
454 }
455 return s;
456 }
457
458
459
460
461
462
463
464
465
466
467
468 private short getShortValue( String s, String attribute, XmlPullParser parser, boolean strict )
469 throws XmlPullParserException
470 {
471 if ( s != null )
472 {
473 try
474 {
475 return Short.valueOf( s ).shortValue();
476 }
477 catch ( NumberFormatException nfe )
478 {
479 if ( strict )
480 {
481 throw new XmlPullParserException( "Unable to parse element '" + attribute + "', must be a short integer", parser, nfe );
482 }
483 }
484 }
485 return 0;
486 }
487
488
489
490
491
492
493
494 private String getTrimmedValue( String s )
495 {
496 if ( s != null )
497 {
498 s = s.trim();
499 }
500 return s;
501 }
502
503
504
505
506
507
508
509
510 private String interpolatedTrimmed( String value, String context )
511 {
512 return getTrimmedValue( contentTransformer.transform( value, context ) );
513 }
514
515
516
517
518
519
520
521
522
523 private int nextTag( XmlPullParser parser )
524 throws IOException, XmlPullParserException
525 {
526 int eventType = parser.next();
527 if ( eventType == XmlPullParser.TEXT )
528 {
529 eventType = parser.next();
530 }
531 if ( eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_TAG )
532 {
533 throw new XmlPullParserException( "expected START_TAG or END_TAG not " + XmlPullParser.TYPES[eventType], parser, null );
534 }
535 return eventType;
536 }
537
538
539
540
541
542
543
544
545
546
547 public ArchetypeModel read( Reader reader, boolean strict )
548 throws IOException, XmlPullParserException
549 {
550 XmlPullParser parser = addDefaultEntities ? new MXParser(EntityReplacementMap.defaultEntityReplacementMap) : new MXParser( );
551
552 parser.setInput( reader );
553
554
555 return read( parser, strict );
556 }
557
558
559
560
561
562
563
564
565
566 public ArchetypeModel read( Reader reader )
567 throws IOException, XmlPullParserException
568 {
569 return read( reader, true );
570 }
571
572
573
574
575
576
577
578
579
580
581 public ArchetypeModel read( InputStream in, boolean strict )
582 throws IOException, XmlPullParserException
583 {
584 return read( ReaderFactory.newXmlReader( in ), strict );
585 }
586
587
588
589
590
591
592
593
594
595 public ArchetypeModel read( InputStream in )
596 throws IOException, XmlPullParserException
597 {
598 return read( ReaderFactory.newXmlReader( in ) );
599 }
600
601
602
603
604
605
606
607
608
609
610 private ArchetypeModel parseArchetypeModel( XmlPullParser parser, boolean strict )
611 throws IOException, XmlPullParserException
612 {
613 String tagName = parser.getName();
614 ArchetypeModel archetypeModel = new ArchetypeModel();
615 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
616 {
617 String name = parser.getAttributeName( i );
618 String value = parser.getAttributeValue( i );
619
620 if ( name.indexOf( ':' ) >= 0 )
621 {
622
623 }
624 else if ( "xmlns".equals( name ) )
625 {
626
627 }
628 else
629 {
630 checkUnknownAttribute( parser, name, tagName, strict );
631 }
632 }
633 java.util.Set parsed = new java.util.HashSet();
634 while ( ( strict ? parser.nextTag() : nextTag( parser ) ) == XmlPullParser.START_TAG )
635 {
636 if ( checkFieldWithDuplicate( parser, "id", null, parsed ) )
637 {
638 archetypeModel.setId( interpolatedTrimmed( parser.nextText(), "id" ) );
639 }
640 else if ( checkFieldWithDuplicate( parser, "allowPartial", null, parsed ) )
641 {
642 archetypeModel.setAllowPartial( getBooleanValue( interpolatedTrimmed( parser.nextText(), "allowPartial" ), "allowPartial", parser, "false" ) );
643 }
644 else if ( checkFieldWithDuplicate( parser, "sources", null, parsed ) )
645 {
646 java.util.List<Source> sources = new java.util.ArrayList<Source>();
647 archetypeModel.setSources( sources );
648 while ( parser.nextTag() == XmlPullParser.START_TAG )
649 {
650 if ( "source".equals( parser.getName() ) )
651 {
652 sources.add( parseSource( parser, strict ) );
653 }
654 else
655 {
656 checkUnknownElement( parser, strict );
657 }
658 }
659 }
660 else if ( checkFieldWithDuplicate( parser, "resources", null, parsed ) )
661 {
662 java.util.List<Resource> resources = new java.util.ArrayList<Resource>();
663 archetypeModel.setResources( resources );
664 while ( parser.nextTag() == XmlPullParser.START_TAG )
665 {
666 if ( "resource".equals( parser.getName() ) )
667 {
668 resources.add( parseResource( parser, strict ) );
669 }
670 else
671 {
672 checkUnknownElement( parser, strict );
673 }
674 }
675 }
676 else if ( checkFieldWithDuplicate( parser, "testSources", null, parsed ) )
677 {
678 java.util.List<Source> testSources = new java.util.ArrayList<Source>();
679 archetypeModel.setTestSources( testSources );
680 while ( parser.nextTag() == XmlPullParser.START_TAG )
681 {
682 if ( "source".equals( parser.getName() ) )
683 {
684 testSources.add( parseSource( parser, strict ) );
685 }
686 else
687 {
688 checkUnknownElement( parser, strict );
689 }
690 }
691 }
692 else if ( checkFieldWithDuplicate( parser, "testResources", null, parsed ) )
693 {
694 java.util.List<Resource> testResources = new java.util.ArrayList<Resource>();
695 archetypeModel.setTestResources( testResources );
696 while ( parser.nextTag() == XmlPullParser.START_TAG )
697 {
698 if ( "resource".equals( parser.getName() ) )
699 {
700 testResources.add( parseResource( parser, strict ) );
701 }
702 else
703 {
704 checkUnknownElement( parser, strict );
705 }
706 }
707 }
708 else if ( checkFieldWithDuplicate( parser, "siteResources", null, parsed ) )
709 {
710 java.util.List<Resource> siteResources = new java.util.ArrayList<Resource>();
711 archetypeModel.setSiteResources( siteResources );
712 while ( parser.nextTag() == XmlPullParser.START_TAG )
713 {
714 if ( "resource".equals( parser.getName() ) )
715 {
716 siteResources.add( parseResource( parser, strict ) );
717 }
718 else
719 {
720 checkUnknownElement( parser, strict );
721 }
722 }
723 }
724 else
725 {
726 checkUnknownElement( parser, strict );
727 }
728 }
729 return archetypeModel;
730 }
731
732
733
734
735
736
737
738
739
740
741 private Resource parseResource( XmlPullParser parser, boolean strict )
742 throws IOException, XmlPullParserException
743 {
744 String tagName = parser.getName();
745 Resource resource = new Resource();
746 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
747 {
748 String name = parser.getAttributeName( i );
749 String value = parser.getAttributeValue( i );
750
751 if ( name.indexOf( ':' ) >= 0 )
752 {
753
754 }
755 else if ( "encoding".equals( name ) )
756 {
757 resource.setEncoding( interpolatedTrimmed( value, "encoding" ) );
758 }
759 else if ( "filtered".equals( name ) )
760 {
761 resource.setFiltered( getBooleanValue( interpolatedTrimmed( value, "filtered" ), "filtered", parser, "true" ) );
762 }
763 else
764 {
765 checkUnknownAttribute( parser, name, tagName, strict );
766 }
767 }
768 resource.setFile( interpolatedTrimmed( parser.nextText(), "file" ) );
769 return resource;
770 }
771
772
773
774
775
776
777
778
779
780
781 private Source parseSource( XmlPullParser parser, boolean strict )
782 throws IOException, XmlPullParserException
783 {
784 String tagName = parser.getName();
785 Source source = new Source();
786 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
787 {
788 String name = parser.getAttributeName( i );
789 String value = parser.getAttributeValue( i );
790
791 if ( name.indexOf( ':' ) >= 0 )
792 {
793
794 }
795 else if ( "encoding".equals( name ) )
796 {
797 source.setEncoding( interpolatedTrimmed( value, "encoding" ) );
798 }
799 else
800 {
801 checkUnknownAttribute( parser, name, tagName, strict );
802 }
803 }
804 source.setFile( interpolatedTrimmed( parser.nextText(), "file" ) );
805 return source;
806 }
807
808
809
810
811
812
813
814
815
816
817 private ArchetypeModel read( XmlPullParser parser, boolean strict )
818 throws IOException, XmlPullParserException
819 {
820 ArchetypeModel archetypeModel = null;
821 int eventType = parser.getEventType();
822 boolean parsed = false;
823 while ( eventType != XmlPullParser.END_DOCUMENT )
824 {
825 if ( eventType == XmlPullParser.START_TAG )
826 {
827 if ( strict && ! "archetype".equals( parser.getName() ) )
828 {
829 throw new XmlPullParserException( "Expected root element 'archetype' but found '" + parser.getName() + "'", parser, null );
830 }
831 else if ( parsed )
832 {
833
834 throw new XmlPullParserException( "Duplicated tag: 'archetype'", parser, null );
835 }
836 archetypeModel = parseArchetypeModel( parser, strict );
837 archetypeModel.setModelEncoding( parser.getInputEncoding() );
838 parsed = true;
839 }
840 eventType = parser.next();
841 }
842 if ( parsed )
843 {
844 return archetypeModel;
845 }
846 throw new XmlPullParserException( "Expected root element 'archetype' but found no element at all: invalid XML document", parser, null );
847 }
848
849
850
851
852
853
854 public void setAddDefaultEntities( boolean addDefaultEntities )
855 {
856 this.addDefaultEntities = addDefaultEntities;
857 }
858
859 public static interface ContentTransformer
860 {
861
862
863
864
865
866
867
868 String transform( String source, String fieldName );
869 }
870
871 }