1
2
3
4
5
6 package org.apache.maven.plugins.pmd.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.plugins.pmd.model.PmdErrorDetail;
17 import org.apache.maven.plugins.pmd.model.PmdFile;
18 import org.apache.maven.plugins.pmd.model.ProcessingError;
19 import org.apache.maven.plugins.pmd.model.SuppressedViolation;
20 import org.apache.maven.plugins.pmd.model.Violation;
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 PmdXpp3Reader
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 PmdXpp3Reader()
63 {
64 this( new ContentTransformer()
65 {
66 public String transform( String source, String fieldName )
67 {
68 return source;
69 }
70 } );
71 }
72
73 public PmdXpp3Reader(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 PmdErrorDetail read( XmlPullParser parser, boolean strict )
567 throws IOException, XmlPullParserException
568 {
569 PmdErrorDetail pmdErrorDetail = null;
570 int eventType = parser.getEventType();
571 boolean parsed = false;
572 while ( eventType != XmlPullParser.END_DOCUMENT )
573 {
574 if ( eventType == XmlPullParser.START_TAG )
575 {
576 if ( strict && ! "pmd".equals( parser.getName() ) )
577 {
578 throw new XmlPullParserException( "Expected root element 'pmd' but found '" + parser.getName() + "'", parser, null );
579 }
580 else if ( parsed )
581 {
582
583 throw new XmlPullParserException( "Duplicated tag: 'pmd'", parser, null );
584 }
585 pmdErrorDetail = parsePmdErrorDetail( parser, strict );
586 pmdErrorDetail.setModelEncoding( parser.getInputEncoding() );
587 parsed = true;
588 }
589 eventType = parser.next();
590 }
591 if ( parsed )
592 {
593 return pmdErrorDetail;
594 }
595 throw new XmlPullParserException( "Expected root element 'pmd' but found no element at all: invalid XML document", parser, null );
596 }
597
598
599
600
601
602
603
604
605
606
607
608 public PmdErrorDetail read( Reader reader, boolean strict )
609 throws IOException, XmlPullParserException
610 {
611 XmlPullParser parser = addDefaultEntities ? new MXParser(EntityReplacementMap.defaultEntityReplacementMap) : new MXParser( );
612
613 parser.setInput( reader );
614
615
616 return read( parser, strict );
617 }
618
619
620
621
622
623
624
625
626
627
628 public PmdErrorDetail read( Reader reader )
629 throws IOException, XmlPullParserException
630 {
631 return read( reader, true );
632 }
633
634
635
636
637
638
639
640
641
642
643
644 public PmdErrorDetail read( InputStream in, boolean strict )
645 throws IOException, XmlPullParserException
646 {
647 return read( ReaderFactory.newXmlReader( in ), strict );
648 }
649
650
651
652
653
654
655
656
657
658
659 public PmdErrorDetail read( InputStream in )
660 throws IOException, XmlPullParserException
661 {
662 return read( ReaderFactory.newXmlReader( in ) );
663 }
664
665
666
667
668
669
670
671
672
673
674
675 private PmdErrorDetail parsePmdErrorDetail( XmlPullParser parser, boolean strict )
676 throws IOException, XmlPullParserException
677 {
678 String tagName = parser.getName();
679 PmdErrorDetail pmdErrorDetail = new PmdErrorDetail();
680 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
681 {
682 String name = parser.getAttributeName( i );
683 String value = parser.getAttributeValue( i );
684
685 if ( name.indexOf( ':' ) >= 0 )
686 {
687
688 }
689 else if ( "xmlns".equals( name ) )
690 {
691
692 }
693 else
694 {
695 checkUnknownAttribute( parser, name, tagName, strict );
696 }
697 }
698 java.util.Set parsed = new java.util.HashSet();
699 while ( ( strict ? parser.nextTag() : nextTag( parser ) ) == XmlPullParser.START_TAG )
700 {
701 if ( "file".equals( parser.getName() ) )
702 {
703 java.util.List<PmdFile> files = pmdErrorDetail.getFiles();
704 if ( files == null )
705 {
706 files = new java.util.ArrayList<PmdFile>();
707 }
708 files.add( parsePmdFile( parser, strict ) );
709 pmdErrorDetail.setFiles( files );
710 }
711 else if ( "suppressedviolation".equals( parser.getName() ) )
712 {
713 java.util.List<SuppressedViolation> suppressedViolations = pmdErrorDetail.getSuppressedViolations();
714 if ( suppressedViolations == null )
715 {
716 suppressedViolations = new java.util.ArrayList<SuppressedViolation>();
717 }
718 suppressedViolations.add( parseSuppressedViolation( parser, strict ) );
719 pmdErrorDetail.setSuppressedViolations( suppressedViolations );
720 }
721 else if ( "error".equals( parser.getName() ) )
722 {
723 java.util.List<ProcessingError> errors = pmdErrorDetail.getErrors();
724 if ( errors == null )
725 {
726 errors = new java.util.ArrayList<ProcessingError>();
727 }
728 errors.add( parseProcessingError( parser, strict ) );
729 pmdErrorDetail.setErrors( errors );
730 }
731 else
732 {
733 checkUnknownElement( parser, strict );
734 }
735 }
736 return pmdErrorDetail;
737 }
738
739
740
741
742
743
744
745
746
747
748
749 private PmdFile parsePmdFile( XmlPullParser parser, boolean strict )
750 throws IOException, XmlPullParserException
751 {
752 String tagName = parser.getName();
753 PmdFile pmdFile = new PmdFile();
754 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
755 {
756 String name = parser.getAttributeName( i );
757 String value = parser.getAttributeValue( i );
758
759 if ( name.indexOf( ':' ) >= 0 )
760 {
761
762 }
763 else if ( "name".equals( name ) )
764 {
765 pmdFile.setName( interpolatedTrimmed( value, "name" ) );
766 }
767 else
768 {
769 checkUnknownAttribute( parser, name, tagName, strict );
770 }
771 }
772 java.util.Set parsed = new java.util.HashSet();
773 while ( ( strict ? parser.nextTag() : nextTag( parser ) ) == XmlPullParser.START_TAG )
774 {
775 if ( "violation".equals( parser.getName() ) )
776 {
777 java.util.List<Violation> violations = pmdFile.getViolations();
778 if ( violations == null )
779 {
780 violations = new java.util.ArrayList<Violation>();
781 }
782 violations.add( parseViolation( parser, strict ) );
783 pmdFile.setViolations( violations );
784 }
785 else
786 {
787 checkUnknownElement( parser, strict );
788 }
789 }
790 return pmdFile;
791 }
792
793
794
795
796
797
798
799
800
801
802
803 private ProcessingError parseProcessingError( XmlPullParser parser, boolean strict )
804 throws IOException, XmlPullParserException
805 {
806 String tagName = parser.getName();
807 ProcessingError processingError = new ProcessingError();
808 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
809 {
810 String name = parser.getAttributeName( i );
811 String value = parser.getAttributeValue( i );
812
813 if ( name.indexOf( ':' ) >= 0 )
814 {
815
816 }
817 else if ( "filename".equals( name ) )
818 {
819 processingError.setFilename( interpolatedTrimmed( value, "filename" ) );
820 }
821 else if ( "msg".equals( name ) )
822 {
823 processingError.setMsg( interpolatedTrimmed( value, "msg" ) );
824 }
825 else
826 {
827 checkUnknownAttribute( parser, name, tagName, strict );
828 }
829 }
830 processingError.setDetail( interpolatedTrimmed( parser.nextText(), "detail" ) );
831 return processingError;
832 }
833
834
835
836
837
838
839
840
841
842
843
844 private SuppressedViolation parseSuppressedViolation( XmlPullParser parser, boolean strict )
845 throws IOException, XmlPullParserException
846 {
847 String tagName = parser.getName();
848 SuppressedViolation suppressedViolation = new SuppressedViolation();
849 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
850 {
851 String name = parser.getAttributeName( i );
852 String value = parser.getAttributeValue( i );
853
854 if ( name.indexOf( ':' ) >= 0 )
855 {
856
857 }
858 else if ( "filename".equals( name ) )
859 {
860 suppressedViolation.setFilename( interpolatedTrimmed( value, "filename" ) );
861 }
862 else if ( "suppressiontype".equals( name ) )
863 {
864 suppressedViolation.setSuppressionType( interpolatedTrimmed( value, "suppressiontype" ) );
865 }
866 else if ( "msg".equals( name ) )
867 {
868 suppressedViolation.setRuleMessage( interpolatedTrimmed( value, "msg" ) );
869 }
870 else if ( "usermsg".equals( name ) )
871 {
872 suppressedViolation.setUserMessage( interpolatedTrimmed( value, "usermsg" ) );
873 }
874 else
875 {
876 checkUnknownAttribute( parser, name, tagName, strict );
877 }
878 }
879 java.util.Set parsed = new java.util.HashSet();
880 while ( ( strict ? parser.nextTag() : nextTag( parser ) ) == XmlPullParser.START_TAG )
881 {
882 checkUnknownElement( parser, strict );
883 }
884 return suppressedViolation;
885 }
886
887
888
889
890
891
892
893
894
895
896
897 private Violation parseViolation( XmlPullParser parser, boolean strict )
898 throws IOException, XmlPullParserException
899 {
900 String tagName = parser.getName();
901 Violation violation = new Violation();
902 for ( int i = parser.getAttributeCount() - 1; i >= 0; i-- )
903 {
904 String name = parser.getAttributeName( i );
905 String value = parser.getAttributeValue( i );
906
907 if ( name.indexOf( ':' ) >= 0 )
908 {
909
910 }
911 else if ( "beginline".equals( name ) )
912 {
913 violation.setBeginline( getIntegerValue( interpolatedTrimmed( value, "beginline" ), "beginline", parser, strict ) );
914 }
915 else if ( "endline".equals( name ) )
916 {
917 violation.setEndline( getIntegerValue( interpolatedTrimmed( value, "endline" ), "endline", parser, strict ) );
918 }
919 else if ( "class".equals( name ) )
920 {
921 violation.setViolationClass( interpolatedTrimmed( value, "class" ) );
922 }
923 else if ( "package".equals( name ) )
924 {
925 violation.setViolationPackage( interpolatedTrimmed( value, "package" ) );
926 }
927 else if ( "rule".equals( name ) )
928 {
929 violation.setRule( interpolatedTrimmed( value, "rule" ) );
930 }
931 else if ( "priority".equals( name ) )
932 {
933 violation.setPriority( getIntegerValue( interpolatedTrimmed( value, "priority" ), "priority", parser, strict ) );
934 }
935 else if ( "externalInfoUrl".equals( name ) )
936 {
937 violation.setExternalInfoUrl( interpolatedTrimmed( value, "externalInfoUrl" ) );
938 }
939 else
940 {
941 checkUnknownAttribute( parser, name, tagName, strict );
942 }
943 }
944 violation.setText( interpolatedTrimmed( parser.nextText(), "text" ) );
945 return violation;
946 }
947
948
949
950
951
952
953 public void setAddDefaultEntities( boolean addDefaultEntities )
954 {
955 this.addDefaultEntities = addDefaultEntities;
956 }
957
958 public static interface ContentTransformer
959 {
960
961
962
963
964
965
966
967 String transform( String source, String fieldName );
968 }
969
970 }