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