View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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.net.URI;
29  import java.nio.charset.Charset;
30  import java.nio.charset.UnsupportedCharsetException;
31  import java.util.ArrayList;
32  import java.util.List;
33  import java.util.Set;
34  import java.util.regex.Pattern;
35  
36  import com.puppycrawl.tools.checkstyle.PropertyType;
37  import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
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      /** Specify the name of the file containing the required header. */
57      private URI headerFile;
58  
59      /** Specify the character encoding to use when reading the headerFile. */
60      @XdocsPropertyType(PropertyType.STRING)
61      private Charset charset;
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       *
72       * @return the header lines to check against.
73       */
74      protected List<String> getHeaderLines() {
75          return List.copyOf(readerLines);
76      }
77  
78      /**
79       * Setter to specify the character encoding to use when reading the headerFile.
80       *
81       * @param charset the charset name to use for loading the header from a file
82       */
83      public void setCharset(String charset) {
84          this.charset = createCharset(charset);
85      }
86  
87      /**
88       * Setter to specify the name of the file containing the required header.
89       *
90       * @param uri the uri of the header to load.
91       * @throws CheckstyleException if fileName is empty.
92       */
93      public void setHeaderFile(URI uri) throws CheckstyleException {
94          if (uri == null) {
95              throw new CheckstyleException(
96                  "property 'headerFile' is missing or invalid in module "
97                      + getConfiguration().getName());
98          }
99  
100         headerFile = uri;
101     }
102 
103     /**
104      * Load the header from a file.
105      *
106      * @throws CheckstyleException if the file cannot be loaded
107      */
108     private void loadHeaderFile() throws CheckstyleException {
109         checkHeaderNotInitialized();
110         try (Reader headerReader = new InputStreamReader(new BufferedInputStream(
111                     headerFile.toURL().openStream()), charset)) {
112             loadHeader(headerReader);
113         }
114         catch (final IOException ex) {
115             throw new CheckstyleException(
116                     "unable to load header file " + headerFile, ex);
117         }
118     }
119 
120     /**
121      * Called before initializing the header.
122      *
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      * Creates charset by name.
135      *
136      * @param name charset name
137      * @return created charset
138      * @throws UnsupportedCharsetException if charset is unsupported
139      */
140     private static Charset createCharset(String name) {
141         if (!Charset.isSupported(name)) {
142             final String message = "unsupported charset: '" + name + "'";
143             throw new UnsupportedCharsetException(message);
144         }
145         return Charset.forName(name);
146     }
147 
148     /**
149      * Specify the required header specified inline.
150      * Individual header lines must be separated by the string
151      * {@code "\n"}(even on platforms with a different line separator).
152      *
153      * @param header header content to check against.
154      * @throws IllegalArgumentException if the header cannot be interpreted
155      */
156     public void setHeader(String header) {
157         if (!CommonUtil.isBlank(header)) {
158             checkHeaderNotInitialized();
159 
160             final String headerExpandedNewLines = ESCAPED_LINE_FEED_PATTERN
161                     .matcher(header).replaceAll("\n");
162 
163             try (Reader headerReader = new StringReader(headerExpandedNewLines)) {
164                 loadHeader(headerReader);
165             }
166             catch (final IOException ex) {
167                 throw new IllegalArgumentException("unable to load header", ex);
168             }
169         }
170     }
171 
172     /**
173      * Load header to check against from a Reader into readerLines.
174      *
175      * @param headerReader delivers the header to check against
176      * @throws IOException if
177      */
178     private void loadHeader(final Reader headerReader) throws IOException {
179         try (LineNumberReader lnr = new LineNumberReader(headerReader)) {
180             String line;
181             do {
182                 line = lnr.readLine();
183                 if (line != null) {
184                     readerLines.add(line);
185                 }
186             } while (line != null);
187             postProcessHeaderLines();
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 = Set.of();
204         }
205         else {
206             result = Set.of(headerFile.toASCIIString());
207         }
208 
209         return result;
210     }
211 
212 }