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