View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.pmd.exec;
20  
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FilterReader;
24  import java.io.IOException;
25  import java.io.InputStreamReader;
26  import java.io.Reader;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.List;
30  
31  import org.apache.maven.plugins.pmd.model.PmdErrorDetail;
32  import org.apache.maven.plugins.pmd.model.PmdFile;
33  import org.apache.maven.plugins.pmd.model.ProcessingError;
34  import org.apache.maven.plugins.pmd.model.SuppressedViolation;
35  import org.apache.maven.plugins.pmd.model.Violation;
36  import org.apache.maven.plugins.pmd.model.io.xpp3.PmdXpp3Reader;
37  import org.apache.maven.reporting.MavenReportException;
38  
39  /**
40   * Provides access to the result of the pmd analysis.
41   */
42  public class PmdResult {
43      private final List<ProcessingError> processingErrors = new ArrayList<>();
44      private final List<Violation> violations = new ArrayList<>();
45      private final List<SuppressedViolation> suppressedViolations = new ArrayList<>();
46  
47      public static final PmdResult EMPTY = new PmdResult();
48  
49      private PmdResult() {}
50  
51      public PmdResult(File pmdFile, String encoding) throws MavenReportException {
52          loadResult(pmdFile, encoding);
53      }
54  
55      public boolean hasViolations() {
56          return !violations.isEmpty();
57      }
58  
59      private void loadResult(File pmdFile, String encoding) throws MavenReportException {
60          try (Reader reader1 = new BomFilter(encoding, new InputStreamReader(new FileInputStream(pmdFile), encoding))) {
61              PmdXpp3Reader reader = new PmdXpp3Reader();
62              PmdErrorDetail details = reader.read(reader1, false);
63              processingErrors.addAll(details.getErrors());
64              suppressedViolations.addAll(details.getSuppressedViolations());
65  
66              for (PmdFile file : details.getFiles()) {
67                  String filename = file.getName();
68                  for (Violation violation : file.getViolations()) {
69                      violation.setFileName(filename);
70                      violations.add(violation);
71                  }
72              }
73          } catch (Exception e) {
74              throw new MavenReportException(e.getMessage(), e);
75          }
76      }
77  
78      // Note: This seems to be a bug in PMD's XMLRenderer. The BOM is rendered multiple times.
79      // once at the beginning of the file, which is Ok, but also in the middle of the file.
80      // This filter just skips all BOMs if the encoding is not UTF-8
81      private static class BomFilter extends FilterReader {
82          private static final char BOM = '\uFEFF';
83          private final boolean filter;
84  
85          BomFilter(String encoding, Reader in) {
86              super(in);
87              filter = !"UTF-8".equalsIgnoreCase(encoding);
88          }
89  
90          @Override
91          public int read() throws IOException {
92              int c = super.read();
93  
94              if (!filter) {
95                  return c;
96              }
97  
98              while (c != -1 && c == BOM) {
99                  c = super.read();
100             }
101             return c;
102         }
103 
104         @Override
105         public int read(char[] cbuf, int off, int len) throws IOException {
106             int count = super.read(cbuf, off, len);
107 
108             if (!filter) {
109                 return count;
110             }
111 
112             if (count != -1) {
113                 for (int i = off; i < off + count; i++) {
114                     if (cbuf[i] == BOM) {
115                         // shift the content one char to the left
116                         System.arraycopy(cbuf, i + 1, cbuf, i, off + count - 1 - i);
117                         count--;
118                     }
119                 }
120             }
121             return count;
122         }
123     }
124 
125     public Collection<Violation> getViolations() {
126         return violations;
127     }
128 
129     public Collection<SuppressedViolation> getSuppressedViolations() {
130         return suppressedViolations;
131     }
132 
133     public Collection<ProcessingError> getErrors() {
134         return processingErrors;
135     }
136 }