View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.tools;
12  
13  import java.io.BufferedInputStream;
14  import java.io.ByteArrayOutputStream;
15  import java.io.IOException;
16  import java.io.InputStream;
17  import java.nio.charset.Charset;
18  import java.nio.charset.StandardCharsets;
19  
20  /**
21   * Reads lines from "backwards" InputStream. This class facilitates reading files from bottom up.
22   *
23   * <p>
24   * This source code was kindly contributed by Kan Ogawa.
25   * </p>
26   */
27  public class BackwardsLineReader {
28  
29    /** The bis. */
30    private BufferedInputStream bis;
31  
32    /** The skip line feed. */
33    private boolean skipLineFeed = true;
34  
35    /** The encoding. */
36    private String encoding;
37  
38    /**
39     * Instantiates a new backwards line reader.
40     *
41     * @param is the is
42     */
43    public BackwardsLineReader(InputStream is) {
44      this(is, null);
45    }
46  
47    /**
48     * Instantiates a new backwards line reader.
49     *
50     * @param is the is
51     * @param encoding the encoding
52     */
53    public BackwardsLineReader(InputStream is, String encoding) {
54      this.bis = new BufferedInputStream(is, 8192);
55      this.encoding = encoding;
56    }
57  
58    /**
59     * Read line.
60     *
61     * @return the string
62     *
63     * @throws IOException Signals that an I/O exception has occurred.
64     */
65    public String readLine() throws IOException {
66      ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
67      boolean empty = false;
68      while (true) {
69        byte chr = (byte) bis.read();
70        if (chr == -1) {
71          // quit this loop, if the first of the backwards stream is
72          // reached
73          if (baos.toByteArray().length == 0) {
74            empty = true;
75          }
76          break;
77        }
78        if (chr == '\n') {
79          skipLineFeed = false;
80          // quit this loop
81          break;
82        }
83        if (chr == '\r') {
84          if (skipLineFeed) {
85            // quit this loop. if the carriage return only was read
86            break;
87          }
88          // go to next loop, if both the carriage return and
89          // the line feed were read
90          continue;
91        }
92        baos.write(chr);
93      }
94      if (!empty) {
95        byte[] byteArray = baos.toByteArray();
96        reverse(byteArray);
97        return encoding == null ? new String(byteArray, StandardCharsets.UTF_8)
98            : new String(byteArray, Charset.forName(encoding));
99      }
100     // return null if the end of the stream has been reached
101     return null;
102   }
103 
104   /**
105    * Close.
106    *
107    * @throws IOException Signals that an I/O exception has occurred.
108    */
109   public void close() throws IOException {
110     if (bis != null) {
111       bis.close();
112     }
113   }
114 
115   /**
116    * Reverse.
117    *
118    * @param byteArray the byte array
119    */
120   private void reverse(byte[] byteArray) {
121     for (int i = 0; i < byteArray.length / 2; i++) {
122       byte temp = byteArray[i];
123       byteArray[i] = byteArray[byteArray.length - i - 1];
124       byteArray[byteArray.length - i - 1] = temp;
125     }
126   }
127 
128 }