git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271212 13f79535-47bb-0310-9956-ffa450edef68master
@@ -0,0 +1,119 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import java.io.PrintWriter; | |||||
import java.io.IOException; | |||||
/** | |||||
* Class to hold manifest attributes | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public class Attribute | |||||
{ | |||||
/** | |||||
* The attribute's name | |||||
*/ | |||||
private String m_name; | |||||
/** | |||||
* The attribute's value | |||||
*/ | |||||
private String m_value; | |||||
/** | |||||
* Construct an empty attribute | |||||
*/ | |||||
public Attribute() | |||||
{ | |||||
} | |||||
/** | |||||
* Construct a manifest by specifying its name and value | |||||
* | |||||
* @param name the attribute's name | |||||
* @param value the Attribute's value | |||||
*/ | |||||
public Attribute( final String name, final String value ) | |||||
{ | |||||
m_name = name; | |||||
m_value = value; | |||||
} | |||||
/** | |||||
* Set the Attribute's name | |||||
* | |||||
* @param name the attribute's name | |||||
*/ | |||||
public void setName( final String name ) | |||||
{ | |||||
m_name = name; | |||||
} | |||||
/** | |||||
* Set the Attribute's value | |||||
* | |||||
* @param value the attribute's value | |||||
*/ | |||||
public void setValue( final String value ) | |||||
{ | |||||
m_value = value; | |||||
} | |||||
/** | |||||
* Get the Attribute's name | |||||
* | |||||
* @return the attribute's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return m_name; | |||||
} | |||||
/** | |||||
* Get the Attribute's value | |||||
* | |||||
* @return the attribute's value. | |||||
*/ | |||||
public String getValue() | |||||
{ | |||||
return m_value; | |||||
} | |||||
/** | |||||
* 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 feature to be added to the Continuation attribute | |||||
*/ | |||||
public void addContinuation( final String line ) | |||||
{ | |||||
m_value += line.substring( 1 ); | |||||
} | |||||
public boolean equals( Object object ) | |||||
{ | |||||
if( !( object instanceof Attribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
final Attribute other = (Attribute)object; | |||||
return | |||||
( null != m_name && null != other.m_name && | |||||
m_name.toLowerCase().equals( other.m_name.toLowerCase() ) && | |||||
m_value != null && m_value.equals( other.m_value ) ); | |||||
} | |||||
} |
@@ -25,24 +25,18 @@ import java.util.Iterator; | |||||
import java.util.jar.Attributes; | import java.util.jar.Attributes; | ||||
import org.apache.myrmidon.api.AbstractTask; | import org.apache.myrmidon.api.AbstractTask; | ||||
import org.apache.myrmidon.api.TaskException; | import org.apache.myrmidon.api.TaskException; | ||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
/** | /** | ||||
* Class to manage Manifest information | * Class to manage Manifest information | ||||
* | * | ||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | * @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | ||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | ||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | * @version $Revision$ $Date$ | ||||
*/ | */ | ||||
public class Manifest | public class Manifest | ||||
extends AbstractTask | extends AbstractTask | ||||
{ | { | ||||
/** | |||||
* The standard manifest version header | |||||
*/ | |||||
public final static String ATTRIBUTE_MANIFEST_VERSION = Attributes.Name.MANIFEST_VERSION.toString(); | |||||
/** | /** | ||||
* The standard Signature Version header | * The standard Signature Version header | ||||
*/ | */ | ||||
@@ -119,11 +113,11 @@ public class Manifest | |||||
BufferedReader reader = new BufferedReader( r ); | BufferedReader reader = new BufferedReader( r ); | ||||
// This should be the manifest version | // This should be the manifest version | ||||
String nextSectionName = m_mainSection.read( reader ); | String nextSectionName = m_mainSection.read( reader ); | ||||
String readManifestVersion = m_mainSection.getAttributeValue( ATTRIBUTE_MANIFEST_VERSION ); | |||||
String readManifestVersion = m_mainSection.getAttributeValue( Attributes.Name.MANIFEST_VERSION.toString() ); | |||||
if( readManifestVersion != null ) | if( readManifestVersion != null ) | ||||
{ | { | ||||
m_manifestVersion = readManifestVersion; | m_manifestVersion = readManifestVersion; | ||||
m_mainSection.removeAttribute( ATTRIBUTE_MANIFEST_VERSION ); | |||||
m_mainSection.removeAttribute( Attributes.Name.MANIFEST_VERSION.toString() ); | |||||
} | } | ||||
String line = null; | String line = null; | ||||
@@ -137,7 +131,7 @@ public class Manifest | |||||
Section section = new Section(); | Section section = new Section(); | ||||
if( nextSectionName == null ) | if( nextSectionName == null ) | ||||
{ | { | ||||
Attribute sectionName = new Attribute( line ); | |||||
Attribute sectionName = ManifestUtil.buildAttribute( line ); | |||||
if( !sectionName.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) ) | if( !sectionName.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) ) | ||||
{ | { | ||||
throw new ManifestException( "Manifest sections should start with a \"" + ATTRIBUTE_NAME + | throw new ManifestException( "Manifest sections should start with a \"" + ATTRIBUTE_NAME + | ||||
@@ -150,7 +144,7 @@ public class Manifest | |||||
// 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 | // this line is the first attribute. set it and then let the normal | ||||
// read handle the rest | // read handle the rest | ||||
Attribute firstAttribute = new Attribute( line ); | |||||
Attribute firstAttribute = ManifestUtil.buildAttribute( line ); | |||||
section.addAttributeAndCheck( firstAttribute ); | section.addAttributeAndCheck( firstAttribute ); | ||||
} | } | ||||
@@ -438,7 +432,7 @@ public class Manifest | |||||
public void write( PrintWriter writer ) | public void write( PrintWriter writer ) | ||||
throws IOException, TaskException | throws IOException, TaskException | ||||
{ | { | ||||
writer.println( ATTRIBUTE_MANIFEST_VERSION + ": " + m_manifestVersion ); | |||||
writer.println( Attributes.Name.MANIFEST_VERSION + ": " + m_manifestVersion ); | |||||
String signatureVersion = m_mainSection.getAttributeValue( ATTRIBUTE_SIGNATURE_VERSION ); | String signatureVersion = m_mainSection.getAttributeValue( ATTRIBUTE_SIGNATURE_VERSION ); | ||||
if( signatureVersion != null ) | if( signatureVersion != null ) | ||||
{ | { | ||||
@@ -465,493 +459,4 @@ public class Manifest | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Class to hold manifest attributes | |||||
* | |||||
* @author RT | |||||
*/ | |||||
public static class Attribute | |||||
{ | |||||
/** | |||||
* The attribute's name | |||||
*/ | |||||
private String name = null; | |||||
/** | |||||
* The attribute's value | |||||
*/ | |||||
private String value = null; | |||||
/** | |||||
* 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 | |||||
* @exception ManifestException Description of Exception | |||||
* @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; | |||||
this.value = value; | |||||
} | |||||
/** | |||||
* Set the Attribute's name | |||||
* | |||||
* @param name the attribute's name | |||||
*/ | |||||
public void setName( String name ) | |||||
{ | |||||
this.name = name; | |||||
} | |||||
/** | |||||
* Set the Attribute's value | |||||
* | |||||
* @param value the attribute's value | |||||
*/ | |||||
public void setValue( String value ) | |||||
{ | |||||
this.value = value; | |||||
} | |||||
/** | |||||
* Get the Attribute's name | |||||
* | |||||
* @return the attribute's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return name; | |||||
} | |||||
/** | |||||
* Get the Attribute's value | |||||
* | |||||
* @return the attribute's value. | |||||
*/ | |||||
public String getValue() | |||||
{ | |||||
return value; | |||||
} | |||||
/** | |||||
* 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 feature to be added to the Continuation attribute | |||||
*/ | |||||
public void addContinuation( String line ) | |||||
{ | |||||
value += line.substring( 1 ); | |||||
} | |||||
public boolean equals( Object rhs ) | |||||
{ | |||||
if( !( rhs instanceof Attribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
Attribute rhsAttribute = (Attribute)rhs; | |||||
return ( name != null && rhsAttribute.name != null && | |||||
name.toLowerCase().equals( rhsAttribute.name.toLowerCase() ) && | |||||
value != null && value.equals( rhsAttribute.value ) ); | |||||
} | |||||
/** | |||||
* 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 ); | |||||
value = line.substring( index + 2 ); | |||||
} | |||||
/** | |||||
* Write the attribute out to a print writer. | |||||
* | |||||
* @param writer the Writer to which the attribute is written | |||||
* @throws IOException if the attribte value cannot be written | |||||
*/ | |||||
public void write( PrintWriter writer ) | |||||
throws IOException | |||||
{ | |||||
String line = name + ": " + value; | |||||
while( line.getBytes().length > MAX_LINE_LENGTH ) | |||||
{ | |||||
// try to find a MAX_LINE_LENGTH byte section | |||||
int breakIndex = MAX_LINE_LENGTH; | |||||
String section = line.substring( 0, breakIndex ); | |||||
while( section.getBytes().length > MAX_LINE_LENGTH && breakIndex > 0 ) | |||||
{ | |||||
breakIndex--; | |||||
section = line.substring( 0, breakIndex ); | |||||
} | |||||
if( breakIndex == 0 ) | |||||
{ | |||||
throw new IOException( "Unable to write manifest line " + name + ": " + value ); | |||||
} | |||||
writer.println( section ); | |||||
line = " " + line.substring( breakIndex ); | |||||
} | |||||
writer.println( line ); | |||||
} | |||||
} | |||||
/** | |||||
* Helper class for Manifest's mode attribute. | |||||
*/ | |||||
public static class ManifestMode extends EnumeratedAttribute | |||||
{ | |||||
public String[] getValues() | |||||
{ | |||||
return new String[]{"update", "replace"}; | |||||
} | |||||
} | |||||
/** | |||||
* Class to represent an individual section in the Manifest. A section | |||||
* consists of a set of attribute values, separated from other sections by a | |||||
* blank line. | |||||
* | |||||
* @author RT | |||||
*/ | |||||
public static class Section | |||||
{ | |||||
private ArrayList warnings = new ArrayList(); | |||||
/** | |||||
* The section's name if any. The main section in a manifest is unnamed. | |||||
*/ | |||||
private String name = null; | |||||
/** | |||||
* The section's attributes. | |||||
*/ | |||||
private Hashtable attributes = new Hashtable(); | |||||
/** | |||||
* Set the Section's name | |||||
* | |||||
* @param name the section's name | |||||
*/ | |||||
public void setName( String name ) | |||||
{ | |||||
this.name = name; | |||||
} | |||||
/** | |||||
* 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 ) | |||||
{ | |||||
Object attribute = attributes.get( attributeName.toLowerCase() ); | |||||
if( attribute == null ) | |||||
{ | |||||
return null; | |||||
} | |||||
if( attribute instanceof Attribute ) | |||||
{ | |||||
return ( (Attribute)attribute ).getValue(); | |||||
} | |||||
else | |||||
{ | |||||
String value = ""; | |||||
for( Iterator e = ( (ArrayList)attribute ).iterator(); e.hasNext(); ) | |||||
{ | |||||
Attribute classpathAttribute = (Attribute)e.next(); | |||||
value += classpathAttribute.getValue() + " "; | |||||
} | |||||
return value.trim(); | |||||
} | |||||
} | |||||
/** | |||||
* Get the Section's name | |||||
* | |||||
* @return the section's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return name; | |||||
} | |||||
public Iterator getWarnings() | |||||
{ | |||||
return warnings.iterator(); | |||||
} | |||||
/** | |||||
* 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 | |||||
* @throws ManifestException if the attribute already exists in this | |||||
* section. | |||||
*/ | |||||
public String addAttributeAndCheck( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
if( attribute.getName() == null || attribute.getValue() == null ) | |||||
{ | |||||
throw new TaskException( "Attributes must have name and value" ); | |||||
} | |||||
if( attribute.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) ) | |||||
{ | |||||
warnings.add( "\"" + 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( attribute.getName().toLowerCase().startsWith( ATTRIBUTE_FROM.toLowerCase() ) ) | |||||
{ | |||||
warnings.add( "Manifest attributes should not start with \"" + | |||||
ATTRIBUTE_FROM + "\" in \"" + attribute.getName() + ": " + attribute.getValue() + "\"" ); | |||||
} | |||||
else | |||||
{ | |||||
// classpath attributes go into a vector | |||||
String attributeName = attribute.getName().toLowerCase(); | |||||
if( attributeName.equals( ATTRIBUTE_CLASSPATH ) ) | |||||
{ | |||||
ArrayList classpathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
if( classpathAttrs == null ) | |||||
{ | |||||
classpathAttrs = new ArrayList(); | |||||
attributes.put( attributeName, classpathAttrs ); | |||||
} | |||||
classpathAttrs.add( 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 ); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public void addAttribute( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
String check = addAttributeAndCheck( attribute ); | |||||
if( check != null ) | |||||
{ | |||||
throw new TaskException( "Specify the section name using the \"name\" attribute of the <section> element rather " + | |||||
"than using a \"Name\" manifest attribute" ); | |||||
} | |||||
} | |||||
public boolean equals( Object rhs ) | |||||
{ | |||||
if( !( rhs instanceof Section ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
Section rhsSection = (Section)rhs; | |||||
if( attributes.size() != rhsSection.attributes.size() ) | |||||
{ | |||||
return false; | |||||
} | |||||
for( Enumeration e = attributes.elements(); e.hasMoreElements(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e.nextElement(); | |||||
Attribute rshAttribute = (Attribute)rhsSection.attributes.get( attribute.getName().toLowerCase() ); | |||||
if( !attribute.equals( rshAttribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* Merge in another section | |||||
* | |||||
* @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 | |||||
{ | |||||
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(); ) | |||||
{ | |||||
String attributeName = (String)e.nextElement(); | |||||
if( attributeName.equals( ATTRIBUTE_CLASSPATH ) && | |||||
attributes.containsKey( attributeName ) ) | |||||
{ | |||||
// classpath entries are vetors which are merged | |||||
ArrayList classpathAttrs = (ArrayList)section.attributes.get( attributeName ); | |||||
ArrayList ourClasspathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
for( Iterator e2 = classpathAttrs.iterator(); e2.hasNext(); ) | |||||
{ | |||||
ourClasspathAttrs.add( e2.next() ); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
// the merge file always wins | |||||
attributes.put( attributeName, section.attributes.get( attributeName ) ); | |||||
} | |||||
} | |||||
// add in the warnings | |||||
for( Iterator e = section.warnings.iterator(); e.hasNext(); ) | |||||
{ | |||||
warnings.add( e.next() ); | |||||
} | |||||
} | |||||
/** | |||||
* 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, TaskException | |||||
{ | |||||
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 ); | |||||
if( nameReadAhead != null ) | |||||
{ | |||||
return nameReadAhead; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Remove tge given attribute from the section | |||||
* | |||||
* @param attributeName the name of the attribute to be removed. | |||||
*/ | |||||
public void removeAttribute( String attributeName ) | |||||
{ | |||||
attributes.remove( attributeName.toLowerCase() ); | |||||
} | |||||
/** | |||||
* Write the section out to a print writer. | |||||
* | |||||
* @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 | |||||
{ | |||||
if( name != null ) | |||||
{ | |||||
Attribute nameAttr = new Attribute( ATTRIBUTE_NAME, name ); | |||||
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 | |||||
{ | |||||
ArrayList attrList = (ArrayList)object; | |||||
for( Iterator e2 = attrList.iterator(); e2.hasNext(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e2.next(); | |||||
attribute.write( writer ); | |||||
} | |||||
} | |||||
} | |||||
writer.println(); | |||||
} | |||||
} | |||||
} | } |
@@ -0,0 +1,27 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
/** | |||||
* Helper class for Manifest's mode attribute. | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public class ManifestMode | |||||
extends EnumeratedAttribute | |||||
{ | |||||
public String[] getValues() | |||||
{ | |||||
return new String[]{"update", "replace"}; | |||||
} | |||||
} |
@@ -0,0 +1,78 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import java.io.IOException; | |||||
import java.io.PrintWriter; | |||||
/** | |||||
* Utility methods for manifest stuff. | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public final class ManifestUtil | |||||
{ | |||||
public static Attribute buildAttribute( final String line ) | |||||
throws ManifestException | |||||
{ | |||||
final Attribute attribute = new Attribute(); | |||||
parse( attribute, line ); | |||||
return attribute; | |||||
} | |||||
/** | |||||
* 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 static void parse( final Attribute attribute, final String line ) | |||||
throws ManifestException | |||||
{ | |||||
final 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 ': ' " ); | |||||
} | |||||
final String name = line.substring( 0, index ); | |||||
final String value = line.substring( index + 2 ); | |||||
attribute.setName( name ); | |||||
attribute.setValue( value ); | |||||
} | |||||
public static void write( final Attribute attribute, final PrintWriter writer ) | |||||
throws IOException | |||||
{ | |||||
final String name = attribute.getName(); | |||||
final String value = attribute.getValue(); | |||||
String line = name + ": " + value; | |||||
while( line.getBytes().length > Manifest.MAX_LINE_LENGTH ) | |||||
{ | |||||
// try to find a MAX_LINE_LENGTH byte section | |||||
int breakIndex = Manifest.MAX_LINE_LENGTH; | |||||
String section = line.substring( 0, breakIndex ); | |||||
while( section.getBytes().length > Manifest.MAX_LINE_LENGTH && breakIndex > 0 ) | |||||
{ | |||||
breakIndex--; | |||||
section = line.substring( 0, breakIndex ); | |||||
} | |||||
if( breakIndex == 0 ) | |||||
{ | |||||
throw new IOException( "Unable to write manifest line " + name + ": " + value ); | |||||
} | |||||
writer.println( section ); | |||||
line = " " + line.substring( breakIndex ); | |||||
} | |||||
writer.println( line ); | |||||
} | |||||
} |
@@ -0,0 +1,332 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import java.io.BufferedReader; | |||||
import java.io.IOException; | |||||
import java.io.PrintWriter; | |||||
import java.util.ArrayList; | |||||
import java.util.Enumeration; | |||||
import java.util.Hashtable; | |||||
import java.util.Iterator; | |||||
import org.apache.myrmidon.api.TaskException; | |||||
/** | |||||
* Class to represent an individual section in the Manifest. A section | |||||
* consists of a set of attribute values, separated from other sections by a | |||||
* blank line. | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public class Section | |||||
{ | |||||
private ArrayList warnings = new ArrayList(); | |||||
/** | |||||
* The section's name if any. The main section in a manifest is unnamed. | |||||
*/ | |||||
private String name = null; | |||||
/** | |||||
* The section's attributes. | |||||
*/ | |||||
private Hashtable attributes = new Hashtable(); | |||||
/** | |||||
* Set the Section's name | |||||
* | |||||
* @param name the section's name | |||||
*/ | |||||
public void setName( String name ) | |||||
{ | |||||
this.name = name; | |||||
} | |||||
/** | |||||
* 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 ) | |||||
{ | |||||
Object attribute = attributes.get( attributeName.toLowerCase() ); | |||||
if( attribute == null ) | |||||
{ | |||||
return null; | |||||
} | |||||
if( attribute instanceof Attribute ) | |||||
{ | |||||
return ( (Attribute)attribute ).getValue(); | |||||
} | |||||
else | |||||
{ | |||||
String value = ""; | |||||
for( Iterator e = ( (ArrayList)attribute ).iterator(); e.hasNext(); ) | |||||
{ | |||||
Attribute classpathAttribute = (Attribute)e.next(); | |||||
value += classpathAttribute.getValue() + " "; | |||||
} | |||||
return value.trim(); | |||||
} | |||||
} | |||||
/** | |||||
* Get the Section's name | |||||
* | |||||
* @return the section's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return name; | |||||
} | |||||
public Iterator getWarnings() | |||||
{ | |||||
return warnings.iterator(); | |||||
} | |||||
/** | |||||
* 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 | |||||
* @throws ManifestException if the attribute already exists in this | |||||
* section. | |||||
*/ | |||||
public String addAttributeAndCheck( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
if( attribute.getName() == null || attribute.getValue() == null ) | |||||
{ | |||||
throw new TaskException( "Attributes must have name and value" ); | |||||
} | |||||
if( attribute.getName().equalsIgnoreCase( Manifest.ATTRIBUTE_NAME ) ) | |||||
{ | |||||
warnings.add( "\"" + Manifest.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( attribute.getName().toLowerCase().startsWith( Manifest.ATTRIBUTE_FROM.toLowerCase() ) ) | |||||
{ | |||||
warnings.add( "Manifest attributes should not start with \"" + | |||||
Manifest.ATTRIBUTE_FROM + "\" in \"" + attribute.getName() + ": " + attribute.getValue() + "\"" ); | |||||
} | |||||
else | |||||
{ | |||||
// classpath attributes go into a vector | |||||
String attributeName = attribute.getName().toLowerCase(); | |||||
if( attributeName.equals( Manifest.ATTRIBUTE_CLASSPATH ) ) | |||||
{ | |||||
ArrayList classpathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
if( classpathAttrs == null ) | |||||
{ | |||||
classpathAttrs = new ArrayList(); | |||||
attributes.put( attributeName, classpathAttrs ); | |||||
} | |||||
classpathAttrs.add( 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 ); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public void addAttribute( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
String check = addAttributeAndCheck( attribute ); | |||||
if( check != null ) | |||||
{ | |||||
throw new TaskException( "Specify the section name using the \"name\" attribute of the <section> element rather " + | |||||
"than using a \"Name\" manifest attribute" ); | |||||
} | |||||
} | |||||
public boolean equals( Object rhs ) | |||||
{ | |||||
if( !( rhs instanceof Section ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
Section rhsSection = (Section)rhs; | |||||
if( attributes.size() != rhsSection.attributes.size() ) | |||||
{ | |||||
return false; | |||||
} | |||||
for( Enumeration e = attributes.elements(); e.hasMoreElements(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e.nextElement(); | |||||
Attribute rshAttribute = (Attribute)rhsSection.attributes.get( attribute.getName().toLowerCase() ); | |||||
if( !attribute.equals( rshAttribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* Merge in another section | |||||
* | |||||
* @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 | |||||
{ | |||||
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(); ) | |||||
{ | |||||
String attributeName = (String)e.nextElement(); | |||||
if( attributeName.equals( Manifest.ATTRIBUTE_CLASSPATH ) && | |||||
attributes.containsKey( attributeName ) ) | |||||
{ | |||||
// classpath entries are vetors which are merged | |||||
ArrayList classpathAttrs = (ArrayList)section.attributes.get( attributeName ); | |||||
ArrayList ourClasspathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
for( Iterator e2 = classpathAttrs.iterator(); e2.hasNext(); ) | |||||
{ | |||||
ourClasspathAttrs.add( e2.next() ); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
// the merge file always wins | |||||
attributes.put( attributeName, section.attributes.get( attributeName ) ); | |||||
} | |||||
} | |||||
// add in the warnings | |||||
for( Iterator e = section.warnings.iterator(); e.hasNext(); ) | |||||
{ | |||||
warnings.add( e.next() ); | |||||
} | |||||
} | |||||
/** | |||||
* 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, TaskException | |||||
{ | |||||
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 = ManifestUtil.buildAttribute( line ); | |||||
String nameReadAhead = addAttributeAndCheck( attribute ); | |||||
if( nameReadAhead != null ) | |||||
{ | |||||
return nameReadAhead; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Remove tge given attribute from the section | |||||
* | |||||
* @param attributeName the name of the attribute to be removed. | |||||
*/ | |||||
public void removeAttribute( String attributeName ) | |||||
{ | |||||
attributes.remove( attributeName.toLowerCase() ); | |||||
} | |||||
/** | |||||
* Write the section out to a print writer. | |||||
* | |||||
* @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 | |||||
{ | |||||
if( name != null ) | |||||
{ | |||||
Attribute nameAttr = new Attribute( Manifest.ATTRIBUTE_NAME, name ); | |||||
ManifestUtil.write( nameAttr, writer ); | |||||
} | |||||
for( Enumeration e = attributes.elements(); e.hasMoreElements(); ) | |||||
{ | |||||
Object object = e.nextElement(); | |||||
if( object instanceof Attribute ) | |||||
{ | |||||
Attribute attribute = (Attribute)object; | |||||
ManifestUtil.write( attribute, writer ); | |||||
} | |||||
else | |||||
{ | |||||
ArrayList attrList = (ArrayList)object; | |||||
for( Iterator e2 = attrList.iterator(); e2.hasNext(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e2.next(); | |||||
ManifestUtil.write( attribute, writer ); | |||||
} | |||||
} | |||||
} | |||||
writer.println(); | |||||
} | |||||
} |
@@ -0,0 +1,119 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import java.io.PrintWriter; | |||||
import java.io.IOException; | |||||
/** | |||||
* Class to hold manifest attributes | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public class Attribute | |||||
{ | |||||
/** | |||||
* The attribute's name | |||||
*/ | |||||
private String m_name; | |||||
/** | |||||
* The attribute's value | |||||
*/ | |||||
private String m_value; | |||||
/** | |||||
* Construct an empty attribute | |||||
*/ | |||||
public Attribute() | |||||
{ | |||||
} | |||||
/** | |||||
* Construct a manifest by specifying its name and value | |||||
* | |||||
* @param name the attribute's name | |||||
* @param value the Attribute's value | |||||
*/ | |||||
public Attribute( final String name, final String value ) | |||||
{ | |||||
m_name = name; | |||||
m_value = value; | |||||
} | |||||
/** | |||||
* Set the Attribute's name | |||||
* | |||||
* @param name the attribute's name | |||||
*/ | |||||
public void setName( final String name ) | |||||
{ | |||||
m_name = name; | |||||
} | |||||
/** | |||||
* Set the Attribute's value | |||||
* | |||||
* @param value the attribute's value | |||||
*/ | |||||
public void setValue( final String value ) | |||||
{ | |||||
m_value = value; | |||||
} | |||||
/** | |||||
* Get the Attribute's name | |||||
* | |||||
* @return the attribute's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return m_name; | |||||
} | |||||
/** | |||||
* Get the Attribute's value | |||||
* | |||||
* @return the attribute's value. | |||||
*/ | |||||
public String getValue() | |||||
{ | |||||
return m_value; | |||||
} | |||||
/** | |||||
* 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 feature to be added to the Continuation attribute | |||||
*/ | |||||
public void addContinuation( final String line ) | |||||
{ | |||||
m_value += line.substring( 1 ); | |||||
} | |||||
public boolean equals( Object object ) | |||||
{ | |||||
if( !( object instanceof Attribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
final Attribute other = (Attribute)object; | |||||
return | |||||
( null != m_name && null != other.m_name && | |||||
m_name.toLowerCase().equals( other.m_name.toLowerCase() ) && | |||||
m_value != null && m_value.equals( other.m_value ) ); | |||||
} | |||||
} |
@@ -25,24 +25,18 @@ import java.util.Iterator; | |||||
import java.util.jar.Attributes; | import java.util.jar.Attributes; | ||||
import org.apache.myrmidon.api.AbstractTask; | import org.apache.myrmidon.api.AbstractTask; | ||||
import org.apache.myrmidon.api.TaskException; | import org.apache.myrmidon.api.TaskException; | ||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
/** | /** | ||||
* Class to manage Manifest information | * Class to manage Manifest information | ||||
* | * | ||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | * @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | ||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | ||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | * @version $Revision$ $Date$ | ||||
*/ | */ | ||||
public class Manifest | public class Manifest | ||||
extends AbstractTask | extends AbstractTask | ||||
{ | { | ||||
/** | |||||
* The standard manifest version header | |||||
*/ | |||||
public final static String ATTRIBUTE_MANIFEST_VERSION = Attributes.Name.MANIFEST_VERSION.toString(); | |||||
/** | /** | ||||
* The standard Signature Version header | * The standard Signature Version header | ||||
*/ | */ | ||||
@@ -119,11 +113,11 @@ public class Manifest | |||||
BufferedReader reader = new BufferedReader( r ); | BufferedReader reader = new BufferedReader( r ); | ||||
// This should be the manifest version | // This should be the manifest version | ||||
String nextSectionName = m_mainSection.read( reader ); | String nextSectionName = m_mainSection.read( reader ); | ||||
String readManifestVersion = m_mainSection.getAttributeValue( ATTRIBUTE_MANIFEST_VERSION ); | |||||
String readManifestVersion = m_mainSection.getAttributeValue( Attributes.Name.MANIFEST_VERSION.toString() ); | |||||
if( readManifestVersion != null ) | if( readManifestVersion != null ) | ||||
{ | { | ||||
m_manifestVersion = readManifestVersion; | m_manifestVersion = readManifestVersion; | ||||
m_mainSection.removeAttribute( ATTRIBUTE_MANIFEST_VERSION ); | |||||
m_mainSection.removeAttribute( Attributes.Name.MANIFEST_VERSION.toString() ); | |||||
} | } | ||||
String line = null; | String line = null; | ||||
@@ -137,7 +131,7 @@ public class Manifest | |||||
Section section = new Section(); | Section section = new Section(); | ||||
if( nextSectionName == null ) | if( nextSectionName == null ) | ||||
{ | { | ||||
Attribute sectionName = new Attribute( line ); | |||||
Attribute sectionName = ManifestUtil.buildAttribute( line ); | |||||
if( !sectionName.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) ) | if( !sectionName.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) ) | ||||
{ | { | ||||
throw new ManifestException( "Manifest sections should start with a \"" + ATTRIBUTE_NAME + | throw new ManifestException( "Manifest sections should start with a \"" + ATTRIBUTE_NAME + | ||||
@@ -150,7 +144,7 @@ public class Manifest | |||||
// 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 | // this line is the first attribute. set it and then let the normal | ||||
// read handle the rest | // read handle the rest | ||||
Attribute firstAttribute = new Attribute( line ); | |||||
Attribute firstAttribute = ManifestUtil.buildAttribute( line ); | |||||
section.addAttributeAndCheck( firstAttribute ); | section.addAttributeAndCheck( firstAttribute ); | ||||
} | } | ||||
@@ -438,7 +432,7 @@ public class Manifest | |||||
public void write( PrintWriter writer ) | public void write( PrintWriter writer ) | ||||
throws IOException, TaskException | throws IOException, TaskException | ||||
{ | { | ||||
writer.println( ATTRIBUTE_MANIFEST_VERSION + ": " + m_manifestVersion ); | |||||
writer.println( Attributes.Name.MANIFEST_VERSION + ": " + m_manifestVersion ); | |||||
String signatureVersion = m_mainSection.getAttributeValue( ATTRIBUTE_SIGNATURE_VERSION ); | String signatureVersion = m_mainSection.getAttributeValue( ATTRIBUTE_SIGNATURE_VERSION ); | ||||
if( signatureVersion != null ) | if( signatureVersion != null ) | ||||
{ | { | ||||
@@ -465,493 +459,4 @@ public class Manifest | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Class to hold manifest attributes | |||||
* | |||||
* @author RT | |||||
*/ | |||||
public static class Attribute | |||||
{ | |||||
/** | |||||
* The attribute's name | |||||
*/ | |||||
private String name = null; | |||||
/** | |||||
* The attribute's value | |||||
*/ | |||||
private String value = null; | |||||
/** | |||||
* 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 | |||||
* @exception ManifestException Description of Exception | |||||
* @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; | |||||
this.value = value; | |||||
} | |||||
/** | |||||
* Set the Attribute's name | |||||
* | |||||
* @param name the attribute's name | |||||
*/ | |||||
public void setName( String name ) | |||||
{ | |||||
this.name = name; | |||||
} | |||||
/** | |||||
* Set the Attribute's value | |||||
* | |||||
* @param value the attribute's value | |||||
*/ | |||||
public void setValue( String value ) | |||||
{ | |||||
this.value = value; | |||||
} | |||||
/** | |||||
* Get the Attribute's name | |||||
* | |||||
* @return the attribute's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return name; | |||||
} | |||||
/** | |||||
* Get the Attribute's value | |||||
* | |||||
* @return the attribute's value. | |||||
*/ | |||||
public String getValue() | |||||
{ | |||||
return value; | |||||
} | |||||
/** | |||||
* 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 feature to be added to the Continuation attribute | |||||
*/ | |||||
public void addContinuation( String line ) | |||||
{ | |||||
value += line.substring( 1 ); | |||||
} | |||||
public boolean equals( Object rhs ) | |||||
{ | |||||
if( !( rhs instanceof Attribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
Attribute rhsAttribute = (Attribute)rhs; | |||||
return ( name != null && rhsAttribute.name != null && | |||||
name.toLowerCase().equals( rhsAttribute.name.toLowerCase() ) && | |||||
value != null && value.equals( rhsAttribute.value ) ); | |||||
} | |||||
/** | |||||
* 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 ); | |||||
value = line.substring( index + 2 ); | |||||
} | |||||
/** | |||||
* Write the attribute out to a print writer. | |||||
* | |||||
* @param writer the Writer to which the attribute is written | |||||
* @throws IOException if the attribte value cannot be written | |||||
*/ | |||||
public void write( PrintWriter writer ) | |||||
throws IOException | |||||
{ | |||||
String line = name + ": " + value; | |||||
while( line.getBytes().length > MAX_LINE_LENGTH ) | |||||
{ | |||||
// try to find a MAX_LINE_LENGTH byte section | |||||
int breakIndex = MAX_LINE_LENGTH; | |||||
String section = line.substring( 0, breakIndex ); | |||||
while( section.getBytes().length > MAX_LINE_LENGTH && breakIndex > 0 ) | |||||
{ | |||||
breakIndex--; | |||||
section = line.substring( 0, breakIndex ); | |||||
} | |||||
if( breakIndex == 0 ) | |||||
{ | |||||
throw new IOException( "Unable to write manifest line " + name + ": " + value ); | |||||
} | |||||
writer.println( section ); | |||||
line = " " + line.substring( breakIndex ); | |||||
} | |||||
writer.println( line ); | |||||
} | |||||
} | |||||
/** | |||||
* Helper class for Manifest's mode attribute. | |||||
*/ | |||||
public static class ManifestMode extends EnumeratedAttribute | |||||
{ | |||||
public String[] getValues() | |||||
{ | |||||
return new String[]{"update", "replace"}; | |||||
} | |||||
} | |||||
/** | |||||
* Class to represent an individual section in the Manifest. A section | |||||
* consists of a set of attribute values, separated from other sections by a | |||||
* blank line. | |||||
* | |||||
* @author RT | |||||
*/ | |||||
public static class Section | |||||
{ | |||||
private ArrayList warnings = new ArrayList(); | |||||
/** | |||||
* The section's name if any. The main section in a manifest is unnamed. | |||||
*/ | |||||
private String name = null; | |||||
/** | |||||
* The section's attributes. | |||||
*/ | |||||
private Hashtable attributes = new Hashtable(); | |||||
/** | |||||
* Set the Section's name | |||||
* | |||||
* @param name the section's name | |||||
*/ | |||||
public void setName( String name ) | |||||
{ | |||||
this.name = name; | |||||
} | |||||
/** | |||||
* 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 ) | |||||
{ | |||||
Object attribute = attributes.get( attributeName.toLowerCase() ); | |||||
if( attribute == null ) | |||||
{ | |||||
return null; | |||||
} | |||||
if( attribute instanceof Attribute ) | |||||
{ | |||||
return ( (Attribute)attribute ).getValue(); | |||||
} | |||||
else | |||||
{ | |||||
String value = ""; | |||||
for( Iterator e = ( (ArrayList)attribute ).iterator(); e.hasNext(); ) | |||||
{ | |||||
Attribute classpathAttribute = (Attribute)e.next(); | |||||
value += classpathAttribute.getValue() + " "; | |||||
} | |||||
return value.trim(); | |||||
} | |||||
} | |||||
/** | |||||
* Get the Section's name | |||||
* | |||||
* @return the section's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return name; | |||||
} | |||||
public Iterator getWarnings() | |||||
{ | |||||
return warnings.iterator(); | |||||
} | |||||
/** | |||||
* 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 | |||||
* @throws ManifestException if the attribute already exists in this | |||||
* section. | |||||
*/ | |||||
public String addAttributeAndCheck( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
if( attribute.getName() == null || attribute.getValue() == null ) | |||||
{ | |||||
throw new TaskException( "Attributes must have name and value" ); | |||||
} | |||||
if( attribute.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) ) | |||||
{ | |||||
warnings.add( "\"" + 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( attribute.getName().toLowerCase().startsWith( ATTRIBUTE_FROM.toLowerCase() ) ) | |||||
{ | |||||
warnings.add( "Manifest attributes should not start with \"" + | |||||
ATTRIBUTE_FROM + "\" in \"" + attribute.getName() + ": " + attribute.getValue() + "\"" ); | |||||
} | |||||
else | |||||
{ | |||||
// classpath attributes go into a vector | |||||
String attributeName = attribute.getName().toLowerCase(); | |||||
if( attributeName.equals( ATTRIBUTE_CLASSPATH ) ) | |||||
{ | |||||
ArrayList classpathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
if( classpathAttrs == null ) | |||||
{ | |||||
classpathAttrs = new ArrayList(); | |||||
attributes.put( attributeName, classpathAttrs ); | |||||
} | |||||
classpathAttrs.add( 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 ); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public void addAttribute( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
String check = addAttributeAndCheck( attribute ); | |||||
if( check != null ) | |||||
{ | |||||
throw new TaskException( "Specify the section name using the \"name\" attribute of the <section> element rather " + | |||||
"than using a \"Name\" manifest attribute" ); | |||||
} | |||||
} | |||||
public boolean equals( Object rhs ) | |||||
{ | |||||
if( !( rhs instanceof Section ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
Section rhsSection = (Section)rhs; | |||||
if( attributes.size() != rhsSection.attributes.size() ) | |||||
{ | |||||
return false; | |||||
} | |||||
for( Enumeration e = attributes.elements(); e.hasMoreElements(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e.nextElement(); | |||||
Attribute rshAttribute = (Attribute)rhsSection.attributes.get( attribute.getName().toLowerCase() ); | |||||
if( !attribute.equals( rshAttribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* Merge in another section | |||||
* | |||||
* @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 | |||||
{ | |||||
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(); ) | |||||
{ | |||||
String attributeName = (String)e.nextElement(); | |||||
if( attributeName.equals( ATTRIBUTE_CLASSPATH ) && | |||||
attributes.containsKey( attributeName ) ) | |||||
{ | |||||
// classpath entries are vetors which are merged | |||||
ArrayList classpathAttrs = (ArrayList)section.attributes.get( attributeName ); | |||||
ArrayList ourClasspathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
for( Iterator e2 = classpathAttrs.iterator(); e2.hasNext(); ) | |||||
{ | |||||
ourClasspathAttrs.add( e2.next() ); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
// the merge file always wins | |||||
attributes.put( attributeName, section.attributes.get( attributeName ) ); | |||||
} | |||||
} | |||||
// add in the warnings | |||||
for( Iterator e = section.warnings.iterator(); e.hasNext(); ) | |||||
{ | |||||
warnings.add( e.next() ); | |||||
} | |||||
} | |||||
/** | |||||
* 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, TaskException | |||||
{ | |||||
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 ); | |||||
if( nameReadAhead != null ) | |||||
{ | |||||
return nameReadAhead; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Remove tge given attribute from the section | |||||
* | |||||
* @param attributeName the name of the attribute to be removed. | |||||
*/ | |||||
public void removeAttribute( String attributeName ) | |||||
{ | |||||
attributes.remove( attributeName.toLowerCase() ); | |||||
} | |||||
/** | |||||
* Write the section out to a print writer. | |||||
* | |||||
* @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 | |||||
{ | |||||
if( name != null ) | |||||
{ | |||||
Attribute nameAttr = new Attribute( ATTRIBUTE_NAME, name ); | |||||
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 | |||||
{ | |||||
ArrayList attrList = (ArrayList)object; | |||||
for( Iterator e2 = attrList.iterator(); e2.hasNext(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e2.next(); | |||||
attribute.write( writer ); | |||||
} | |||||
} | |||||
} | |||||
writer.println(); | |||||
} | |||||
} | |||||
} | } |
@@ -0,0 +1,27 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
/** | |||||
* Helper class for Manifest's mode attribute. | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public class ManifestMode | |||||
extends EnumeratedAttribute | |||||
{ | |||||
public String[] getValues() | |||||
{ | |||||
return new String[]{"update", "replace"}; | |||||
} | |||||
} |
@@ -0,0 +1,78 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import java.io.IOException; | |||||
import java.io.PrintWriter; | |||||
/** | |||||
* Utility methods for manifest stuff. | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public final class ManifestUtil | |||||
{ | |||||
public static Attribute buildAttribute( final String line ) | |||||
throws ManifestException | |||||
{ | |||||
final Attribute attribute = new Attribute(); | |||||
parse( attribute, line ); | |||||
return attribute; | |||||
} | |||||
/** | |||||
* 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 static void parse( final Attribute attribute, final String line ) | |||||
throws ManifestException | |||||
{ | |||||
final 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 ': ' " ); | |||||
} | |||||
final String name = line.substring( 0, index ); | |||||
final String value = line.substring( index + 2 ); | |||||
attribute.setName( name ); | |||||
attribute.setValue( value ); | |||||
} | |||||
public static void write( final Attribute attribute, final PrintWriter writer ) | |||||
throws IOException | |||||
{ | |||||
final String name = attribute.getName(); | |||||
final String value = attribute.getValue(); | |||||
String line = name + ": " + value; | |||||
while( line.getBytes().length > Manifest.MAX_LINE_LENGTH ) | |||||
{ | |||||
// try to find a MAX_LINE_LENGTH byte section | |||||
int breakIndex = Manifest.MAX_LINE_LENGTH; | |||||
String section = line.substring( 0, breakIndex ); | |||||
while( section.getBytes().length > Manifest.MAX_LINE_LENGTH && breakIndex > 0 ) | |||||
{ | |||||
breakIndex--; | |||||
section = line.substring( 0, breakIndex ); | |||||
} | |||||
if( breakIndex == 0 ) | |||||
{ | |||||
throw new IOException( "Unable to write manifest line " + name + ": " + value ); | |||||
} | |||||
writer.println( section ); | |||||
line = " " + line.substring( breakIndex ); | |||||
} | |||||
writer.println( line ); | |||||
} | |||||
} |
@@ -0,0 +1,332 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE.txt file. | |||||
*/ | |||||
package org.apache.tools.ant.taskdefs.manifest; | |||||
import java.io.BufferedReader; | |||||
import java.io.IOException; | |||||
import java.io.PrintWriter; | |||||
import java.util.ArrayList; | |||||
import java.util.Enumeration; | |||||
import java.util.Hashtable; | |||||
import java.util.Iterator; | |||||
import org.apache.myrmidon.api.TaskException; | |||||
/** | |||||
* Class to represent an individual section in the Manifest. A section | |||||
* consists of a set of attribute values, separated from other sections by a | |||||
* blank line. | |||||
* | |||||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public class Section | |||||
{ | |||||
private ArrayList warnings = new ArrayList(); | |||||
/** | |||||
* The section's name if any. The main section in a manifest is unnamed. | |||||
*/ | |||||
private String name = null; | |||||
/** | |||||
* The section's attributes. | |||||
*/ | |||||
private Hashtable attributes = new Hashtable(); | |||||
/** | |||||
* Set the Section's name | |||||
* | |||||
* @param name the section's name | |||||
*/ | |||||
public void setName( String name ) | |||||
{ | |||||
this.name = name; | |||||
} | |||||
/** | |||||
* 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 ) | |||||
{ | |||||
Object attribute = attributes.get( attributeName.toLowerCase() ); | |||||
if( attribute == null ) | |||||
{ | |||||
return null; | |||||
} | |||||
if( attribute instanceof Attribute ) | |||||
{ | |||||
return ( (Attribute)attribute ).getValue(); | |||||
} | |||||
else | |||||
{ | |||||
String value = ""; | |||||
for( Iterator e = ( (ArrayList)attribute ).iterator(); e.hasNext(); ) | |||||
{ | |||||
Attribute classpathAttribute = (Attribute)e.next(); | |||||
value += classpathAttribute.getValue() + " "; | |||||
} | |||||
return value.trim(); | |||||
} | |||||
} | |||||
/** | |||||
* Get the Section's name | |||||
* | |||||
* @return the section's name. | |||||
*/ | |||||
public String getName() | |||||
{ | |||||
return name; | |||||
} | |||||
public Iterator getWarnings() | |||||
{ | |||||
return warnings.iterator(); | |||||
} | |||||
/** | |||||
* 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 | |||||
* @throws ManifestException if the attribute already exists in this | |||||
* section. | |||||
*/ | |||||
public String addAttributeAndCheck( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
if( attribute.getName() == null || attribute.getValue() == null ) | |||||
{ | |||||
throw new TaskException( "Attributes must have name and value" ); | |||||
} | |||||
if( attribute.getName().equalsIgnoreCase( Manifest.ATTRIBUTE_NAME ) ) | |||||
{ | |||||
warnings.add( "\"" + Manifest.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( attribute.getName().toLowerCase().startsWith( Manifest.ATTRIBUTE_FROM.toLowerCase() ) ) | |||||
{ | |||||
warnings.add( "Manifest attributes should not start with \"" + | |||||
Manifest.ATTRIBUTE_FROM + "\" in \"" + attribute.getName() + ": " + attribute.getValue() + "\"" ); | |||||
} | |||||
else | |||||
{ | |||||
// classpath attributes go into a vector | |||||
String attributeName = attribute.getName().toLowerCase(); | |||||
if( attributeName.equals( Manifest.ATTRIBUTE_CLASSPATH ) ) | |||||
{ | |||||
ArrayList classpathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
if( classpathAttrs == null ) | |||||
{ | |||||
classpathAttrs = new ArrayList(); | |||||
attributes.put( attributeName, classpathAttrs ); | |||||
} | |||||
classpathAttrs.add( 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 ); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public void addAttribute( Attribute attribute ) | |||||
throws ManifestException, TaskException | |||||
{ | |||||
String check = addAttributeAndCheck( attribute ); | |||||
if( check != null ) | |||||
{ | |||||
throw new TaskException( "Specify the section name using the \"name\" attribute of the <section> element rather " + | |||||
"than using a \"Name\" manifest attribute" ); | |||||
} | |||||
} | |||||
public boolean equals( Object rhs ) | |||||
{ | |||||
if( !( rhs instanceof Section ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
Section rhsSection = (Section)rhs; | |||||
if( attributes.size() != rhsSection.attributes.size() ) | |||||
{ | |||||
return false; | |||||
} | |||||
for( Enumeration e = attributes.elements(); e.hasMoreElements(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e.nextElement(); | |||||
Attribute rshAttribute = (Attribute)rhsSection.attributes.get( attribute.getName().toLowerCase() ); | |||||
if( !attribute.equals( rshAttribute ) ) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* Merge in another section | |||||
* | |||||
* @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 | |||||
{ | |||||
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(); ) | |||||
{ | |||||
String attributeName = (String)e.nextElement(); | |||||
if( attributeName.equals( Manifest.ATTRIBUTE_CLASSPATH ) && | |||||
attributes.containsKey( attributeName ) ) | |||||
{ | |||||
// classpath entries are vetors which are merged | |||||
ArrayList classpathAttrs = (ArrayList)section.attributes.get( attributeName ); | |||||
ArrayList ourClasspathAttrs = (ArrayList)attributes.get( attributeName ); | |||||
for( Iterator e2 = classpathAttrs.iterator(); e2.hasNext(); ) | |||||
{ | |||||
ourClasspathAttrs.add( e2.next() ); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
// the merge file always wins | |||||
attributes.put( attributeName, section.attributes.get( attributeName ) ); | |||||
} | |||||
} | |||||
// add in the warnings | |||||
for( Iterator e = section.warnings.iterator(); e.hasNext(); ) | |||||
{ | |||||
warnings.add( e.next() ); | |||||
} | |||||
} | |||||
/** | |||||
* 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, TaskException | |||||
{ | |||||
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 = ManifestUtil.buildAttribute( line ); | |||||
String nameReadAhead = addAttributeAndCheck( attribute ); | |||||
if( nameReadAhead != null ) | |||||
{ | |||||
return nameReadAhead; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Remove tge given attribute from the section | |||||
* | |||||
* @param attributeName the name of the attribute to be removed. | |||||
*/ | |||||
public void removeAttribute( String attributeName ) | |||||
{ | |||||
attributes.remove( attributeName.toLowerCase() ); | |||||
} | |||||
/** | |||||
* Write the section out to a print writer. | |||||
* | |||||
* @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 | |||||
{ | |||||
if( name != null ) | |||||
{ | |||||
Attribute nameAttr = new Attribute( Manifest.ATTRIBUTE_NAME, name ); | |||||
ManifestUtil.write( nameAttr, writer ); | |||||
} | |||||
for( Enumeration e = attributes.elements(); e.hasMoreElements(); ) | |||||
{ | |||||
Object object = e.nextElement(); | |||||
if( object instanceof Attribute ) | |||||
{ | |||||
Attribute attribute = (Attribute)object; | |||||
ManifestUtil.write( attribute, writer ); | |||||
} | |||||
else | |||||
{ | |||||
ArrayList attrList = (ArrayList)object; | |||||
for( Iterator e2 = attrList.iterator(); e2.hasNext(); ) | |||||
{ | |||||
Attribute attribute = (Attribute)e2.next(); | |||||
ManifestUtil.write( attribute, writer ); | |||||
} | |||||
} | |||||
} | |||||
writer.println(); | |||||
} | |||||
} |