1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2015 the original author or authors.
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 }