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 37 kB

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