LineEnds.java Source Code

  • LineEnds Documentation and Examples
  • LineEnds Javadoc
    /*
     * Adjusts line endings.
     * Copyright (C) 2001-2010 Stephen Ostermiller
     * http://ostermiller.org/contact.pl?regarding=Java+Utilities
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program 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 General Public License for more details.
     *
     * See LICENSE.txt for details.
     */
    
    package com.Ostermiller.util;
    
    import java.io.*;
    import java.text.MessageFormat;
    import java.util.ResourceBundle;
    import java.util.Locale;
    
    /**
     * Stream editor to alter the line separators on text to match
     * that of a given platform.
     * More information about this class is available from <a target="_top" href=
     * "http://ostermiller.org/utils/LineEnds.html">ostermiller.org</a>.
     *
     * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
     * @since ostermillerutils 1.00.00
     */
    public class LineEnds {
    
    	/**
    	 * Version number of this program
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static final String version = "1.2";
    
    	/**
    	 * Locale specific strings displayed to the user.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	protected static ResourceBundle labels = ResourceBundle.getBundle("com.Ostermiller.util.LineEnds",  Locale.getDefault());
    
    	private enum LineEndsCmdLnOption {
    		/** --help */
    		HELP(new CmdLnOption(labels.getString("help.option")).setDescription( labels.getString("help.message"))),
    		/** --version */
    		VERSION(new CmdLnOption(labels.getString("version.option")).setDescription(labels.getString("version.message"))),
    		/** --about */
    		ABOUT(new CmdLnOption(labels.getString("about.option")).setDescription(labels.getString("about.message"))),
    		/** --dos --windows */
    		DOS(new CmdLnOption(new String[]{labels.getString("dos.option"),labels.getString("windows.option")}, new char[]{'d'}).setDescription(labels.getString("d.message"))),
    		/** --unix --java */
    		UNIX(new CmdLnOption(new String[]{labels.getString("unix.option"),labels.getString("java.option")}, new char[]{'n'}).setDescription(labels.getString("n.message"))),
    		/** --mac */
    		MAC(new CmdLnOption(labels.getString("mac.option"), 'r').setDescription(labels.getString("r.message"))),
    		/** --system */
    		SYSTEM(new CmdLnOption(labels.getString("system.option"), 's').setDescription(labels.getString("s.message") + " (" + labels.getString("default") + ")")),
    		/** --force */
    		FORCE(new CmdLnOption(labels.getString("force.option"), 'f').setDescription(labels.getString("f.message"))),
    		/** --quiet */
    		QUIET(new CmdLnOption(labels.getString("quiet.option"), 'q').setDescription(labels.getString("q.message") + " (" + labels.getString("default") + ")")),
    		/** --reallyquiet */
    		REALLYQUIET(new CmdLnOption(labels.getString("reallyquiet.option"), 'Q').setDescription(labels.getString("Q.message"))),
    		/** --verbose */
    		VERBOSE(new CmdLnOption(labels.getString("verbose.option"), 'v').setDescription(labels.getString("v.message"))),
    		/** --reallyverbose */
    		REALLYVERBOSE(new CmdLnOption(labels.getString("reallyverbose.option"), 'V').setDescription(labels.getString("V.message"))),
    		/** --noforce */
    		NOFORCE(new CmdLnOption(labels.getString("noforce.option")).setDescription(labels.getString("noforce.message") + " (" + labels.getString("default") + ")"));
    
    		private CmdLnOption option;
    
    		private LineEndsCmdLnOption(CmdLnOption option){
    			option.setUserObject(this);
    			this.option = option;
    		}
    
    		private CmdLnOption getCmdLineOption(){
    			return option;
    		}
    	}
    
    	/**
    	 * Converts the line ending on files, or standard input.
    	 * Run with --help argument for more information.
    	 *
    	 * @param args Command line arguments.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static void main(String[] args){
    
    		CmdLn commandLine = new CmdLn(
    			args
    		).setDescription(
    			labels.getString("lineends") + labels.getString("purpose.message")
    		);
    		for (LineEndsCmdLnOption option: LineEndsCmdLnOption.values()){
    			commandLine.addOption(option.getCmdLineOption());
    		}
    		int style = STYLE_SYSTEM;
    		boolean force = false;
    		boolean printMessages = false;
    		boolean printExtraMessages = false;
    		boolean printErrors = true;
    		for(CmdLnResult result: commandLine.getResults()){
    			switch((LineEndsCmdLnOption)result.getOption().getUserObject()){
    				case HELP:{
    					// print out the help message
    					commandLine.printHelp();
    					System.exit(0);
    				} break;
    				case VERSION:{
    					// print out the version message
    					System.out.println(MessageFormat.format(labels.getString("version"), (Object[])new String[] {version}));
    					System.exit(0);
    				} break;
    				case ABOUT:{
    					System.out.println(
    						labels.getString("lineends") + " -- " + labels.getString("purpose.message") + "\n" +
    						MessageFormat.format(labels.getString("copyright"), (Object[])new String[] {"2001", "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Java+Utilities)"}) + "\n\n" +
    						labels.getString("license")
    					);
    					System.exit(0);
    				} break;
    				case DOS:{
    					style = STYLE_RN;
    				} break;
    				case UNIX:{
    					style = STYLE_N;
    				} break;
    				case MAC:{
    					style = STYLE_R;
    				} break;
    				case SYSTEM:{
    					style = STYLE_SYSTEM;
    				} break;
    				case FORCE:{
    					force = true;
    				} break;
    				case NOFORCE:{
    					force = false;
    				} break;
    				case REALLYVERBOSE:{
    					printExtraMessages = true;
    					printMessages = true;
    					printErrors = true;
    				} break;
    				case VERBOSE:{
    					printExtraMessages = false;
    					printMessages = true;
    					printErrors = true;
    				} break;
    				case QUIET:{
    					printExtraMessages = false;
    					printMessages = false;
    					printErrors = true;
    				} break;
    				case REALLYQUIET:{
    					printExtraMessages = false;
    					printMessages = false;
    					printErrors = false;
    				} break;
    			}
    		}
    
    		int exitCond = 0;
    		boolean done = false;
    		for (String argument: commandLine.getNonOptionArguments()){
    			done = true;
    			File source = new File(argument);
    			if (!source.exists()){
    				if(printErrors){
    					System.err.println(MessageFormat.format(labels.getString("does"+"not"+"exist"), (Object[])new String[] {argument}));
    				}
    				exitCond = 1;
    			} else if (!source.canRead()){
    				if(printErrors){
    					System.err.println(MessageFormat.format(labels.getString("cantread"), (Object[])new String[] {argument}));
    				}
    				exitCond = 1;
    			} else if (!source.canWrite()){
    				if(printErrors){
    					System.err.println(MessageFormat.format(labels.getString("cantwrite"), (Object[])new String[] {argument}));
    				}
    				exitCond = 1;
    			} else {
    				try {
    					if(convert (source, style, !force)){
    						if (printMessages){
    							System.out.println(MessageFormat.format(labels.getString("modified"), (Object[])new String[] {argument}));
    						}
    					} else {
    						if (printExtraMessages){
    							System.out.println(MessageFormat.format(labels.getString("alreadycorrect"), (Object[])new String[] {argument}));
    						}
    					}
    				} catch (IOException x){
    					if(printErrors){
    						System.err.println(argument + ": " + x.getMessage());
    					}
    					exitCond = 1;
    				}
    			}
    		}
    		if (!done){
    			try {
    				convert (System.in, System.out, style, !force);
    			} catch (IOException x){
    				System.err.println(x.getMessage());
    				exitCond = 1;
    			}
    		}
    		System.exit(exitCond);
    	}
    
    	/**
    	 * The system line ending as determined
    	 * by System.getProperty("line.separator")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_SYSTEM = 0;
    	/**
    	 * The Windows and DOS line ending ("\r\n")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_WINDOWS = 1;
    	/**
    	 * The Windows and DOS line ending ("\r\n")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_DOS = 1;
    	/**
    	 * The Windows and DOS line ending ("\r\n")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_RN = 1;
    	/**
    	 * The UNIX and Java line ending ("\n")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_UNIX = 2;
    	/**
    	 * The UNIX and Java line ending ("\n")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_N = 2;
    	/**
    	 * The UNIX and Java line ending ("\n")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_JAVA = 2;
    	/**
    	 * The Macintosh line ending ("\r")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_MAC = 3;
    	/**
    	 * The Macintosh line ending ("\r")
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public final static int STYLE_R = 3;
    
    	/**
    	 * Buffer size when reading from input stream.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	private final static int BUFFER_SIZE = 1024;
    	private final static int STATE_INIT = 0;
    	private final static int STATE_R = 1;
    
    	private final static int MASK_N = 0x01;
    	private final static int MASK_R = 0x02;
    	private final static int MASK_RN = 0x04;
    
    	/**
    	 * Change the line endings of the text on the input stream and write
    	 * it to the output stream.
    	 *
    	 * The current system's line separator is used.
    	 *
    	 * @param in stream that contains the text which needs line number conversion.
    	 * @param out stream where converted text is written.
    	 * @return true if the output was modified from the input, false if it is exactly the same
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(InputStream in, OutputStream out) throws IOException {
    		return convert(in, out, STYLE_SYSTEM, true);
    	}
    
    	/**
    	 * Change the line endings of the text on the input stream and write
    	 * it to the output stream.
    	 *
    	 * @param in stream that contains the text which needs line number conversion.
    	 * @param out stream where converted text is written.
    	 * @param style line separator style.
    	 * @return true if the output was modified from the input, false if it is exactly the same
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 * @throws IllegalArgumentException if an unknown style is requested.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(InputStream in, OutputStream out, int style) throws IOException {
    		return convert(in, out, style, true);
    	}
    
    	/**
    	 * Change the line endings of the text on the input stream and write
    	 * it to the output stream.
    	 *
    	 * The current system's line separator is used.
    	 *
    	 * @param in stream that contains the text which needs line number conversion.
    	 * @param out stream where converted text is written.
    	 * @param binaryException throw an exception and abort the operation if binary data is encountered and binaryExcepion is false.
    	 * @return true if the output was modified from the input, false if it is exactly the same
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(InputStream in, OutputStream out, boolean binaryException) throws IOException {
    		return convert(in, out, STYLE_SYSTEM, binaryException);
    	}
    
    	/**
    	 * Change the line endings of the text on the input stream and write
    	 * it to the output stream.
    	 *
    	 * @param in stream that contains the text which needs line number conversion.
    	 * @param out stream where converted text is written.
    	 * @param style line separator style.
    	 * @param binaryException throw an exception and abort the operation if binary data is encountered and binaryExcepion is false.
    	 * @return true if the output was modified from the input, false if it is exactly the same
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 * @throws IllegalArgumentException if an unknown style is requested.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(InputStream in, OutputStream out, int style, boolean binaryException) throws IOException {
    		byte[] lineEnding;
    		switch (style) {
    			case STYLE_SYSTEM: {
    				 lineEnding = System.getProperty("line.separator").getBytes();
    			} break;
    			case STYLE_RN: {
    				 lineEnding = new byte[]{(byte)'\r',(byte)'\n'};
    			} break;
    			case STYLE_R: {
    				 lineEnding = new byte[]{(byte)'\r'};
    			} break;
    			case STYLE_N: {
    				 lineEnding = new byte[]{(byte)'\n'};
    			} break;
    			default: {
    				throw new IllegalArgumentException("Unknown line break style: " + style);
    			}
    		}
    		byte[] buffer = new byte[BUFFER_SIZE];
    		int read;
    		int state = STATE_INIT;
    		int seen = 0x00;
    		while((read = in.read(buffer)) != -1){
    			for (int i=0; i<read; i++){
    				byte b = buffer[i];
    				if (state==STATE_R){
    					if(b!='\n'){
    						out.write(lineEnding);
    						seen |= MASK_R;
    					}
    				}
    				if (b=='\r'){
    					state = STATE_R;
    				} else {
    					if (b=='\n'){
    						if (state==STATE_R){
    							seen |= MASK_RN;
    						} else {
    							seen |= MASK_N;
    						}
    						out.write(lineEnding);
    					} else if(binaryException && b!='\t' && b!='\f' && (b & 0xff)<32){
    						throw new BinaryDataException(labels.getString("binaryexcepion"));
    					} else {
    						out.write(b);
    					}
    					state = STATE_INIT;
    				}
    			}
    		}
    		if (state==STATE_R){
    			out.write(lineEnding);
    			seen |= MASK_R;
    		}
    		if (lineEnding.length==2 && lineEnding[0]=='\r' && lineEnding[1]=='\n'){
    			return ((seen & ~MASK_RN)!=0);
    		} else if (lineEnding.length==1 && lineEnding[0]=='\r'){
    			return ((seen & ~MASK_R)!=0);
    		} else if (lineEnding.length==1 && lineEnding[0]=='\n'){
    			return ((seen & ~MASK_N)!=0);
    		} else {
    			return true;
    		}
    	}
    
    	/**
    	 * Change the line endings on given file.
    	 *
    	 * The current system's line separator is used.
    	 *
    	 * @param f File to be converted.
    	 * @return true if the file was modified, false if it was already in the correct format
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(File f) throws IOException {
    		return convert(f, STYLE_SYSTEM, true);
    	}
    
    	/**
    	 * Change the line endings on given file.
    	 *
    	 * @param f File to be converted.
    	 * @param style line separator style.
    	 * @return true if the file was modified, false if it was already in the correct format
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 * @throws IllegalArgumentException if an unknown style is requested.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(File f, int style) throws IOException {
    		return convert(f, style, true);
    	}
    
    	/**
    	 * Change the line endings on given file.
    	 *
    	 * The current system's line separator is used.
    	 *
    	 * @param f File to be converted.
    	 * @param binaryException throw an exception and abort the operation if binary data is encountered and binaryExcepion is false.
    	 * @return true if the file was modified, false if it was already in the correct format
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(File f, boolean binaryException) throws IOException {
    		return convert(f, STYLE_SYSTEM, binaryException);
    	}
    
    	/**
    	 * Change the line endings on given file.
    	 *
    	 * @param f File to be converted.
    	 * @param style line separator style.
    	 * @param binaryException throw an exception and abort the operation if binary data is encountered and binaryExcepion is false.
    	 * @return true if the file was modified, false if it was already in the correct format
    	 * @throws BinaryDataException if non-text data is encountered.
    	 * @throws IOException if an input or output error occurs.
    	 * @throws IllegalArgumentException if an unknown style is requested.
    	 *
    	 * @since ostermillerutils 1.00.00
    	 */
    	public static boolean convert(File f, int style, boolean binaryException) throws IOException {
    		File temp = null;
    		InputStream in = null;
    		OutputStream out = null;
    		boolean modified = false;
    		try {
    			in = new FileInputStream(f);
    			temp = File.createTempFile("LineEnds", null, null);
    			out = new FileOutputStream(temp);
    			modified = convert (in, out, style, binaryException);
    			in.close();
    			in = null;
    			out.flush();
    			out.close();
    			out = null;
    			if (modified){
    				FileHelper.move(temp, f, true);
    			} else {
    				if (!temp.delete()){
    					throw new IOException(
    						MessageFormat.format(
    							labels.getString("temp"+"delete"+"error"),
    							(Object[])new String[] {temp.toString()}
    						)
    					);
    				}
    			}
    		} finally {
    			if (in != null){
    				in.close();
    				in = null;
    			}
    			if (out != null){
    				out.flush();
    				out.close();
    				out = null;
    			}
    		}
    		return modified;
    	}
    }