View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2014  Oliver Burn
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.header;
21  
22  import java.util.Arrays;
23  
24  import java.io.File;
25  import java.util.List;
26  import java.util.regex.Pattern;
27  import java.util.regex.PatternSyntaxException;
28  
29  import org.apache.commons.beanutils.ConversionException;
30  
31  import com.google.common.collect.Lists;
32  import com.puppycrawl.tools.checkstyle.api.Utils;
33  
34  /**
35   * Checks the header of the source against a header file that contains a
36   * {@link java.util.regex.Pattern regular expression}
37   * for each line of the source header.
38   *
39   * @author Lars Kühne
40   * @author o_sukhodolsky
41   */
42  public class RegexpHeaderCheck extends AbstractHeaderCheck
43  {
44      /** empty array to avoid instantiations. */
45      private static final int[] EMPTY_INT_ARRAY = new int[0];
46  
47      /** the compiled regular expressions */
48      private final List<Pattern> headerRegexps = Lists.newArrayList();
49  
50      /** the header lines to repeat (0 or more) in the check, sorted. */
51      private int[] multiLines = EMPTY_INT_ARRAY;
52  
53      /**
54       * Set the lines numbers to repeat in the header check.
55       * @param list comma separated list of line numbers to repeat in header.
56       */
57      public void setMultiLines(int[] list)
58      {
59          if ((list == null) || (list.length == 0)) {
60              multiLines = EMPTY_INT_ARRAY;
61              return;
62          }
63  
64          multiLines = new int[list.length];
65          System.arraycopy(list, 0, multiLines, 0, list.length);
66          Arrays.sort(multiLines);
67      }
68  
69      @Override
70      protected void processFiltered(File file, List<String> lines)
71      {
72          final int headerSize = getHeaderLines().size();
73          final int fileSize = lines.size();
74  
75          if (headerSize - multiLines.length > fileSize) {
76              log(1, "header.missing");
77          }
78          else {
79              int headerLineNo = 0;
80              int i;
81              for (i = 0; (headerLineNo < headerSize) && (i < fileSize); i++) {
82                  final String line = lines.get(i);
83                  boolean isMatch = isMatch(line, headerLineNo);
84                  while (!isMatch && isMultiLine(headerLineNo)) {
85                      headerLineNo++;
86                      isMatch = (headerLineNo == headerSize)
87                              || isMatch(line, headerLineNo);
88                  }
89                  if (!isMatch) {
90                      log(i + 1, "header.mismatch", getHeaderLines().get(
91                              headerLineNo));
92                      break; // stop checking
93                  }
94                  if (!isMultiLine(headerLineNo)) {
95                      headerLineNo++;
96                  }
97              }
98              if (i == fileSize) {
99                  // if file finished, but we have at least one non-multi-line
100                 // header isn't completed
101                 for (; headerLineNo < headerSize; headerLineNo++) {
102                     if (!isMultiLine(headerLineNo)) {
103                         log(1, "header.missing");
104                         break;
105                     }
106                 }
107             }
108         }
109     }
110 
111     /**
112      * Checks if a code line matches the required header line.
113      * @param line the code line
114      * @param headerLineNo the header line number.
115      * @return true if and only if the line matches the required header line.
116      */
117     private boolean isMatch(String line, int headerLineNo)
118     {
119         return headerRegexps.get(headerLineNo).matcher(line).find();
120     }
121 
122     /**
123      * @param lineNo a line number
124      * @return if <code>lineNo</code> is one of the repeat header lines.
125      */
126     private boolean isMultiLine(int lineNo)
127     {
128         return (Arrays.binarySearch(multiLines, lineNo + 1) >= 0);
129     }
130 
131     @Override
132     protected void postprocessHeaderLines()
133     {
134         final List<String> headerLines = getHeaderLines();
135         headerRegexps.clear();
136         for (String line : headerLines) {
137             try {
138                 // TODO: Not sure if cache in Utils is still necessary
139                 headerRegexps.add(Utils.getPattern(line));
140             }
141             catch (final PatternSyntaxException ex) {
142                 throw new ConversionException("line "
143                         + (headerRegexps.size() + 1)
144                         + " in header specification"
145                         + " is not a regular expression");
146             }
147         }
148     }
149 
150 }