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