You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

FixCRLF.java 22 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.io.BufferedReader;
  20. import java.io.File;
  21. import java.io.FileInputStream;
  22. import java.io.FileReader;
  23. import java.io.IOException;
  24. import java.io.InputStreamReader;
  25. import java.io.Reader;
  26. import java.util.Enumeration;
  27. import java.util.NoSuchElementException;
  28. import java.util.Vector;
  29. import org.apache.tools.ant.BuildException;
  30. import org.apache.tools.ant.DirectoryScanner;
  31. import org.apache.tools.ant.Project;
  32. import org.apache.tools.ant.filters.ChainableReader;
  33. import org.apache.tools.ant.filters.FixCrLfFilter;
  34. import org.apache.tools.ant.types.EnumeratedAttribute;
  35. import org.apache.tools.ant.types.FilterChain;
  36. import org.apache.tools.ant.util.FileUtils;
  37. /**
  38. * Converts text source files to local OS formatting conventions, as
  39. * well as repair text files damaged by misconfigured or misguided editors or
  40. * file transfer programs.
  41. * <p>
  42. * This task can take the following arguments:
  43. * <ul>
  44. * <li>srcdir
  45. * <li>destdir
  46. * <li>include
  47. * <li>exclude
  48. * <li>cr
  49. * <li>eol
  50. * <li>tab
  51. * <li>eof
  52. * <li>encoding
  53. * <li>targetencoding
  54. * </ul>
  55. * Of these arguments, only <b>sourcedir</b> is required.
  56. * <p>
  57. * When this task executes, it will scan the srcdir based on the include
  58. * and exclude properties.
  59. * <p>
  60. * This version generalises the handling of EOL characters, and allows
  61. * for CR-only line endings (the standard on Mac systems prior to OS X).
  62. * Tab handling has also been generalised to accommodate any tabwidth
  63. * from 2 to 80, inclusive. Importantly, it will leave untouched any
  64. * literal TAB characters embedded within string or character constants.
  65. * <p>
  66. * <em>Warning:</em> do not run on binary files.
  67. * <em>Caution:</em> run with care on carefully formatted files.
  68. * This may sound obvious, but if you don't specify asis, presume that
  69. * your files are going to be modified. If "tabs" is "add" or "remove",
  70. * whitespace characters may be added or removed as necessary. Similarly,
  71. * for CR's - in fact "eol"="crlf" or cr="add" can result in cr
  72. * characters being removed in one special case accommodated, i.e.,
  73. * CRCRLF is regarded as a single EOL to handle cases where other
  74. * programs have converted CRLF into CRCRLF.
  75. *
  76. * @since Ant 1.1
  77. *
  78. * @ant.task category="filesystem"
  79. */
  80. public class FixCRLF extends MatchingTask implements ChainableReader {
  81. private static final String FIXCRLF_ERROR = "<fixcrlf> error: ";
  82. /** error string for using srcdir and file */
  83. public static final String ERROR_FILE_AND_SRCDIR
  84. = FIXCRLF_ERROR + "srcdir and file are mutually exclusive";
  85. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  86. private boolean preserveLastModified = false;
  87. private File srcDir;
  88. private File destDir = null;
  89. private File file;
  90. private FixCrLfFilter filter = new FixCrLfFilter();
  91. private Vector<FilterChain> fcv = null;
  92. /**
  93. * Encoding to assume for the files
  94. */
  95. private String encoding = null;
  96. /**
  97. * Encoding to use for output files
  98. */
  99. private String outputEncoding = null;
  100. /**
  101. * Chain this task as a reader.
  102. * @param rdr Reader to chain.
  103. * @return a Reader.
  104. * @since Ant 1.7?
  105. */
  106. public final Reader chain(final Reader rdr) {
  107. return filter.chain(rdr);
  108. }
  109. /**
  110. * Set the source dir to find the source text files.
  111. * @param srcDir the source directory.
  112. */
  113. public void setSrcdir(File srcDir) {
  114. this.srcDir = srcDir;
  115. }
  116. /**
  117. * Set the destination where the fixed files should be placed.
  118. * Default is to replace the original file.
  119. * @param destDir the destination directory.
  120. */
  121. public void setDestdir(File destDir) {
  122. this.destDir = destDir;
  123. }
  124. /**
  125. * Set to true if modifying Java source files.
  126. * @param javafiles whether modifying Java files.
  127. */
  128. public void setJavafiles(boolean javafiles) {
  129. filter.setJavafiles(javafiles);
  130. }
  131. /**
  132. * Set a single file to convert.
  133. * @since Ant 1.6.3
  134. * @param file the file to convert.
  135. */
  136. public void setFile(File file) {
  137. this.file = file;
  138. }
  139. /**
  140. * Specify how EndOfLine characters are to be handled.
  141. *
  142. * @param attr valid values:
  143. * <ul>
  144. * <li>asis: leave line endings alone
  145. * <li>cr: convert line endings to CR
  146. * <li>lf: convert line endings to LF
  147. * <li>crlf: convert line endings to CRLF
  148. * </ul>
  149. */
  150. public void setEol(CrLf attr) {
  151. filter.setEol(FixCrLfFilter.CrLf.newInstance(attr.getValue()));
  152. }
  153. /**
  154. * Specify how carriage return (CR) characters are to be handled.
  155. *
  156. * @param attr valid values:
  157. * <ul>
  158. * <li>add: ensure that there is a CR before every LF
  159. * <li>asis: leave CR characters alone
  160. * <li>remove: remove all CR characters
  161. * </ul>
  162. *
  163. * @deprecated since 1.4.x.
  164. * Use {@link #setEol setEol} instead.
  165. */
  166. public void setCr(AddAsisRemove attr) {
  167. log("DEPRECATED: The cr attribute has been deprecated,",
  168. Project.MSG_WARN);
  169. log("Please use the eol attribute instead", Project.MSG_WARN);
  170. String option = attr.getValue();
  171. CrLf c = new CrLf();
  172. if (option.equals("remove")) {
  173. c.setValue("lf");
  174. } else if (option.equals("asis")) {
  175. c.setValue("asis");
  176. } else {
  177. // must be "add"
  178. c.setValue("crlf");
  179. }
  180. setEol(c);
  181. }
  182. /**
  183. * Specify how tab characters are to be handled.
  184. *
  185. * @param attr valid values:
  186. * <ul>
  187. * <li>add: convert sequences of spaces which span a tab stop to tabs
  188. * <li>asis: leave tab and space characters alone
  189. * <li>remove: convert tabs to spaces
  190. * </ul>
  191. */
  192. public void setTab(AddAsisRemove attr) {
  193. filter.setTab(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue()));
  194. }
  195. /**
  196. * Specify tab length in characters.
  197. *
  198. * @param tlength specify the length of tab in spaces.
  199. * @throws BuildException on error.
  200. */
  201. public void setTablength(int tlength) throws BuildException {
  202. try {
  203. filter.setTablength(tlength);
  204. } catch (IOException e) {
  205. // filter.setTablength throws IOException that would better be
  206. // a BuildException
  207. throw new BuildException(e.getMessage(), e);
  208. }
  209. }
  210. /**
  211. * Specify how DOS EOF (control-z) characters are to be handled.
  212. *
  213. * @param attr valid values:
  214. * <ul>
  215. * <li>add: ensure that there is an eof at the end of the file
  216. * <li>asis: leave eof characters alone
  217. * <li>remove: remove any eof character found at the end
  218. * </ul>
  219. */
  220. public void setEof(AddAsisRemove attr) {
  221. filter.setEof(FixCrLfFilter.AddAsisRemove.newInstance(attr.getValue()));
  222. }
  223. /**
  224. * Specifies the encoding Ant expects the files to be
  225. * in--defaults to the platforms default encoding.
  226. * @param encoding String encoding name.
  227. */
  228. public void setEncoding(String encoding) {
  229. this.encoding = encoding;
  230. }
  231. /**
  232. * Specifies the encoding that the files are
  233. * to be written in--same as input encoding by default.
  234. * @param outputEncoding String outputEncoding name.
  235. */
  236. public void setOutputEncoding(String outputEncoding) {
  237. this.outputEncoding = outputEncoding;
  238. }
  239. /**
  240. * Specify whether a missing EOL will be added
  241. * to the final line of a file.
  242. * @param fixlast whether to fix the last line.
  243. */
  244. public void setFixlast(boolean fixlast) {
  245. filter.setFixlast(fixlast);
  246. }
  247. /**
  248. * Set whether to preserve the last modified time as the original files.
  249. * @param preserve true if timestamps should be preserved.
  250. * @since Ant 1.6.3
  251. */
  252. public void setPreserveLastModified(boolean preserve) {
  253. preserveLastModified = preserve;
  254. }
  255. /**
  256. * Executes the task.
  257. * @throws BuildException on error.
  258. */
  259. public void execute() throws BuildException {
  260. // first off, make sure that we've got a srcdir and destdir
  261. validate();
  262. // log options used
  263. String enc = encoding == null ? "default" : encoding;
  264. log("options:"
  265. + " eol=" + filter.getEol().getValue()
  266. + " tab=" + filter.getTab().getValue()
  267. + " eof=" + filter.getEof().getValue()
  268. + " tablength=" + filter.getTablength()
  269. + " encoding=" + enc
  270. + " outputencoding="
  271. + (outputEncoding == null ? enc : outputEncoding),
  272. Project.MSG_VERBOSE);
  273. DirectoryScanner ds = super.getDirectoryScanner(srcDir);
  274. String[] files = ds.getIncludedFiles();
  275. for (int i = 0; i < files.length; i++) {
  276. processFile(files[i]);
  277. }
  278. }
  279. private void validate() throws BuildException {
  280. if (file != null) {
  281. if (srcDir != null) {
  282. throw new BuildException(ERROR_FILE_AND_SRCDIR);
  283. }
  284. //patch file into the fileset
  285. fileset.setFile(file);
  286. //set our parent dir
  287. srcDir = file.getParentFile();
  288. }
  289. if (srcDir == null) {
  290. throw new BuildException(
  291. FIXCRLF_ERROR + "srcdir attribute must be set!");
  292. }
  293. if (!srcDir.exists()) {
  294. throw new BuildException(
  295. FIXCRLF_ERROR + "srcdir does not exist: '" + srcDir + "'");
  296. }
  297. if (!srcDir.isDirectory()) {
  298. throw new BuildException(
  299. FIXCRLF_ERROR + "srcdir is not a directory: '" + srcDir + "'");
  300. }
  301. if (destDir != null) {
  302. if (!destDir.exists()) {
  303. throw new BuildException(
  304. FIXCRLF_ERROR + "destdir does not exist: '"
  305. + destDir + "'");
  306. }
  307. if (!destDir.isDirectory()) {
  308. throw new BuildException(
  309. FIXCRLF_ERROR + "destdir is not a directory: '"
  310. + destDir + "'");
  311. }
  312. }
  313. }
  314. private void processFile(String file) throws BuildException {
  315. File srcFile = new File(srcDir, file);
  316. long lastModified = srcFile.lastModified();
  317. File destD = destDir == null ? srcDir : destDir;
  318. if (fcv == null) {
  319. FilterChain fc = new FilterChain();
  320. fc.add(filter);
  321. fcv = new Vector<FilterChain>(1);
  322. fcv.add(fc);
  323. }
  324. File tmpFile = FILE_UTILS.createTempFile("fixcrlf", "", null, true, true);
  325. try {
  326. FILE_UTILS.copyFile(srcFile, tmpFile, null, fcv, true, false,
  327. encoding, outputEncoding == null ? encoding : outputEncoding,
  328. getProject());
  329. File destFile = new File(destD, file);
  330. boolean destIsWrong = true;
  331. if (destFile.exists()) {
  332. // Compare the destination with the temp file
  333. log("destFile " + destFile + " exists", Project.MSG_DEBUG);
  334. destIsWrong = !FILE_UTILS.contentEquals(destFile, tmpFile);
  335. log(destFile + (destIsWrong ? " is being written"
  336. : " is not written, as the contents are identical"),
  337. Project.MSG_DEBUG);
  338. }
  339. if (destIsWrong) {
  340. FILE_UTILS.rename(tmpFile, destFile);
  341. if (preserveLastModified) {
  342. log("preserved lastModified for " + destFile,
  343. Project.MSG_DEBUG);
  344. FILE_UTILS.setFileLastModified(destFile, lastModified);
  345. }
  346. }
  347. } catch (IOException e) {
  348. throw new BuildException("error running fixcrlf on file " + srcFile, e);
  349. } finally {
  350. if (tmpFile != null && tmpFile.exists()) {
  351. FILE_UTILS.tryHardToDelete(tmpFile);
  352. }
  353. }
  354. }
  355. /**
  356. * Deprecated, the functionality has been moved to filters.FixCrLfFilter.
  357. * @deprecated since 1.7.0.
  358. */
  359. protected class OneLiner implements Enumeration<Object> {
  360. private static final int UNDEF = -1;
  361. private static final int NOTJAVA = 0;
  362. private static final int LOOKING = 1;
  363. private static final int INBUFLEN = 8192;
  364. private static final int LINEBUFLEN = 200;
  365. private static final char CTRLZ = '\u001A';
  366. private int state = filter.getJavafiles() ? LOOKING : NOTJAVA;
  367. private StringBuffer eolStr = new StringBuffer(LINEBUFLEN);
  368. private StringBuffer eofStr = new StringBuffer();
  369. private BufferedReader reader;
  370. private StringBuffer line = new StringBuffer();
  371. private boolean reachedEof = false;
  372. private File srcFile;
  373. /**
  374. * Constructor.
  375. * @param srcFile the file to read.
  376. * @throws BuildException if there is an error.
  377. */
  378. public OneLiner(File srcFile)
  379. throws BuildException {
  380. this.srcFile = srcFile;
  381. try {
  382. reader = new BufferedReader(
  383. ((encoding == null) ? new FileReader(srcFile)
  384. : new InputStreamReader(
  385. new FileInputStream(srcFile), encoding)), INBUFLEN);
  386. nextLine();
  387. } catch (IOException e) {
  388. throw new BuildException(srcFile + ": " + e.getMessage(),
  389. e, getLocation());
  390. }
  391. }
  392. /**
  393. * Move to the next line.
  394. * @throws BuildException if there is an error.
  395. */
  396. protected void nextLine()
  397. throws BuildException {
  398. int ch = -1;
  399. int eolcount = 0;
  400. eolStr = new StringBuffer();
  401. line = new StringBuffer();
  402. try {
  403. ch = reader.read();
  404. while (ch != -1 && ch != '\r' && ch != '\n') {
  405. line.append((char) ch);
  406. ch = reader.read();
  407. }
  408. if (ch == -1 && line.length() == 0) {
  409. // Eof has been reached
  410. reachedEof = true;
  411. return;
  412. }
  413. switch ((char) ch) {
  414. case '\r':
  415. // Check for \r, \r\n and \r\r\n
  416. // Regard \r\r not followed by \n as two lines
  417. ++eolcount;
  418. eolStr.append('\r');
  419. reader.mark(2);
  420. ch = reader.read();
  421. switch (ch) {
  422. case '\r':
  423. ch = reader.read();
  424. if ((char) (ch) == '\n') {
  425. eolcount += 2;
  426. eolStr.append("\r\n");
  427. } else {
  428. reader.reset();
  429. }
  430. break;
  431. case '\n':
  432. ++eolcount;
  433. eolStr.append('\n');
  434. break;
  435. case -1:
  436. // don't reposition when we've reached the end
  437. // of the stream
  438. break;
  439. default:
  440. reader.reset();
  441. break;
  442. } // end of switch ((char)(ch = reader.read()))
  443. break;
  444. case '\n':
  445. ++eolcount;
  446. eolStr.append('\n');
  447. break;
  448. default:
  449. // Fall tru
  450. } // end of switch ((char) ch)
  451. // if at eolcount == 0 and trailing characters of string
  452. // are CTRL-Zs, set eofStr
  453. if (eolcount == 0) {
  454. int i = line.length();
  455. while (--i >= 0 && line.charAt(i) == CTRLZ) {
  456. // keep searching for the first ^Z
  457. }
  458. if (i < line.length() - 1) {
  459. // Trailing characters are ^Zs
  460. // Construct new line and eofStr
  461. eofStr.append(line.toString().substring(i + 1));
  462. if (i < 0) {
  463. line.setLength(0);
  464. reachedEof = true;
  465. } else {
  466. line.setLength(i + 1);
  467. }
  468. }
  469. } // end of if (eolcount == 0)
  470. } catch (IOException e) {
  471. throw new BuildException(srcFile + ": " + e.getMessage(),
  472. e, getLocation());
  473. }
  474. }
  475. /**
  476. * get the eof string.
  477. * @return the eof string.
  478. */
  479. public String getEofStr() {
  480. return eofStr.substring(0);
  481. }
  482. /**
  483. * get the state.
  484. * @return the state.
  485. */
  486. public int getState() {
  487. return state;
  488. }
  489. /**
  490. * Set the state.
  491. * @param state the value to use.
  492. */
  493. public void setState(int state) {
  494. this.state = state;
  495. }
  496. /**
  497. * @return true if there is more elements.
  498. */
  499. public boolean hasMoreElements() {
  500. return !reachedEof;
  501. }
  502. /**
  503. * get the next element.
  504. * @return the next element.
  505. * @throws NoSuchElementException if there is no more.
  506. */
  507. public Object nextElement()
  508. throws NoSuchElementException {
  509. if (!hasMoreElements()) {
  510. throw new NoSuchElementException("OneLiner");
  511. }
  512. BufferLine tmpLine =
  513. new BufferLine(line.toString(), eolStr.substring(0));
  514. nextLine();
  515. return tmpLine;
  516. }
  517. /**
  518. * Close the reader.
  519. * @throws IOException if there is an error.
  520. */
  521. public void close() throws IOException {
  522. if (reader != null) {
  523. reader.close();
  524. }
  525. }
  526. class BufferLine {
  527. private int next = 0;
  528. private int column = 0;
  529. private int lookahead = UNDEF;
  530. private String line;
  531. private String eolStr;
  532. public BufferLine(String line, String eolStr)
  533. throws BuildException {
  534. next = 0;
  535. column = 0;
  536. this.line = line;
  537. this.eolStr = eolStr;
  538. }
  539. public int getNext() {
  540. return next;
  541. }
  542. public void setNext(int next) {
  543. this.next = next;
  544. }
  545. public int getLookahead() {
  546. return lookahead;
  547. }
  548. public void setLookahead(int lookahead) {
  549. this.lookahead = lookahead;
  550. }
  551. public char getChar(int i) {
  552. return line.charAt(i);
  553. }
  554. public char getNextChar() {
  555. return getChar(next);
  556. }
  557. public char getNextCharInc() {
  558. return getChar(next++);
  559. }
  560. public int getColumn() {
  561. return column;
  562. }
  563. public void setColumn(int col) {
  564. column = col;
  565. }
  566. public int incColumn() {
  567. return column++;
  568. }
  569. public int length() {
  570. return line.length();
  571. }
  572. public int getEolLength() {
  573. return eolStr.length();
  574. }
  575. public String getLineString() {
  576. return line;
  577. }
  578. public String getEol() {
  579. return eolStr;
  580. }
  581. public String substring(int begin) {
  582. return line.substring(begin);
  583. }
  584. public String substring(int begin, int end) {
  585. return line.substring(begin, end);
  586. }
  587. public void setState(int state) {
  588. OneLiner.this.setState(state);
  589. }
  590. public int getState() {
  591. return OneLiner.this.getState();
  592. }
  593. }
  594. }
  595. /**
  596. * Enumerated attribute with the values "asis", "add" and "remove".
  597. */
  598. public static class AddAsisRemove extends EnumeratedAttribute {
  599. /** {@inheritDoc}. */
  600. public String[] getValues() {
  601. return new String[] {"add", "asis", "remove"};
  602. }
  603. }
  604. /**
  605. * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
  606. */
  607. public static class CrLf extends EnumeratedAttribute {
  608. /**
  609. * @see EnumeratedAttribute#getValues
  610. * {@inheritDoc}.
  611. */
  612. public String[] getValues() {
  613. return new String[] {"asis", "cr", "lf", "crlf", "mac", "unix",
  614. "dos"};
  615. }
  616. }
  617. }