1 18 19 package org.apache.tools.ant.taskdefs; 20 21 import java.io.BufferedReader ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.io.InputStreamReader ; 25 import java.io.PrintWriter ; 26 import java.io.Reader ; 27 import java.io.StringWriter ; 28 import java.io.UnsupportedEncodingException ; 29 import java.util.Enumeration ; 30 import java.util.Hashtable ; 31 import java.util.Vector ; 32 import org.apache.tools.ant.BuildException; 33 import org.apache.tools.ant.util.FileUtils; 34 35 51 public class Manifest { 52 53 public static final String ATTRIBUTE_MANIFEST_VERSION 54 = "Manifest-Version"; 55 56 57 public static final String ATTRIBUTE_SIGNATURE_VERSION 58 = "Signature-Version"; 59 60 61 public static final String ATTRIBUTE_NAME = "Name"; 62 63 64 public static final String ATTRIBUTE_FROM = "From"; 65 66 67 public static final String ATTRIBUTE_CLASSPATH = "Class-Path"; 68 69 70 public static final String DEFAULT_MANIFEST_VERSION = "1.0"; 71 72 73 public static final int MAX_LINE_LENGTH = 72; 74 75 79 public static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2; 80 81 82 public static final String EOL = "\r\n"; 83 84 public static final String ERROR_FROM_FORBIDDEN = "Manifest attributes should not start " 85 + "with \"" + ATTRIBUTE_FROM + "\" in \""; 86 87 88 public static final String JAR_ENCODING = "UTF-8"; 89 90 94 public static class Attribute { 95 96 101 private static final int MAX_NAME_VALUE_LENGTH = 68; 102 103 111 private static final int MAX_NAME_LENGTH = 70; 112 113 114 private String name = null; 115 116 117 private Vector values = new Vector (); 118 119 123 private int currentIndex = 0; 124 125 127 public Attribute() { 128 } 129 130 137 public Attribute(String line) throws ManifestException { 138 parse(line); 139 } 140 141 147 public Attribute(String name, String value) { 148 this.name = name; 149 setValue(value); 150 } 151 152 156 public int hashCode() { 157 int hashCode = 0; 158 159 if (name != null) { 160 hashCode += getKey().hashCode(); 161 } 162 163 hashCode += values.hashCode(); 164 return hashCode; 165 } 166 167 172 public boolean equals(Object rhs) { 173 if (rhs == null || rhs.getClass() != getClass()) { 174 return false; 175 } 176 177 if (rhs == this) { 178 return true; 179 } 180 181 Attribute rhsAttribute = (Attribute) rhs; 182 String lhsKey = getKey(); 183 String rhsKey = rhsAttribute.getKey(); 184 if ((lhsKey == null && rhsKey != null) 185 || (lhsKey != null && rhsKey == null) 186 || !lhsKey.equals(rhsKey)) { 187 return false; 188 } 189 190 return values.equals(rhsAttribute.values); 191 } 192 193 201 public void parse(String line) throws ManifestException { 202 int index = line.indexOf(": "); 203 if (index == -1) { 204 throw new ManifestException("Manifest line \"" + line 205 + "\" is not valid as it does not " 206 + "contain a name and a value separated by ': ' "); 207 } 208 name = line.substring(0, index); 209 setValue(line.substring(index + 2)); 210 } 211 212 217 public void setName(String name) { 218 this.name = name; 219 } 220 221 226 public String getName() { 227 return name; 228 } 229 230 235 public String getKey() { 236 if (name == null) { 237 return null; 238 } 239 return name.toLowerCase(); 240 } 241 242 247 public void setValue(String value) { 248 if (currentIndex >= values.size()) { 249 values.addElement(value); 250 currentIndex = values.size() - 1; 251 } else { 252 values.setElementAt(value, currentIndex); 253 } 254 } 255 256 261 public String getValue() { 262 if (values.size() == 0) { 263 return null; 264 } 265 266 String fullValue = ""; 267 for (Enumeration e = getValues(); e.hasMoreElements();) { 268 String value = (String ) e.nextElement(); 269 fullValue += value + " "; 270 } 271 return fullValue.trim(); 272 } 273 274 279 public void addValue(String value) { 280 currentIndex++; 281 setValue(value); 282 } 283 284 289 public Enumeration getValues() { 290 return values.elements(); 291 } 292 293 302 public void addContinuation(String line) { 303 String currentValue = (String ) values.elementAt(currentIndex); 304 setValue(currentValue + line.substring(1)); 305 } 306 307 314 public void write(PrintWriter writer) throws IOException { 315 for (Enumeration e = getValues(); e.hasMoreElements();) { 316 writeValue(writer, (String ) e.nextElement()); 317 } 318 } 319 320 328 private void writeValue(PrintWriter writer, String value) 329 throws IOException { 330 String line = null; 331 int nameLength = name.getBytes(JAR_ENCODING).length; 332 if (nameLength > MAX_NAME_VALUE_LENGTH) { 333 if (nameLength > MAX_NAME_LENGTH) { 334 throw new IOException ("Unable to write manifest line " 335 + name + ": " + value); 336 } 337 writer.print(name + ": " + EOL); 338 line = " " + value; 339 } else { 340 line = name + ": " + value; 341 } 342 while (line.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH) { 343 int breakIndex = MAX_SECTION_LENGTH; 345 if (breakIndex >= line.length()) { 346 breakIndex = line.length() - 1; 347 } 348 String section = line.substring(0, breakIndex); 349 while (section.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH 350 && breakIndex > 0) { 351 breakIndex--; 352 section = line.substring(0, breakIndex); 353 } 354 if (breakIndex == 0) { 355 throw new IOException ("Unable to write manifest line " 356 + name + ": " + value); 357 } 358 writer.print(section + EOL); 359 line = " " + line.substring(breakIndex); 360 } 361 writer.print(line + EOL); 362 } 363 } 364 365 370 public static class Section { 371 372 private Vector warnings = new Vector (); 373 374 378 private String name = null; 379 380 381 private Hashtable attributes = new Hashtable (); 382 383 384 private Vector attributeIndex = new Vector (); 385 386 390 public void setName(String name) { 391 this.name = name; 392 } 393 394 399 public String getName() { 400 return name; 401 } 402 403 416 public String read(BufferedReader reader) 417 throws ManifestException, IOException { 418 Attribute attribute = null; 419 while (true) { 420 String line = reader.readLine(); 421 if (line == null || line.length() == 0) { 422 return null; 423 } 424 if (line.charAt(0) == ' ') { 425 if (attribute == null) { 427 if (name != null) { 428 name += line.substring(1); 432 } else { 433 throw new ManifestException("Can't start an " 434 + "attribute with a continuation line " + line); 435 } 436 } else { 437 attribute.addContinuation(line); 438 } 439 } else { 440 attribute = new Attribute(line); 441 String nameReadAhead = addAttributeAndCheck(attribute); 442 attribute = getAttribute(attribute.getKey()); 444 if (nameReadAhead != null) { 445 return nameReadAhead; 446 } 447 } 448 } 449 } 450 451 458 public void merge(Section section) throws ManifestException { 459 if (name == null && section.getName() != null 460 || name != null 461 && !(name.equalsIgnoreCase(section.getName()))) { 462 throw new ManifestException("Unable to merge sections " 463 + "with different names"); 464 } 465 466 Enumeration e = section.getAttributeKeys(); 467 Attribute classpathAttribute = null; 468 while (e.hasMoreElements()) { 469 String attributeName = (String ) e.nextElement(); 470 Attribute attribute = section.getAttribute(attributeName); 471 if (attributeName.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) { 472 if (classpathAttribute == null) { 473 classpathAttribute = new Attribute(); 474 classpathAttribute.setName(ATTRIBUTE_CLASSPATH); 475 } 476 Enumeration cpe = attribute.getValues(); 477 while (cpe.hasMoreElements()) { 478 String value = (String ) cpe.nextElement(); 479 classpathAttribute.addValue(value); 480 } 481 } else { 482 storeAttribute(attribute); 484 } 485 } 486 487 if (classpathAttribute != null) { 488 storeAttribute(classpathAttribute); 490 } 491 492 Enumeration warnEnum = section.warnings.elements(); 494 while (warnEnum.hasMoreElements()) { 495 warnings.addElement(warnEnum.nextElement()); 496 } 497 } 498 499 506 public void write(PrintWriter writer) throws IOException { 507 if (name != null) { 508 Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name); 509 nameAttr.write(writer); 510 } 511 Enumeration e = getAttributeKeys(); 512 while (e.hasMoreElements()) { 513 String key = (String ) e.nextElement(); 514 Attribute attribute = getAttribute(key); 515 attribute.write(writer); 516 } 517 writer.print(EOL); 518 } 519 520 528 public Attribute getAttribute(String attributeName) { 529 return (Attribute) attributes.get(attributeName.toLowerCase()); 530 } 531 532 538 public Enumeration getAttributeKeys() { 539 return attributeIndex.elements(); 540 } 541 542 550 public String getAttributeValue(String attributeName) { 551 Attribute attribute = getAttribute(attributeName.toLowerCase()); 552 if (attribute == null) { 553 return null; 554 } 555 return attribute.getValue(); 556 } 557 558 563 public void removeAttribute(String attributeName) { 564 String key = attributeName.toLowerCase(); 565 attributes.remove(key); 566 attributeIndex.removeElement(key); 567 } 568 569 576 public void addConfiguredAttribute(Attribute attribute) 577 throws ManifestException { 578 String check = addAttributeAndCheck(attribute); 579 if (check != null) { 580 throw new BuildException("Specify the section name using " 581 + "the \"name\" attribute of the <section> element rather " 582 + "than using a \"Name\" manifest attribute"); 583 } 584 } 585 586 597 public String addAttributeAndCheck(Attribute attribute) 598 throws ManifestException { 599 if (attribute.getName() == null || attribute.getValue() == null) { 600 throw new BuildException("Attributes must have name and value"); 601 } 602 if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) { 603 warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes " 604 + "should not occur in the main section and must be the " 605 + "first element in all other sections: \"" 606 + attribute.getName() + ": " + attribute.getValue() + "\""); 607 return attribute.getValue(); 608 } 609 610 if (attribute.getKey().startsWith(ATTRIBUTE_FROM.toLowerCase())) { 611 warnings.addElement(ERROR_FROM_FORBIDDEN 612 + attribute.getName() + ": " + attribute.getValue() + "\""); 613 } else { 614 String attributeKey = attribute.getKey(); 616 if (attributeKey.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) { 617 Attribute classpathAttribute = 618 (Attribute) attributes.get(attributeKey); 619 620 if (classpathAttribute == null) { 621 storeAttribute(attribute); 622 } else { 623 warnings.addElement("Multiple Class-Path attributes " 624 + "are supported but violate the Jar " 625 + "specification and may not be correctly " 626 + "processed in all environments"); 627 Enumeration e = attribute.getValues(); 628 while (e.hasMoreElements()) { 629 String value = (String ) e.nextElement(); 630 classpathAttribute.addValue(value); 631 } 632 } 633 } else if (attributes.containsKey(attributeKey)) { 634 throw new ManifestException("The attribute \"" 635 + attribute.getName() + "\" may not occur more " 636 + "than once in the same section"); 637 } else { 638 storeAttribute(attribute); 639 } 640 } 641 return null; 642 } 643 644 650 public Object clone() { 651 Section cloned = new Section(); 652 cloned.setName(name); 653 Enumeration e = getAttributeKeys(); 654 while (e.hasMoreElements()) { 655 String key = (String ) e.nextElement(); 656 Attribute attribute = getAttribute(key); 657 cloned.storeAttribute(new Attribute(attribute.getName(), 658 attribute.getValue())); 659 } 660 return cloned; 661 } 662 663 668 private void storeAttribute(Attribute attribute) { 669 if (attribute == null) { 670 return; 671 } 672 String attributeKey = attribute.getKey(); 673 attributes.put(attributeKey, attribute); 674 if (!attributeIndex.contains(attributeKey)) { 675 attributeIndex.addElement(attributeKey); 676 } 677 } 678 679 684 public Enumeration getWarnings() { 685 return warnings.elements(); 686 } 687 688 692 public int hashCode() { 693 return attributes.hashCode(); 694 } 695 696 701 public boolean equals(Object rhs) { 702 if (rhs == null || rhs.getClass() != getClass()) { 703 return false; 704 } 705 706 if (rhs == this) { 707 return true; 708 } 709 710 Section rhsSection = (Section) rhs; 711 712 return attributes.equals(rhsSection.attributes); 713 } 714 } 715 716 717 718 private String manifestVersion = DEFAULT_MANIFEST_VERSION; 719 720 721 private Section mainSection = new Section(); 722 723 724 private Hashtable sections = new Hashtable (); 725 726 727 private Vector sectionIndex = new Vector (); 728 729 736 public static Manifest getDefaultManifest() throws BuildException { 737 InputStream in = null; 738 InputStreamReader insr = null; 739 try { 740 String defManifest = "/org/apache/tools/ant/defaultManifest.mf"; 741 in = Manifest.class.getResourceAsStream(defManifest); 742 if (in == null) { 743 throw new BuildException("Could not find default manifest: " 744 + defManifest); 745 } 746 try { 747 insr = new InputStreamReader (in, "UTF-8"); 748 Manifest defaultManifest = new Manifest(insr); 749 Attribute createdBy = new Attribute("Created-By", 750 System.getProperty("java.vm.version") + " (" 751 + System.getProperty("java.vm.vendor") + ")"); 752 defaultManifest.getMainSection().storeAttribute(createdBy); 753 return defaultManifest; 754 } catch (UnsupportedEncodingException e) { 755 insr = new InputStreamReader (in); 756 return new Manifest(insr); 757 } 758 } catch (ManifestException e) { 759 throw new BuildException("Default manifest is invalid !!", e); 760 } catch (IOException e) { 761 throw new BuildException("Unable to read default manifest", e); 762 } finally { 763 FileUtils.close(insr); 764 FileUtils.close(in); 765 } 766 } 767 768 769 public Manifest() { 770 manifestVersion = null; 771 } 772 773 782 public Manifest(Reader r) throws ManifestException, IOException { 783 BufferedReader reader = new BufferedReader (r); 784 String nextSectionName = mainSection.read(reader); 786 String readManifestVersion 787 = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION); 788 if (readManifestVersion != null) { 789 manifestVersion = readManifestVersion; 790 mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION); 791 } 792 793 String line = null; 794 while ((line = reader.readLine()) != null) { 795 if (line.length() == 0) { 796 continue; 797 } 798 799 Section section = new Section(); 800 if (nextSectionName == null) { 801 Attribute sectionName = new Attribute(line); 802 if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) { 803 throw new ManifestException("Manifest sections should " 804 + "start with a \"" + ATTRIBUTE_NAME 805 + "\" attribute and not \"" 806 + sectionName.getName() + "\""); 807 } 808 nextSectionName = sectionName.getValue(); 809 } else { 810 Attribute firstAttribute = new Attribute(line); 814 section.addAttributeAndCheck(firstAttribute); 815 } 816 817 section.setName(nextSectionName); 818 nextSectionName = section.read(reader); 819 addConfiguredSection(section); 820 } 821 } 822 823 830 public void addConfiguredSection(Section section) 831 throws ManifestException { 832 String sectionName = section.getName(); 833 if (sectionName == null) { 834 throw new BuildException("Sections must have a name"); 835 } 836 sections.put(sectionName, section); 837 if (!sectionIndex.contains(sectionName)) { 838 sectionIndex.addElement(sectionName); 839 } 840 } 841 842 849 public void addConfiguredAttribute(Attribute attribute) 850 throws ManifestException { 851 if (attribute.getKey() == null || attribute.getValue() == null) { 852 throw new BuildException("Attributes must have name and value"); 853 } 854 if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_MANIFEST_VERSION)) { 855 manifestVersion = attribute.getValue(); 856 } else { 857 mainSection.addConfiguredAttribute(attribute); 858 } 859 } 860 861 869 public void merge(Manifest other) throws ManifestException { 870 merge(other, false); 871 } 872 873 883 public void merge(Manifest other, boolean overwriteMain) 884 throws ManifestException { 885 if (other != null) { 886 if (overwriteMain) { 887 mainSection = (Section) other.mainSection.clone(); 888 } else { 889 mainSection.merge(other.mainSection); 890 } 891 892 if (other.manifestVersion != null) { 893 manifestVersion = other.manifestVersion; 894 } 895 896 Enumeration e = other.getSectionNames(); 897 while (e.hasMoreElements()) { 898 String sectionName = (String ) e.nextElement(); 899 Section ourSection = (Section) sections.get(sectionName); 900 Section otherSection 901 = (Section) other.sections.get(sectionName); 902 if (ourSection == null) { 903 if (otherSection != null) { 904 addConfiguredSection((Section) otherSection.clone()); 905 } 906 } else { 907 ourSection.merge(otherSection); 908 } 909 } 910 } 911 } 912 913 920 public void write(PrintWriter writer) throws IOException { 921 writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL); 922 String signatureVersion 923 = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION); 924 if (signatureVersion != null) { 925 writer.print(ATTRIBUTE_SIGNATURE_VERSION + ": " 926 + signatureVersion + EOL); 927 mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION); 928 } 929 mainSection.write(writer); 930 931 if (signatureVersion != null) { 933 try { 934 Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION, 935 signatureVersion); 936 mainSection.addConfiguredAttribute(svAttr); 937 } catch (ManifestException e) { 938 } 940 } 941 942 Enumeration e = sectionIndex.elements(); 943 while (e.hasMoreElements()) { 944 String sectionName = (String ) e.nextElement(); 945 Section section = getSection(sectionName); 946 section.write(writer); 947 } 948 } 949 950 956 public String toString() { 957 StringWriter sw = new StringWriter (); 958 try { 959 write(new PrintWriter (sw)); 960 } catch (IOException e) { 961 return null; 962 } 963 return sw.toString(); 964 } 965 966 971 public Enumeration getWarnings() { 972 Vector warnings = new Vector (); 973 974 Enumeration warnEnum = mainSection.getWarnings(); 975 while (warnEnum.hasMoreElements()) { 976 warnings.addElement(warnEnum.nextElement()); 977 } 978 979 Enumeration e = sections.elements(); 981 while (e.hasMoreElements()) { 982 Section section = (Section) e.nextElement(); 983 Enumeration e2 = section.getWarnings(); 984 while (e2.hasMoreElements()) { 985 warnings.addElement(e2.nextElement()); 986 } 987 } 988 989 return warnings.elements(); 990 } 991 992 996 public int hashCode() { 997 int hashCode = 0; 998 999 if (manifestVersion != null) { 1000 hashCode += manifestVersion.hashCode(); 1001 } 1002 hashCode += mainSection.hashCode(); 1003 hashCode += sections.hashCode(); 1004 1005 return hashCode; 1006 } 1007 1008 1013 public boolean equals(Object rhs) { 1014 if (rhs == null || rhs.getClass() != getClass()) { 1015 return false; 1016 } 1017 1018 if (rhs == this) { 1019 return true; 1020 } 1021 1022 Manifest rhsManifest = (Manifest) rhs; 1023 if (manifestVersion == null) { 1024 if (rhsManifest.manifestVersion != null) { 1025 return false; 1026 } 1027 } else if (!manifestVersion.equals(rhsManifest.manifestVersion)) { 1028 return false; 1029 } 1030 1031 if (!mainSection.equals(rhsManifest.mainSection)) { 1032 return false; 1033 } 1034 1035 return sections.equals(rhsManifest.sections); 1036 } 1037 1038 1043 public String getManifestVersion() { 1044 return manifestVersion; 1045 } 1046 1047 1052 public Section getMainSection() { 1053 return mainSection; 1054 } 1055 1056 1063 public Section getSection(String name) { 1064 return (Section) sections.get(name); 1065 } 1066 1067 1072 public Enumeration getSectionNames() { 1073 return sectionIndex.elements(); 1074 } 1075} 1076 | Popular Tags |