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; 020 021import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 022import com.puppycrawl.tools.checkstyle.api.Utils; 023import java.io.File; 024import java.io.IOException; 025import java.io.RandomAccessFile; 026import java.util.List; 027import org.apache.commons.beanutils.ConversionException; 028 029/** 030 * <p> 031 * Checks that there is a newline at the end of each file. 032 * </p> 033 * <p> 034 * An example of how to configure the check is: 035 * </p> 036 * <pre> 037 * <module name="NewlineAtEndOfFile"/></pre> 038 * <p> 039 * This will check against the platform-specific default line separator. 040 * </p> 041 * <p> 042 * It is also possible to enforce the use of a specific line-separator across 043 * platforms, with the 'lineSeparator' property: 044 * </p> 045 * <pre> 046 * <module name="NewlineAtEndOfFile"> 047 * <property name="lineSeparator" value="lf"/> 048 * </module></pre> 049 * <p> 050 * Valid values for the 'lineSeparator' property are 'system' (system default), 051 * 'crlf' (windows), 'cr' (mac) and 'lf' (unix). 052 * </p> 053 * 054 * @author Christopher Lenz 055 * @author lkuehne 056 * @version 1.0 057 */ 058public class NewlineAtEndOfFileCheck 059 extends AbstractFileSetCheck 060{ 061 /** the line separator to check against. */ 062 private LineSeparatorOption lineSeparator = LineSeparatorOption.SYSTEM; 063 064 @Override 065 protected void processFiltered(File file, List<String> lines) 066 { 067 // Cannot use lines as the line separators have been removed! 068 RandomAccessFile randomAccessFile = null; 069 try { 070 randomAccessFile = new RandomAccessFile(file, "r"); 071 if (!endsWithNewline(randomAccessFile)) { 072 log(0, "noNewlineAtEOF", file.getPath()); 073 } 074 } 075 catch (final IOException e) { 076 log(0, "unable.open", file.getPath()); 077 } 078 finally { 079 Utils.closeQuietly(randomAccessFile); 080 } 081 } 082 083 /** 084 * Sets the line separator to one of 'crlf', 'lf' or 'cr'. 085 * 086 * @param lineSeparatorParam The line separator to set 087 * @throws IllegalArgumentException If the specified line separator is not 088 * one of 'crlf', 'lf' or 'cr' 089 */ 090 public void setLineSeparator(String lineSeparatorParam) 091 { 092 try { 093 lineSeparator = 094 Enum.valueOf(LineSeparatorOption.class, lineSeparatorParam.trim() 095 .toUpperCase()); 096 } 097 catch (IllegalArgumentException iae) { 098 throw new ConversionException("unable to parse " + lineSeparatorParam, 099 iae); 100 } 101 } 102 103 /** 104 * Checks whether the content provided by the Reader ends with the platform 105 * specific line separator. 106 * @param randomAccessFile The reader for the content to check 107 * @return boolean Whether the content ends with a line separator 108 * @throws IOException When an IO error occurred while reading from the 109 * provided reader 110 */ 111 private boolean endsWithNewline(RandomAccessFile randomAccessFile) 112 throws IOException 113 { 114 final int len = lineSeparator.length(); 115 if (randomAccessFile.length() < len) { 116 return false; 117 } 118 randomAccessFile.seek(randomAccessFile.length() - len); 119 final byte[] lastBytes = new byte[len]; 120 final int readBytes = randomAccessFile.read(lastBytes); 121 if (readBytes != len) { 122 throw new IOException("Unable to read " + len + " bytes, got " 123 + readBytes); 124 } 125 return lineSeparator.matches(lastBytes); 126 } 127}