Coverage Report - com.puppycrawl.tools.checkstyle.checks.header.RegexpHeaderCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
RegexpHeaderCheck
100%
51/51
100%
34/34
3.571
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 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.io.File;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Arrays;
 25  
 import java.util.List;
 26  
 import java.util.regex.Pattern;
 27  
 import java.util.regex.PatternSyntaxException;
 28  
 
 29  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 30  
 import com.puppycrawl.tools.checkstyle.api.FileText;
 31  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 32  
 
 33  
 /**
 34  
  * Checks the header of the source against a header file that contains a
 35  
  * {@link Pattern regular expression}
 36  
  * for each line of the source header. In default configuration,
 37  
  * if header is not specified, the default value of header is set to null
 38  
  * and the check does not rise any violations.
 39  
  *
 40  
  * @author Lars K├╝hne
 41  
  * @author o_sukhodolsky
 42  
  */
 43  
 @StatelessCheck
 44  34
 public class RegexpHeaderCheck extends AbstractHeaderCheck {
 45  
 
 46  
     /**
 47  
      * A key is pointing to the warning message text in "messages.properties"
 48  
      * file.
 49  
      */
 50  
     public static final String MSG_HEADER_MISSING = "header.missing";
 51  
 
 52  
     /**
 53  
      * A key is pointing to the warning message text in "messages.properties"
 54  
      * file.
 55  
      */
 56  
     public static final String MSG_HEADER_MISMATCH = "header.mismatch";
 57  
 
 58  
     /** Empty array to avoid instantiations. */
 59  2
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 60  
 
 61  
     /** The compiled regular expressions. */
 62  34
     private final List<Pattern> headerRegexps = new ArrayList<>();
 63  
 
 64  
     /** The header lines to repeat (0 or more) in the check, sorted. */
 65  34
     private int[] multiLines = EMPTY_INT_ARRAY;
 66  
 
 67  
     /**
 68  
      * Set the lines numbers to repeat in the header check.
 69  
      * @param list comma separated list of line numbers to repeat in header.
 70  
      */
 71  
     public void setMultiLines(int... list) {
 72  11
         if (list.length == 0) {
 73  1
             multiLines = EMPTY_INT_ARRAY;
 74  
         }
 75  
         else {
 76  10
             multiLines = new int[list.length];
 77  10
             System.arraycopy(list, 0, multiLines, 0, list.length);
 78  10
             Arrays.sort(multiLines);
 79  
         }
 80  11
     }
 81  
 
 82  
     @Override
 83  
     protected void processFiltered(File file, FileText fileText) {
 84  20
         final int headerSize = getHeaderLines().size();
 85  20
         final int fileSize = fileText.size();
 86  
 
 87  20
         if (headerSize - multiLines.length > fileSize) {
 88  2
             log(1, MSG_HEADER_MISSING);
 89  
         }
 90  
         else {
 91  18
             int headerLineNo = 0;
 92  
             int index;
 93  95
             for (index = 0; headerLineNo < headerSize && index < fileSize; index++) {
 94  83
                 final String line = fileText.get(index);
 95  83
                 boolean isMatch = isMatch(line, headerLineNo);
 96  98
                 while (!isMatch && isMultiLine(headerLineNo)) {
 97  15
                     headerLineNo++;
 98  15
                     isMatch = headerLineNo == headerSize
 99  13
                             || isMatch(line, headerLineNo);
 100  
                 }
 101  83
                 if (!isMatch) {
 102  6
                     log(index + 1, MSG_HEADER_MISMATCH, getHeaderLines().get(
 103  
                             headerLineNo));
 104  6
                     break;
 105  
                 }
 106  77
                 if (!isMultiLine(headerLineNo)) {
 107  55
                     headerLineNo++;
 108  
                 }
 109  
             }
 110  18
             if (index == fileSize) {
 111  
                 // if file finished, but we have at least one non-multi-line
 112  
                 // header isn't completed
 113  3
                 logFirstSinglelineLine(headerLineNo, headerSize);
 114  
             }
 115  
         }
 116  20
     }
 117  
 
 118  
     /**
 119  
      * Logs warning if any non-multiline lines left in header regexp.
 120  
      * @param startHeaderLine header line number to start from
 121  
      * @param headerSize whole header size
 122  
      */
 123  
     private void logFirstSinglelineLine(int startHeaderLine, int headerSize) {
 124  5
         for (int lineNum = startHeaderLine; lineNum < headerSize; lineNum++) {
 125  3
             if (!isMultiLine(lineNum)) {
 126  1
                 log(1, MSG_HEADER_MISSING);
 127  1
                 break;
 128  
             }
 129  
         }
 130  3
     }
 131  
 
 132  
     /**
 133  
      * Checks if a code line matches the required header line.
 134  
      * @param line the code line
 135  
      * @param headerLineNo the header line number.
 136  
      * @return true if and only if the line matches the required header line.
 137  
      */
 138  
     private boolean isMatch(String line, int headerLineNo) {
 139  96
         return headerRegexps.get(headerLineNo).matcher(line).find();
 140  
     }
 141  
 
 142  
     /**
 143  
      * Returns true if line is multiline header lines or false.
 144  
      * @param lineNo a line number
 145  
      * @return if {@code lineNo} is one of the repeat header lines.
 146  
      */
 147  
     private boolean isMultiLine(int lineNo) {
 148  101
         return Arrays.binarySearch(multiLines, lineNo + 1) >= 0;
 149  
     }
 150  
 
 151  
     @Override
 152  
     protected void postProcessHeaderLines() {
 153  25
         final List<String> headerLines = getHeaderLines();
 154  25
         for (String line : headerLines) {
 155  
             try {
 156  139
                 headerRegexps.add(Pattern.compile(line));
 157  
             }
 158  2
             catch (final PatternSyntaxException ex) {
 159  2
                 throw new IllegalArgumentException("line "
 160  2
                         + (headerRegexps.size() + 1)
 161  
                         + " in header specification"
 162  
                         + " is not a regular expression", ex);
 163  137
             }
 164  137
         }
 165  23
     }
 166  
 
 167  
     /**
 168  
      * Validates the {@code header} by compiling it with
 169  
      * {@link Pattern#compile(String) } and throws
 170  
      * {@link IllegalArgumentException} if {@code header} isn't a valid pattern.
 171  
      * @param header the header value to validate and set (in that order)
 172  
      */
 173  
     @Override
 174  
     public void setHeader(String header) {
 175  9
         if (!CommonUtils.isBlank(header)) {
 176  7
             if (!CommonUtils.isPatternValid(header)) {
 177  1
                 throw new IllegalArgumentException("Unable to parse format: " + header);
 178  
             }
 179  6
             super.setHeader(header);
 180  
         }
 181  7
     }
 182  
 
 183  
 }