001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.header; 021 022import java.io.BufferedInputStream; 023import java.io.IOException; 024import java.io.InputStreamReader; 025import java.io.LineNumberReader; 026import java.io.Reader; 027import java.io.StringReader; 028import java.net.URI; 029import java.nio.charset.Charset; 030import java.nio.charset.StandardCharsets; 031import java.nio.charset.UnsupportedCharsetException; 032import java.util.ArrayList; 033import java.util.Collections; 034import java.util.List; 035import java.util.Set; 036import java.util.regex.Pattern; 037 038import com.puppycrawl.tools.checkstyle.PropertyType; 039import com.puppycrawl.tools.checkstyle.XdocsPropertyType; 040import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 041import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 042import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; 043import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 044 045/** 046 * Abstract super class for header checks. 047 * Provides support for header and headerFile properties. 048 */ 049public abstract class AbstractHeaderCheck extends AbstractFileSetCheck 050 implements ExternalResourceHolder { 051 052 /** Pattern to detect occurrences of '\n' in text. */ 053 private static final Pattern ESCAPED_LINE_FEED_PATTERN = Pattern.compile("\\\\n"); 054 055 /** The lines of the header file. */ 056 private final List<String> readerLines = new ArrayList<>(); 057 058 /** Specify the name of the file containing the required header. */ 059 private URI headerFile; 060 061 /** Specify the character encoding to use when reading the headerFile. */ 062 @XdocsPropertyType(PropertyType.STRING) 063 private Charset charset = createCharset(System.getProperty("file.encoding", 064 StandardCharsets.UTF_8.name())); 065 066 /** 067 * Hook method for post processing header lines. 068 * This implementation does nothing. 069 */ 070 protected abstract void postProcessHeaderLines(); 071 072 /** 073 * Return the header lines to check against. 074 * 075 * @return the header lines to check against. 076 */ 077 protected List<String> getHeaderLines() { 078 final List<String> copy = new ArrayList<>(readerLines); 079 return Collections.unmodifiableList(copy); 080 } 081 082 /** 083 * Setter to specify the charset to use when reading the headerFile. 084 * 085 * @param charset the charset name to use for loading the header from a file 086 */ 087 public void setCharset(String charset) { 088 this.charset = createCharset(charset); 089 } 090 091 /** 092 * Setter to specify the name of the file containing the required header.. 093 * 094 * @param uri the uri of the header to load. 095 * @throws CheckstyleException if fileName is empty. 096 */ 097 public void setHeaderFile(URI uri) throws CheckstyleException { 098 if (uri == null) { 099 throw new CheckstyleException( 100 "property 'headerFile' is missing or invalid in module " 101 + getConfiguration().getName()); 102 } 103 104 headerFile = uri; 105 } 106 107 /** 108 * Load the header from a file. 109 * 110 * @throws CheckstyleException if the file cannot be loaded 111 */ 112 private void loadHeaderFile() throws CheckstyleException { 113 checkHeaderNotInitialized(); 114 try (Reader 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 } 123 124 /** 125 * Called before initializing the header. 126 * 127 * @throws IllegalArgumentException if header has already been set 128 */ 129 private void checkHeaderNotInitialized() { 130 if (!readerLines.isEmpty()) { 131 throw new IllegalArgumentException( 132 "header has already been set - " 133 + "set either header or headerFile, not both"); 134 } 135 } 136 137 /** 138 * Creates charset by name. 139 * 140 * @param name charset name 141 * @return created charset 142 * @throws UnsupportedCharsetException if charset is unsupported 143 */ 144 private static Charset createCharset(String name) { 145 if (!Charset.isSupported(name)) { 146 final String message = "unsupported charset: '" + name + "'"; 147 throw new UnsupportedCharsetException(message); 148 } 149 return Charset.forName(name); 150 } 151 152 /** 153 * Set the header to check against. Individual lines in the header 154 * must be separated by '\n' characters. 155 * 156 * @param header header content to check against. 157 * @throws IllegalArgumentException if the header cannot be interpreted 158 */ 159 public void setHeader(String header) { 160 if (!CommonUtil.isBlank(header)) { 161 checkHeaderNotInitialized(); 162 163 final String headerExpandedNewLines = ESCAPED_LINE_FEED_PATTERN 164 .matcher(header).replaceAll("\n"); 165 166 try (Reader headerReader = new StringReader(headerExpandedNewLines)) { 167 loadHeader(headerReader); 168 } 169 catch (final IOException ex) { 170 throw new IllegalArgumentException("unable to load header", ex); 171 } 172 } 173 } 174 175 /** 176 * Load header to check against from a Reader into readerLines. 177 * 178 * @param headerReader delivers the header to check against 179 * @throws IOException if 180 */ 181 private void loadHeader(final Reader headerReader) throws IOException { 182 try (LineNumberReader lnr = new LineNumberReader(headerReader)) { 183 String line; 184 do { 185 line = lnr.readLine(); 186 if (line != null) { 187 readerLines.add(line); 188 } 189 } while (line != null); 190 postProcessHeaderLines(); 191 } 192 } 193 194 @Override 195 protected final void finishLocalSetup() throws CheckstyleException { 196 if (headerFile != null) { 197 loadHeaderFile(); 198 } 199 } 200 201 @Override 202 public Set<String> getExternalResourceLocations() { 203 final Set<String> result; 204 205 if (headerFile == null) { 206 result = Collections.emptySet(); 207 } 208 else { 209 result = Collections.singleton(headerFile.toString()); 210 } 211 212 return result; 213 } 214 215}