From 6518cd22e07e4c95cbb65a9ce11232b9744c73f4 Mon Sep 17 00:00:00 2001 From: Peter Donald Date: Thu, 7 Feb 2002 13:22:03 +0000 Subject: [PATCH] Start to clean up the clean up the manifest task and extract the useful bits into separate classes and adaptors. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271212 13f79535-47bb-0310-9956-ffa450edef68 --- .../ant/taskdefs/manifest/Attribute.java | 119 ++++ .../tools/ant/taskdefs/manifest/Manifest.java | 507 +----------------- .../ant/taskdefs/manifest/ManifestMode.java | 27 + .../ant/taskdefs/manifest/ManifestUtil.java | 78 +++ .../tools/ant/taskdefs/manifest/Section.java | 332 ++++++++++++ .../ant/taskdefs/manifest/Attribute.java | 119 ++++ .../tools/ant/taskdefs/manifest/Manifest.java | 507 +----------------- .../ant/taskdefs/manifest/ManifestMode.java | 27 + .../ant/taskdefs/manifest/ManifestUtil.java | 78 +++ .../tools/ant/taskdefs/manifest/Section.java | 332 ++++++++++++ 10 files changed, 1124 insertions(+), 1002 deletions(-) create mode 100644 proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Attribute.java create mode 100644 proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java create mode 100644 proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java create mode 100644 proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Section.java create mode 100644 proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Attribute.java create mode 100644 proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java create mode 100644 proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java create mode 100644 proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Section.java diff --git a/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Attribute.java b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Attribute.java new file mode 100644 index 000000000..7c7bb5514 --- /dev/null +++ b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Attribute.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @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 ) ); + } + +} diff --git a/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Manifest.java b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Manifest.java index 5cf5289a5..b531ad3bf 100644 --- a/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Manifest.java +++ b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Manifest.java @@ -25,24 +25,18 @@ import java.util.Iterator; import java.util.jar.Attributes; import org.apache.myrmidon.api.AbstractTask; import org.apache.myrmidon.api.TaskException; -import org.apache.tools.ant.types.EnumeratedAttribute; /** * Class to manage Manifest information * + * @author Peter Donald * @author Conor MacNeill * @author Stefan Bodewig - * @author Peter Donald * @version $Revision$ $Date$ */ public class Manifest extends AbstractTask { - /** - * The standard manifest version header - */ - public final static String ATTRIBUTE_MANIFEST_VERSION = Attributes.Name.MANIFEST_VERSION.toString(); - /** * The standard Signature Version header */ @@ -119,11 +113,11 @@ public class Manifest BufferedReader reader = new BufferedReader( r ); // This should be the manifest version 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 ) { m_manifestVersion = readManifestVersion; - m_mainSection.removeAttribute( ATTRIBUTE_MANIFEST_VERSION ); + m_mainSection.removeAttribute( Attributes.Name.MANIFEST_VERSION.toString() ); } String line = null; @@ -137,7 +131,7 @@ public class Manifest Section section = new Section(); if( nextSectionName == null ) { - Attribute sectionName = new Attribute( line ); + Attribute sectionName = ManifestUtil.buildAttribute( line ); if( !sectionName.getName().equalsIgnoreCase( 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 // this line is the first attribute. set it and then let the normal // read handle the rest - Attribute firstAttribute = new Attribute( line ); + Attribute firstAttribute = ManifestUtil.buildAttribute( line ); section.addAttributeAndCheck( firstAttribute ); } @@ -438,7 +432,7 @@ public class Manifest public void write( PrintWriter writer ) 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 ); 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
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(); - } - } - } diff --git a/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java new file mode 100644 index 000000000..a551bd1ef --- /dev/null +++ b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @version $Revision$ $Date$ + */ +public class ManifestMode + extends EnumeratedAttribute +{ + public String[] getValues() + { + return new String[]{"update", "replace"}; + } +} diff --git a/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java new file mode 100644 index 000000000..8a2a46809 --- /dev/null +++ b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @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 ); + } +} diff --git a/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Section.java b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Section.java new file mode 100644 index 000000000..a15b51b19 --- /dev/null +++ b/proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/manifest/Section.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @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
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(); + } +} diff --git a/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Attribute.java b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Attribute.java new file mode 100644 index 000000000..7c7bb5514 --- /dev/null +++ b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Attribute.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @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 ) ); + } + +} diff --git a/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Manifest.java b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Manifest.java index 5cf5289a5..b531ad3bf 100644 --- a/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Manifest.java +++ b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Manifest.java @@ -25,24 +25,18 @@ import java.util.Iterator; import java.util.jar.Attributes; import org.apache.myrmidon.api.AbstractTask; import org.apache.myrmidon.api.TaskException; -import org.apache.tools.ant.types.EnumeratedAttribute; /** * Class to manage Manifest information * + * @author Peter Donald * @author Conor MacNeill * @author Stefan Bodewig - * @author Peter Donald * @version $Revision$ $Date$ */ public class Manifest extends AbstractTask { - /** - * The standard manifest version header - */ - public final static String ATTRIBUTE_MANIFEST_VERSION = Attributes.Name.MANIFEST_VERSION.toString(); - /** * The standard Signature Version header */ @@ -119,11 +113,11 @@ public class Manifest BufferedReader reader = new BufferedReader( r ); // This should be the manifest version 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 ) { m_manifestVersion = readManifestVersion; - m_mainSection.removeAttribute( ATTRIBUTE_MANIFEST_VERSION ); + m_mainSection.removeAttribute( Attributes.Name.MANIFEST_VERSION.toString() ); } String line = null; @@ -137,7 +131,7 @@ public class Manifest Section section = new Section(); if( nextSectionName == null ) { - Attribute sectionName = new Attribute( line ); + Attribute sectionName = ManifestUtil.buildAttribute( line ); if( !sectionName.getName().equalsIgnoreCase( 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 // this line is the first attribute. set it and then let the normal // read handle the rest - Attribute firstAttribute = new Attribute( line ); + Attribute firstAttribute = ManifestUtil.buildAttribute( line ); section.addAttributeAndCheck( firstAttribute ); } @@ -438,7 +432,7 @@ public class Manifest public void write( PrintWriter writer ) 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 ); 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
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(); - } - } - } diff --git a/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java new file mode 100644 index 000000000..a551bd1ef --- /dev/null +++ b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestMode.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @version $Revision$ $Date$ + */ +public class ManifestMode + extends EnumeratedAttribute +{ + public String[] getValues() + { + return new String[]{"update", "replace"}; + } +} diff --git a/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java new file mode 100644 index 000000000..8a2a46809 --- /dev/null +++ b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/ManifestUtil.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @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 ); + } +} diff --git a/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Section.java b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Section.java new file mode 100644 index 000000000..a15b51b19 --- /dev/null +++ b/proposal/myrmidon/src/todo/org/apache/tools/ant/taskdefs/manifest/Section.java @@ -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 Conor MacNeill + * @author Stefan Bodewig + * @author Peter Donald + * @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
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(); + } +}