Browse Source

Retain order of sections and attributes in manifests.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271919 13f79535-47bb-0310-9956-ffa450edef68
master
Conor MacNeill 23 years ago
parent
commit
eca5133693
3 changed files with 437 additions and 165 deletions
  1. +36
    -0
      src/etc/testcases/taskdefs/manifest.xml
  2. +354
    -163
      src/main/org/apache/tools/ant/taskdefs/Manifest.java
  3. +47
    -2
      src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java

+ 36
- 0
src/etc/testcases/taskdefs/manifest.xml View File

@@ -128,6 +128,42 @@
</unjar> </unjar>
</target> </target>


<target name="testOrder1">
<jar file="mftestOrder1.jar">
<manifest>
<section name="Test1">
<attribute name="TestAttr1" value="Test1"/>
<attribute name="TestAttr2" value="Test2"/>
</section>
<section name="Test2">
<attribute name="TestAttrx" value="Testx"/>
</section>
</manifest>
</jar>
<unjar src="mftestOrder1.jar" dest="manifests">
<include name="META-INF/MANIFEST.MF"/>
</unjar>
</target>

<target name="testOrder2">
<jar file="mftestOrder2.jar">
<manifest>
<section name="Test2">
<attribute name="TestAttrx" value="Testx"/>
</section>
<section name="Test1">
<attribute name="TestAttr2" value="Test2"/>
<attribute name="TestAttr1" value="Test1"/>
</section>
</manifest>
</jar>
<unjar src="mftestOrder2.jar" dest="manifests">
<include name="META-INF/MANIFEST.MF"/>
</unjar>
</target>

<target name="testReplace"> <target name="testReplace">
<copy file="manifests/test2.mf" toFile="mftest.mf" /> <copy file="manifests/test2.mf" toFile="mftest.mf" />
<manifest file="mftest.mf" /> <manifest file="mftest.mf" />


+ 354
- 163
src/main/org/apache/tools/ant/taskdefs/Manifest.java View File

