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.google.common.io.Closeables; 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.CommonUtils; 043 044/** 045 * Abstract super class for header checks. 046 * Provides support for header and headerFile properties. 047 * @author o_sukhosolsky 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 /** The file that contains the header to check against. */ 059 private URI headerFile; 060 061 /** Name of a charset to use for loading the header from a file. */ 062 private String charset = System.getProperty("file.encoding", StandardCharsets.UTF_8.name()); 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 * @return the header lines to check against. 073 */ 074 protected List<String> getHeaderLines() { 075 final List<String> copy = new ArrayList<>(readerLines); 076 return Collections.unmodifiableList(copy); 077 } 078 079 /** 080 * Set the charset to use for loading the header from a file. 081 * @param charset the charset to use for loading the header from a file 082 * @throws UnsupportedEncodingException if charset is unsupported 083 */ 084 public void setCharset(String charset) throws UnsupportedEncodingException { 085 if (!Charset.isSupported(charset)) { 086 final String message = "unsupported charset: '" + charset + "'"; 087 throw new UnsupportedEncodingException(message); 088 } 089 this.charset = charset; 090 } 091 092 /** 093 * Set the header file to check against. 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 * @throws CheckstyleException if the file cannot be loaded 110 */ 111 private void loadHeaderFile() throws CheckstyleException { 112 checkHeaderNotInitialized(); 113 Reader headerReader = null; 114 try { 115 headerReader = new InputStreamReader(new BufferedInputStream( 116 headerFile.toURL().openStream()), charset); 117 loadHeader(headerReader); 118 } 119 catch (final IOException ex) { 120 throw new CheckstyleException( 121 "unable to load header file " + headerFile, ex); 122 } 123 finally { 124 Closeables.closeQuietly(headerReader); 125 } 126 } 127 128 /** 129 * Called before initializing the header. 130 * @throws IllegalArgumentException if header has already been set 131 */ 132 private void checkHeaderNotInitialized() { 133 if (!readerLines.isEmpty()) { 134 throw new IllegalArgumentException( 135 "header has already been set - " 136 + "set either header or headerFile, not both"); 137 } 138 } 139 140 /** 141 * Set the header to check against. Individual lines in the header 142 * must be separated by '\n' characters. 143 * @param header header content to check against. 144 * @throws IllegalArgumentException if the header cannot be interpreted 145 */ 146 public void setHeader(String header) { 147 if (!CommonUtils.isBlank(header)) { 148 checkHeaderNotInitialized(); 149 150 final String headerExpandedNewLines = ESCAPED_LINE_FEED_PATTERN 151 .matcher(header).replaceAll("\n"); 152 153 final Reader headerReader = new StringReader(headerExpandedNewLines); 154 try { 155 loadHeader(headerReader); 156 } 157 catch (final IOException ex) { 158 throw new IllegalArgumentException("unable to load header", ex); 159 } 160 finally { 161 Closeables.closeQuietly(headerReader); 162 } 163 } 164 } 165 166 /** 167 * Load header to check against from a Reader into readerLines. 168 * @param headerReader delivers the header to check against 169 * @throws IOException if 170 */ 171 private void loadHeader(final Reader headerReader) throws IOException { 172 final LineNumberReader lnr = new LineNumberReader(headerReader); 173 try { 174 while (true) { 175 String line = lnr.readLine(); 176 if (line == null) { 177 break; 178 } 179 if (line.isEmpty()) { 180 line = "^$"; 181 } 182 readerLines.add(line); 183 } 184 postProcessHeaderLines(); 185 } 186 finally { 187 Closeables.closeQuietly(lnr); 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 = Collections.emptySet(); 204 } 205 else { 206 result = Collections.singleton(headerFile.toString()); 207 } 208 209 return result; 210 } 211 212}