File Source: ReverseFileReader.java

         /* 
    P/P   *  Method: com.dmdirc.addons.logging.ReverseFileReader__static_init
          */
     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc.addons.logging;
    24  
    25  import java.io.File;
    26  import java.io.FileNotFoundException;
    27  import java.io.RandomAccessFile;
    28  import java.io.IOException;
    29  import java.io.EOFException;
    30  import java.nio.charset.Charset;
    31  import java.util.Stack;
    32  import java.util.ArrayList;
    33  
    34  /**
    35   * Reads a file in reverse.
    36   *
    37   * @author Shane 'Dataforce' McCormack
    38   */
    39  public class ReverseFileReader {
    40  	/** File to manipulate. */
    41  	private RandomAccessFile file;
    42  	/** Number of bytes to skip backwards at a time. */
    43  	private byte seekLength = 50;
    44  
    45  	/**
    46  	 * Create a new ReverseFileReader.
    47  	 *
    48  	 * @param filename File to open.
    49  	 * @throws FileNotFoundException If the file is not a regular file.
    50  	 * @throws SecurityException If a security manager exists and its checkRead method denies read access to the file.
    51  	 * @throws IOException If there is an error seeking to the end of the file.
    52  	 */
        	 /* 
    P/P 	  *  Method: void com.dmdirc.addons.logging.ReverseFileReader(String)
        	  * 
        	  *  Postconditions:
        	  *    this.file == &new RandomAccessFile(ReverseFileReader#1)
        	  *    this.seekLength == 50
        	  *    new RandomAccessFile(ReverseFileReader#1) num objects == 1
        	  */
    53  	public ReverseFileReader(String filename) throws FileNotFoundException, SecurityException, IOException {
    54  		file = new RandomAccessFile(filename, "r");
    55  		reset();
    56  	}
    57  	
    58  	/**
    59  	 * Create a new ReverseFileReader.
    60  	 *
    61  	 * @param myFile Existing file to use.
    62  	 * @throws FileNotFoundException If the file is not a regular file.
    63  	 * @throws SecurityException If a security manager exists and its checkRead method denies read access to the file.
    64  	 * @throws IOException If there is an error seeking to the end of the file.
    65  	 */
        	 /* 
    P/P 	  *  Method: void com.dmdirc.addons.logging.ReverseFileReader(File)
        	  * 
        	  *  Postconditions:
        	  *    this.file == &new RandomAccessFile(ReverseFileReader#1)
        	  *    this.seekLength == 50
        	  *    new RandomAccessFile(ReverseFileReader#1) num objects == 1
        	  */
    66  	public ReverseFileReader(File myFile) throws FileNotFoundException, SecurityException, IOException {
    67  		file = new RandomAccessFile(myFile, "r");
    68  		reset();
    69  	}
    70  	
    71  	/**
    72  	 * Reset the file pointer to the end of the file.
    73  	 *
    74  	 * @throws IOException If there is an error seeking, or the file is closed.
    75  	 */
    76  	public void reset() throws IOException {
        		 /* 
    P/P 		  *  Method: void reset()
        		  * 
        		  *  Preconditions:
        		  *    this.file != null
        		  */
    77  		if (file == null) { throw new IOException("File has been closed."); }
    78  		file.seek(file.length());
    79  	}
    80  	
    81  	/**
    82  	 * Get the current seekLength.
    83  	 *
    84  	 * @return current seekLength
    85  	 */
    86  	public byte getSeekLength() {
        		 /* 
    P/P 		  *  Method: byte getSeekLength()
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.seekLength)
        		  * 
        		  *  Postconditions:
        		  *    return_value == this.seekLength
        		  *    init'ed(return_value)
        		  */
    87  		return seekLength;
    88  	}
    89  	
    90  	/**
    91  	 * Set the seekLength.
    92  	 *
    93  	 * @param newValue New value for seekLength
    94  	 */
    95  	public void setSeekLength(final byte newValue) {
        		 /* 
    P/P 		  *  Method: void setSeekLength(byte)
        		  * 
        		  *  Postconditions:
        		  *    this.seekLength == newValue
        		  *    init'ed(this.seekLength)
        		  */
    96  		seekLength = newValue;
    97  	}
    98  	
    99  	/**
   100  	 * Close the file pointer.
   101  	 * This should be called before removing the reference to this object
   102  	 *
   103  	 * @throws IOException If there is an error closing the file, or if it has been closed already.
   104  	 */
   105  	public void close() throws IOException {
        		 /* 
    P/P 		  *  Method: void close()
        		  * 
        		  *  Preconditions:
        		  *    this.file != null
        		  * 
        		  *  Postconditions:
        		  *    this.file == null
        		  */
   106  		if (file == null) { throw new IOException("File has been closed."); }
   107  		file.close();
   108  		file = null;
   109  	}
   110  
   111  	/**
   112  	 * Get the next full line.
   113  	 *
   114  	 * @return The next full line.
   115  	 * @throws EOFException If there is no more lines.
   116  	 * @throws IOException If an error reading or seeking occured, or if the fiel is closed.
   117  	 */
   118  	public String getNextLine() throws EOFException, IOException {
        		 /* 
    P/P 		  *  Method: String getNextLine()
        		  * 
        		  *  Preconditions:
        		  *    this.file != null
        		  *    init'ed(this.seekLength)
        		  * 
        		  *  Presumptions:
        		  *    java.io.RandomAccessFile:getFilePointer(...)@134 != 0
        		  *    java.io.RandomAccessFile:getFilePointer(...)@145 >= -231
        		  *    java.io.RandomAccessFile:getFilePointer(...)@145 - this.seekLength in {-2_147_483_903..264-1}
        		  *    java.util.ArrayList:get(...)@205 != null
        		  *    java.util.ArrayList:size(...)@204 >= 1
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    return_value == &new String(getNextLine#4)
        		  *    new String(getNextLine#4) num objects == 1
        		  * 
        		  *  Test Vectors:
        		  *    bytes[i]@162: {13}, {10}, {-128..9, 11,12, 14..255}
        		  *    java.io.RandomAccessFile:getFilePointer(...)@145 - this.seekLength: {1..264-1}, {-2_147_483_903..-1}
        		  */
   119  		if (file == null) { throw new IOException("File has been closed."); }
   120  		// Used to store result to output.
   121  //		StringBuilder line = new StringBuilder();
   122  		final ArrayList<Byte> line = new ArrayList<Byte>(seekLength);
   123  		// Used to store position in file pre-read
   124  		long fp = 0;
   125  		// Used to store position in file when this is called
   126  		long startfp = 0;
   127  		// Used to store read bytes
   128  		byte[] bytes;
   129  		// Distance seeked
   130  		int seekDistance = 0;
   131  		
   132  		// Check current position, if 0 we are at the start of the file
   133  		// and should throw an exception.
   134  		startfp = file.getFilePointer();
   135  		if (startfp == 0) {
   136  			throw new EOFException("Reached Start of file");
   137  		}
   138  		
   139  		// Keep looping untill we get a full line, or the end of the file
   140  		boolean keepLooping = true;
   141  		boolean gotNewLine;
   142  		while (keepLooping) {
   143  			gotNewLine = false;
   144  			// Get Current Position
   145  			fp = file.getFilePointer();
   146  			
   147  			// Check how far to seek backwards (seekLength or to the start of the file)
   148  			if (fp < seekLength) {
   149  				// Seek to the start of the file;
   150  				seekDistance = (int)fp;
   151  				fp = 0;
   152  			} else {
   153  				// Seek to position current-seekLength
   154  				seekDistance = seekLength;
   155  				fp = fp - seekDistance;
   156  			}
   157  			// Seek!
   158  			file.seek(fp);
   159  			
   160  			bytes = new byte[seekDistance];
   161  			// Read into the bytes array
   162  			file.read(bytes);
   163  			
   164  			// And loop looking for data
   165  			// This uses seekDistance so that only wanted data is checked.
   166  			for (int i = seekDistance-1; i >= 0; --i) {
   167  				// Check for New line Character, or a non carraige-return char
   168  				if (bytes[i] == '\n') {
   169  					// Seek to the location of this character and exit this loop.
   170  					file.seek(fp+i);
   171  					gotNewLine = true;
   172  					break;
   173  				} else if (bytes[i] != '\r') {
   174  					// Add to the result, the loop will continue going.
   175  					line.add(0, bytes[i]);
   176  				}
   177  			}
   178  			
   179  			// We have now processed the data we read (Either added it all to the
   180  			// buffer, or found a newline character.)
   181  			
   182  			if (fp == 0 && !gotNewLine) {
   183  				// This part of the loop started from the start of the file, but didn't
   184  				// find a new line anywhere. no more loops are posssible, so Treat
   185  				// this as "got new line"
   186  				gotNewLine = true;
   187  				file.seek(0);
   188  			}
   189  			
   190  			// Do we need to continue?
   191  			if (gotNewLine) {
   192  				// We have found a new line somewhere, thus we don't need
   193  				// to read any more bytes, so exit the while loop!
   194  				keepLooping = false;
   195  			} else {
   196  				// We have not found a new line anywhere,
   197  				// Seek to the pre-read position, and repeat.
   198  				file.seek(fp);
   199  			}
   200  			
   201  		}
   202  		
   203  		// Return the data obtained.
   204  		byte[] result = new byte[line.size()];
   205  		for (int i = 0; i < line.size(); ++i) { result[i] = line.get(i); }
   206  		return new String(result, Charset.forName("UTF-8"));
   207  	}
   208  
   209  	/**
   210  	 * Try and get x number of lines.
   211  	 * If the file is closed, an empty stack will be returned.
   212  	 *
   213  	 * @param numLines Number of lines to try and get.
   214  	 * @return The requested lines
   215  	 */
   216  	public Stack<String> getLines(final int numLines) {
        		 /* 
    P/P 		  *  Method: Stack getLines(int)
        		  * 
        		  *  Preconditions:
        		  *    (soft) this.file != null
        		  *    (soft) init'ed(this.seekLength)
        		  * 
        		  *  Postconditions:
        		  *    return_value == &amp;new Stack(getLines#1)
        		  *    new Stack(getLines#1) num objects == 1
        		  */
   217  		final Stack<String> result = new Stack<String>();
   218  		for (int i = 0; i < numLines; ++i) {
   219  			try {
   220  				result.push(getNextLine());
   221  			} catch (IOException e) {
   222  				break;
   223  			}
   224  		}
   225  		return result;
   226  	}
   227  	
   228  	/**
   229  	 * Try and get x number of lines and return a \n delimited String.
   230  	 * If the file is closed, an empty string will be returned.
   231  	 *
   232  	 * @param numLines Number of lines to try and get.
   233  	 * @return The requested lines
   234  	 */
   235  	public String getLinesAsString(final int numLines) {
        		 /* 
    P/P 		  *  Method: String getLinesAsString(int)
        		  * 
        		  *  Preconditions:
        		  *    (soft) this.file != null
        		  *    (soft) init'ed(this.seekLength)
        		  * 
        		  *  Postconditions:
        		  *    java.lang.StringBuilder:toString(...)._tainted == 0
        		  *    return_value == &amp;java.lang.StringBuilder:toString(...)
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.StringBuilder:charAt(...)@245: {0..9, 11..216-1}, {10}
        		  */
   236  		final StringBuilder result = new StringBuilder();
   237  		for (int i = 0; i < numLines; ++i) {
   238  			try {
   239  				result.insert(0, "\n");
   240  				result.insert(0, getNextLine());
   241  			} catch (IOException e) {
   242  				break;
   243  			}
   244  		}
   245  		if (result.charAt(0) == '\n') {
   246  			result.deleteCharAt(0);
   247  		}
   248  		return result.toString();
   249  	}
   250  }








SofCheck Inspector Build Version : 2.17854
ReverseFileReader.java 2009-Jun-25 01:54:24
ReverseFileReader.class 2009-Sep-02 17:04:15