1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.api;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.Reader;
29 import java.io.StringReader;
30 import java.nio.charset.Charset;
31 import java.nio.charset.CharsetDecoder;
32 import java.nio.charset.CodingErrorAction;
33 import java.nio.charset.UnsupportedCharsetException;
34 import java.nio.file.Files;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40
41 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
42
43
44
45
46
47
48
49
50
51 public final class FileText {
52
53
54
55
56 private static final int READ_BUFFER_SIZE = 1024;
57
58
59
60
61 private static final Pattern LINE_TERMINATOR = Pattern.compile("\\n|\\r\\n?");
62
63
64
65
66
67
68
69
70
71
72 private final File file;
73
74
75
76
77
78 private final Charset charset;
79
80
81
82
83 private final String fullText;
84
85
86
87
88 private final String[] lines;
89
90
91
92
93 private int[] lineBreaks;
94
95
96
97
98
99
100
101
102
103
104
105
106
107 public FileText(File file, String charsetName) throws IOException {
108 this.file = file;
109
110
111
112 final CharsetDecoder decoder;
113 try {
114 charset = Charset.forName(charsetName);
115 decoder = charset.newDecoder();
116 decoder.onMalformedInput(CodingErrorAction.REPLACE);
117 decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
118 }
119 catch (final UnsupportedCharsetException ex) {
120 final String message = "Unsupported charset: " + charsetName;
121 throw new IllegalStateException(message, ex);
122 }
123
124 fullText = readFile(file, decoder);
125
126
127
128
129 try (BufferedReader reader = new BufferedReader(new StringReader(fullText))) {
130 final ArrayList<String> textLines = new ArrayList<>();
131 while (true) {
132 final String line = reader.readLine();
133 if (line == null) {
134 break;
135 }
136 textLines.add(line);
137 }
138 lines = textLines.toArray(CommonUtil.EMPTY_STRING_ARRAY);
139 }
140 }
141
142
143
144
145
146 public FileText(FileText fileText) {
147 file = fileText.file;
148 charset = fileText.charset;
149 fullText = fileText.fullText;
150 lines = fileText.lines.clone();
151 if (fileText.lineBreaks == null) {
152 lineBreaks = null;
153 }
154 else {
155 lineBreaks = fileText.lineBreaks.clone();
156 }
157 }
158
159
160
161
162
163
164
165
166
167
168
169
170 public FileText(File file, List<String> lines) {
171 final StringBuilder buf = new StringBuilder(1024);
172 for (final String line : lines) {
173 buf.append(line).append('\n');
174 }
175
176 this.file = file;
177 charset = null;
178 fullText = buf.toString();
179 this.lines = lines.toArray(CommonUtil.EMPTY_STRING_ARRAY);
180 }
181
182
183
184
185
186
187
188
189 private static String readFile(final File inputFile, final CharsetDecoder decoder)
190 throws IOException {
191 if (!inputFile.exists()) {
192 throw new FileNotFoundException(inputFile.getPath() + " (No such file or directory)");
193 }
194 final StringBuilder buf = new StringBuilder(1024);
195 final InputStream stream = Files.newInputStream(inputFile.toPath());
196 try (Reader reader = new InputStreamReader(stream, decoder)) {
197 final char[] chars = new char[READ_BUFFER_SIZE];
198 while (true) {
199 final int len = reader.read(chars);
200 if (len == -1) {
201 break;
202 }
203 buf.append(chars, 0, len);
204 }
205 }
206 return buf.toString();
207 }
208
209
210
211
212
213 public File getFile() {
214 return file;
215 }
216
217
218
219
220
221
222 public Charset getCharset() {
223 return charset;
224 }
225
226
227
228
229
230 public CharSequence getFullText() {
231 return fullText;
232 }
233
234
235
236
237
238
239
240 public String[] toLinesArray() {
241 return lines.clone();
242 }
243
244
245
246
247
248 private int[] findLineBreaks() {
249 if (lineBreaks == null) {
250 final int[] lineBreakPositions = new int[size() + 1];
251 lineBreakPositions[0] = 0;
252 int lineNo = 1;
253 final Matcher matcher = LINE_TERMINATOR.matcher(fullText);
254 while (matcher.find()) {
255 lineBreakPositions[lineNo] = matcher.end();
256 lineNo++;
257 }
258 if (lineNo < lineBreakPositions.length) {
259 lineBreakPositions[lineNo] = fullText.length();
260 }
261 lineBreaks = lineBreakPositions;
262 }
263 return lineBreaks;
264 }
265
266
267
268
269
270
271 public LineColumn lineColumn(int pos) {
272 final int[] lineBreakPositions = findLineBreaks();
273 int lineNo = Arrays.binarySearch(lineBreakPositions, pos);
274 if (lineNo < 0) {
275
276
277 lineNo = -lineNo - 2;
278 }
279 final int startOfLine = lineBreakPositions[lineNo];
280 final int columnNo = pos - startOfLine;
281
282 return new LineColumn(lineNo + 1, columnNo);
283 }
284
285
286
287
288
289
290
291 public String get(final int lineNo) {
292 return lines[lineNo];
293 }
294
295
296
297
298
299 public int size() {
300 return lines.length;
301 }
302
303 }