001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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.io.UnsupportedEncodingException; 029import java.net.URI; 030import java.nio.charset.Charset; 031import java.nio.charset.StandardCharsets; 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.api.AbstractFileSetCheck; 039import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 040import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; 041import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 042 043/** 044 * Abstract super class for header checks. 045 * Provides support for header and headerFile properties. 046 */ 047public abstract class AbstractHeaderCheck extends AbstractFileSetCheck 048 implements ExternalResourceHolder { 049 050 /** Pattern to detect occurrences of '\n' in text. */ 051 private static final Pattern ESCAPED_LINE_FEED_PATTERN = Pattern.compile("\\\\n"); 052 053 /** The lines of the header file. */ 054 private final List<String> readerLines = new ArrayList<>(); 055 056 /** The file that contains the header to check against. */ 057 private URI headerFile; 058 059 /** Name of a charset to use for loading the header from a file. */ 060 private String charset = System.getProperty("file.encoding", StandardCharsets.UTF_8.name()); 061 062 /** 063 * Hook method for post processing header lines. 064 * This implementation does nothing. 065 */ 066 protected abstract void postProcessHeaderLines(); 067 068 /** 069 * Return the header lines to check against. 070 * @return the header lines to check against. 071 */ 072 protected List<String> getHeaderLines() { 073 final List<String> copy = new ArrayList<>(readerLines); 074 return Collections.unmodifiableList(copy); 075 } 076 077 /** 078 * Set the charset to use for loading the header from a file. 079 * @param charset the charset to use for loading the header from a file 080 * @throws UnsupportedEncodingException if charset is unsupported 081 */ 082 public void setCharset(String charset) throws UnsupportedEncodingException { 083 if (!Charset.isSupported(charset)) { 084 final String message = "unsupported charset: '" + charset + "'"; 085 throw new UnsupportedEncodingException(message); 086 } 087 this.charset = charset; 088 } 089 090 /** 091 * Set the header file to check against. 092 * @param uri the uri of the header to load. 093 * @throws CheckstyleException if fileName is empty. 094 */ 095 public void setHeaderFile(URI uri) throws CheckstyleException { 096 if (uri == null) { 097 throw new CheckstyleException( 098 "property 'headerFile' is missing or invalid in module " 099 + 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}