001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.header; 020 021import java.io.BufferedInputStream; 022import java.io.File; 023import java.io.FileNotFoundException; 024import java.io.IOException; 025import java.io.InputStreamReader; 026import java.io.LineNumberReader; 027import java.io.Reader; 028import java.io.StringReader; 029import java.io.UnsupportedEncodingException; 030import java.net.MalformedURLException; 031import java.net.URI; 032import java.net.URISyntaxException; 033import java.net.URL; 034import java.nio.charset.Charset; 035import java.util.List; 036 037import org.apache.commons.beanutils.ConversionException; 038 039import com.google.common.collect.ImmutableList; 040import com.google.common.collect.Lists; 041import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 042import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 043import com.puppycrawl.tools.checkstyle.api.Utils; 044 045/** 046 * Abstract super class for header checks. 047 * Provides support for header and headerFile properties. 048 * @author o_sukhosolsky 049 */ 050public abstract class AbstractHeaderCheck extends AbstractFileSetCheck 051{ 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 /** 063 * Return the header lines to check against. 064 * @return the header lines to check against. 065 */ 066 protected ImmutableList<String> getHeaderLines() 067 { 068 return ImmutableList.copyOf(readerLines); 069 } 070 071 /** 072 * Set the charset to use for loading the header from a file. 073 * @param charset the charset to use for loading the header from a file 074 * @throws UnsupportedEncodingException if charset is unsupported 075 */ 076 public void setCharset(String charset) throws UnsupportedEncodingException 077 { 078 if (!Charset.isSupported(charset)) { 079 final String message = "unsupported charset: '" + charset + "'"; 080 throw new UnsupportedEncodingException(message); 081 } 082 this.charset = charset; 083 } 084 085 /** 086 * Set the header file to check against. 087 * @param fileName the file that contains the header to check against. 088 */ 089 public void setHeaderFile(String fileName) 090 { 091 // Handle empty param 092 if ((fileName == null) || (fileName.trim().length() == 0)) { 093 return; 094 } 095 096 filename = fileName; 097 } 098 099 /** 100 * Load the header from a file. 101 * @throws CheckstyleException if the file cannot be loaded 102 */ 103 private void loadHeaderFile() throws CheckstyleException 104 { 105 checkHeaderNotInitialized(); 106 Reader headerReader = null; 107 try { 108 final URI uri = resolveHeaderFile(); 109 headerReader = new InputStreamReader(new BufferedInputStream( 110 uri.toURL().openStream()), charset); 111 loadHeader(headerReader); 112 } 113 catch (final IOException ex) { 114 throw new CheckstyleException( 115 "unable to load header file " + filename, ex); 116 } 117 finally { 118 Utils.closeQuietly(headerReader); 119 } 120 } 121 122 /** 123 * Resolve the specified filename param to a URI. 124 * @return resolved header file URI 125 * @throws IOException on failure 126 */ 127 private URI resolveHeaderFile() throws IOException 128 { 129 // figure out if this is a File or a URL 130 URI uri; 131 try { 132 final URL url = new URL(filename); 133 uri = url.toURI(); 134 } 135 catch (final MalformedURLException ex) { 136 uri = null; 137 } 138 catch (final URISyntaxException ex) { 139 // URL violating RFC 2396 140 uri = null; 141 } 142 if (uri == null) { 143 final File file = new File(filename); 144 if (file.exists()) { 145 uri = file.toURI(); 146 } 147 else { 148 // check to see if the file is in the classpath 149 try { 150 final URL configUrl = AbstractHeaderCheck.class 151 .getResource(filename); 152 if (configUrl == null) { 153 throw new FileNotFoundException(filename); 154 } 155 uri = configUrl.toURI(); 156 } 157 catch (final URISyntaxException e) { 158 throw new FileNotFoundException(filename); 159 } 160 } 161 } 162 return uri; 163 } 164 165 /** 166 * Called before initializing the header. 167 * @throws ConversionException if header has already been set 168 */ 169 private void checkHeaderNotInitialized() 170 { 171 if (!readerLines.isEmpty()) { 172 throw new ConversionException( 173 "header has already been set - " 174 + "set either header or headerFile, not both"); 175 } 176 } 177 178 /** 179 * Set the header to check against. Individual lines in the header 180 * must be separated by '\n' characters. 181 * @param header header content to check against. 182 * @throws ConversionException if the header cannot be interpreted 183 */ 184 public void setHeader(String header) 185 { 186 if ((header == null) || (header.trim().length() == 0)) { 187 return; 188 } 189 190 checkHeaderNotInitialized(); 191 192 final String headerExpandedNewLines = header.replaceAll("\\\\n", "\n"); 193 194 final Reader headerReader = new StringReader(headerExpandedNewLines); 195 try { 196 loadHeader(headerReader); 197 } 198 catch (final IOException ex) { 199 throw new ConversionException("unable to load header", ex); 200 } 201 finally { 202 Utils.closeQuietly(headerReader); 203 } 204 } 205 206 /** 207 * Load header to check against from a Reader into readerLines. 208 * @param headerReader delivers the header to check against 209 * @throws IOException if 210 */ 211 private void loadHeader(final Reader headerReader) throws IOException 212 { 213 final LineNumberReader lnr = new LineNumberReader(headerReader); 214 readerLines.clear(); 215 while (true) { 216 final String l = lnr.readLine(); 217 if (l == null) { 218 break; 219 } 220 readerLines.add(l); 221 } 222 postprocessHeaderLines(); 223 } 224 225 /** 226 * Hook method for post processing header lines. 227 * This implementation does nothing. 228 */ 229 protected void postprocessHeaderLines() 230 { 231 } 232 233 @Override 234 protected final void finishLocalSetup() throws CheckstyleException 235 { 236 if (filename != null) { 237 loadHeaderFile(); 238 } 239 if (readerLines.isEmpty()) { 240 throw new CheckstyleException( 241 "property 'headerFile' is missing or invalid in module " 242 + getConfiguration().getName()); 243 } 244 } 245}