1 | /*
|
---|
2 | * Copyright 2000-2005 The Apache Software Foundation
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | */
|
---|
17 |
|
---|
18 | package org.apache.tools.ant.taskdefs;
|
---|
19 |
|
---|
20 | import java.io.BufferedReader;
|
---|
21 | import java.io.BufferedWriter;
|
---|
22 | import java.io.File;
|
---|
23 | import java.io.FileInputStream;
|
---|
24 | import java.io.FileOutputStream;
|
---|
25 | import java.io.FileReader;
|
---|
26 | import java.io.FileWriter;
|
---|
27 | import java.io.IOException;
|
---|
28 | import java.io.InputStreamReader;
|
---|
29 | import java.io.OutputStreamWriter;
|
---|
30 | import java.io.Reader;
|
---|
31 | import java.io.Writer;
|
---|
32 | import java.util.Enumeration;
|
---|
33 | import java.util.NoSuchElementException;
|
---|
34 | import org.apache.tools.ant.BuildException;
|
---|
35 | import org.apache.tools.ant.DirectoryScanner;
|
---|
36 | import org.apache.tools.ant.Project;
|
---|
37 | import org.apache.tools.ant.taskdefs.condition.Os;
|
---|
38 | import org.apache.tools.ant.types.EnumeratedAttribute;
|
---|
39 | import org.apache.tools.ant.util.FileUtils;
|
---|
40 |
|
---|
41 | /**
|
---|
42 | * Converts text source files to local OS formatting conventions, as
|
---|
43 | * well as repair text files damaged by misconfigured or misguided editors or
|
---|
44 | * file transfer programs.
|
---|
45 | * <p>
|
---|
46 | * This task can take the following arguments:
|
---|
47 | * <ul>
|
---|
48 | * <li>srcdir
|
---|
49 | * <li>destdir
|
---|
50 | * <li>include
|
---|
51 | * <li>exclude
|
---|
52 | * <li>cr
|
---|
53 | * <li>eol
|
---|
54 | * <li>tab
|
---|
55 | * <li>eof
|
---|
56 | * <li>encoding
|
---|
57 | * </ul>
|
---|
58 | * Of these arguments, only <b>sourcedir</b> is required.
|
---|
59 | * <p>
|
---|
60 | * When this task executes, it will scan the srcdir based on the include
|
---|
61 | * and exclude properties.
|
---|
62 | * <p>
|
---|
63 | * This version generalises the handling of EOL characters, and allows
|
---|
64 | * for CR-only line endings (which I suspect is the standard on Macs.)
|
---|
65 | * Tab handling has also been generalised to accommodate any tabwidth
|
---|
66 | * from 2 to 80, inclusive. Importantly, it will leave untouched any
|
---|
67 | * literal TAB characters embedded within string or character constants.
|
---|
68 | * <p>
|
---|
69 | * <em>Warning:</em> do not run on binary files.
|
---|
70 | * <em>Caution:</em> run with care on carefully formatted files.
|
---|
71 | * This may sound obvious, but if you don't specify asis, presume that
|
---|
72 | * your files are going to be modified. If "tabs" is "add" or "remove",
|
---|
73 | * whitespace characters may be added or removed as necessary. Similarly,
|
---|
74 | * for CR's - in fact "eol"="crlf" or cr="add" can result in cr
|
---|
75 | * characters being removed in one special case accommodated, i.e.,
|
---|
76 | * CRCRLF is regarded as a single EOL to handle cases where other
|
---|
77 | * programs have converted CRLF into CRCRLF.
|
---|
78 | *
|
---|
79 | * @since Ant 1.1
|
---|
80 | *
|
---|
81 | * @ant.task category="filesystem"
|
---|
82 | */
|
---|
83 |
|
---|
84 | public class FixCRLF extends MatchingTask {
|
---|
85 |
|
---|
86 | private static final int UNDEF = -1;
|
---|
87 | private static final int NOTJAVA = 0;
|
---|
88 | private static final int LOOKING = 1;
|
---|
89 | private static final int IN_CHAR_CONST = 2;
|
---|
90 | private static final int IN_STR_CONST = 3;
|
---|
91 | private static final int IN_SINGLE_COMMENT = 4;
|
---|
92 | private static final int IN_MULTI_COMMENT = 5;
|
---|
93 |
|
---|
94 | private static final int ASIS = 0;
|
---|
95 | private static final int CR = 1;
|
---|
96 | private static final int LF = 2;
|
---|
97 | private static final int CRLF = 3;
|
---|
98 | private static final int ADD = 1;
|
---|
99 | private static final int REMOVE = -1;
|
---|
100 | private static final int SPACES = -1;
|
---|
101 | private static final int TABS = 1;
|
---|
102 |
|
---|
103 | private static final int INBUFLEN = 8192;
|
---|
104 | private static final int LINEBUFLEN = 200;
|
---|
105 |
|
---|
106 | private static final char CTRLZ = '\u001A';
|
---|
107 |
|
---|
108 | private int tablength = 8;
|
---|
109 | private String spaces = " ";
|
---|
110 | private StringBuffer linebuf = new StringBuffer(1024);
|
---|
111 | private StringBuffer linebuf2 = new StringBuffer(1024);
|
---|
112 | private int eol;
|
---|
113 | private String eolstr;
|
---|
114 | private int ctrlz;
|
---|
115 | private int tabs;
|
---|
116 | private boolean javafiles = false;
|
---|
117 | private boolean fixlast = true;
|
---|
118 |
|
---|
119 | private File srcDir;
|
---|
120 | private File destDir = null;
|
---|
121 |
|
---|
122 | private FileUtils fileUtils = FileUtils.newFileUtils();
|
---|
123 |
|
---|
124 | /**
|
---|
125 | * Encoding to assume for the files
|
---|
126 | */
|
---|
127 | private String encoding = null;
|
---|
128 |
|
---|
129 | /**
|
---|
130 | * Defaults the properties based on the system type.
|
---|
131 | * <ul><li>Unix: eol="LF" tab="asis" eof="remove"
|
---|
132 | * <li>Mac: eol="CR" tab="asis" eof="remove"
|
---|
133 | * <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul>
|
---|
134 | */
|
---|
135 | public FixCRLF () {
|
---|
136 | tabs = ASIS;
|
---|
137 | if (Os.isFamily("mac")) {
|
---|
138 | ctrlz = REMOVE;
|
---|
139 | eol = CR;
|
---|
140 | eolstr = "\r";
|
---|
141 | } else if (Os.isFamily("dos")) {
|
---|
142 | ctrlz = ASIS;
|
---|
143 | eol = CRLF;
|
---|
144 | eolstr = "\r\n";
|
---|
145 | } else {
|
---|
146 | ctrlz = REMOVE;
|
---|
147 | eol = LF;
|
---|
148 | eolstr = "\n";
|
---|
149 | }
|
---|
150 | }
|
---|
151 |
|
---|
152 | /**
|
---|
153 | * Set the source dir to find the source text files.
|
---|
154 | */
|
---|
155 | public void setSrcdir(File srcDir) {
|
---|
156 | this.srcDir = srcDir;
|
---|
157 | }
|
---|
158 |
|
---|
159 | /**
|
---|
160 | * Set the destination where the fixed files should be placed.
|
---|
161 | * Default is to replace the original file.
|
---|
162 | */
|
---|
163 | public void setDestdir(File destDir) {
|
---|
164 | this.destDir = destDir;
|
---|
165 | }
|
---|
166 |
|
---|
167 | /**
|
---|
168 | * Set to true if modifying Java source files.
|
---|
169 | */
|
---|
170 | public void setJavafiles(boolean javafiles) {
|
---|
171 | this.javafiles = javafiles;
|
---|
172 | }
|
---|
173 |
|
---|
174 |
|
---|
175 | /**
|
---|
176 | * Specify how EndOfLine characters are to be handled.
|
---|
177 | *
|
---|
178 | * @param attr valid values:
|
---|
179 | * <ul>
|
---|
180 | * <li>asis: leave line endings alone
|
---|
181 | * <li>cr: convert line endings to CR
|
---|
182 | * <li>lf: convert line endings to LF
|
---|
183 | * <li>crlf: convert line endings to CRLF
|
---|
184 | * </ul>
|
---|
185 | */
|
---|
186 | public void setEol(CrLf attr) {
|
---|
187 | String option = attr.getValue();
|
---|
188 | if (option.equals("asis")) {
|
---|
189 | eol = ASIS;
|
---|
190 | } else if (option.equals("cr") || option.equals("mac")) {
|
---|
191 | eol = CR;
|
---|
192 | eolstr = "\r";
|
---|
193 | } else if (option.equals("lf") || option.equals("unix")) {
|
---|
194 | eol = LF;
|
---|
195 | eolstr = "\n";
|
---|
196 | } else {
|
---|
197 | // Must be "crlf"
|
---|
198 | eol = CRLF;
|
---|
199 | eolstr = "\r\n";
|
---|
200 | }
|
---|
201 | }
|
---|
202 |
|
---|
203 | /**
|
---|
204 | * Specify how carriage return (CR) characters are to be handled.
|
---|
205 | *
|
---|
206 | * @param attr valid values:
|
---|
207 | * <ul>
|
---|
208 | * <li>add: ensure that there is a CR before every LF
|
---|
209 | * <li>asis: leave CR characters alone
|
---|
210 | * <li>remove: remove all CR characters
|
---|
211 | * </ul>
|
---|
212 | *
|
---|
213 | * @deprecated use {@link #setEol setEol} instead.
|
---|
214 | */
|
---|
215 | public void setCr(AddAsisRemove attr) {
|
---|
216 | log("DEPRECATED: The cr attribute has been deprecated,",
|
---|
217 | Project.MSG_WARN);
|
---|
218 | log("Please use the eol attribute instead", Project.MSG_WARN);
|
---|
219 | String option = attr.getValue();
|
---|
220 | CrLf c = new CrLf();
|
---|
221 | if (option.equals("remove")) {
|
---|
222 | c.setValue("lf");
|
---|
223 | } else if (option.equals("asis")) {
|
---|
224 | c.setValue("asis");
|
---|
225 | } else {
|
---|
226 | // must be "add"
|
---|
227 | c.setValue("crlf");
|
---|
228 | }
|
---|
229 | setEol(c);
|
---|
230 | }
|
---|
231 |
|
---|
232 | /**
|
---|
233 | * Specify how tab characters are to be handled.
|
---|
234 | *
|
---|
235 | * @param attr valid values:
|
---|
236 | * <ul>
|
---|
237 | * <li>add: convert sequences of spaces which span a tab stop to tabs
|
---|
238 | * <li>asis: leave tab and space characters alone
|
---|
239 | * <li>remove: convert tabs to spaces
|
---|
240 | * </ul>
|
---|
241 | */
|
---|
242 | public void setTab(AddAsisRemove attr) {
|
---|
243 | String option = attr.getValue();
|
---|
244 | if (option.equals("remove")) {
|
---|
245 | tabs = SPACES;
|
---|
246 | } else if (option.equals("asis")) {
|
---|
247 | tabs = ASIS;
|
---|
248 | } else {
|
---|
249 | // must be "add"
|
---|
250 | tabs = TABS;
|
---|
251 | }
|
---|
252 | }
|
---|
253 |
|
---|
254 | /**
|
---|
255 | * Specify tab length in characters.
|
---|
256 | *
|
---|
257 | * @param tlength specify the length of tab in spaces,
|
---|
258 | */
|
---|
259 | public void setTablength(int tlength) throws BuildException {
|
---|
260 | if (tlength < 2 || tlength > 80) {
|
---|
261 | throw new BuildException("tablength must be between 2 and 80",
|
---|
262 | getLocation());
|
---|
263 | }
|
---|
264 | tablength = tlength;
|
---|
265 | StringBuffer sp = new StringBuffer();
|
---|
266 | for (int i = 0; i < tablength; i++) {
|
---|
267 | sp.append(' ');
|
---|
268 | }
|
---|
269 | spaces = sp.toString();
|
---|
270 | }
|
---|
271 |
|
---|
272 | /**
|
---|
273 | * Specify how DOS EOF (control-z) characters are to be handled.
|
---|
274 | *
|
---|
275 | * @param attr valid values:
|
---|
276 | * <ul>
|
---|
277 | * <li>add: ensure that there is an eof at the end of the file
|
---|
278 | * <li>asis: leave eof characters alone
|
---|
279 | * <li>remove: remove any eof character found at the end
|
---|
280 | * </ul>
|
---|
281 | */
|
---|
282 | public void setEof(AddAsisRemove attr) {
|
---|
283 | String option = attr.getValue();
|
---|
284 | if (option.equals("remove")) {
|
---|
285 | ctrlz = REMOVE;
|
---|
286 | } else if (option.equals("asis")) {
|
---|
287 | ctrlz = ASIS;
|
---|
288 | } else {
|
---|
289 | // must be "add"
|
---|
290 | ctrlz = ADD;
|
---|
291 | }
|
---|
292 | }
|
---|
293 |
|
---|
294 | /**
|
---|
295 | * Specifies the encoding Ant expects the files to be in -
|
---|
296 | * defaults to the platforms default encoding.
|
---|
297 | */
|
---|
298 | public void setEncoding(String encoding) {
|
---|
299 | this.encoding = encoding;
|
---|
300 | }
|
---|
301 |
|
---|
302 | /**
|
---|
303 | * Specify whether a missing EOL will be added
|
---|
304 | * to the final line of a file.
|
---|
305 | */
|
---|
306 | public void setFixlast(boolean fixlast) {
|
---|
307 | this.fixlast = fixlast;
|
---|
308 | }
|
---|
309 |
|
---|
310 | /**
|
---|
311 | * Executes the task.
|
---|
312 | */
|
---|
313 | public void execute() throws BuildException {
|
---|
314 | // first off, make sure that we've got a srcdir and destdir
|
---|
315 |
|
---|
316 | if (srcDir == null) {
|
---|
317 | throw new BuildException("srcdir attribute must be set!");
|
---|
318 | }
|
---|
319 | if (!srcDir.exists()) {
|
---|
320 | throw new BuildException("srcdir does not exist!");
|
---|
321 | }
|
---|
322 | if (!srcDir.isDirectory()) {
|
---|
323 | throw new BuildException("srcdir is not a directory!");
|
---|
324 | }
|
---|
325 | if (destDir != null) {
|
---|
326 | if (!destDir.exists()) {
|
---|
327 | throw new BuildException("destdir does not exist!");
|
---|
328 | }
|
---|
329 | if (!destDir.isDirectory()) {
|
---|
330 | throw new BuildException("destdir is not a directory!");
|
---|
331 | }
|
---|
332 | }
|
---|
333 |
|
---|
334 | // log options used
|
---|
335 | log("options:"
|
---|
336 | + " eol="
|
---|
337 | + (eol == ASIS ? "asis" : eol == CR ? "cr" : eol == LF ? "lf" : "crlf")
|
---|
338 | + " tab=" + (tabs == TABS ? "add" : tabs == ASIS ? "asis" : "remove")
|
---|
339 | + " eof=" + (ctrlz == ADD ? "add" : ctrlz == ASIS ? "asis" : "remove")
|
---|
340 | + " tablength=" + tablength
|
---|
341 | + " encoding=" + (encoding == null ? "default" : encoding),
|
---|
342 | Project.MSG_VERBOSE);
|
---|
343 |
|
---|
344 | DirectoryScanner ds = super.getDirectoryScanner(srcDir);
|
---|
345 | String[] files = ds.getIncludedFiles();
|
---|
346 |
|
---|
347 | for (int i = 0; i < files.length; i++) {
|
---|
348 | processFile(files[i]);
|
---|
349 | }
|
---|
350 | }
|
---|
351 |
|
---|
352 | /**
|
---|
353 | * Creates a Reader reading from a given file an taking the user
|
---|
354 | * defined encoding into account.
|
---|
355 | */
|
---|
356 | private Reader getReader(File f) throws IOException {
|
---|
357 | return (encoding == null) ? new FileReader(f)
|
---|
358 | : new InputStreamReader(new FileInputStream(f), encoding);
|
---|
359 | }
|
---|
360 |
|
---|
361 |
|
---|
362 | private void processFile(String file) throws BuildException {
|
---|
363 | File srcFile = new File(srcDir, file);
|
---|
364 | File destD = destDir == null ? srcDir : destDir;
|
---|
365 | File tmpFile = null;
|
---|
366 | BufferedWriter outWriter;
|
---|
367 | OneLiner.BufferLine line;
|
---|
368 |
|
---|
369 | // read the contents of the file
|
---|
370 | OneLiner lines = new OneLiner(srcFile);
|
---|
371 |
|
---|
372 | try {
|
---|
373 | // Set up the output Writer
|
---|
374 | try {
|
---|
375 | tmpFile = fileUtils.createTempFile("fixcrlf", "", null);
|
---|
376 | tmpFile.deleteOnExit();
|
---|
377 | Writer writer = (encoding == null) ? new FileWriter(tmpFile)
|
---|
378 | : new OutputStreamWriter(new FileOutputStream(tmpFile),
|
---|
379 | encoding);
|
---|
380 | outWriter = new BufferedWriter(writer);
|
---|
381 | } catch (IOException e) {
|
---|
382 | throw new BuildException(e);
|
---|
383 | }
|
---|
384 |
|
---|
385 | while (lines.hasMoreElements()) {
|
---|
386 | // In-line states
|
---|
387 | int endComment;
|
---|
388 |
|
---|
389 | try {
|
---|
390 | line = (OneLiner.BufferLine) lines.nextElement();
|
---|
391 | } catch (NoSuchElementException e) {
|
---|
392 | throw new BuildException(e);
|
---|
393 | }
|
---|
394 |
|
---|
395 | String lineString = line.getLineString();
|
---|
396 | int linelen = line.length();
|
---|
397 |
|
---|
398 | // Note - all of the following processing NOT done for
|
---|
399 | // tabs ASIS
|
---|
400 |
|
---|
401 | if (tabs == ASIS) {
|
---|
402 | // Just copy the body of the line across
|
---|
403 | try {
|
---|
404 | outWriter.write(lineString);
|
---|
405 | } catch (IOException e) {
|
---|
406 | throw new BuildException(e);
|
---|
407 | } // end of try-catch
|
---|
408 |
|
---|
409 | } else { // (tabs != ASIS)
|
---|
410 |
|
---|
411 | while (line.getNext() < linelen) {
|
---|
412 |
|
---|
413 | switch (lines.getState()) {
|
---|
414 |
|
---|
415 | case NOTJAVA:
|
---|
416 | notInConstant(line, line.length(), outWriter);
|
---|
417 | break;
|
---|
418 |
|
---|
419 | case IN_MULTI_COMMENT:
|
---|
420 | endComment
|
---|
421 | = lineString.indexOf("*/", line.getNext());
|
---|
422 | if (endComment >= 0) {
|
---|
423 | // End of multiLineComment on this line
|
---|
424 | endComment += 2; // Include the end token
|
---|
425 | lines.setState(LOOKING);
|
---|
426 | } else {
|
---|
427 | endComment = linelen;
|
---|
428 | }
|
---|
429 |
|
---|
430 | notInConstant(line, endComment, outWriter);
|
---|
431 | break;
|
---|
432 |
|
---|
433 | case IN_SINGLE_COMMENT:
|
---|
434 | notInConstant(line, line.length(), outWriter);
|
---|
435 | lines.setState(LOOKING);
|
---|
436 | break;
|
---|
437 |
|
---|
438 | case IN_CHAR_CONST:
|
---|
439 | case IN_STR_CONST:
|
---|
440 | // Got here from LOOKING by finding an
|
---|
441 | // opening "\'" next points to that quote
|
---|
442 | // character.
|
---|
443 | // Find the end of the constant. Watch
|
---|
444 | // out for backslashes. Literal tabs are
|
---|
445 | // left unchanged, and the column is
|
---|
446 | // adjusted accordingly.
|
---|
447 |
|
---|
448 | int begin = line.getNext();
|
---|
449 | char terminator = (lines.getState() == IN_STR_CONST
|
---|
450 | ? '\"'
|
---|
451 | : '\'');
|
---|
452 | endOfCharConst(line, terminator);
|
---|
453 | while (line.getNext() < line.getLookahead()) {
|
---|
454 | if (line.getNextCharInc() == '\t') {
|
---|
455 | line.setColumn(line.getColumn()
|
---|
456 | + tablength
|
---|
457 | - (line.getColumn() % tablength));
|
---|
458 | } else {
|
---|
459 | line.incColumn();
|
---|
460 | }
|
---|
461 | }
|
---|
462 |
|
---|
463 | // Now output the substring
|
---|
464 | try {
|
---|
465 | outWriter.write(line.substring(begin,
|
---|
466 | line.getNext()));
|
---|
467 | } catch (IOException e) {
|
---|
468 | throw new BuildException(e);
|
---|
469 | }
|
---|
470 |
|
---|
471 | lines.setState(LOOKING);
|
---|
472 |
|
---|
473 | break;
|
---|
474 |
|
---|
475 |
|
---|
476 | case LOOKING:
|
---|
477 | nextStateChange(line);
|
---|
478 | notInConstant(line, line.getLookahead(), outWriter);
|
---|
479 | break;
|
---|
480 |
|
---|
481 | } // end of switch (state)
|
---|
482 |
|
---|
483 | } // end of while (line.getNext() < linelen)
|
---|
484 |
|
---|
485 | } // end of else (tabs != ASIS)
|
---|
486 |
|
---|
487 | if (!("".equals(line.getEol())) || fixlast) {
|
---|
488 | try {
|
---|
489 | outWriter.write(eolstr);
|
---|
490 | } catch (IOException e) {
|
---|
491 | throw new BuildException(e);
|
---|
492 | } // end of try-catch
|
---|
493 | } //end if non-blank original eol or fixlast
|
---|
494 |
|
---|
495 | } // end of while (lines.hasNext())
|
---|
496 |
|
---|
497 | try {
|
---|
498 | // Handle CTRLZ
|
---|
499 | if (ctrlz == ASIS) {
|
---|
500 | outWriter.write(lines.getEofStr());
|
---|
501 | } else if (ctrlz == ADD) {
|
---|
502 | outWriter.write(CTRLZ);
|
---|
503 | }
|
---|
504 | } catch (IOException e) {
|
---|
505 | throw new BuildException(e);
|
---|
506 | } finally {
|
---|
507 | try {
|
---|
508 | outWriter.close();
|
---|
509 | } catch (IOException e) {
|
---|
510 | throw new BuildException(e);
|
---|
511 | }
|
---|
512 | }
|
---|
513 |
|
---|
514 |
|
---|
515 | try {
|
---|
516 | lines.close();
|
---|
517 | lines = null;
|
---|
518 | } catch (IOException e) {
|
---|
519 | throw new BuildException("Unable to close source file "
|
---|
520 | + srcFile);
|
---|
521 | }
|
---|
522 |
|
---|
523 | File destFile = new File(destD, file);
|
---|
524 |
|
---|
525 | boolean destIsWrong = true;
|
---|
526 | if (destFile.exists()) {
|
---|
527 | // Compare the destination with the temp file
|
---|
528 | log("destFile exists", Project.MSG_DEBUG);
|
---|
529 | if (!fileUtils.contentEquals(destFile, tmpFile)) {
|
---|
530 | log(destFile + " is being written", Project.MSG_DEBUG);
|
---|
531 | } else {
|
---|
532 | log(destFile + " is not written, as the contents "
|
---|
533 | + "are identical", Project.MSG_DEBUG);
|
---|
534 | destIsWrong = false;
|
---|
535 | }
|
---|
536 | }
|
---|
537 |
|
---|
538 | if (destIsWrong) {
|
---|
539 | fileUtils.rename(tmpFile, destFile);
|
---|
540 | tmpFile = null;
|
---|
541 | }
|
---|
542 |
|
---|
543 | } catch (IOException e) {
|
---|
544 | throw new BuildException(e);
|
---|
545 | } finally {
|
---|
546 | try {
|
---|
547 | if (lines != null) {
|
---|
548 | lines.close();
|
---|
549 | }
|
---|
550 | } catch (IOException io) {
|
---|
551 | log("Error closing " + srcFile, Project.MSG_ERR);
|
---|
552 | } // end of catch
|
---|
553 |
|
---|
554 | if (tmpFile != null) {
|
---|
555 | tmpFile.delete();
|
---|
556 | }
|
---|
557 | } // end of finally
|
---|
558 | }
|
---|
559 |
|
---|
560 | /**
|
---|
561 | * Scan a BufferLine for the next state changing token: the beginning
|
---|
562 | * of a single or multi-line comment, a character or a string constant.
|
---|
563 | *
|
---|
564 | * As a side-effect, sets the buffer state to the next state, and sets
|
---|
565 | * field lookahead to the first character of the state-changing token, or
|
---|
566 | * to the next eol character.
|
---|
567 | *
|
---|
568 | * @param bufline BufferLine containing the string
|
---|
569 | * to be processed
|
---|
570 | * @exception org.apache.tools.ant.BuildException
|
---|
571 | * Thrown when end of line is reached
|
---|
572 | * before the terminator is found.
|
---|
573 | */
|
---|
574 | private void nextStateChange(OneLiner.BufferLine bufline)
|
---|
575 | throws BuildException {
|
---|
576 | int eol = bufline.length();
|
---|
577 | int ptr = bufline.getNext();
|
---|
578 |
|
---|
579 |
|
---|
580 | // Look for next single or double quote, double slash or slash star
|
---|
581 | while (ptr < eol) {
|
---|
582 | switch (bufline.getChar(ptr++)) {
|
---|
583 | case '\'':
|
---|
584 | bufline.setState(IN_CHAR_CONST);
|
---|
585 | bufline.setLookahead(--ptr);
|
---|
586 | return;
|
---|
587 | case '\"':
|
---|
588 | bufline.setState(IN_STR_CONST);
|
---|
589 | bufline.setLookahead(--ptr);
|
---|
590 | return;
|
---|
591 | case '/':
|
---|
592 | if (ptr < eol) {
|
---|
593 | if (bufline.getChar(ptr) == '*') {
|
---|
594 | bufline.setState(IN_MULTI_COMMENT);
|
---|
595 | bufline.setLookahead(--ptr);
|
---|
596 | return;
|
---|
597 | } else if (bufline.getChar(ptr) == '/') {
|
---|
598 | bufline.setState(IN_SINGLE_COMMENT);
|
---|
599 | bufline.setLookahead(--ptr);
|
---|
600 | return;
|
---|
601 | }
|
---|
602 | }
|
---|
603 | break;
|
---|
604 | } // end of switch (bufline.getChar(ptr++))
|
---|
605 |
|
---|
606 | } // end of while (ptr < eol)
|
---|
607 | // Eol is the next token
|
---|
608 | bufline.setLookahead(ptr);
|
---|
609 | }
|
---|
610 |
|
---|
611 |
|
---|
612 | /**
|
---|
613 | * Scan a BufferLine forward from the 'next' pointer
|
---|
614 | * for the end of a character constant. Set 'lookahead' pointer to the
|
---|
615 | * character following the terminating quote.
|
---|
616 | *
|
---|
617 | * @param bufline BufferLine containing the string
|
---|
618 | * to be processed
|
---|
619 | * @param terminator The constant terminator
|
---|
620 | *
|
---|
621 | * @exception org.apache.tools.ant.BuildException
|
---|
622 | * Thrown when end of line is reached
|
---|
623 | * before the terminator is found.
|
---|
624 | */
|
---|
625 | private void endOfCharConst(OneLiner.BufferLine bufline, char terminator)
|
---|
626 | throws BuildException {
|
---|
627 | int ptr = bufline.getNext();
|
---|
628 | int eol = bufline.length();
|
---|
629 | char c;
|
---|
630 | ptr++; // skip past initial quote
|
---|
631 | while (ptr < eol) {
|
---|
632 | if ((c = bufline.getChar(ptr++)) == '\\') {
|
---|
633 | ptr++;
|
---|
634 | } else {
|
---|
635 | if (c == terminator) {
|
---|
636 | bufline.setLookahead(ptr);
|
---|
637 | return;
|
---|
638 | }
|
---|
639 | }
|
---|
640 | } // end of while (ptr < eol)
|
---|
641 | // Must have fallen through to the end of the line
|
---|
642 | throw new BuildException("endOfCharConst: unterminated char constant");
|
---|
643 | }
|
---|
644 |
|
---|
645 |
|
---|
646 | /**
|
---|
647 | * Process a BufferLine string which is not part of a string constant.
|
---|
648 | * The start position of the string is given by the 'next' field.
|
---|
649 | * Sets the 'next' and 'column' fields in the BufferLine.
|
---|
650 | *
|
---|
651 | * @param bufline BufferLine containing the string
|
---|
652 | * to be processed
|
---|
653 | * @param end Index just past the end of the
|
---|
654 | * string
|
---|
655 | * @param outWriter Sink for the processed string
|
---|
656 | */
|
---|
657 | private void notInConstant(OneLiner.BufferLine bufline, int end,
|
---|
658 | BufferedWriter outWriter) {
|
---|
659 | // N.B. both column and string index are zero-based
|
---|
660 | // Process a string not part of a constant;
|
---|
661 | // i.e. convert tabs<->spaces as required
|
---|
662 | // This is NOT called for ASIS tab handling
|
---|
663 | int nextTab;
|
---|
664 | int nextStop;
|
---|
665 | int tabspaces;
|
---|
666 | String line = bufline.substring(bufline.getNext(), end);
|
---|
667 | int place = 0; // Zero-based
|
---|
668 | int col = bufline.getColumn(); // Zero-based
|
---|
669 |
|
---|
670 | // process sequences of white space
|
---|
671 | // first convert all tabs to spaces
|
---|
672 | linebuf = new StringBuffer();
|
---|
673 | while ((nextTab = line.indexOf((int) '\t', place)) >= 0) {
|
---|
674 | linebuf.append(line.substring(place, nextTab)); // copy to the TAB
|
---|
675 | col += nextTab - place;
|
---|
676 | tabspaces = tablength - (col % tablength);
|
---|
677 | linebuf.append(spaces.substring(0, tabspaces));
|
---|
678 | col += tabspaces;
|
---|
679 | place = nextTab + 1;
|
---|
680 | } // end of while
|
---|
681 | linebuf.append(line.substring(place, line.length()));
|
---|
682 | // if converting to spaces, all finished
|
---|
683 | String linestring = new String(linebuf.substring(0));
|
---|
684 | if (tabs == REMOVE) {
|
---|
685 | try {
|
---|
686 | outWriter.write(linestring);
|
---|
687 | } catch (IOException e) {
|
---|
688 | throw new BuildException(e);
|
---|
689 | } // end of try-catch
|
---|
690 | } else { // tabs == ADD
|
---|
691 | int tabCol;
|
---|
692 | linebuf2 = new StringBuffer();
|
---|
693 | place = 0;
|
---|
694 | col = bufline.getColumn();
|
---|
695 | int placediff = col - 0;
|
---|
696 | // for the length of the string, cycle through the tab stop
|
---|
697 | // positions, checking for a space preceded by at least one
|
---|
698 | // other space at the tab stop. if so replace the longest possible
|
---|
699 | // preceding sequence of spaces with a tab.
|
---|
700 | nextStop = col + (tablength - col % tablength);
|
---|
701 | if (nextStop - col < 2) {
|
---|
702 | linebuf2.append(linestring.substring(
|
---|
703 | place, nextStop - placediff));
|
---|
704 | place = nextStop - placediff;
|
---|
705 | nextStop += tablength;
|
---|
706 | }
|
---|
707 |
|
---|
708 | for (; nextStop - placediff <= linestring.length();
|
---|
709 | nextStop += tablength) {
|
---|
710 | for (tabCol = nextStop;
|
---|
711 | --tabCol - placediff >= place
|
---|
712 | && linestring.charAt(tabCol - placediff) == ' ';) {
|
---|
713 | ; // Loop for the side-effects
|
---|
714 | }
|
---|
715 | // tabCol is column index of the last non-space character
|
---|
716 | // before the next tab stop
|
---|
717 | if (nextStop - tabCol > 2) {
|
---|
718 | linebuf2.append(linestring.substring(
|
---|
719 | place, ++tabCol - placediff));
|
---|
720 | linebuf2.append('\t');
|
---|
721 | } else {
|
---|
722 | linebuf2.append(linestring.substring(
|
---|
723 | place, nextStop - placediff));
|
---|
724 | } // end of else
|
---|
725 |
|
---|
726 | place = nextStop - placediff;
|
---|
727 | } // end of for (nextStop ... )
|
---|
728 |
|
---|
729 | // pick up that last bit, if any
|
---|
730 | linebuf2.append(linestring.substring(place, linestring.length()));
|
---|
731 |
|
---|
732 | try {
|
---|
733 | outWriter.write(linebuf2.substring(0));
|
---|
734 | } catch (IOException e) {
|
---|
735 | throw new BuildException(e);
|
---|
736 | } // end of try-catch
|
---|
737 |
|
---|
738 | } // end of else tabs == ADD
|
---|
739 |
|
---|
740 | // Set column position as modified by this method
|
---|
741 | bufline.setColumn(bufline.getColumn() + linestring.length());
|
---|
742 | bufline.setNext(end);
|
---|
743 |
|
---|
744 | }
|
---|
745 |
|
---|
746 |
|
---|
747 | class OneLiner implements Enumeration {
|
---|
748 |
|
---|
749 | private int state = javafiles ? LOOKING : NOTJAVA;
|
---|
750 |
|
---|
751 | private StringBuffer eolStr = new StringBuffer(LINEBUFLEN);
|
---|
752 | private StringBuffer eofStr = new StringBuffer();
|
---|
753 |
|
---|
754 | private BufferedReader reader;
|
---|
755 | private StringBuffer line = new StringBuffer();
|
---|
756 | private boolean reachedEof = false;
|
---|
757 | private File srcFile;
|
---|
758 |
|
---|
759 | public OneLiner(File srcFile)
|
---|
760 | throws BuildException {
|
---|
761 | this.srcFile = srcFile;
|
---|
762 | try {
|
---|
763 | reader = new BufferedReader
|
---|
764 | (getReader(srcFile), INBUFLEN);
|
---|
765 | nextLine();
|
---|
766 | } catch (IOException e) {
|
---|
767 | throw new BuildException(srcFile + ": " + e.getMessage(),
|
---|
768 | e, getLocation());
|
---|
769 | }
|
---|
770 | }
|
---|
771 |
|
---|
772 | protected void nextLine()
|
---|
773 | throws BuildException {
|
---|
774 | int ch = -1;
|
---|
775 | int eolcount = 0;
|
---|
776 |
|
---|
777 | eolStr = new StringBuffer();
|
---|
778 | line = new StringBuffer();
|
---|
779 |
|
---|
780 | try {
|
---|
781 | ch = reader.read();
|
---|
782 | while (ch != -1 && ch != '\r' && ch != '\n') {
|
---|
783 | line.append((char) ch);
|
---|
784 | ch = reader.read();
|
---|
785 | }
|
---|
786 |
|
---|
787 | if (ch == -1 && line.length() == 0) {
|
---|
788 | // Eof has been reached
|
---|
789 | reachedEof = true;
|
---|
790 | return;
|
---|
791 | }
|
---|
792 |
|
---|
793 | switch ((char) ch) {
|
---|
794 | case '\r':
|
---|
795 | // Check for \r, \r\n and \r\r\n
|
---|
796 | // Regard \r\r not followed by \n as two lines
|
---|
797 | ++eolcount;
|
---|
798 | eolStr.append('\r');
|
---|
799 | reader.mark(2);
|
---|
800 | switch ((ch = reader.read())) {
|
---|
801 | case '\r':
|
---|
802 | if ((char) (ch = reader.read()) == '\n') {
|
---|
803 | eolcount += 2;
|
---|
804 | eolStr.append("\r\n");
|
---|
805 | } else {
|
---|
806 | reader.reset();
|
---|
807 | }
|
---|
808 | break;
|
---|
809 | case '\n':
|
---|
810 | ++eolcount;
|
---|
811 | eolStr.append('\n');
|
---|
812 | break;
|
---|
813 | case -1:
|
---|
814 | // don't reposition when we've reached the end
|
---|
815 | // of the stream
|
---|
816 | break;
|
---|
817 | default:
|
---|
818 | reader.reset();
|
---|
819 | break;
|
---|
820 | } // end of switch ((char)(ch = reader.read()))
|
---|
821 | break;
|
---|
822 |
|
---|
823 | case '\n':
|
---|
824 | ++eolcount;
|
---|
825 | eolStr.append('\n');
|
---|
826 | break;
|
---|
827 |
|
---|
828 | } // end of switch ((char) ch)
|
---|
829 |
|
---|
830 | // if at eolcount == 0 and trailing characters of string
|
---|
831 | // are CTRL-Zs, set eofStr
|
---|
832 | if (eolcount == 0) {
|
---|
833 | int i = line.length();
|
---|
834 | while (--i >= 0 && line.charAt(i) == CTRLZ) {
|
---|
835 | // keep searching for the first ^Z
|
---|
836 | }
|
---|
837 | if (i < line.length() - 1) {
|
---|
838 | // Trailing characters are ^Zs
|
---|
839 | // Construct new line and eofStr
|
---|
840 | eofStr.append(line.toString().substring(i + 1));
|
---|
841 | if (i < 0) {
|
---|
842 | line.setLength(0);
|
---|
843 | reachedEof = true;
|
---|
844 | } else {
|
---|
845 | line.setLength(i + 1);
|
---|
846 | }
|
---|
847 | }
|
---|
848 |
|
---|
849 | } // end of if (eolcount == 0)
|
---|
850 |
|
---|
851 | } catch (IOException e) {
|
---|
852 | throw new BuildException(srcFile + ": " + e.getMessage(),
|
---|
853 | e, getLocation());
|
---|
854 | }
|
---|
855 | }
|
---|
856 |
|
---|
857 | public String getEofStr() {
|
---|
858 | return eofStr.substring(0);
|
---|
859 | }
|
---|
860 |
|
---|
861 | public int getState() {
|
---|
862 | return state;
|
---|
863 | }
|
---|
864 |
|
---|
865 | public void setState(int state) {
|
---|
866 | this.state = state;
|
---|
867 | }
|
---|
868 |
|
---|
869 | public boolean hasMoreElements() {
|
---|
870 | return !reachedEof;
|
---|
871 | }
|
---|
872 |
|
---|
873 | public Object nextElement()
|
---|
874 | throws NoSuchElementException {
|
---|
875 | if (!hasMoreElements()) {
|
---|
876 | throw new NoSuchElementException("OneLiner");
|
---|
877 | }
|
---|
878 | BufferLine tmpLine =
|
---|
879 | new BufferLine(line.toString(), eolStr.substring(0));
|
---|
880 | nextLine();
|
---|
881 | return tmpLine;
|
---|
882 | }
|
---|
883 |
|
---|
884 | public void close() throws IOException {
|
---|
885 | if (reader != null) {
|
---|
886 | reader.close();
|
---|
887 | }
|
---|
888 | }
|
---|
889 |
|
---|
890 | class BufferLine {
|
---|
891 | private int next = 0;
|
---|
892 | private int column = 0;
|
---|
893 | private int lookahead = UNDEF;
|
---|
894 | private String line;
|
---|
895 | private String eolStr;
|
---|
896 |
|
---|
897 | public BufferLine(String line, String eolStr)
|
---|
898 | throws BuildException {
|
---|
899 | next = 0;
|
---|
900 | column = 0;
|
---|
901 | this.line = line;
|
---|
902 | this.eolStr = eolStr;
|
---|
903 | }
|
---|
904 |
|
---|
905 | public int getNext() {
|
---|
906 | return next;
|
---|
907 | }
|
---|
908 |
|
---|
909 | public void setNext(int next) {
|
---|
910 | this.next = next;
|
---|
911 | }
|
---|
912 |
|
---|
913 | public int getLookahead() {
|
---|
914 | return lookahead;
|
---|
915 | }
|
---|
916 |
|
---|
917 | public void setLookahead(int lookahead) {
|
---|
918 | this.lookahead = lookahead;
|
---|
919 | }
|
---|
920 |
|
---|
921 | public char getChar(int i) {
|
---|
922 | return line.charAt(i);
|
---|
923 | }
|
---|
924 |
|
---|
925 | public char getNextChar() {
|
---|
926 | return getChar(next);
|
---|
927 | }
|
---|
928 |
|
---|
929 | public char getNextCharInc() {
|
---|
930 | return getChar(next++);
|
---|
931 | }
|
---|
932 |
|
---|
933 | public int getColumn() {
|
---|
934 | return column;
|
---|
935 | }
|
---|
936 |
|
---|
937 | public void setColumn(int col) {
|
---|
938 | column = col;
|
---|
939 | }
|
---|
940 |
|
---|
941 | public int incColumn() {
|
---|
942 | return column++;
|
---|
943 | }
|
---|
944 |
|
---|
945 | public int length() {
|
---|
946 | return line.length();
|
---|
947 | }
|
---|
948 |
|
---|
949 | public int getEolLength() {
|
---|
950 | return eolStr.length();
|
---|
951 | }
|
---|
952 |
|
---|
953 | public String getLineString() {
|
---|
954 | return line;
|
---|
955 | }
|
---|
956 |
|
---|
957 | public String getEol() {
|
---|
958 | return eolStr;
|
---|
959 | }
|
---|
960 |
|
---|
961 | public String substring(int begin) {
|
---|
962 | return line.substring(begin);
|
---|
963 | }
|
---|
964 |
|
---|
965 | public String substring(int begin, int end) {
|
---|
966 | return line.substring(begin, end);
|
---|
967 | }
|
---|
968 |
|
---|
969 | public void setState(int state) {
|
---|
970 | OneLiner.this.setState(state);
|
---|
971 | }
|
---|
972 |
|
---|
973 | public int getState() {
|
---|
974 | return OneLiner.this.getState();
|
---|
975 | }
|
---|
976 | }
|
---|
977 | }
|
---|
978 |
|
---|
979 | /**
|
---|
980 | * Enumerated attribute with the values "asis", "add" and "remove".
|
---|
981 | */
|
---|
982 | public static class AddAsisRemove extends EnumeratedAttribute {
|
---|
983 | public String[] getValues() {
|
---|
984 | return new String[] {"add", "asis", "remove"};
|
---|
985 | }
|
---|
986 | }
|
---|
987 |
|
---|
988 | /**
|
---|
989 | * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
|
---|
990 | */
|
---|
991 | public static class CrLf extends EnumeratedAttribute {
|
---|
992 | /**
|
---|
993 | * @see EnumeratedAttribute#getValues
|
---|
994 | */
|
---|
995 | public String[] getValues() {
|
---|
996 | return new String[] {"asis", "cr", "lf", "crlf",
|
---|
997 | "mac", "unix", "dos"};
|
---|
998 | }
|
---|
999 | }
|
---|
1000 |
|
---|
1001 | }
|
---|