1 18 19 package org.apache.tools.ant.taskdefs; 20 21 import java.io.File ; 22 import java.io.Reader ; 23 import java.io.Writer ; 24 import java.io.FileReader ; 25 import java.io.InputStream ; 26 import java.io.IOException ; 27 import java.io.PrintWriter ; 28 import java.io.OutputStream ; 29 import java.io.StringReader ; 30 import java.io.BufferedReader ; 31 import java.io.BufferedWriter ; 32 import java.io.FileInputStream ; 33 import java.io.FileOutputStream ; 34 import java.io.InputStreamReader ; 35 import java.io.OutputStreamWriter ; 36 import java.util.Arrays ; 37 import java.util.Vector ; 38 import java.util.Iterator ; 39 import org.apache.tools.ant.Task; 40 import org.apache.tools.ant.Project; 41 import org.apache.tools.ant.BuildException; 42 import org.apache.tools.ant.ProjectComponent; 43 import org.apache.tools.ant.filters.util.ChainReaderHelper; 44 import org.apache.tools.ant.types.Path; 45 import org.apache.tools.ant.types.FileSet; 46 import org.apache.tools.ant.types.FileList; 47 import org.apache.tools.ant.types.FilterChain; 48 import org.apache.tools.ant.types.Resource; 49 import org.apache.tools.ant.types.ResourceCollection; 50 import org.apache.tools.ant.types.resources.Restrict; 51 import org.apache.tools.ant.types.resources.Resources; 52 import org.apache.tools.ant.types.resources.FileResource; 53 import org.apache.tools.ant.types.resources.StringResource; 54 import org.apache.tools.ant.types.resources.selectors.Not; 55 import org.apache.tools.ant.types.resources.selectors.Exists; 56 import org.apache.tools.ant.types.resources.selectors.ResourceSelector; 57 import org.apache.tools.ant.util.FileUtils; 58 import org.apache.tools.ant.util.ConcatResourceInputStream; 59 60 77 public class Concat extends Task { 78 79 private static final int BUFFER_SIZE = 8192; 81 82 private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); 83 84 private static final ResourceSelector EXISTS = new Exists(); 85 private static final ResourceSelector NOT_EXISTS = new Not(EXISTS); 86 87 89 93 private File destinationFile; 94 95 100 private boolean append; 101 102 105 private String encoding; 106 107 108 private String outputEncoding; 109 110 111 private boolean binary; 112 113 115 118 private StringBuffer textBuffer; 119 120 124 private Resources rc; 125 126 127 private Vector filterChains; 128 129 private boolean forceOverwrite = true; 130 131 private TextElement footer; 132 133 private TextElement header; 134 135 private boolean fixLastLine = false; 136 137 private String eolString; 138 139 private Writer outputWriter = null; 140 141 144 public Concat() { 145 reset(); 146 } 147 148 151 public void reset() { 152 append = false; 153 forceOverwrite = true; 154 destinationFile = null; 155 encoding = null; 156 outputEncoding = null; 157 fixLastLine = false; 158 filterChains = null; 159 footer = null; 160 header = null; 161 binary = false; 162 outputWriter = null; 163 textBuffer = null; 164 eolString = System.getProperty("line.separator"); 165 rc = null; 166 } 167 168 170 174 public void setDestfile(File destinationFile) { 175 this.destinationFile = destinationFile; 176 } 177 178 185 public void setAppend(boolean append) { 186 this.append = append; 187 } 188 189 194 public void setEncoding(String encoding) { 195 this.encoding = encoding; 196 if (outputEncoding == null) { 197 outputEncoding = encoding; 198 } 199 } 200 201 206 public void setOutputEncoding(String outputEncoding) { 207 this.outputEncoding = outputEncoding; 208 } 209 210 216 public void setForce(boolean force) { 217 this.forceOverwrite = force; 218 } 219 220 222 227 public Path createPath() { 228 Path path = new Path(getProject()); 229 add(path); 230 return path; 231 } 232 233 237 public void addFileset(FileSet set) { 238 add(set); 239 } 240 241 245 public void addFilelist(FileList list) { 246 add(list); 247 } 248 249 254 public void add(ResourceCollection c) { 255 rc = rc == null ? new Resources() : rc; 256 rc.add(c); 257 } 258 259 264 public void addFilterChain(FilterChain filterChain) { 265 if (filterChains == null) { 266 filterChains = new Vector (); 267 } 268 filterChains.addElement(filterChain); 269 } 270 271 275 public void addText(String text) { 276 if (textBuffer == null) { 277 textBuffer = new StringBuffer (text.length()); 280 } 281 282 textBuffer.append(text); 285 } 286 287 292 public void addHeader(TextElement headerToAdd) { 293 this.header = headerToAdd; 294 } 295 296 301 public void addFooter(TextElement footerToAdd) { 302 this.footer = footerToAdd; 303 } 304 305 312 public void setFixLastLine(boolean fixLastLine) { 313 this.fixLastLine = fixLastLine; 314 } 315 316 324 public void setEol(FixCRLF.CrLf crlf) { 325 String s = crlf.getValue(); 326 if (s.equals("cr") || s.equals("mac")) { 327 eolString = "\r"; 328 } else if (s.equals("lf") || s.equals("unix")) { 329 eolString = "\n"; 330 } else if (s.equals("crlf") || s.equals("dos")) { 331 eolString = "\r\n"; 332 } 333 } 334 335 341 public void setWriter(Writer outputWriter) { 342 this.outputWriter = outputWriter; 343 } 344 345 352 public void setBinary(boolean binary) { 353 this.binary = binary; 354 } 355 356 359 private ResourceCollection validate() { 360 361 sanitizeText(); 363 364 if (binary) { 366 if (destinationFile == null) { 367 throw new BuildException( 368 "destfile attribute is required for binary concatenation"); 369 } 370 if (textBuffer != null) { 371 throw new BuildException( 372 "Nested text is incompatible with binary concatenation"); 373 } 374 if (encoding != null || outputEncoding != null) { 375 throw new BuildException( 376 "Seting input or output encoding is incompatible with binary" 377 + " concatenation"); 378 } 379 if (filterChains != null) { 380 throw new BuildException( 381 "Setting filters is incompatible with binary concatenation"); 382 } 383 if (fixLastLine) { 384 throw new BuildException( 385 "Setting fixlastline is incompatible with binary concatenation"); 386 } 387 if (header != null || footer != null) { 388 throw new BuildException( 389 "Nested header or footer is incompatible with binary concatenation"); 390 } 391 } 392 if (destinationFile != null && outputWriter != null) { 393 throw new BuildException( 394 "Cannot specify both a destination file and an output writer"); 395 } 396 if (rc == null && textBuffer == null) { 398 throw new BuildException( 400 "At least one resource must be provided, or some text."); 401 } 402 if (rc != null) { 403 if (textBuffer != null) { 407 throw new BuildException( 408 "Cannot include inline text when using resources."); 409 } 410 Restrict noexistRc = new Restrict(); 411 noexistRc.add(NOT_EXISTS); 412 noexistRc.add(rc); 413 for (Iterator i = noexistRc.iterator(); i.hasNext();) { 414 log(i.next() + " does not exist.", Project.MSG_ERR); 415 } 416 if (destinationFile != null) { 417 for (Iterator i = rc.iterator(); i.hasNext();) { 418 Object o = i.next(); 419 if (o instanceof FileResource) { 420 File f = ((FileResource) o).getFile(); 421 if (FILE_UTILS.fileNameEquals(f, destinationFile)) { 422 throw new BuildException("Input file \"" 423 + f + "\" is the same as the output file."); 424 } 425 } 426 } 427 } 428 Restrict existRc = new Restrict(); 429 existRc.add(EXISTS); 430 existRc.add(rc); 431 boolean outofdate = destinationFile == null || forceOverwrite; 432 if (!outofdate) { 433 for (Iterator i = existRc.iterator(); !outofdate && i.hasNext();) { 434 Resource r = (Resource) i.next(); 435 outofdate = 436 (r.getLastModified() == 0L 437 || r.getLastModified() > destinationFile.lastModified()); 438 } 439 } 440 if (!outofdate) { 441 log(destinationFile + " is up-to-date.", Project.MSG_VERBOSE); 442 return null; } 444 return existRc; 445 } else { 446 StringResource s = new StringResource(); 447 s.setProject(getProject()); 448 s.setValue(textBuffer.toString()); 449 return s; 450 } 451 } 452 453 456 public void execute() { 457 ResourceCollection c = validate(); 458 if (c == null) { 459 return; 460 } 461 if (c.size() < 1 && header == null && footer == null) { 463 log("No existing resources and no nested text, doing nothing", 464 Project.MSG_INFO); 465 return; 466 } 467 if (binary) { 468 binaryCat(c); 469 } else { 470 cat(c); 471 } 472 } 473 474 475 private void binaryCat(ResourceCollection c) { 476 log("Binary concatenation of " + c.size() 477 + " resources to " + destinationFile); 478 FileOutputStream out = null; 479 InputStream in = null; 480 try { 481 try { 482 out = new FileOutputStream (destinationFile); 483 } catch (Exception t) { 484 throw new BuildException("Unable to open " 485 + destinationFile + " for writing", t); 486 } 487 in = new ConcatResourceInputStream(c); 488 ((ConcatResourceInputStream) in).setManagingComponent(this); 489 Thread t = new Thread (new StreamPumper(in, out)); 490 t.start(); 491 try { 492 t.join(); 493 } catch (InterruptedException e) { 494 try { 495 t.join(); 496 } catch (InterruptedException ee) { 497 } 499 } 500 } finally { 501 FileUtils.close(in); 502 if (out != null) { 503 try { 504 out.close(); 505 } catch (Exception ex) { 506 throw new BuildException( 507 "Unable to close " + destinationFile, ex); 508 } 509 } 510 } 511 } 512 513 514 private void cat(ResourceCollection c) { 515 OutputStream os = null; 516 char[] buffer = new char[BUFFER_SIZE]; 517 518 try { 519 PrintWriter writer = null; 520 521 if (outputWriter != null) { 522 writer = new PrintWriter (outputWriter); 523 } else { 524 if (destinationFile == null) { 525 os = new LogOutputStream(this, Project.MSG_WARN); 527 } else { 528 File parent = destinationFile.getParentFile(); 530 if (!parent.exists()) { 531 parent.mkdirs(); 532 } 533 os = new FileOutputStream (destinationFile.getAbsolutePath(), 534 append); 535 } 536 if (outputEncoding == null) { 537 writer = new PrintWriter ( 538 new BufferedWriter ( 539 new OutputStreamWriter (os))); 540 } else { 541 writer = new PrintWriter ( 542 new BufferedWriter ( 543 new OutputStreamWriter (os, outputEncoding))); 544 } 545 } 546 if (header != null) { 547 if (header.getFiltering()) { 548 concatenate( 549 buffer, writer, new StringReader (header.getValue())); 550 } else { 551 writer.print(header.getValue()); 552 } 553 } 554 if (c.size() > 0) { 555 concatenate(buffer, writer, new MultiReader(c)); 556 } 557 if (footer != null) { 558 if (footer.getFiltering()) { 559 concatenate( 560 buffer, writer, new StringReader (footer.getValue())); 561 } else { 562 writer.print(footer.getValue()); 563 } 564 } 565 writer.flush(); 566 if (os != null) { 567 os.flush(); 568 } 569 } catch (IOException ioex) { 570 throw new BuildException("Error while concatenating: " 571 + ioex.getMessage(), ioex); 572 } finally { 573 FileUtils.close(os); 574 } 575 } 576 577 578 private void concatenate(char[] buffer, Writer writer, Reader in) 579 throws IOException { 580 if (filterChains != null) { 581 ChainReaderHelper helper = new ChainReaderHelper(); 582 helper.setBufferSize(BUFFER_SIZE); 583 helper.setPrimaryReader(in); 584 helper.setFilterChains(filterChains); 585 helper.setProject(getProject()); 586 in = new BufferedReader (helper.getAssembledReader()); 587 } 588 while (true) { 589 int nRead = in.read(buffer, 0, buffer.length); 590 if (nRead == -1) { 591 break; 592 } 593 writer.write(buffer, 0, nRead); 594 } 595 writer.flush(); 596 } 597 598 604 private void sanitizeText() { 605 if (textBuffer != null) { 606 if (textBuffer.substring(0).trim().length() == 0) { 607 textBuffer = null; 608 } 609 } 610 } 611 612 615 public static class TextElement extends ProjectComponent { 616 private String value = ""; 617 private boolean trimLeading = false; 618 private boolean trim = false; 619 private boolean filtering = true; 620 private String encoding = null; 621 622 629 public void setFiltering(boolean filtering) { 630 this.filtering = filtering; 631 } 632 633 634 private boolean getFiltering() { 635 return filtering; 636 } 637 638 643 public void setEncoding(String encoding) { 644 this.encoding = encoding; 645 } 646 647 653 public void setFile(File file) throws BuildException { 654 if (!file.exists()) { 656 throw new BuildException("File " + file + " does not exist."); 657 } 658 659 BufferedReader reader = null; 660 try { 661 if (this.encoding == null) { 662 reader = new BufferedReader (new FileReader (file)); 663 } else { 664 reader = new BufferedReader ( 665 new InputStreamReader (new FileInputStream (file), 666 this.encoding)); 667 } 668 value = FileUtils.readFully(reader); 669 } catch (IOException ex) { 670 throw new BuildException(ex); 671 } finally { 672 FileUtils.close(reader); 673 } 674 } 675 676 680 public void addText(String value) { 681 this.value += getProject().replaceProperties(value); 682 } 683 684 688 public void setTrimLeading(boolean strip) { 689 this.trimLeading = strip; 690 } 691 692 696 public void setTrim(boolean trim) { 697 this.trim = trim; 698 } 699 700 703 public String getValue() { 704 if (value == null) { 705 value = ""; 706 } 707 if (value.trim().length() == 0) { 708 value = ""; 709 } 710 if (trimLeading) { 711 char[] current = value.toCharArray(); 712 StringBuffer b = new StringBuffer (current.length); 713 boolean startOfLine = true; 714 int pos = 0; 715 while (pos < current.length) { 716 char ch = current[pos++]; 717 if (startOfLine) { 718 if (ch == ' ' || ch == '\t') { 719 continue; 720 } 721 startOfLine = false; 722 } 723 b.append(ch); 724 if (ch == '\n' || ch == '\r') { 725 startOfLine = true; 726 } 727 } 728 value = b.toString(); 729 } 730 if (trim) { 731 value = value.trim(); 732 } 733 return value; 734 } 735 } 736 737 742 private class MultiReader extends Reader { 743 private Reader reader = null; 744 private int lastPos = 0; 745 private char[] lastChars = new char[eolString.length()]; 746 private boolean needAddSeparator = false; 747 private Iterator i; 748 749 private MultiReader(ResourceCollection c) { 750 i = c.iterator(); 751 } 752 753 private Reader getReader() throws IOException { 754 if (reader == null && i.hasNext()) { 755 Resource r = (Resource) i.next(); 756 log("Concating " + r.toLongString(), Project.MSG_VERBOSE); 757 InputStream is = r.getInputStream(); 758 reader = new BufferedReader (encoding == null 759 ? new InputStreamReader (is) 760 : new InputStreamReader (is, encoding)); 761 Arrays.fill(lastChars, (char) 0); 762 } 763 return reader; 764 } 765 766 private void nextReader() throws IOException { 767 close(); 768 reader = null; 769 } 770 771 778 public int read() throws IOException { 779 if (needAddSeparator) { 780 int ret = eolString.charAt(lastPos++); 781 if (lastPos >= eolString.length()) { 782 lastPos = 0; 783 needAddSeparator = false; 784 } 785 return ret; 786 } 787 while (getReader() != null) { 788 int ch = getReader().read(); 789 if (ch == -1) { 790 nextReader(); 791 if (fixLastLine && isMissingEndOfLine()) { 792 needAddSeparator = true; 793 lastPos = 0; 794 } 795 } else { 796 addLastChar((char) ch); 797 return ch; 798 } 799 } 800 return -1; 801 } 802 803 811 public int read(char[] cbuf, int off, int len) 812 throws IOException { 813 814 int amountRead = 0; 815 while (getReader() != null || needAddSeparator) { 816 if (needAddSeparator) { 817 cbuf[off] = eolString.charAt(lastPos++); 818 if (lastPos >= eolString.length()) { 819 lastPos = 0; 820 needAddSeparator = false; 821 } 822 len--; 823 off++; 824 amountRead++; 825 if (len == 0) { 826 return amountRead; 827 } 828 continue; 829 } 830 int nRead = getReader().read(cbuf, off, len); 831 if (nRead == -1 || nRead == 0) { 832 nextReader(); 833 if (fixLastLine && isMissingEndOfLine()) { 834 needAddSeparator = true; 835 lastPos = 0; 836 } 837 } else { 838 if (fixLastLine) { 839 for (int i = nRead; 840 i > (nRead - lastChars.length); 841 --i) { 842 if (i <= 0) { 843 break; 844 } 845 addLastChar(cbuf[off + i - 1]); 846 } 847 } 848 len -= nRead; 849 off += nRead; 850 amountRead += nRead; 851 if (len == 0) { 852 return amountRead; 853 } 854 } 855 } 856 if (amountRead == 0) { 857 return -1; 858 } else { 859 return amountRead; 860 } 861 } 862 863 866 public void close() throws IOException { 867 if (reader != null) { 868 reader.close(); 869 } 870 } 871 872 876 private void addLastChar(char ch) { 877 for (int i = lastChars.length - 2; i >= 0; --i) { 878 lastChars[i] = lastChars[i + 1]; 879 } 880 lastChars[lastChars.length - 1] = ch; 881 } 882 883 887 private boolean isMissingEndOfLine() { 888 for (int i = 0; i < lastChars.length; ++i) { 889 if (lastChars[i] != eolString.charAt(i)) { 890 return true; 891 } 892 } 893 return false; 894 } 895 } 896 897 } 898 899 | Popular Tags |