@@ -83,30 +83,37 @@ import org.apache.tools.ant.types.EnumeratedAttribute;
*/ */
public class Manifest extends Task { public class Manifest extends Task {
/** The standard manifest version header */ /** The standard manifest version header */
public final static String ATTRIBUTE_MANIFEST_VERSION = "Manifest-Version";
public static final String ATTRIBUTE_MANIFEST_VERSION
= "Manifest-Version";


/** The standard Signature Version header */ /** The standard Signature Version header */
public final static String ATTRIBUTE_SIGNATURE_VERSION = "Signature-Version";
public static final String ATTRIBUTE_SIGNATURE_VERSION
= "Signature-Version";


/** The Name Attribute is the first in a named section */ /** The Name Attribute is the first in a named section */
public final static String ATTRIBUTE_NAME = "Name";
public static final String ATTRIBUTE_NAME = "Name";


/** The From Header is disallowed in a Manifest */ /** The From Header is disallowed in a Manifest */
public final static String ATTRIBUTE_FROM = "From";
public static final String ATTRIBUTE_FROM = "From";


/** The Class-Path Header is special - it can be duplicated */ /** The Class-Path Header is special - it can be duplicated */
public final static String ATTRIBUTE_CLASSPATH = "class-path";
public static final String ATTRIBUTE_CLASSPATH = "class-path";


/** Default Manifest version if one is not specified */ /** Default Manifest version if one is not specified */
public final static String DEFAULT_MANIFEST_VERSION = "1.0";
public static final String DEFAULT_MANIFEST_VERSION = "1.0";


/** The max length of a line in a Manifest */ /** The max length of a line in a Manifest */
public final static int MAX_LINE_LENGTH = 72;
public static final int MAX_LINE_LENGTH = 72;


/** /**
* Helper class for Manifest's mode attribute. * Helper class for Manifest's mode attribute.
*/ */
public static class Mode extends EnumeratedAttribute { public static class Mode extends EnumeratedAttribute {
/**
* Get Allowed values for the mode attribute.
*
* @return a String array of the allowed values.
*/
public String[] getValues() { public String[] getValues() {
return new String[] {"update", "replace"}; return new String[] {"update", "replace"};
} }
@@ -120,8 +127,14 @@ public class Manifest extends Task {
private String name = null; private String name = null;


/** The attribute's value */ /** The attribute's value */
private String value = null;
private Vector values = new Vector();


/**
* For multivalued attributes, this is the index of the attribute
* currently being defined.
*/
private int currentIndex = 0;
/** /**
* Construct an empty attribute */ * Construct an empty attribute */
public Attribute() { public Attribute() {
@@ -146,9 +159,12 @@ public class Manifest extends Task {
*/ */
public Attribute(String name, String value) { public Attribute(String name, String value) {
this.name = name; this.name = name;
this.value = value;
setValue(value);
} }


/**
* @see java.lang.Object#equals
*/
public boolean equals(Object rhs) { public boolean equals(Object rhs) {
if (!(rhs instanceof Attribute)) { if (!(rhs instanceof Attribute)) {
return false; return false;
@@ -156,8 +172,8 @@ public class Manifest extends Task {


Attribute rhsAttribute = (Attribute)rhs; Attribute rhsAttribute = (Attribute)rhs;
return (name != null && rhsAttribute.name != null && return (name != null && rhsAttribute.name != null &&
name.toLowerCase().equals(rhsAttribute.name.toLowerCase()) &&
value != null && value.equals(rhsAttribute.value));
getKey().equals(rhsAttribute.getKey()) &&
values != null && values.equals(rhsAttribute.values));
} }


/** /**
@@ -171,11 +187,12 @@ public class Manifest extends Task {
public void parse(String line) throws ManifestException { public void parse(String line) throws ManifestException {
int index = line.indexOf(": "); int index = line.indexOf(": ");
if (index == -1) { if (index == -1) {
throw new ManifestException("Manifest line \"" + line + "\" is not valid as it does not " +
"contain a name and a value separated by ': ' ");
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); name = line.substring(0, index);
value = line.substring(index + 2);
setValue(line.substring(index + 2));
} }


/** /**
@@ -196,13 +213,30 @@ public class Manifest extends Task {
return name; 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();
}
/** /**
* Set the Attribute's value * Set the Attribute's value
* *
* @param value the attribute's value * @param value the attribute's value
*/ */
public void setValue(String value) { public void setValue(String value) {
this.value = value;
if (currentIndex >= values.size()) {
values.addElement(value);
currentIndex = values.size() - 1;
} else {
values.setElementAt(value, currentIndex);
}
} }


/** /**
@@ -211,18 +245,49 @@ public class Manifest extends Task {
* @return the attribute's value. * @return the attribute's value.
*/ */
public String getValue() { public String getValue() {
return value;
if (values.size() == 0) {
return null;
}
String fullValue = "";
for (Enumeration e = getValues(); e.hasMoreElements();) {
String value = (String)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 getValues() {
return values.elements();
}
/** /**
* Add a continuation line from the Manifest file * Add a continuation line from the Manifest file
* *
* When lines are too long in a manifest, they are continued on the * 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 * next line by starting with a space. This method adds the continuation
* data to the attribute value by skipping the first character. * data to the attribute value by skipping the first character.
*
* @param line the continuation line.
*/ */
public void addContinuation(String line) { public void addContinuation(String line) {
value += line.substring(1);
String currentValue = (String)values.elementAt(currentIndex);
setValue(currentValue + line.substring(1));
} }


/** /**
@@ -233,17 +298,34 @@ public class Manifest extends Task {
* @throws IOException if the attribte value cannot be written * @throws IOException if the attribte value cannot be written
*/ */
public void write(PrintWriter writer) throws IOException { public void write(PrintWriter writer) throws IOException {
for (Enumeration e = getValues(); e.hasMoreElements();) {
writeValue(writer, (String)e.nextElement());
}
}
/**
* 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 attribte value cannot be written
*/
private void writeValue(PrintWriter writer, String value)
throws IOException {
String line = name + ": " + value; String line = name + ": " + value;
while (line.getBytes().length > MAX_LINE_LENGTH) { while (line.getBytes().length > MAX_LINE_LENGTH) {
// try to find a MAX_LINE_LENGTH byte section // try to find a MAX_LINE_LENGTH byte section
int breakIndex = MAX_LINE_LENGTH; int breakIndex = MAX_LINE_LENGTH;
String section = line.substring(0, breakIndex); String section = line.substring(0, breakIndex);
while (section.getBytes().length > MAX_LINE_LENGTH && breakIndex > 0) {
while (section.getBytes().length > MAX_LINE_LENGTH
&& breakIndex > 0) {
breakIndex--; breakIndex--;
section = line.substring(0, breakIndex); section = line.substring(0, breakIndex);
} }
if (breakIndex == 0) { if (breakIndex == 0) {
throw new IOException("Unable to write manifest line " + name + ": " + value);
throw new IOException("Unable to write manifest line "
+ name + ": " + value);
} }
writer.println(section); writer.println(section);
line = " " + line.substring(breakIndex); line = " " + line.substring(breakIndex);
@@ -251,21 +333,28 @@ public class Manifest extends Task {
writer.println(line); writer.println(line);
} }
} }
/** /**
* Class to represent an individual section in the * Class to represent an individual section in the
* Manifest. A section consists of a set of attribute values, * Manifest. A section consists of a set of attribute values,
* separated from other sections by a blank line. * separated from other sections by a blank line.
*/ */
public static class Section { public static class Section {
/** Warnings for this section */
private Vector warnings = new Vector(); private Vector warnings = new Vector();


/** The section's name if any. The main section in a manifest is unnamed.*/
/**
* The section's name if any. The main section in a
* manifest is unnamed.
*/
private String name = null; private String name = null;


/** The section's attributes.*/ /** The section's attributes.*/
private Hashtable attributes = new Hashtable(); private Hashtable attributes = new Hashtable();


/** Index used to retain the attribute ordering */
private Vector attributeIndex = new Vector();
/** /**
* Set the Section's name * Set the Section's name
* *
@@ -289,13 +378,16 @@ public class Manifest extends Task {
* *
* @param reader the reader from which the section is read * @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.
* @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 ManifestException if the section is not valid according
* to the JAR spec
* @throws IOException if the section cannot be read from the reader. * @throws IOException if the section cannot be read from the reader.
*/ */
public String read(BufferedReader reader) throws ManifestException, IOException {
public String read(BufferedReader reader)
throws ManifestException, IOException {
Attribute attribute = null; Attribute attribute = null;
while (true) { while (true) {
String line = reader.readLine(); String line = reader.readLine();
@@ -306,19 +398,18 @@ public class Manifest extends Task {
// continuation line // continuation line
if (attribute == null) { if (attribute == null) {
if (name != null) { if (name != null) {
// a continuation on the first line is a continuation of the name - concatenate
// this line and the name
// a continuation on the first line is a
// continuation of the name - concatenate this
// line and the name
name += line.substring(1); name += line.substring(1);
} else {
throw new ManifestException("Can't start an "
+ "attribute with a continuation line " + line);
} }
else {
throw new ManifestException("Can't start an attribute with a continuation line " + line);
}
}
else {
} else {
attribute.addContinuation(line); attribute.addContinuation(line);
} }
}
else {
} else {
attribute = new Attribute(line); attribute = new Attribute(line);
String nameReadAhead = addAttributeAndCheck(attribute); String nameReadAhead = addAttributeAndCheck(attribute);
if (nameReadAhead != null) { if (nameReadAhead != null) {
@@ -336,31 +427,35 @@ public class Manifest extends Task {
* @throws ManifestException if the sections cannot be merged. * @throws ManifestException if the sections cannot be merged.
*/ */
public void merge(Section section) throws ManifestException { public void merge(Section section) throws ManifestException {
if (name == null && section.getName() != null ||
name != null && !(name.equalsIgnoreCase(section.getName()))) {
throw new ManifestException("Unable to merge sections with different names");
if (name == null && section.getName() != null
|| name != null
&& !(name.equalsIgnoreCase(section.getName()))) {
throw new ManifestException("Unable to merge sections "
+ "with different names");
} }


for (Enumeration e = section.attributes.keys(); e.hasMoreElements();) {
Enumeration e = section.getAttributeKeys();
while (e.hasMoreElements()) {
String attributeName = (String)e.nextElement(); String attributeName = (String)e.nextElement();
Attribute attribute = section.getAttribute(attributeName);
if (attributeName.equals(ATTRIBUTE_CLASSPATH) && if (attributeName.equals(ATTRIBUTE_CLASSPATH) &&
attributes.containsKey(attributeName)) { attributes.containsKey(attributeName)) {
// classpath entries are vetors which are merged
Vector classpathAttrs = (Vector)section.attributes.get(attributeName);
Vector ourClasspathAttrs = (Vector)attributes.get(attributeName);
for (Enumeration e2 = classpathAttrs.elements(); e2.hasMoreElements();) {
ourClasspathAttrs.addElement(e2.nextElement());
Attribute ourClassPath = getAttribute(attributeName);
Enumeration cpe = attribute.getValues();
while (cpe.hasMoreElements()) {
String value = (String)cpe.nextElement();
ourClassPath.addValue(value);
} }
}
else {
} else {
// the merge file always wins // the merge file always wins
attributes.put(attributeName, section.attributes.get(attributeName));
storeAttribute(attribute);
} }
} }


// add in the warnings // add in the warnings
for (Enumeration e = section.warnings.elements(); e.hasMoreElements();) {
warnings.addElement(e.nextElement());
Enumeration warnEnum = section.warnings.elements();
while (warnEnum.hasMoreElements()) {
warnings.addElement(warnEnum.nextElement());
} }
} }


@@ -376,19 +471,11 @@ public class Manifest extends Task {
Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name); Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
nameAttr.write(writer); nameAttr.write(writer);
} }
for (Enumeration e = attributes.elements(); e.hasMoreElements();) {
Object object = e.nextElement();
if (object instanceof Attribute) {
Attribute attribute = (Attribute)object;
attribute.write(writer);
}
else {
Vector attrList = (Vector)object;
for (Enumeration e2 = attrList.elements(); e2.hasMoreElements();) {
Attribute attribute = (Attribute)e2.nextElement();
attribute.write(writer);
}
}
Enumeration e = getAttributeKeys();
while (e.hasMoreElements()) {
String key = (String)e.nextElement();
Attribute attribute = getAttribute(key);
attribute.write(writer);
} }
writer.println(); writer.println();
} }
@@ -401,10 +488,20 @@ public class Manifest extends Task {
* single-valued, otherwise a Vector of Manifest.Attribute * single-valued, otherwise a Vector of Manifest.Attribute
* instances. * instances.
*/ */
public Object getAttribute(String attributeName) {
return attributes.get(attributeName.toLowerCase());
public Attribute getAttribute(String attributeName) {
return (Attribute)attributes.get(attributeName.toLowerCase());
} }


/**
* Get the attribute keys.
*
* @return an Enumeration of Strings, each string being the lower case
* key of an attribute of the section.
*/
public Enumeration getAttributeKeys() {
return attributeIndex.elements();
}
/** /**
* Get the value of the attribute with the name given. * Get the value of the attribute with the name given.
* *
@@ -414,21 +511,11 @@ public class Manifest extends Task {
* in the section * in the section
*/ */
public String getAttributeValue(String attributeName) { public String getAttributeValue(String attributeName) {
Object attribute = attributes.get(attributeName.toLowerCase());
Attribute attribute = getAttribute(attributeName.toLowerCase());
if (attribute == null) { if (attribute == null) {
return null; return null;
} }
if (attribute instanceof Attribute) {
return ((Attribute)attribute).getValue();
}
else {
String value = "";
for (Enumeration e = ((Vector)attribute).elements(); e.hasMoreElements();) {
Attribute classpathAttribute = (Attribute)e.nextElement();
value += classpathAttribute.getValue() + " ";
}
return value.trim();
}
return attribute.getValue();
} }


/** /**
@@ -437,14 +524,25 @@ public class Manifest extends Task {
* @param attributeName the name of the attribute to be removed. * @param attributeName the name of the attribute to be removed.
*/ */
public void removeAttribute(String attributeName) { public void removeAttribute(String attributeName) {
attributes.remove(attributeName.toLowerCase());
String key = attributeName.toLowerCase();
attributes.remove(key);
attributeIndex.removeElement(key);
} }


public void addConfiguredAttribute(Attribute attribute) throws ManifestException {
/**
* 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); String check = addAttributeAndCheck(attribute);
if (check != null) { 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");
throw new BuildException("Specify the section name using "
+ "the \"name\" attribute of the <section> element rather "
+ "than using a \"Name\" manifest attribute");
} }
} }


@@ -453,51 +551,84 @@ public class Manifest extends Task {
* *
* @param attribute the attribute to be added. * @param attribute the attribute to be added.
* *
* @return the value of the attribute if it is a name attribute - null other wise
* @return the value of the attribute if it is a name
* attribute - null other wise
* *
* @throws ManifestException if the attribute already exists in this section.
* @exception ManifestException if the attribute already
* exists in this section.
*/ */
public String addAttributeAndCheck(Attribute attribute) throws ManifestException {
public String addAttributeAndCheck(Attribute attribute)
throws ManifestException {
if (attribute.getName() == null || attribute.getValue() == null) { if (attribute.getName() == null || attribute.getValue() == null) {
throw new BuildException("Attributes must have name and value"); throw new BuildException("Attributes must have name and value");
} }
if (attribute.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
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() + "\"");
if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) {
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(); return attribute.getValue();
} }


if (attribute.getName().toLowerCase().startsWith(ATTRIBUTE_FROM.toLowerCase())) {
warnings.addElement("Manifest attributes should not start with \"" +
ATTRIBUTE_FROM + "\" in \"" +attribute.getName() + ": " + attribute.getValue() + "\"");
}
else {
if (attribute.getKey().startsWith(ATTRIBUTE_FROM.toLowerCase())) {
warnings.addElement("Manifest attributes should not start "
+ "with \"" + ATTRIBUTE_FROM + "\" in \""
+ attribute.getName() + ": " + attribute.getValue() + "\"");
} else {
// classpath attributes go into a vector // classpath attributes go into a vector
String attributeName = attribute.getName().toLowerCase();
if (attributeName.equals(ATTRIBUTE_CLASSPATH)) {
Vector classpathAttrs = (Vector)attributes.get(attributeName);
if (classpathAttrs == null) {
classpathAttrs = new Vector();
attributes.put(attributeName, classpathAttrs);
String attributeKey = attribute.getKey();
if (attributeKey.equals(ATTRIBUTE_CLASSPATH)) {
Attribute classpathAttribute =
(Attribute)attributes.get(attributeKey);
if (classpathAttribute == null) {
storeAttribute(attribute);
} else {
Enumeration e = attribute.getValues();
while (e.hasMoreElements()) {
String value = (String)e.nextElement();
classpathAttribute.addValue(value);
}
} }
classpathAttrs.addElement(attribute);
}
else if (attributes.containsKey(attributeName)) {
throw new ManifestException("The attribute \"" + attribute.getName() + "\" may not " +
"occur more than once in the same section");
}
else {
attributes.put(attributeName, attribute);
} 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; return null;
} }
/**
* 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);
if (!attributeIndex.contains(attributeKey)) {
attributeIndex.addElement(attributeKey);
}
}


/**
* Get the warnings for this section.
*
* @return an Enumeration of warning strings.
*/
public Enumeration getWarnings() { public Enumeration getWarnings() {
return warnings.elements(); return warnings.elements();
} }


/**
* @see java.lang.Object#equals
*/
public boolean equals(Object rhs) { public boolean equals(Object rhs) {
if (!(rhs instanceof Section)) { if (!(rhs instanceof Section)) {
return false; return false;
@@ -511,7 +642,8 @@ public class Manifest extends Task {
for (Enumeration e = attributes.keys(); e.hasMoreElements();) { for (Enumeration e = attributes.keys(); e.hasMoreElements();) {
String attributeName = (String)e.nextElement(); String attributeName = (String)e.nextElement();
Object attributeValue = attributes.get(attributeName); Object attributeValue = attributes.get(attributeName);
Object rshAttributeValue = rhsSection.attributes.get(attributeName);
Object rshAttributeValue
= rhsSection.attributes.get(attributeName);
if (!attributeValue.equals(rshAttributeValue)) { if (!attributeValue.equals(rshAttributeValue)) {
return false; return false;
} }
@@ -531,26 +663,43 @@ public class Manifest extends Task {
/** The named sections of this manifest */ /** The named sections of this manifest */
private Hashtable sections = new Hashtable(); private Hashtable sections = new Hashtable();


/** Index of sections - used to retain order of sections in manifest */
private Vector sectionIndex = new Vector();

/**
* The file to which the manifest should be written when used as a task
*/
private File manifestFile;

/**
* The mode with which the manifest file is written
*/
private Mode mode;

/** /**
* Construct a manifest from Ant's default manifest file. * 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 { public static Manifest getDefaultManifest() throws BuildException {
try { try {
String s = "/org/apache/tools/ant/defaultManifest.mf";
InputStream in = Manifest.class.getResourceAsStream(s);
String defManifest = "/org/apache/tools/ant/defaultManifest.mf";
InputStream in = Manifest.class.getResourceAsStream(defManifest);
if (in == null) { if (in == null) {
throw new BuildException("Could not find default manifest: " + s);
throw new BuildException("Could not find default manifest: "
+ defManifest);
} }
try { try {
return new Manifest(new InputStreamReader(in, "ASCII")); return new Manifest(new InputStreamReader(in, "ASCII"));
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
return new Manifest(new InputStreamReader(in)); return new Manifest(new InputStreamReader(in));
} }
}
catch (ManifestException e) {
} catch (ManifestException e) {
throw new BuildException("Default manifest is invalid !!", e); throw new BuildException("Default manifest is invalid !!", e);
}
catch (IOException e) {
} catch (IOException e) {
throw new BuildException("Unable to read default manifest", e); throw new BuildException("Unable to read default manifest", e);
} }
} }
@@ -565,16 +714,18 @@ public class Manifest extends Task {
/** /**
* Read a manifest file from the given reader * Read a manifest file from the given reader
* *
* @param is the reader from which the Manifest is read
* @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 ManifestException if the manifest is not valid according
* to the JAR spec
* @throws IOException if the manifest cannot be read from the reader. * @throws IOException if the manifest cannot be read from the reader.
*/ */
public Manifest(Reader r) throws ManifestException, IOException { public Manifest(Reader r) throws ManifestException, IOException {
BufferedReader reader = new BufferedReader(r); BufferedReader reader = new BufferedReader(r);
// This should be the manifest version // This should be the manifest version
String nextSectionName = mainSection.read(reader); String nextSectionName = mainSection.read(reader);
String readManifestVersion = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
String readManifestVersion
= mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
if (readManifestVersion != null) { if (readManifestVersion != null) {
manifestVersion = readManifestVersion; manifestVersion = readManifestVersion;
mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION); mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
@@ -590,15 +741,16 @@ public class Manifest extends Task {
if (nextSectionName == null) { if (nextSectionName == null) {
Attribute sectionName = new Attribute(line); Attribute sectionName = new Attribute(line);
if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) { if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
throw new ManifestException("Manifest sections should start with a \"" + ATTRIBUTE_NAME +
"\" attribute and not \"" + sectionName.getName() + "\"");
throw new ManifestException("Manifest sections should "
+ "start with a \"" + ATTRIBUTE_NAME
+ "\" attribute and not \""
+ sectionName.getName() + "\"");
} }
nextSectionName = sectionName.getValue(); nextSectionName = sectionName.getValue();
}
else {
} else {
// we have already started reading this section // we have already started reading this section
// this line is the first attribute. set it and then let the normal
// read handle the rest
// this line is the first attribute. set it and then
// let the normal read handle the rest
Attribute firstAttribute = new Attribute(line); Attribute firstAttribute = new Attribute(line);
section.addAttributeAndCheck(firstAttribute); section.addAttributeAndCheck(firstAttribute);
} }
@@ -609,14 +761,34 @@ public class Manifest extends Task {
} }
} }


public void addConfiguredSection(Section section) throws ManifestException {
if (section.getName() == null) {
/**
* 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"); throw new BuildException("Sections must have a name");
} }
sections.put(section.getName(), section);
sections.put(sectionName, section);
if (!sectionIndex.contains(sectionName)) {
sectionIndex.addElement(sectionName);
}
} }


public void addConfiguredAttribute(Attribute attribute) throws ManifestException {
/**
* 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 {
mainSection.addConfiguredAttribute(attribute); mainSection.addConfiguredAttribute(attribute);
} }


@@ -625,8 +797,8 @@ public class Manifest extends Task {
* *
* @param other the Manifest to be merged with this one. * @param other the Manifest to be merged with this one.
* *
* @throws ManifestException if there is a problem merging the manfest according
* to the Manifest spec.
* @throws ManifestException if there is a problem merging the
* manfest according to the Manifest spec.
*/ */
public void merge(Manifest other) throws ManifestException { public void merge(Manifest other) throws ManifestException {
merge(other, false); merge(other, false);
@@ -636,17 +808,18 @@ public class Manifest extends Task {
* Merge the contents of the given manifest into this manifest * Merge the contents of the given manifest into this manifest
* *
* @param other the Manifest to be merged with this one. * @param other the Manifest to be merged with this one.
* @param overwriteMain whether to overwrite the main section of the current manifest
* @param overwriteMain whether to overwrite the main section
* of the current manifest
* *
* @throws ManifestException if there is a problem merging the manfest according
* to the Manifest spec.
* @throws ManifestException if there is a problem merging the
* manfest according to the Manifest spec.
*/ */
public void merge(Manifest other, boolean overwriteMain) throws ManifestException {
public void merge(Manifest other, boolean overwriteMain)
throws ManifestException {
if (other != null) { if (other != null) {
if (overwriteMain) { if (overwriteMain) {
mainSection = other.mainSection; mainSection = other.mainSection;
}
else {
} else {
mainSection.merge(other.mainSection); mainSection.merge(other.mainSection);
} }


@@ -654,16 +827,17 @@ public class Manifest extends Task {
manifestVersion = other.manifestVersion; manifestVersion = other.manifestVersion;
} }


for (Enumeration e = other.sections.keys(); e.hasMoreElements();) {
Enumeration e = other.getSectionNames();
while (e.hasMoreElements()) {
String sectionName = (String)e.nextElement(); String sectionName = (String)e.nextElement();
Section ourSection = (Section)sections.get(sectionName); Section ourSection = (Section)sections.get(sectionName);
Section otherSection = (Section)other.sections.get(sectionName);
Section otherSection
= (Section)other.sections.get(sectionName);
if (ourSection == null) { if (ourSection == null) {
if (otherSection != null) { if (otherSection != null) {
sections.put(sectionName.toLowerCase(), otherSection);
addConfiguredSection(otherSection);
} }
}
else {
} else {
ourSection.merge(otherSection); ourSection.merge(otherSection);
} }
} }
@@ -679,23 +853,30 @@ public class Manifest extends Task {
*/ */
public void write(PrintWriter writer) throws IOException { public void write(PrintWriter writer) throws IOException {
writer.println(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion); writer.println(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion);
String signatureVersion = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
String signatureVersion
= mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
if (signatureVersion != null) { if (signatureVersion != null) {
writer.println(ATTRIBUTE_SIGNATURE_VERSION + ": " + signatureVersion);
writer.println(ATTRIBUTE_SIGNATURE_VERSION + ": "
+ signatureVersion);
mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION); mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
} }
mainSection.write(writer); mainSection.write(writer);
// add it back
if (signatureVersion != null) { if (signatureVersion != null) {
try { try {
mainSection.addConfiguredAttribute(new Attribute(ATTRIBUTE_SIGNATURE_VERSION, signatureVersion));
}
catch (ManifestException e) {
Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION,
signatureVersion);
mainSection.addConfiguredAttribute(svAttr);
} catch (ManifestException e) {
// shouldn't happen - ignore // shouldn't happen - ignore
} }
} }


for (Enumeration e = sections.elements(); e.hasMoreElements();) {
Section section = (Section)e.nextElement();
Enumeration e = sectionIndex.elements();
while (e.hasMoreElements()) {
String sectionName = (String)e.nextElement();
Section section = getSection(sectionName);
section.write(writer); section.write(writer);
} }
} }
@@ -703,14 +884,14 @@ public class Manifest extends Task {
/** /**
* Convert the manifest to its string representation * Convert the manifest to its string representation
* *
* @return a multiline string with the Manifest as it appears in a Manifest file.
* @return a multiline string with the Manifest as it
* appears in a Manifest file.
*/ */
public String toString() { public String toString() {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
try { try {
write(new PrintWriter(sw)); write(new PrintWriter(sw));
}
catch (IOException e) {
} catch (IOException e) {
return null; return null;
} }
return sw.toString(); return sw.toString();
@@ -724,14 +905,17 @@ public class Manifest extends Task {
public Enumeration getWarnings() { public Enumeration getWarnings() {
Vector warnings = new Vector(); Vector warnings = new Vector();


for (Enumeration e2 = mainSection.getWarnings(); e2.hasMoreElements();) {
warnings.addElement(e2.nextElement());
Enumeration warnEnum = mainSection.getWarnings();
while (warnEnum.hasMoreElements()) {
warnings.addElement(warnEnum.nextElement());
} }


// create a vector and add in the warnings for all the sections // create a vector and add in the warnings for all the sections
for (Enumeration e = sections.elements(); e.hasMoreElements();) {
Enumeration e = sections.elements();
while (e.hasMoreElements()) {
Section section = (Section)e.nextElement(); Section section = (Section)e.nextElement();
for (Enumeration e2 = section.getWarnings(); e2.hasMoreElements();) {
Enumeration e2 = section.getWarnings();
while (e2.hasMoreElements()) {
warnings.addElement(e2.nextElement()); warnings.addElement(e2.nextElement());
} }
} }
@@ -739,6 +923,9 @@ public class Manifest extends Task {
return warnings.elements(); return warnings.elements();
} }


/**
* @see java.lang.Object#equals
*/
public boolean equals(Object rhs) { public boolean equals(Object rhs) {
if (!(rhs instanceof Manifest)) { if (!(rhs instanceof Manifest)) {
return false; return false;
@@ -760,9 +947,11 @@ public class Manifest extends Task {
return false; return false;
} }


for (Enumeration e = sections.elements(); e.hasMoreElements();) {
Enumeration e = sections.elements();
while (e.hasMoreElements()) {
Section section = (Section)e.nextElement(); Section section = (Section)e.nextElement();
Section rhsSection = (Section)rhsManifest.sections.get(section.getName());
Section rhsSection
= (Section)rhsManifest.sections.get(section.getName());
if (!section.equals(rhsSection)) { if (!section.equals(rhsSection)) {
return false; return false;
} }
@@ -771,19 +960,19 @@ public class Manifest extends Task {
return true; return true;
} }


private File manifestFile;

/** /**
* The name of the manifest file to write (if used as a task). * The name of the manifest file to write (if used as a task).
*
* @param f the Manifest file to be written
*/ */
public void setFile(File f) { public void setFile(File f) {
manifestFile = f; manifestFile = f;
} }


private Mode mode;

/** /**
* Shall we update or replace an existing manifest? * Shall we update or replace an existing manifest?
*
* @param m the mode value - update or replace.
*/ */
public void setMode(Mode m) { public void setMode(Mode m) {
mode = m; mode = m;
@@ -824,11 +1013,13 @@ public class Manifest extends Task {
* @return an Enumeration of section names * @return an Enumeration of section names
*/ */
public Enumeration getSectionNames() { public Enumeration getSectionNames() {
return sections.keys();
return sectionIndex.elements();
} }


/** /**
* Create or update the Manifest when used as a task. * Create or update the Manifest when used as a task.
*
* @throws BuildException if the manifst cannot be written.
*/ */
public void execute() throws BuildException { public void execute() throws BuildException {
if (manifestFile == null) { if (manifestFile == null) {
@@ -843,10 +1034,10 @@ public class Manifest extends Task {
f = new FileReader(manifestFile); f = new FileReader(manifestFile);
toWrite.merge(new Manifest(f)); toWrite.merge(new Manifest(f));
} catch (ManifestException m) { } catch (ManifestException m) {
throw new BuildException("Existing manifest "+manifestFile
throw new BuildException("Existing manifest " + manifestFile
+ " is invalid", m, location); + " is invalid", m, location);
} catch (IOException e) { } catch (IOException e) {
throw new BuildException("Failed to read "+manifestFile,
throw new BuildException("Failed to read " + manifestFile,
e, location); e, location);
} finally { } finally {
if (f != null) { if (f != null) {
@@ -868,7 +1059,7 @@ public class Manifest extends Task {
w = new PrintWriter(new FileWriter(manifestFile)); w = new PrintWriter(new FileWriter(manifestFile));
toWrite.write(w); toWrite.write(w);
} catch (IOException e) { } catch (IOException e) {
throw new BuildException("Failed to write "+manifestFile,
throw new BuildException("Failed to write " + manifestFile,
e, location); e, location);
} finally { } finally {
if (w != null) { if (w != null) {


+ 47
- 2
src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java View File

@@ -59,6 +59,7 @@ import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.Vector; import java.util.Vector;
import java.util.Enumeration;
import org.apache.tools.ant.BuildFileTest; import org.apache.tools.ant.BuildFileTest;
import org.apache.tools.ant.Project; import org.apache.tools.ant.Project;


@@ -222,8 +223,6 @@ public class ManifestTest extends BuildFileTest {
String classpath = mainSection.getAttributeValue("class-path"); String classpath = mainSection.getAttributeValue("class-path");
assertEquals("Class-Path attribute was not set correctly - ", assertEquals("Class-Path attribute was not set correctly - ",
"Test1 Test2 Test3 Test4", classpath); "Test1 Test2 Test3 Test4", classpath);
Object classPathAttr = mainSection.getAttribute("class-PATH");
assertTrue("Class path attribute should be a Vector", classPathAttr instanceof Vector);
} }
/** /**
@@ -241,6 +240,52 @@ public class ManifestTest extends BuildFileTest {
LONG_LINE, classpath); LONG_LINE, classpath);
} }
/**
* Tests ordering of sections
*/
public void testOrder1() throws IOException, ManifestException {
executeTarget("testOrder1");

Manifest manifest = getManifest(EXPANDED_MANIFEST);
Enumeration e = manifest.getSectionNames();
String section1 = (String)e.nextElement();
String section2 = (String)e.nextElement();
assertEquals("First section name unexpected", "Test1", section1);
assertEquals("Second section name unexpected", "Test2", section2);
Manifest.Section section = manifest.getSection("Test1");
e = section.getAttributeKeys();
String attr1Key = (String)e.nextElement();
String attr2Key = (String)e.nextElement();
String attr1 = section.getAttribute(attr1Key).getName();
String attr2 = section.getAttribute(attr2Key).getName();
assertEquals("First attribute name unexpected", "TestAttr1", attr1);
assertEquals("Second attribute name unexpected", "TestAttr2", attr2);
}
/**
* Tests ordering of sections
*/
public void testOrder2() throws IOException, ManifestException {
executeTarget("testOrder2");

Manifest manifest = getManifest(EXPANDED_MANIFEST);
Enumeration e = manifest.getSectionNames();
String section1 = (String)e.nextElement();
String section2 = (String)e.nextElement();
assertEquals("First section name unexpected", "Test2", section1);
assertEquals("Second section name unexpected", "Test1", section2);

Manifest.Section section = manifest.getSection("Test1");
e = section.getAttributeKeys();
String attr1Key = (String)e.nextElement();
String attr2Key = (String)e.nextElement();
String attr1 = section.getAttribute(attr1Key).getName();
String attr2 = section.getAttribute(attr2Key).getName();
assertEquals("First attribute name unexpected", "TestAttr2", attr1);
assertEquals("Second attribute name unexpected", "TestAttr1", attr2);
}
/** /**
* file attribute for manifest task is required. * file attribute for manifest task is required.
*/ */


Loading…
Cancel
Save