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