aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java
new file mode 100644
index 00000000..06c74ddc
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Manifest.java
@@ -0,0 +1,1183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Holds the data of a jar manifest.
+ *
+ * Manifests are processed according to the
+ * {@link <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html">Jar
+ * file specification.</a>}.
+ * Specifically, a manifest element consists of
+ * a set of attributes and sections. These sections in turn may contain
+ * attributes. Note in particular that this may result in manifest lines
+ * greater than 72 bytes being wrapped and continued on the next
+ * line. If an application can not handle the continuation mechanism, it
+ * is a defect in the application, not this task.
+ *
+ *
+ * @since Ant 1.4
+ */
+public class Manifest {
+ /** The standard manifest version header */
+ public static final String ATTRIBUTE_MANIFEST_VERSION
+ = "Manifest-Version";
+
+ /** The standard Signature Version header */
+ public static final String ATTRIBUTE_SIGNATURE_VERSION
+ = "Signature-Version";
+
+ /** The Name Attribute is the first in a named section */
+ public static final String ATTRIBUTE_NAME = "Name";
+
+ /** The From Header is disallowed in a Manifest */
+ public static final String ATTRIBUTE_FROM = "From";
+
+ /** The Class-Path Header is special - it can be duplicated */
+ public static final String ATTRIBUTE_CLASSPATH = "Class-Path";
+
+ /** Default Manifest version if one is not specified */
+ public static final String DEFAULT_MANIFEST_VERSION = "1.0";
+
+ /** The max length of a line in a Manifest */
+ public static final int MAX_LINE_LENGTH = 72;
+
+ /**
+ * Max length of a line section which is continued. Need to allow
+ * for the CRLF.
+ */
+ public static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
+
+ /** The End-Of-Line marker in manifests */
+ public static final String EOL = "\r\n";
+ /** Error for attributes */
+ public static final String ERROR_FROM_FORBIDDEN = "Manifest attributes should not start "
+ + "with \"" + ATTRIBUTE_FROM + "\" in \"";
+
+ /** Encoding to be used for JAR files. */
+ public static final String JAR_ENCODING = "UTF-8";
+
+ private static final String ATTRIBUTE_MANIFEST_VERSION_LC =
+ ATTRIBUTE_MANIFEST_VERSION.toLowerCase(Locale.ENGLISH);
+ private static final String ATTRIBUTE_NAME_LC =
+ ATTRIBUTE_NAME.toLowerCase(Locale.ENGLISH);
+ private static final String ATTRIBUTE_FROM_LC =
+ ATTRIBUTE_FROM.toLowerCase(Locale.ENGLISH);
+ private static final String ATTRIBUTE_CLASSPATH_LC =
+ ATTRIBUTE_CLASSPATH.toLowerCase(Locale.ENGLISH);
+
+ /**
+ * An attribute for the manifest.
+ * Those attributes that are not nested into a section will be added to the "Main" section.
+ */
+ public static class Attribute {
+
+ /**
+ * Maximum length of the name to have the value starting on the same
+ * line as the name. This to stay under 72 bytes total line length
+ * (including CRLF).
+ */
+ private static final int MAX_NAME_VALUE_LENGTH = 68;
+
+ /**
+ * Maximum length of the name according to the jar specification.
+ * In this case the first line will have 74 bytes total line length
+ * (including CRLF). This conflicts with the 72 bytes total line length
+ * max, but is the only possible conclusion from the manifest specification, if
+ * names with 70 bytes length are allowed, have to be on the first line, and
+ * have to be followed by ": ".
+ */
+ private static final int MAX_NAME_LENGTH = 70;
+
+ /** The attribute's name */
+ private String name = null;
+
+ /** The attribute's value */
+ private Vector<String> values = new Vector<String>();
+
+ /**
+ * For multivalued attributes, this is the index of the attribute
+ * currently being defined.
+ */
+ private int currentIndex = 0;
+
+ /**
+ * Construct an empty attribute */
+ public Attribute() {
+ }
+
+ /**
+ * Construct an attribute by parsing a line from the Manifest
+ *
+ * @param line the line containing the attribute name and value
+ *
+ * @throws ManifestException if the line is not valid
+ */
+ public Attribute(String line) throws ManifestException {
+ parse(line);
+ }
+
+ /**
+ * Construct a manifest by specifying its name and value
+ *
+ * @param name the attribute's name
+ * @param value the Attribute's value
+ */
+ public Attribute(String name, String value) {
+ this.name = name;
+ setValue(value);
+ }
+
+ /**
+ * @see java.lang.Object#hashCode
+ * @return a hashcode based on the key and values.
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = 0;
+
+ if (name != null) {
+ hashCode += getKey().hashCode();
+ }
+
+ hashCode += values.hashCode();
+ return hashCode;
+ }
+
+ /**
+ * @param rhs the object to check for equality.
+ * @see java.lang.Object#equals
+ * @return true if the key and values are the same.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs == null || rhs.getClass() != getClass()) {
+ return false;
+ }
+
+ if (rhs == this) {
+ return true;
+ }
+
+ Attribute rhsAttribute = (Attribute) rhs;
+ String lhsKey = getKey();
+ String rhsKey = rhsAttribute.getKey();
+ if ((lhsKey == null && rhsKey != null)
+ || (lhsKey != null && !lhsKey.equals(rhsKey))) {
+ return false;
+ }
+
+ return values.equals(rhsAttribute.values);
+ }
+
+ /**
+ * Parse a line into name and value pairs
+ *
+ * @param line the line to be parsed
+ *
+ * @throws ManifestException if the line does not contain a colon
+ * separating the name and value
+ */
+ public void parse(String line) throws ManifestException {
+ int index = line.indexOf(": ");
+ if (index == -1) {
+ throw new ManifestException("Manifest line \"" + line
+ + "\" is not valid as it does not "
+ + "contain a name and a value separated by ': ' ");
+ }
+ name = line.substring(0, index);
+ setValue(line.substring(index + 2));
+ }
+
+ /**
+ * Set the Attribute's name; required
+ *
+ * @param name the attribute's name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the Attribute's name
+ *
+ * @return the attribute's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the attribute's Key - its name in lower case.
+ *
+ * @return the attribute's key.
+ */
+ public String getKey() {
+ if (name == null) {
+ return null;
+ }
+ return name.toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Set the Attribute's value; required
+ *
+ * @param value the attribute's value
+ */
+ public void setValue(String value) {
+ if (currentIndex >= values.size()) {
+ values.addElement(value);
+ currentIndex = values.size() - 1;
+ } else {
+ values.setElementAt(value, currentIndex);
+ }
+ }
+
+ /**
+ * Get the Attribute's value.
+ *
+ * @return the attribute's value.
+ */
+ public String getValue() {
+ if (values.size() == 0) {
+ return null;
+ }
+
+ String fullValue = "";
+ for (Enumeration<String> e = getValues(); e.hasMoreElements();) {
+ String value = e.nextElement();
+ fullValue += value + " ";
+ }
+ return fullValue.trim();
+ }
+
+ /**
+ * Add a new value to this attribute - making it multivalued.
+ *
+ * @param value the attribute's additional value
+ */
+ public void addValue(String value) {
+ currentIndex++;
+ setValue(value);
+ }
+
+ /**
+ * Get all the attribute's values.
+ *
+ * @return an enumeration of the attributes values
+ */
+ public Enumeration<String> getValues() {
+ return values.elements();
+ }
+
+ /**
+ * Add a continuation line from the Manifest file.
+ *
+ * When lines are too long in a manifest, they are continued on the
+ * next line by starting with a space. This method adds the continuation
+ * data to the attribute value by skipping the first character.
+ *
+ * @param line the continuation line.
+ */
+ public void addContinuation(String line) {
+ String currentValue = values.elementAt(currentIndex);
+ setValue(currentValue + line.substring(1));
+ }
+
+ /**
+ * Write the attribute out to a print writer without
+ * flattening multi-values attributes (i.e. Class-Path).
+ *
+ * @param writer the Writer to which the attribute is written
+ *
+ * @throws IOException if the attribute value cannot be written
+ */
+ public void write(PrintWriter writer) throws IOException {
+ write(writer, false);
+ }
+
+ /**
+ * Write the attribute out to a print writer.
+ *
+ * @param writer the Writer to which the attribute is written
+ * @param flatten whether to collapse multi-valued attributes
+ * (i.e. potentially Class-Path) Class-Path into a
+ * single attribute.
+ *
+ * @throws IOException if the attribute value cannot be written
+ * @since Ant 1.8.0
+ */
+ public void write(PrintWriter writer, boolean flatten)
+ throws IOException {
+ if (!flatten) {
+ for (Enumeration<String> e = getValues(); e.hasMoreElements();) {
+ writeValue(writer, e.nextElement());
+ }
+ } else {
+ writeValue(writer, getValue());
+ }
+ }
+
+ /**
+ * Write a single attribute value out
+ *
+ * @param writer the Writer to which the attribute is written
+ * @param value the attribute value
+ *
+ * @throws IOException if the attribute value cannot be written
+ */
+ private void writeValue(PrintWriter writer, String value)
+ throws IOException {
+ String line = null;
+ int nameLength = name.getBytes(JAR_ENCODING).length;
+ if (nameLength > MAX_NAME_VALUE_LENGTH) {
+ if (nameLength > MAX_NAME_LENGTH) {
+ throw new IOException("Unable to write manifest line "
+ + name + ": " + value);
+ }
+ writer.print(name + ": " + EOL);
+ line = " " + value;
+ } else {
+ line = name + ": " + value;
+ }
+ while (line.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH) {
+ // try to find a MAX_LINE_LENGTH byte section
+ int breakIndex = MAX_SECTION_LENGTH;
+ if (breakIndex >= line.length()) {
+ breakIndex = line.length() - 1;
+ }
+ String section = line.substring(0, breakIndex);
+ while (section.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH
+ && breakIndex > 0) {
+ breakIndex--;
+ section = line.substring(0, breakIndex);
+ }
+ if (breakIndex == 0) {
+ throw new IOException("Unable to write manifest line "
+ + name + ": " + value);
+ }
+ writer.print(section + EOL);
+ line = " " + line.substring(breakIndex);
+ }
+ writer.print(line + EOL);
+ }
+ }
+
+ /**
+ * A manifest section - you can nest attribute elements into sections.
+ * A section consists of a set of attribute values,
+ * separated from other sections by a blank line.
+ */
+ public static class Section {
+ /** Warnings for this section */
+ private Vector<String> warnings = new Vector<String>();
+
+ /**
+ * The section's name if any. The main section in a
+ * manifest is unnamed.
+ */
+ private String name = null;
+
+ /** The section's attributes.*/
+ private Map<String, Attribute> attributes = new LinkedHashMap<String, Attribute>();
+
+ /**
+ * The name of the section; optional -default is the main section.
+ * @param name the section's name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the Section's name.
+ *
+ * @return the section's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Read a section through a reader.
+ *
+ * @param reader the reader from which the section is read
+ *
+ * @return the name of the next section if it has been read as
+ * part of this section - This only happens if the
+ * Manifest is malformed.
+ *
+ * @throws ManifestException if the section is not valid according
+ * to the JAR spec
+ * @throws IOException if the section cannot be read from the reader.
+ */
+ public String read(BufferedReader reader)
+ throws ManifestException, IOException {
+ Attribute attribute = null;
+ while (true) {
+ String line = reader.readLine();
+ if (line == null || line.length() == 0) {
+ return null;
+ }
+ if (line.charAt(0) == ' ') {
+ // continuation line
+ if (attribute == null) {
+ if (name != null) {
+ // a continuation on the first line is a
+ // continuation of the name - concatenate this
+ // line and the name
+ name += line.substring(1);
+ } else {
+ throw new ManifestException("Can't start an "
+ + "attribute with a continuation line " + line);
+ }
+ } else {
+ attribute.addContinuation(line);
+ }
+ } else {
+ attribute = new Attribute(line);
+ String nameReadAhead = addAttributeAndCheck(attribute);
+ // refresh attribute in case of multivalued attributes.
+ attribute = getAttribute(attribute.getKey());
+ if (nameReadAhead != null) {
+ return nameReadAhead;
+ }
+ }
+ }
+ }
+
+ /**
+ * Merge in another section without merging Class-Path attributes.
+ *
+ * @param section the section to be merged with this one.
+ *
+ * @throws ManifestException if the sections cannot be merged.
+ */
+ public void merge(Section section) throws ManifestException {
+ merge(section, false);
+ }
+
+ /**
+ * Merge in another section
+ *
+ * @param section the section to be merged with this one.
+ * @param mergeClassPaths whether Class-Path attributes should
+ * be merged.
+ *
+ * @throws ManifestException if the sections cannot be merged.
+ */
+ public void merge(Section section, boolean mergeClassPaths)
+ throws ManifestException {
+ if (name == null && section.getName() != null
+ || (name != null && section.getName() != null
+ && !(name.toLowerCase(Locale.ENGLISH)
+ .equals(section.getName().toLowerCase(Locale.ENGLISH))))
+ ) {
+ throw new ManifestException("Unable to merge sections "
+ + "with different names");
+ }
+
+ Enumeration<String> e = section.getAttributeKeys();
+ Attribute classpathAttribute = null;
+ while (e.hasMoreElements()) {
+ String attributeName = e.nextElement();
+ Attribute attribute = section.getAttribute(attributeName);
+ if (attributeName.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
+ if (classpathAttribute == null) {
+ classpathAttribute = new Attribute();
+ classpathAttribute.setName(ATTRIBUTE_CLASSPATH);
+ }
+ Enumeration<String> cpe = attribute.getValues();
+ while (cpe.hasMoreElements()) {
+ String value = cpe.nextElement();
+ classpathAttribute.addValue(value);
+ }
+ } else {
+ // the merge file always wins
+ storeAttribute(attribute);
+ }
+ }
+
+ if (classpathAttribute != null) {
+ if (mergeClassPaths) {
+ Attribute currentCp = getAttribute(ATTRIBUTE_CLASSPATH);
+ if (currentCp != null) {
+ for (Enumeration<String> attribEnum = currentCp.getValues();
+ attribEnum.hasMoreElements();) {
+ String value = attribEnum.nextElement();
+ classpathAttribute.addValue(value);
+ }
+ }
+ }
+ storeAttribute(classpathAttribute);
+ }
+
+ // add in the warnings
+ Enumeration<String> warnEnum = section.warnings.elements();
+ while (warnEnum.hasMoreElements()) {
+ warnings.addElement(warnEnum.nextElement());
+ }
+ }
+
+ /**
+ * Write the section out to a print writer without flattening
+ * multi-values attributes (i.e. Class-Path).
+ *
+ * @param writer the Writer to which the section is written
+ *
+ * @throws IOException if the section cannot be written
+ */
+ public void write(PrintWriter writer) throws IOException {
+ write(writer, false);
+ }
+
+ /**
+ * Write the section out to a print writer.
+ *
+ * @param writer the Writer to which the section is written
+ * @param flatten whether to collapse multi-valued attributes
+ * (i.e. potentially Class-Path) Class-Path into a
+ * single attribute.
+ *
+ * @throws IOException if the section cannot be written
+ * @since Ant 1.8.0
+ */
+ public void write(PrintWriter writer, boolean flatten)
+ throws IOException {
+ if (name != null) {
+ Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
+ nameAttr.write(writer);
+ }
+ Enumeration<String> e = getAttributeKeys();
+ while (e.hasMoreElements()) {
+ String key = e.nextElement();
+ Attribute attribute = getAttribute(key);
+ attribute.write(writer, flatten);
+ }
+ writer.print(EOL);
+ }
+
+ /**
+ * Get a attribute of the section
+ *
+ * @param attributeName the name of the attribute
+ * @return a Manifest.Attribute instance if the attribute is
+ * single-valued, otherwise a Vector of Manifest.Attribute
+ * instances.
+ */
+ public Attribute getAttribute(String attributeName) {
+ return attributes.get(attributeName.toLowerCase(Locale.ENGLISH));
+ }
+
+ /**
+ * Get the attribute keys.
+ *
+ * @return an Enumeration of Strings, each string being the lower case
+ * key of an attribute of the section.
+ */
+ public Enumeration<String> getAttributeKeys() {
+ return CollectionUtils.asEnumeration(attributes.keySet().iterator());
+ }
+
+ /**
+ * Get the value of the attribute with the name given.
+ *
+ * @param attributeName the name of the attribute to be returned.
+ *
+ * @return the attribute's value or null if the attribute does not exist
+ * in the section
+ */
+ public String getAttributeValue(String attributeName) {
+ Attribute attribute = getAttribute(attributeName.toLowerCase(Locale.ENGLISH));
+ if (attribute == null) {
+ return null;
+ }
+ return attribute.getValue();
+ }
+
+ /**
+ * Remove the given attribute from the section
+ *
+ * @param attributeName the name of the attribute to be removed.
+ */
+ public void removeAttribute(String attributeName) {
+ String key = attributeName.toLowerCase(Locale.ENGLISH);
+ attributes.remove(key);
+ }
+
+ /**
+ * Add an attribute to the section.
+ *
+ * @param attribute the attribute to be added to the section
+ *
+ * @exception ManifestException if the attribute is not valid.
+ */
+ public void addConfiguredAttribute(Attribute attribute)
+ throws ManifestException {
+ String check = addAttributeAndCheck(attribute);
+ if (check != null) {
+ throw new BuildException("Specify the section name using "
+ + "the \"name\" attribute of the <section> element rather "
+ + "than using a \"Name\" manifest attribute");
+ }
+ }
+
+ /**
+ * Add an attribute to the section
+ *
+ * @param attribute the attribute to be added.
+ *
+ * @return the value of the attribute if it is a name
+ * attribute - null other wise
+ *
+ * @exception ManifestException if the attribute already
+ * exists in this section.
+ */
+ public String addAttributeAndCheck(Attribute attribute)
+ throws ManifestException {
+ if (attribute.getName() == null || attribute.getValue() == null) {
+ throw new BuildException("Attributes must have name and value");
+ }
+ String attributeKey = attribute.getKey();
+ if (attributeKey.equals(ATTRIBUTE_NAME_LC)) {
+ warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes "
+ + "should not occur in the main section and must be the "
+ + "first element in all other sections: \""
+ + attribute.getName() + ": " + attribute.getValue() + "\"");
+ return attribute.getValue();
+ }
+
+ if (attributeKey.startsWith(ATTRIBUTE_FROM_LC)) {
+ warnings.addElement(ERROR_FROM_FORBIDDEN
+ + attribute.getName() + ": " + attribute.getValue() + "\"");
+ } else {
+ // classpath attributes go into a vector
+ if (attributeKey.equals(ATTRIBUTE_CLASSPATH_LC)) {
+ Attribute classpathAttribute =
+ attributes.get(attributeKey);
+
+ if (classpathAttribute == null) {
+ storeAttribute(attribute);
+ } else {
+ warnings.addElement("Multiple Class-Path attributes "
+ + "are supported but violate the Jar "
+ + "specification and may not be correctly "
+ + "processed in all environments");
+ Enumeration<String> e = attribute.getValues();
+ while (e.hasMoreElements()) {
+ String value = e.nextElement();
+ classpathAttribute.addValue(value);
+ }
+ }
+ } else if (attributes.containsKey(attributeKey)) {
+ throw new ManifestException("The attribute \""
+ + attribute.getName() + "\" may not occur more "
+ + "than once in the same section");
+ } else {
+ storeAttribute(attribute);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Clone this section
+ *
+ * @return the cloned Section
+ * @since Ant 1.5.2
+ */
+ @Override
+ public Object clone() {
+ Section cloned = new Section();
+ cloned.setName(name);
+ Enumeration<String> e = getAttributeKeys();
+ while (e.hasMoreElements()) {
+ String key = e.nextElement();
+ Attribute attribute = getAttribute(key);
+ cloned.storeAttribute(new Attribute(attribute.getName(),
+ attribute.getValue()));
+ }
+ return cloned;
+ }
+
+ /**
+ * Store an attribute and update the index.
+ *
+ * @param attribute the attribute to be stored
+ */
+ private void storeAttribute(Attribute attribute) {
+ if (attribute == null) {
+ return;
+ }
+ String attributeKey = attribute.getKey();
+ attributes.put(attributeKey, attribute);
+ }
+
+ /**
+ * Get the warnings for this section.
+ *
+ * @return an Enumeration of warning strings.
+ */
+ public Enumeration<String> getWarnings() {
+ return warnings.elements();
+ }
+
+ /**
+ * @see java.lang.Object#hashCode
+ * @return a hash value based on the attributes.
+ */
+ @Override
+ public int hashCode() {
+ return attributes.hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#equals
+ * @param rhs the object to check for equality.
+ * @return true if the attributes are the same.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs == null || rhs.getClass() != getClass()) {
+ return false;
+ }
+
+ if (rhs == this) {
+ return true;
+ }
+
+ Section rhsSection = (Section) rhs;
+
+ return attributes.equals(rhsSection.attributes);
+ }
+ }
+
+
+ /** The version of this manifest */
+ private String manifestVersion = DEFAULT_MANIFEST_VERSION;
+
+ /** The main section of this manifest */
+ private Section mainSection = new Section();
+
+ /** The named sections of this manifest */
+ private Map<String, Section> sections = new LinkedHashMap<String, Section>();
+
+ /**
+ * Construct a manifest from Ant's default manifest file.
+ *
+ * @return the default manifest.
+ * @exception BuildException if there is a problem loading the
+ * default manifest
+ */
+ public static Manifest getDefaultManifest() throws BuildException {
+ InputStream in = null;
+ InputStreamReader insr = null;
+ try {
+ String defManifest = "/org/apache/tools/ant/defaultManifest.mf";
+ in = Manifest.class.getResourceAsStream(defManifest);
+ if (in == null) {
+ throw new BuildException("Could not find default manifest: "
+ + defManifest);
+ }
+ try {
+ insr = new InputStreamReader(in, "UTF-8");
+ Manifest defaultManifest = new Manifest(insr);
+ String version = System.getProperty("java.runtime.version");
+ if (version == null) {
+ version = System.getProperty("java.vm.version");
+ }
+ Attribute createdBy = new Attribute("Created-By",
+ version + " ("
+ + System.getProperty("java.vm.vendor") + ")");
+ defaultManifest.getMainSection().storeAttribute(createdBy);
+ return defaultManifest;
+ } catch (UnsupportedEncodingException e) {
+ insr = new InputStreamReader(in);
+ return new Manifest(insr);
+ }
+ } catch (ManifestException e) {
+ throw new BuildException("Default manifest is invalid !!", e);
+ } catch (IOException e) {
+ throw new BuildException("Unable to read default manifest", e);
+ } finally {
+ FileUtils.close(insr);
+ FileUtils.close(in);
+ }
+ }
+
+ /** Construct an empty manifest */
+ public Manifest() {
+ manifestVersion = null;
+ }
+
+ /**
+ * Read a manifest file from the given reader
+ *
+ * @param r is the reader from which the Manifest is read
+ *
+ * @throws ManifestException if the manifest is not valid according
+ * to the JAR spec
+ * @throws IOException if the manifest cannot be read from the reader.
+ */
+ public Manifest(Reader r) throws ManifestException, IOException {
+ BufferedReader reader = new BufferedReader(r);
+ // This should be the manifest version
+ String nextSectionName = mainSection.read(reader);
+ String readManifestVersion
+ = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
+ if (readManifestVersion != null) {
+ manifestVersion = readManifestVersion;
+ mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
+ }
+
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ if (line.length() == 0) {
+ continue;
+ }
+
+ Section section = new Section();
+ if (nextSectionName == null) {
+ Attribute sectionName = new Attribute(line);
+ if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
+ throw new ManifestException("Manifest sections should "
+ + "start with a \"" + ATTRIBUTE_NAME
+ + "\" attribute and not \""
+ + sectionName.getName() + "\"");
+ }
+ nextSectionName = sectionName.getValue();
+ } else {
+ // we have already started reading this section
+ // this line is the first attribute. set it and then
+ // let the normal read handle the rest
+ Attribute firstAttribute = new Attribute(line);
+ section.addAttributeAndCheck(firstAttribute);
+ }
+
+ section.setName(nextSectionName);
+ nextSectionName = section.read(reader);
+ addConfiguredSection(section);
+ }
+ }
+
+ /**
+ * Add a section to the manifest
+ *
+ * @param section the manifest section to be added
+ *
+ * @exception ManifestException if the secti0on is not valid.
+ */
+ public void addConfiguredSection(Section section)
+ throws ManifestException {
+ String sectionName = section.getName();
+ if (sectionName == null) {
+ throw new BuildException("Sections must have a name");
+ }
+ sections.put(sectionName, section);
+ }
+
+ /**
+ * Add an attribute to the manifest - it is added to the main section.
+ *
+ * @param attribute the attribute to be added.
+ *
+ * @exception ManifestException if the attribute is not valid.
+ */
+ public void addConfiguredAttribute(Attribute attribute)
+ throws ManifestException {
+ if (attribute.getKey() == null || attribute.getValue() == null) {
+ throw new BuildException("Attributes must have name and value");
+ }
+ if (attribute.getKey().equals(ATTRIBUTE_MANIFEST_VERSION_LC)) {
+ manifestVersion = attribute.getValue();
+ } else {
+ mainSection.addConfiguredAttribute(attribute);
+ }
+ }
+
+ /**
+ * Merge the contents of the given manifest into this manifest
+ * without merging Class-Path attributes.
+ *
+ * @param other the Manifest to be merged with this one.
+ *
+ * @throws ManifestException if there is a problem merging the
+ * manifest according to the Manifest spec.
+ */
+ public void merge(Manifest other) throws ManifestException {
+ merge(other, false);
+ }
+
+ /**
+ * Merge the contents of the given manifest into this manifest
+ * without merging Class-Path attributes.
+ *
+ * @param other the Manifest to be merged with this one.
+ * @param overwriteMain whether to overwrite the main section
+ * of the current manifest
+ *
+ * @throws ManifestException if there is a problem merging the
+ * manifest according to the Manifest spec.
+ */
+ public void merge(Manifest other, boolean overwriteMain)
+ throws ManifestException {
+ merge(other, overwriteMain, false);
+ }
+
+ /**
+ * Merge the contents of the given manifest into this manifest
+ *
+ * @param other the Manifest to be merged with this one.
+ * @param overwriteMain whether to overwrite the main section
+ * of the current manifest
+ * @param mergeClassPaths whether Class-Path attributes should be
+ * merged.
+ *
+ * @throws ManifestException if there is a problem merging the
+ * manifest according to the Manifest spec.
+ *
+ * @since Ant 1.8.0
+ */
+ public void merge(Manifest other, boolean overwriteMain,
+ boolean mergeClassPaths)
+ throws ManifestException {
+ if (other != null) {
+ if (overwriteMain) {
+ mainSection = (Section) other.mainSection.clone();
+ } else {
+ mainSection.merge(other.mainSection, mergeClassPaths);
+ }
+
+ if (other.manifestVersion != null) {
+ manifestVersion = other.manifestVersion;
+ }
+
+ Enumeration<String> e = other.getSectionNames();
+ while (e.hasMoreElements()) {
+ String sectionName = e.nextElement();
+ Section ourSection = sections.get(sectionName);
+ Section otherSection
+ = other.sections.get(sectionName);
+ if (ourSection == null) {
+ if (otherSection != null) {
+ addConfiguredSection((Section) otherSection.clone());
+ }
+ } else {
+ ourSection.merge(otherSection, mergeClassPaths);
+ }
+ }
+ }
+ }
+
+ /**
+ * Write the manifest out to a print writer without flattening
+ * multi-values attributes (i.e. Class-Path).
+ *
+ * @param writer the Writer to which the manifest is written
+ *
+ * @throws IOException if the manifest cannot be written
+ */
+ public void write(PrintWriter writer) throws IOException {
+ write(writer, false);
+ }
+
+ /**
+ * Write the manifest out to a print writer.
+ *
+ * @param writer the Writer to which the manifest is written
+ * @param flatten whether to collapse multi-valued attributes
+ * (i.e. potentially Class-Path) Class-Path into a single
+ * attribute.
+ *
+ * @throws IOException if the manifest cannot be written
+ * @since Ant 1.8.0
+ */
+ public void write(PrintWriter writer, boolean flatten) throws IOException {
+ writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL);
+ String signatureVersion
+ = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
+ if (signatureVersion != null) {
+ writer.print(ATTRIBUTE_SIGNATURE_VERSION + ": "
+ + signatureVersion + EOL);
+ mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
+ }
+ mainSection.write(writer, flatten);
+
+ // add it back
+ if (signatureVersion != null) {
+ try {
+ Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION,
+ signatureVersion);
+ mainSection.addConfiguredAttribute(svAttr);
+ } catch (ManifestException e) {
+ // shouldn't happen - ignore
+ }
+ }
+
+ for (String sectionName : sections.keySet()) {
+ Section section = getSection(sectionName);
+ section.write(writer, flatten);
+ }
+ }
+
+ /**
+ * Convert the manifest to its string representation
+ *
+ * @return a multiline string with the Manifest as it
+ * appears in a Manifest file.
+ */
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ try {
+ write(new PrintWriter(sw));
+ } catch (IOException e) {
+ return null;
+ }
+ return sw.toString();
+ }
+
+ /**
+ * Get the warnings for this manifest.
+ *
+ * @return an enumeration of warning strings
+ */
+ public Enumeration<String> getWarnings() {
+ Vector<String> warnings = new Vector<String>();
+
+ Enumeration<String> warnEnum = mainSection.getWarnings();
+ while (warnEnum.hasMoreElements()) {
+ warnings.addElement(warnEnum.nextElement());
+ }
+
+ // create a vector and add in the warnings for all the sections
+ for (Section section : sections.values()) {
+ Enumeration<String> e2 = section.getWarnings();
+ while (e2.hasMoreElements()) {
+ warnings.addElement(e2.nextElement());
+ }
+ }
+
+ return warnings.elements();
+ }
+
+ /**
+ * @see java.lang.Object#hashCode
+ * @return a hashcode based on the version, main and sections.
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = 0;
+
+ if (manifestVersion != null) {
+ hashCode += manifestVersion.hashCode();
+ }
+ hashCode += mainSection.hashCode();
+ hashCode += sections.hashCode();
+
+ return hashCode;
+ }
+
+ /**
+ * @see java.lang.Object#equals
+ * @param rhs the object to check for equality.
+ * @return true if the version, main and sections are the same.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs == null || rhs.getClass() != getClass()) {
+ return false;
+ }
+
+ if (rhs == this) {
+ return true;
+ }
+
+ Manifest rhsManifest = (Manifest) rhs;
+ if (manifestVersion == null) {
+ if (rhsManifest.manifestVersion != null) {
+ return false;
+ }
+ } else if (!manifestVersion.equals(rhsManifest.manifestVersion)) {
+ return false;
+ }
+
+ if (!mainSection.equals(rhsManifest.mainSection)) {
+ return false;
+ }
+
+ return sections.equals(rhsManifest.sections);
+ }
+
+ /**
+ * Get the version of the manifest
+ *
+ * @return the manifest's version string
+ */
+ public String getManifestVersion() {
+ return manifestVersion;
+ }
+
+ /**
+ * Get the main section of the manifest
+ *
+ * @return the main section of the manifest
+ */
+ public Section getMainSection() {
+ return mainSection;
+ }
+
+ /**
+ * Get a particular section from the manifest
+ *
+ * @param name the name of the section desired.
+ * @return the specified section or null if that section
+ * does not exist in the manifest
+ */
+ public Section getSection(String name) {
+ return sections.get(name);
+ }
+
+ /**
+ * Get the section names in this manifest.
+ *
+ * @return an Enumeration of section names
+ */
+ public Enumeration<String> getSectionNames() {
+ return CollectionUtils.asEnumeration(sections.keySet().iterator());
+ }
+}