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 }