View Javadoc
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.BufferedInputStream;
23  import java.io.IOException;
24  import java.io.InputStreamReader;
25  import java.io.LineNumberReader;
26  import java.io.Reader;
27  import java.io.StringReader;
28  import java.io.UnsupportedEncodingException;
29  import java.net.URI;
30  import java.nio.charset.Charset;
31  import java.nio.charset.StandardCharsets;
32  import java.util.ArrayList;
33  import java.util.Collections;
34  import java.util.List;
35  import java.util.Set;
36  import java.util.regex.Pattern;
37  
38  import com.google.common.io.Closeables;
39  import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
40  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41  import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
42  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
43  
44  /**
45   * Abstract super class for header checks.
46   * Provides support for header and headerFile properties.
47   * @author o_sukhosolsky
48   */
49  public abstract class AbstractHeaderCheck extends AbstractFileSetCheck
50      implements ExternalResourceHolder {
51      /** Pattern to detect occurrences of '\n' in text. */
52      private static final Pattern ESCAPED_LINE_FEED_PATTERN = Pattern.compile("\\\\n");
53  
54      /** The lines of the header file. */
55      private final List<String> readerLines = new ArrayList<>();
56  
57      /** The file that contains the header to check against. */
58      private URI headerFile;
59  
60      /** Name of a charset to use for loading the header from a file. */
61      private String charset = System.getProperty("file.encoding", StandardCharsets.UTF_8.name());
62  
63      /**
64       * Hook method for post processing header lines.
65       * This implementation does nothing.
66       */
67      protected abstract void postProcessHeaderLines();
68  
69      /**
70       * Return the header lines to check against.
71       * @return the header lines to check against.
72       */
73      protected List<String> getHeaderLines() {
74          final List<String> copy = new ArrayList<>(readerLines);
75          return Collections.unmodifiableList(copy);
76      }
77  
78      /**
79       * Set the charset to use for loading the header from a file.
80       * @param charset the charset to use for loading the header from a file
81       * @throws UnsupportedEncodingException if charset is unsupported
82       */
83      public void setCharset(String charset) throws UnsupportedEncodingException {
84          if (!Charset.isSupported(charset)) {
85              final String message = "unsupported charset: '" + charset + "'";
86              throw new UnsupportedEncodingException(message);
87          }
88          this.charset = charset;
89      }
90  
91      /**
92       * Set the header file to check against.
93       * @param uri the uri of the header to load.
94       * @throws CheckstyleException if fileName is empty.
95       */
96      public void setHeaderFile(URI uri) throws CheckstyleException {
97          if (uri == null) {
98              throw new CheckstyleException(
99                  "property 'headerFile' is missing or invalid in module "
100                     + getConfiguration().getName());
101         }
102 
103         headerFile = uri;
104     }
105 
106     /**
107      * Load the header from a file.
108      * @throws CheckstyleException if the file cannot be loaded
109      */
110     private void loadHeaderFile() throws CheckstyleException {
111         checkHeaderNotInitialized();
112         Reader headerReader = null;
113         try {
114             headerReader = new InputStreamReader(new BufferedInputStream(
115                     headerFile.toURL().openStream()), charset);
116             loadHeader(headerReader);
117         }
118         catch (final IOException ex) {
119             throw new CheckstyleException(
120                     "unable to load header file " + headerFile, ex);
121         }
122         finally {
123             Closeables.closeQuietly(headerReader);
124         }
125     }
126 
127     /**
128      * Called before initializing the header.
129      * @throws IllegalArgumentException if header has already been set
130      */
131     private void checkHeaderNotInitialized() {
132         if (!readerLines.isEmpty()) {
133             throw new IllegalArgumentException(
134                     "header has already been set - "
135                     + "set either header or headerFile, not both");
136         }
137     }
138 
139     /**
140      * Set the header to check against. Individual lines in the header
141      * must be separated by '\n' characters.
142      * @param header header content to check against.
143      * @throws IllegalArgumentException if the header cannot be interpreted
144      */
145     public void setHeader(String header) {
146         if (!CommonUtils.isBlank(header)) {
147             checkHeaderNotInitialized();
148 
149             final String headerExpandedNewLines = ESCAPED_LINE_FEED_PATTERN
150                     .matcher(header).replaceAll("\n");
151 
152             final Reader headerReader = new StringReader(headerExpandedNewLines);
153             try {
154                 loadHeader(headerReader);
155             }
156             catch (final IOException ex) {
157                 throw new IllegalArgumentException("unable to load header", ex);
158             }
159             finally {
160                 Closeables.closeQuietly(headerReader);
161             }
162         }
163     }
164 
165     /**
166      * Load header to check against from a Reader into readerLines.
167      * @param headerReader delivers the header to check against
168      * @throws IOException if
169      */
170     private void loadHeader(final Reader headerReader) throws IOException {
171         final LineNumberReader lnr = new LineNumberReader(headerReader);
172         try {
173             while (true) {
174                 String line = lnr.readLine();
175                 if (line == null) {
176                     break;
177                 }
178                 if (line.isEmpty()) {
179                     line = "^$";
180                 }
181                 readerLines.add(line);
182             }
183             postProcessHeaderLines();
184         }
185         finally {
186             Closeables.closeQuietly(lnr);
187         }
188 
189     }
190 
191     @Override
192     protected final void finishLocalSetup() throws CheckstyleException {
193         if (headerFile != null) {
194             loadHeaderFile();
195         }
196     }
197 
198     @Override
199     public Set<String> getExternalResourceLocations() {
200         final Set<String> result;
201 
202         if (headerFile == null) {
203             result = Collections.emptySet();
204         }
205         else {
206             result = Collections.singleton(headerFile.toString());
207         }
208 
209         return result;
210     }
211 }