From 59aa156470fbdf19a54382fb2caaca2a95f4dbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20K=C3=BChne?= Date: Sun, 6 Apr 2003 12:16:03 +0000 Subject: [PATCH] added NewlineAtEndOfFile check, contributed by Christopher Lenz --- .../checks/LineSeparatorOption.java | 91 ++++++++++ .../checks/NewlineAtEndOfFileCheck.java | 170 ++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 src/checkstyle/com/puppycrawl/tools/checkstyle/checks/LineSeparatorOption.java create mode 100644 src/checkstyle/com/puppycrawl/tools/checkstyle/checks/NewlineAtEndOfFileCheck.java diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/LineSeparatorOption.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/LineSeparatorOption.java new file mode 100644 index 000000000..c028efcee --- /dev/null +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/LineSeparatorOption.java @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code for adherence to a set of rules. +// Copyright (C) 2001-2002 Oliver Burn +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//////////////////////////////////////////////////////////////////////////////// +package com.puppycrawl.tools.checkstyle.checks; + +import java.util.Map; +import java.util.HashMap; + +/** + * Represents the options for line separator settings. + * + * @author lkuehne + * @see NewlineAtEndOfFileCheck + */ +public final class LineSeparatorOption extends AbstractOption +{ + /** maps from a string representation to an option */ + private static final Map STR_TO_OPT = new HashMap(); + + /** Windows-style line separators **/ + public static final LineSeparatorOption CRLF = + new LineSeparatorOption("crlf", "\r\n"); + + /** Mac-style line separators **/ + public static final LineSeparatorOption CR = + new LineSeparatorOption("cr", "\r"); + + /** Unix-style line separators **/ + public static final LineSeparatorOption LF = + new LineSeparatorOption("lf", "\n"); + + /** System default line separators **/ + public static final LineSeparatorOption SYSTEM = new LineSeparatorOption( + "system", System.getProperty("line.separator")); + + /** the line separator representation */ + private final String mLineSeparator; + + /** + * Creates a new LineSeparatorOption instance. + * @param aStrRep the string representation + * @param aSep the line separator, e.g. "\r\n" + */ + private LineSeparatorOption(String aStrRep, String aSep) + { + super(aStrRep); + mLineSeparator = aSep; + } + + /** + * @param aBytes a bytes array to check + * @return if aBytes is equal to the byte representation + * of this line separator + */ + public boolean matches(byte[] aBytes) + { + final String s = new String(aBytes); + return s.equals(mLineSeparator); + } + + /** + * @return the length of the file separator, + * e.g. 1 for CR, 2 for CRLF, ... + */ + public int length() + { + return mLineSeparator.length(); + } + + /** @see com.puppycrawl.tools.checkstyle.checks.AbstractOption */ + protected Map getStrToOpt() + { + return STR_TO_OPT; + } + +} diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/NewlineAtEndOfFileCheck.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/NewlineAtEndOfFileCheck.java new file mode 100644 index 000000000..42c1698af --- /dev/null +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/NewlineAtEndOfFileCheck.java @@ -0,0 +1,170 @@ +//////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code for adherence to a set of rules. +// Copyright (C) 2001-2002 Oliver Burn +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//////////////////////////////////////////////////////////////////////////////// +package com.puppycrawl.tools.checkstyle.checks; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +import com.puppycrawl.tools.checkstyle.Defn; +import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; +import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; +import com.puppycrawl.tools.checkstyle.api.MessageDispatcher; +import com.puppycrawl.tools.checkstyle.api.LocalizedMessages; +import org.apache.commons.beanutils.ConversionException; + +/** + *

+ * Checks that there is a newline at the end of each file. + *

+ *

+ * An example of how to configure the check is: + *

+ *
+ * <module name="NewlineAtEndOfFile"/>
+ *

+ * This will check against the platform-specific default line separator. + *

+ *

+ * It is also possible to enforce the use of a specific line-separator across + * platforms, with the 'lineSeparator' property: + *

+ *
+ * <module name="NewlineAtEndOfFile">
+ *   <property name="lineSeparator" value="lf"/>
+ * </module>
+ *

+ * Valid values for the 'lineSeparator' property are 'system' (system default), + * 'crlf' (windows), 'cr' (mac) and 'lf' (unix). + *

+ * + * @author Christopher Lenz + * @author lkuehne + * @version 1.0 + */ +public class NewlineAtEndOfFileCheck + extends AbstractFileSetCheck +{ + /** the line separator to check against. */ + private LineSeparatorOption mLineSeparator = + LineSeparatorOption.SYSTEM; + + /** @see com.puppycrawl.tools.checkstyle.api.Check */ + public int[] getDefaultTokens() + { + return new int[0]; + } + + /** + * @see com.puppycrawl.tools.checkstyle.api.FileSetCheck#process + */ + public void process(File[] aFiles) + { + final File[] files = filter(aFiles); + final MessageDispatcher dispatcher = getMessageDispatcher(); + for (int i = 0; i < files.length; i++) { + final File file = files[i]; + final String path = file.getPath(); + dispatcher.fireFileStarted(path); + RandomAccessFile randomAccessFile = null; + try { + randomAccessFile = new RandomAccessFile(file, "r"); + if (!endsWithNewline(randomAccessFile)) { + log(0, "noNewlineAtEOF", path); + } + } + catch (IOException e) { + logIOException(e); + } + finally { + if (randomAccessFile != null) { + try { + randomAccessFile.close(); + } + catch (IOException e) { + logIOException(e); + } + } + } + final LocalizedMessages messageList = getMessageCollector(); + final LocalizedMessage[] messages = messageList.getMessages(); + dispatcher.fireErrors(path, messages); + dispatcher.fireFileFinished(path); + } + } + + /** + * Sets the line separator to one of 'crlf', 'lf' or 'cr'. + * + * @param aLineSeparator The line separator to set + * @throws IllegalArgumentException If the specified line separator is not + * one of 'crlf', 'lf' or 'cr' + */ + public void setLineSeparator(String aLineSeparator) + { + AbstractOption option = + LineSeparatorOption.SYSTEM.decode(aLineSeparator); + + if (option == null) { + throw new ConversionException("unable to parse " + aLineSeparator); + } + + mLineSeparator = (LineSeparatorOption) option; + } + + /** + * Checks whether the content provided by the Reader ends with the platform + * specific line separator. + * @param aRandomAccessFile The reader for the content to check + * @return boolean Whether the content ends with a line separator + * @throws IOException When an IO error occurred while reading from the + * provided reader + */ + private boolean endsWithNewline(RandomAccessFile aRandomAccessFile) + throws IOException + { + final int len = mLineSeparator.length(); + if (aRandomAccessFile.length() < len) { + return false; + } + aRandomAccessFile.seek(aRandomAccessFile.length() - len); + final byte lastBytes[] = new byte[len]; + aRandomAccessFile.read(lastBytes); + return mLineSeparator.matches(lastBytes); + } + + /** + * Helper method to log an IO exception. + * @param aEx the exception that occured + */ + private void logIOException(IOException aEx) + { + String[] args = null; + String key = "general.fileNotFound"; + if (!(aEx instanceof FileNotFoundException)) { + args = new String[] {aEx.getMessage()}; + key = "general.exception"; + } + LocalizedMessage message = + new LocalizedMessage(0, Defn.CHECKSTYLE_BUNDLE, key, args); + getMessageCollector().add(message); + } + +}