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