001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.UnsupportedCharsetException; 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.List; 034import java.util.Set; 035import java.util.regex.Pattern; 036 037import com.puppycrawl.tools.checkstyle.PropertyType; 038import com.puppycrawl.tools.checkstyle.XdocsPropertyType; 039import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 040import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 041import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; 042import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 043 044/** 045 * Abstract super class for header checks. 046 * Provides support for header and headerFile properties. 047 */ 048public abstract class AbstractHeaderCheck extends AbstractFileSetCheck 049 implements ExternalResourceHolder { 050 051 /** Pattern to detect occurrences of '\n' in text. */ 052 private static final Pattern ESCAPED_LINE_FEED_PATTERN = Pattern.compile("\\\\n"); 053 054 /** The lines of the header file. */ 055 private final List<String> readerLines = new ArrayList<>(); 056 057 /** Specify the name of the file containing the required header. */ 058 private URI headerFile; 059 060 /** Specify the character encoding to use when reading the headerFile. */ 061 @XdocsPropertyType(PropertyType.STRING) 062 private Charset charset; 063 064 /** 065 * Hook method for post-processing header lines. 066 * This implementation does nothing. 067 */ 068 protected abstract void postProcessHeaderLines(); 069 070 /** 071 * Return the header lines to check against. 072 * 073 * @return the header lines to check against. 074 */ 075 protected List<String> getHeaderLines() { 076 return List.copyOf(readerLines); 077 } 078 079 /** 080 * Setter to specify the character encoding to use when reading the headerFile. 081 * 082 * @param charset the charset name to use for loading the header from a file 083 */ 084 public void setCharset(String charset) { 085 this.charset = createCharset(charset); 086 } 087 088 /** 089 * Setter to specify the name of the file containing the required header. 090 * 091 * @param uri the uri of the header to load. 092 * @throws CheckstyleException if fileName is empty. 093 */ 094 public void setHeaderFile(URI uri) throws CheckstyleException { 095 if (uri == null) { 096 throw new CheckstyleException( 097 "property 'headerFile' is missing or invalid in module " 098 + getConfiguration().getName()); 099 } 100 101 headerFile = uri; 102 } 103 104 /** 105 * Load the header from a file. 106 * 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 * 124 * @throws IllegalArgumentException if header has already been set 125 */ 126 private void checkHeaderNotInitialized() { 127 if (!readerLines.isEmpty()) { 128 throw new IllegalArgumentException( 129 "header has already been set - " 130 + "set either header or headerFile, not both"); 131 } 132 } 133 134 /** 135 * Creates charset by name. 136 * 137 * @param name charset name 138 * @return created charset 139 * @throws UnsupportedCharsetException if charset is unsupported 140 */ 141 private static Charset createCharset(String name) { 142 if (!Charset.isSupported(name)) { 143 final String message = "unsupported charset: '" + name + "'"; 144 throw new UnsupportedCharsetException(message); 145 } 146 return Charset.forName(name); 147 } 148 149 /** 150 * Specify the required header specified inline. 151 * Individual header lines must be separated by the string 152 * {@code "\n"}(even on platforms with a different line separator). 153 * 154 * @param header header content to check against. 155 * @throws IllegalArgumentException if the header cannot be interpreted 156 */ 157 public void setHeader(String header) { 158 if (!CommonUtil.isBlank(header)) { 159 checkHeaderNotInitialized(); 160 161 final String headerExpandedNewLines = ESCAPED_LINE_FEED_PATTERN 162 .matcher(header).replaceAll("\n"); 163 164 try (Reader headerReader = new StringReader(headerExpandedNewLines)) { 165 loadHeader(headerReader); 166 } 167 catch (final IOException ex) { 168 throw new IllegalArgumentException("unable to load header", ex); 169 } 170 } 171 } 172 173 /** 174 * Load header to check against from a Reader into readerLines. 175 * 176 * @param headerReader delivers the header to check against 177 * @throws IOException if 178 */ 179 private void loadHeader(final Reader headerReader) throws IOException { 180 try (LineNumberReader lnr = new LineNumberReader(headerReader)) { 181 String line; 182 do { 183 line = lnr.readLine(); 184 if (line != null) { 185 readerLines.add(line); 186 } 187 } while (line != null); 188 postProcessHeaderLines(); 189 } 190 } 191 192 @Override 193 protected final void finishLocalSetup() throws CheckstyleException { 194 if (headerFile != null) { 195 loadHeaderFile(); 196 } 197 } 198 199 @Override 200 public Set<String> getExternalResourceLocations() { 201 final Set<String> result; 202 203 if (headerFile == null) { 204 result = Collections.emptySet(); 205 } 206 else { 207 result = Collections.singleton(headerFile.toString()); 208 } 209 210 return result; 211 } 212 213}