001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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.util.List; 032import java.util.regex.Pattern; 033 034import org.apache.commons.beanutils.ConversionException; 035import org.apache.commons.lang3.StringUtils; 036 037import com.google.common.collect.ImmutableList; 038import com.google.common.collect.Lists; 039import com.google.common.io.Closeables; 040import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 041import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 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 /** Pattern to detect occurrences of '\n' in text. */ 051 private static final Pattern ESCAPED_LINE_FEED_PATTERN = Pattern.compile("\\\\n"); 052 /** The file that contains the header to check against. */ 053 private String filename; 054 055 /** Name of a charset to use for loading the header from a file. */ 056 private String charset = System.getProperty("file.encoding", "UTF-8"); 057 058 /** The lines of the header file. */ 059 private final List<String> readerLines = Lists.newArrayList(); 060 061 /** 062 * Return the header lines to check against. 063 * @return the header lines to check against. 064 */ 065 protected ImmutableList<String> getHeaderLines() { 066 return ImmutableList.copyOf(readerLines); 067 } 068 069 /** 070 * Set the charset to use for loading the header from a file. 071 * @param charset the charset to use for loading the header from a file 072 * @throws UnsupportedEncodingException if charset is unsupported 073 */ 074 public void setCharset(String charset) throws UnsupportedEncodingException { 075 if (!Charset.isSupported(charset)) { 076 final String message = "unsupported charset: '" + charset + "'"; 077 throw new UnsupportedEncodingException(message); 078 } 079 this.charset = charset; 080 } 081 082 /** 083 * Set the header file to check against. 084 * @param fileName the file that contains the header to check against. 085 * @throws CheckstyleException if fileName is empty. 086 */ 087 public void setHeaderFile(String fileName) throws CheckstyleException { 088 if (StringUtils.isBlank(fileName)) { 089 throw new CheckstyleException( 090 "property 'headerFile' is missing or invalid in module " 091 + getConfiguration().getName()); 092 } 093 094 filename = fileName; 095 } 096 097 /** 098 * Load the header from a file. 099 * @throws CheckstyleException if the file cannot be loaded 100 */ 101 private void loadHeaderFile() throws CheckstyleException { 102 checkHeaderNotInitialized(); 103 Reader headerReader = null; 104 try { 105 final URI uri = CommonUtils.getUriByFilename(filename); 106 headerReader = new InputStreamReader(new BufferedInputStream( 107 uri.toURL().openStream()), charset); 108 loadHeader(headerReader); 109 } 110 catch (final IOException ex) { 111 throw new CheckstyleException( 112 "unable to load header file " + filename, ex); 113 } 114 finally { 115 Closeables.closeQuietly(headerReader); 116 } 117 } 118 119 /** 120 * Called before initializing the header. 121 * @throws ConversionException if header has already been set 122 */ 123 private void checkHeaderNotInitialized() { 124 if (!readerLines.isEmpty()) { 125 throw new ConversionException( 126 "header has already been set - " 127 + "set either header or headerFile, not both"); 128 } 129 } 130 131 /** 132 * Set the header to check against. Individual lines in the header 133 * must be separated by '\n' characters. 134 * @param header header content to check against. 135 * @throws ConversionException if the header cannot be interpreted 136 */ 137 public void setHeader(String header) { 138 if (StringUtils.isBlank(header)) { 139 return; 140 } 141 142 checkHeaderNotInitialized(); 143 144 final String headerExpandedNewLines = ESCAPED_LINE_FEED_PATTERN 145 .matcher(header).replaceAll("\n"); 146 147 final Reader headerReader = new StringReader(headerExpandedNewLines); 148 try { 149 loadHeader(headerReader); 150 } 151 catch (final IOException ex) { 152 throw new ConversionException("unable to load header", ex); 153 } 154 finally { 155 Closeables.closeQuietly(headerReader); 156 } 157 } 158 159 /** 160 * Load header to check against from a Reader into readerLines. 161 * @param headerReader delivers the header to check against 162 * @throws IOException if 163 */ 164 private void loadHeader(final Reader headerReader) throws IOException { 165 final LineNumberReader lnr = new LineNumberReader(headerReader); 166 readerLines.clear(); 167 while (true) { 168 final String line = lnr.readLine(); 169 if (line == null) { 170 break; 171 } 172 readerLines.add(line); 173 } 174 postProcessHeaderLines(); 175 } 176 177 /** 178 * Hook method for post processing header lines. 179 * This implementation does nothing. 180 */ 181 protected void postProcessHeaderLines() { 182 // No code by default 183 } 184 185 @Override 186 protected final void finishLocalSetup() throws CheckstyleException { 187 if (filename != null) { 188 loadHeaderFile(); 189 } 190 if (readerLines.isEmpty()) { 191 setHeader(null); 192 } 193 } 194}