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