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); + } + +}