adammurdoch 02/03/10 22:07:24
Modified: proposal/myrmidon/etc/testcases/org/apache/antlib/core
property.ant
proposal/myrmidon/src/java/org/apache/myrmidon/components/builder
DefaultProjectBuilder.java Resources.properties
proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace
DefaultTaskContext.java Resources.properties
proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/builder
ProjectBuilder.java
proposal/myrmidon/src/testcases/org/apache/antlib/core
PropertyTest.java
proposal/myrmidon/src/xdocs todo.xml
Added:
proposal/myrmidon/etc/testcases/org/apache/myrmidon/components/builder
bad-project-name.ant bad-target-name.ant
proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/builder
ProjectException.java
proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model
DefaultNameValidator.java NameValidator.java
Resources.properties
proposal/myrmidon/src/testcases/org/apache/myrmidon/components/builder
DefaultProjectBuilderTest.java
proposal/myrmidon/src/testcases/org/apache/myrmidon/interfaces/model
DefaultNameValidatorTest.java
Log:
* Added NameValidator, to provide a reasonably flexible mechanism for
specifying/testing name validity.
* NameValidator is used by DefaultProjectBuilder for project and target
names, and by DefaultTaskContext for Property names.
* Added ProjectException, thrown by ProjectBuilder.build().
* Tidy-up error messages for project building errors.
* Added a bunch of tests
Submitted by Darrell DeBoer [EMAIL PROTECTED]
Revision Changes Path
1.3 +12 -0
jakarta-ant/proposal/myrmidon/etc/testcases/org/apache/antlib/core/property.ant
Index: property.ant
===================================================================
RCS file:
/home/cvs/jakarta-ant/proposal/myrmidon/etc/testcases/org/apache/antlib/core/property.ant,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- property.ant 9 Mar 2002 10:13:03 -0000 1.2
+++ property.ant 11 Mar 2002 06:07:23 -0000 1.3
@@ -50,4 +50,16 @@
<property-test-type value="value 3"/>
</property>
</target>
+
+ <!-- Test properties with invalid names -->
+ <target name="bad-prop-name1">
+ <property name="badname!" value="value"/>
+ </target>
+ <target name="bad-prop-name2">
+ <property name="bad name" value="value"/>
+ </target>
+ <target name="bad-prop-name3">
+ <property name="" value="value"/>
+ </target>
+
</project>
1.1
jakarta-ant/proposal/myrmidon/etc/testcases/org/apache/myrmidon/components/builder/bad-project-name.ant
Index: bad-project-name.ant
===================================================================
<!-- Project with an invalid name -->
<project version="2.0" name="!badname">
<target name="main"/>
</project>
1.1
jakarta-ant/proposal/myrmidon/etc/testcases/org/apache/myrmidon/components/builder/bad-target-name.ant
Index: bad-target-name.ant
===================================================================
<!-- Target with an invalid name -->
<project version="2.0" name="ok name">
<target name="main"/>
<target name="bad ^ name"/>
</project>
1.37 +135 -71
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/DefaultProjectBuilder.java
Index: DefaultProjectBuilder.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/DefaultProjectBuilder.java,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -r1.36 -r1.37
--- DefaultProjectBuilder.java 4 Mar 2002 04:23:37 -0000 1.36
+++ DefaultProjectBuilder.java 11 Mar 2002 06:07:23 -0000 1.37
@@ -8,6 +8,7 @@
package org.apache.myrmidon.components.builder;
import java.io.File;
+import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
@@ -22,11 +23,13 @@
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.SAXConfigurationHandler;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.myrmidon.framework.conditions.Condition;
import org.apache.myrmidon.framework.conditions.AndCondition;
+import org.apache.myrmidon.framework.conditions.Condition;
import org.apache.myrmidon.framework.conditions.IsSetCondition;
import org.apache.myrmidon.framework.conditions.NotCondition;
import org.apache.myrmidon.interfaces.builder.ProjectBuilder;
+import org.apache.myrmidon.interfaces.builder.ProjectException;
+import org.apache.myrmidon.interfaces.model.DefaultNameValidator;
import org.apache.myrmidon.interfaces.model.Project;
import org.apache.myrmidon.interfaces.model.Target;
import org.apache.myrmidon.interfaces.model.TypeLib;
@@ -36,7 +39,8 @@
* Default implementation to construct project from a build file.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Peter Donald</a>
- * @version $Revision: 1.36 $ $Date: 2002/03/04 04:23:37 $
+ * @version $Revision: 1.37 $ $Date: 2002/03/11 06:07:23 $
+ *
* @ant:type type="project-builder" name="xml"
* @ant:type type="project-builder" name="ant"
*/
@@ -54,36 +58,37 @@
private final static int IMPLICIT_TASKS = 2;
private final static int TARGETS = 3;
+ // Use a name validator with the default rules.
+ private DefaultNameValidator m_nameValidator = new
DefaultNameValidator();
+
/**
* build a project from file.
*
* @param source the source
* @return the constructed Project
- * @exception Exception if an error occurs
+ * @exception ProjectException if an error occurs
*/
public Project build( final String source )
- throws Exception
+ throws ProjectException
{
final File file = new File( source );
return build( file, new HashMap() );
}
private Project build( final File file, final HashMap projects )
- throws Exception
+ throws ProjectException
{
- final URL systemID = file.toURL();
+ final URL systemID = extractURL( file );
final Project result = (Project)projects.get( systemID.toString() );
if( null != result )
{
return result;
}
- final SAXConfigurationHandler handler = new
SAXConfigurationHandler();
-
- process( systemID, handler );
-
- final Configuration configuration = handler.getConfiguration();
+ // Parse the project file
+ final Configuration configuration = parseProject( systemID );
+ // Build the project model
final DefaultProject project = buildProject( file, configuration );
projects.put( systemID.toString(), project );
@@ -94,20 +99,33 @@
return project;
}
- protected void process( final URL systemID,
- final SAXConfigurationHandler handler )
- throws Exception
- {
- final SAXParserFactory saxParserFactory =
SAXParserFactory.newInstance();
- final SAXParser saxParser = saxParserFactory.newSAXParser();
- final XMLReader parser = saxParser.getXMLReader();
- parser.setFeature( "http://xml.org/sax/features/namespace-prefixes",
false );
- parser.setFeature( "http://xml.org/sax/features/namespaces", false );
- //parser.setFeature( "http://xml.org/sax/features/validation", false
);
-
- parser.setContentHandler( handler );
- parser.setErrorHandler( handler );
- parser.parse( systemID.toString() );
+ /**
+ * Parses the project.
+ */
+ private Configuration parseProject( final URL systemID )
+ throws ProjectException
+ {
+ try
+ {
+ final SAXConfigurationHandler handler = new
SAXConfigurationHandler();
+ final SAXParserFactory saxParserFactory =
SAXParserFactory.newInstance();
+ final SAXParser saxParser = saxParserFactory.newSAXParser();
+ final XMLReader parser = saxParser.getXMLReader();
+ parser.setFeature(
"http://xml.org/sax/features/namespace-prefixes", false );
+ parser.setFeature( "http://xml.org/sax/features/namespaces",
false );
+ //parser.setFeature( "http://xml.org/sax/features/validation",
false );
+
+ parser.setContentHandler( handler );
+ parser.setErrorHandler( handler );
+ parser.parse( systemID.toString() );
+
+ return handler.getConfiguration();
+ }
+ catch( Exception e )
+ {
+ String message = REZ.getString( "ant.project-parse.error" );
+ throw new ProjectException( message, e );
+ }
}
/**
@@ -116,23 +134,20 @@
* @param file the file from which configuration was loaded
* @param configuration the configuration loaded
* @return the created Project
- * @exception Exception if an error occurs
- * @exception Exception if an error occurs
- * @exception ConfigurationException if an error occurs
+ * @exception ProjectException if an error occurs building the project
*/
private DefaultProject buildProject( final File file,
final Configuration configuration )
- throws Exception
+ throws ProjectException
{
if( !configuration.getName().equals( "project" ) )
{
final String message = REZ.getString(
"ant.no-project-element.error" );
- throw new Exception( message );
+ throw new ProjectException( message );
}
//get project-level attributes
- final String projectName = configuration.getAttribute( "name",
-
FileUtil.removeExtension( file.getName() ) );
+ final String projectName = getProjectName( configuration, file );
final String baseDirectoryName = configuration.getAttribute(
"basedir", null );
final String defaultTarget = configuration.getAttribute( "default",
"main" );
final Version version = getVersion( configuration );
@@ -141,7 +156,7 @@
{
final String message =
REZ.getString( "ant.bad-version.error", VERSION, version );
- throw new Exception( message );
+ throw new ProjectException( message );
}
//determine base directory for project. Use the directory containing
@@ -169,11 +184,51 @@
}
/**
+ * Get the project name from the configuration, or create a default name
if none
+ * was supplied.
+ */
+ private String getProjectName( final Configuration configuration, final
File file )
+ throws ProjectException
+ {
+ String projectName = configuration.getAttribute( "name", null );
+
+ if( projectName == null )
+ {
+ // Create a name based on the file name.
+ String fileNameBase = FileUtil.removeExtension( file.getName() );
+ try
+ {
+ projectName = m_nameValidator.makeValidName( fileNameBase );
+ }
+ catch( Exception e )
+ {
+ String message = REZ.getString(
"ant.project-create-name.error" );
+ throw new ProjectException( message, e );
+ }
+ }
+ else
+ {
+ // Make sure the supplied name is valid.
+ try
+ {
+ m_nameValidator.validate( projectName );
+ }
+ catch( Exception e )
+ {
+ String message = REZ.getString( "ant.project-bad-name.error"
);
+ throw new ProjectException( message, e );
+ }
+ }
+ return projectName;
+
+ }
+
+ /**
* Retrieve the version attribute from the specified configuration
element.
* Throw exceptions with meaningful errors if malformed or missing.
*/
private Version getVersion( final Configuration configuration )
- throws Exception
+ throws ProjectException
{
try
{
@@ -183,7 +238,7 @@
catch( final ConfigurationException ce )
{
final String message = REZ.getString(
"ant.version-missing.error" );
- throw new ConfigurationException( message, ce );
+ throw new ProjectException( message, ce );
}
}
@@ -191,7 +246,7 @@
* Utility function to extract version
*/
private Version parseVersion( final String versionString )
- throws Exception
+ throws ProjectException
{
try
@@ -202,8 +257,7 @@
{
final String message =
REZ.getString( "ant.malformed.version", versionString );
- getLogger().warn( message );
- throw new ConfigurationException( message, e );
+ throw new ProjectException( message, e );
}
}
@@ -212,12 +266,12 @@
*
* @param project the project
* @param configuration the Configuration
- * @exception Exception if an error occurs
+ * @exception ProjectException if an error occurs
*/
private void buildTopLevelProject( final DefaultProject project,
final Configuration configuration,
final HashMap projects )
- throws Exception
+ throws ProjectException
{
final ArrayList implicitTaskList = new ArrayList();
final Configuration[] children = configuration.getChildren();
@@ -277,7 +331,7 @@
{
final String message =
REZ.getString( "ant.unknown-toplevel-element.error",
name, element.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message );
}
}
@@ -291,7 +345,7 @@
private void buildProjectRef( final DefaultProject project,
final Configuration element,
final HashMap projects )
- throws Exception
+ throws ProjectException
{
final String name = element.getAttribute( "name", null );
final String location = element.getAttribute( "location", null );
@@ -300,27 +354,31 @@
{
final String message =
REZ.getString( "ant.projectref-no-name.error",
element.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message );
}
- if( !validName( name ) )
+ try
+ {
+ m_nameValidator.validate( name );
+ }
+ catch( Exception e )
{
final String message =
REZ.getString( "ant.projectref-bad-name.error",
element.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message, e );
}
if( null == location )
{
final String message =
REZ.getString( "ant.projectref-no-location.error",
element.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message );
}
// Build the URL of the referenced projects
final File baseDirectory = project.getBaseDirectory();
final File file = FileUtil.resolveFile( baseDirectory, location );
- final String systemID = file.toURL().toString();
+ final String systemID = extractURL( file ).toString();
// Locate the referenced project, building it if necessary
Project other = (Project)projects.get( systemID );
@@ -333,9 +391,22 @@
project.addProject( name, other );
}
+ private URL extractURL( final File file ) throws ProjectException
+ {
+ try
+ {
+ return file.toURL();
+ }
+ catch( MalformedURLException e )
+ {
+ final String message = REZ.getString(
"ant.project-unexpected.error" );
+ throw new ProjectException( message, e );
+ }
+ }
+
private void buildTypeLib( final DefaultProject project,
final Configuration element )
- throws Exception
+ throws ProjectException
{
final String library = element.getAttribute( "library", null );
final String name = element.getAttribute( "name", null );
@@ -345,7 +416,7 @@
{
final String message =
REZ.getString( "ant.import-no-library.error",
element.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message );
}
if( null == name || null == type )
@@ -354,7 +425,7 @@
{
final String message =
REZ.getString( "ant.import-malformed.error",
element.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message );
}
}
@@ -368,14 +439,14 @@
* @param target the Configuration
*/
private void buildTarget( final DefaultProject project, final
Configuration target )
- throws Exception
+ throws ProjectException
{
final String name = target.getAttribute( "name", null );
final String depends = target.getAttribute( "depends", null );
final String ifCondition = target.getAttribute( "if", null );
final String unlessCondition = target.getAttribute( "unless", null );
- verifyName( name, target );
+ verifyTargetName( name, target );
if( getLogger().isDebugEnabled() )
{
@@ -392,24 +463,30 @@
project.addTarget( name, defaultTarget );
}
- private void verifyName( final String name, final Configuration target )
throws Exception
+ private void verifyTargetName( final String name, final Configuration
target )
+ throws ProjectException
{
if( null == name )
{
final String message =
REZ.getString( "ant.target-noname.error",
target.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message );
}
- if( !validName( name ) )
+ try
+ {
+ m_nameValidator.validate( name );
+ }
+ catch( Exception e )
{
final String message =
REZ.getString( "ant.target-bad-name.error",
target.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message, e );
}
}
- private String[] buildDependsList( final String depends, final
Configuration target ) throws Exception
+ private String[] buildDependsList( final String depends, final
Configuration target )
+ throws ProjectException
{
String[] dependencies = null;
@@ -428,7 +505,7 @@
final String message = REZ.getString(
"ant.target-bad-dependency.error",
target.getName(),
target.getLocation() );
- throw new Exception( message );
+ throw new ProjectException( message );
}
if( getLogger().isDebugEnabled() )
@@ -447,7 +524,6 @@
private Condition buildCondition( final String ifCondition,
final String unlessCondition )
- throws Exception
{
final AndCondition condition = new AndCondition();
@@ -474,17 +550,5 @@
}
return condition;
- }
-
- protected boolean validName( final String name )
- {
- if( -1 != name.indexOf( "->" ) )
- {
- return false;
- }
- else
- {
- return true;
- }
}
}
1.6 +5 -1
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/Resources.properties
Index: Resources.properties
===================================================================
RCS file:
/home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/Resources.properties,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- Resources.properties 3 Mar 2002 02:19:10 -0000 1.5
+++ Resources.properties 11 Mar 2002 06:07:23 -0000 1.6
@@ -10,10 +10,14 @@
ant.project-banner.notice=Project {0} base directory: {1}.
ant.target-parse.notice=Parsing target: {0}.
ant.target-if.notice=Target if condition: {0}
-ant.target- unless.notice=Target unless condition: {0}
+ant.target-unless.notice=Target unless condition: {0}
ant.target-dependency.notice=Target dependency: {0}
+ant.project-unexpected.error=Unexpected error building project.
+ant.project-parse.error=Error parsing project.
ant.no-project-element.error=Project file must be enclosed in project
element.
ant.unknown-toplevel-element.error=Unknown top-level element {0} at {1}.
+ant.project-bad-name.error=Invalid project name.
+ant.project-create-name.error=Could not create a name for this project.
ant.projectref-no-name.error=Malformed projectref without a name attribute
at {0}.
ant.projectref-bad-name.error=Projectref with an invalid name attribute at
{0}.
ant.projectref-no-location.error=Malformed projectref without a location
attribute at {0}.
1.20 +25 -1
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java
Index: DefaultTaskContext.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- DefaultTaskContext.java 9 Mar 2002 02:04:26 -0000 1.19
+++ DefaultTaskContext.java 11 Mar 2002 06:07:24 -0000 1.20
@@ -20,13 +20,14 @@
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.myrmidon.api.TaskContext;
import org.apache.myrmidon.api.TaskException;
+import org.apache.myrmidon.interfaces.model.DefaultNameValidator;
import org.apache.myrmidon.interfaces.workspace.PropertyResolver;
/**
* Default implementation of TaskContext.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Peter Donald</a>
- * @version $Revision: 1.19 $ $Date: 2002/03/09 02:04:26 $
+ * @version $Revision: 1.20 $ $Date: 2002/03/11 06:07:24 $
*/
public class DefaultTaskContext
implements TaskContext, Context
@@ -34,6 +35,12 @@
private final static Resources REZ =
ResourceManager.getPackageResources( DefaultTaskContext.class );
+ // Property name validator allows digits, but no internal whitespace.
+ private static DefaultNameValidator m_propertyNameValidator = new
DefaultNameValidator();
+ static {
+ m_propertyNameValidator.setAllowInternalWhitespace( false );
+ }
+
private final Map m_contextData = new Hashtable();
private final TaskContext m_parent;
private ServiceManager m_serviceManager;
@@ -199,6 +206,7 @@
public void setProperty( final String name, final Object value )
throws TaskException
{
+ checkPropertyName( name );
checkPropertyValid( name, value );
m_contextData.put( name, value );
}
@@ -360,6 +368,22 @@
throw new ContextException( message );
}
return value;
+ }
+
+ /**
+ * Checks that the supplied property name is valid.
+ */
+ private void checkPropertyName( final String name ) throws TaskException
+ {
+ try
+ {
+ m_propertyNameValidator.validate( name );
+ }
+ catch( Exception e )
+ {
+ String message = REZ.getString( "bad-property-name.error" );
+ throw new TaskException( message, e );
+ }
}
/**
1.9 +1 -0
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/Resources.properties
Index: Resources.properties
===================================================================
RCS file:
/home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/Resources.properties,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- Resources.properties 9 Mar 2002 02:04:26 -0000 1.8
+++ Resources.properties 11 Mar 2002 06:07:24 -0000 1.9
@@ -13,6 +13,7 @@
#DefaultTaskContext
unknown-prop.error=Unknown property {0}.
bad-property.error=Property {0} must have a value of type {1}.
+bad-property-name.error=Invalid property name.
null-resolved-value.error=Value "{0}" resolved to null.
bad-resolve.error=Unable to resolve value "{0}".
bad-find-service.error=Could not find service "{0}".
1.9 +3 -3
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/builder/ProjectBuilder.java
Index: ProjectBuilder.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/builder/ProjectBuilder.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- ProjectBuilder.java 21 Feb 2002 11:06:42 -0000 1.8
+++ ProjectBuilder.java 11 Mar 2002 06:07:24 -0000 1.9
@@ -13,7 +13,7 @@
* Interface implemented by components that build projects from sources.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Peter Donald</a>
- * @version $Revision: 1.8 $ $Date: 2002/02/21 11:06:42 $
+ * @version $Revision: 1.9 $ $Date: 2002/03/11 06:07:24 $
* @ant:role shorthand="project-builder"
*/
public interface ProjectBuilder
@@ -25,8 +25,8 @@
*
* @param source the source
* @return the constructed Project
- * @exception Exception if an error occurs
+ * @exception ProjectException if an error occurs
*/
Project build( String source )
- throws Exception;
+ throws ProjectException;
}
1.1
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/builder/ProjectException.java
Index: ProjectException.java
===================================================================
/*
* 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.myrmidon.interfaces.builder;
/**
* A cascading exception thrown on a problem constructing a Project model.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Darrell DeBoer</a>
* @version $Revision: 1.1 $ $Date: 2002/03/11 06:07:24 $
*/
public class ProjectException
extends Exception
{
/**
* If this exception is cascaded, the cause of this exception.
*/
private final Throwable m_throwable;
/**
* Constructs an non-cascaded exception with a message
*
* @param message the message
*/
public ProjectException( final String message )
{
this( message, null );
}
/**
* Constructs a cascaded exception with the supplied message, which links
the
* Throwable provided.
*
* @param message the message
* @param throwable the throwable that caused this exception
*/
public ProjectException( final String message, final Throwable throwable )
{
super( message );
m_throwable = throwable;
}
/**
* Retrieve root cause of the exception.
*
* @return the root cause
*/
public final Throwable getCause()
{
return m_throwable;
}
}
1.1
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model/DefaultNameValidator.java
Index: DefaultNameValidator.java
===================================================================
/*
* 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.myrmidon.interfaces.model;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
/**
* Simple helper class which determines the validity of names used
* in ant projects.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Darrell DeBoer</a>
* @version $Revision: 1.1 $ $Date: 2002/03/11 06:07:24 $
*/
public class DefaultNameValidator
implements NameValidator
{
private final static Resources REZ =
ResourceManager.getPackageResources( DefaultNameValidator.class );
/**
* Determines whether the supplied name may include surrounding
whitespace.
*/
private boolean m_allowSurroundingWhitespace;
// Settings for initial characters.
private boolean m_allowInitialDigit;
private String m_additionalInitialCharacters;
// Settings for internal characters.
private boolean m_allowInternalDigits;
private boolean m_allowInternalWhitespace;
private String m_additionalInternalCharacters;
/**
* Construct a default name validator.
* Letters, digits and "_" are permitted as initial character.
* Letters, digits, whitespace and "_-." are permitted as internal
characters.
* Surrounding whitespace is not permitted.
*/
public DefaultNameValidator()
{
this( false, true, "_", true, true, "_-." );
}
/**
* Contstruct a NameValidator with the specified rules.
* @param allowSurroundingWhitespace
* specified if names are trimmed before checking
* @param allowInitialDigit
* specifies if digits are permitted as intial characters
* @param additionalInitialCharacters
* extra characters to allow as initial characters.
* @param allowInternalDigits
* specifies if digits are permitted as internal characters
* @param allowInternalWhitespace
* specifies if whitespace is permitted internally in names
* @param additionalInternalCharacters
* extra characters permitted in names
*/
public DefaultNameValidator( final boolean allowSurroundingWhitespace,
final boolean allowInitialDigit,
final String additionalInitialCharacters,
final boolean allowInternalDigits,
final boolean allowInternalWhitespace,
final String additionalInternalCharacters )
{
setAllowSurroundingWhitespace( allowSurroundingWhitespace );
setAllowInitialDigit( allowInitialDigit );
setAdditionalInitialCharacters( additionalInitialCharacters );
setAllowInternalDigits( allowInternalDigits );
setAllowInternalWhitespace( allowInternalWhitespace );
setAdditionalInternalCharacters( additionalInternalCharacters );
}
/**
* Creates a valid name based on the supplied string value, removing
invalid
* characters. If no valid characters are present, an exception is thrown.
* @param baseName the name used to construct the valid name
* @throws Exception if no valid name could be constructed.
*/
public String makeValidName( final String baseName ) throws Exception
{
final StringBuffer buffer = new StringBuffer( baseName );
while( buffer.length() > 0 && !isValidInitialChar( buffer.charAt( 0 )
) )
{
buffer.delete( 0, 1 );
}
if( buffer.length() == 0 )
{
final String message = REZ.getString(
"name.could-not-create.error", baseName );
throw new Exception( message );
}
for( int i = 1; i < buffer.length(); )
{
if( !isValidInternalChar( buffer.charAt( i ) ) )
{
buffer.delete( i, i + 1 );
}
else
{
i++;
}
}
return buffer.toString();
}
/**
* Validates the supplied name, failing if it is not.
* @throws Exception is the supplied name is not valid.
*/
public void validate( final String name ) throws Exception
{
String testName = name;
// If surrounding whitespace is allowed, trim it. Otherwise, check.
if( m_allowSurroundingWhitespace )
{
testName = testName.trim();
}
else
{
checkSurroundingWhitespace( testName );
}
// Zero-length name is invalid.
if( testName.length() == 0 )
{
final String message = REZ.getString( "name.zero-char-name.error"
);
throw new Exception( message );
}
// Check first character.
final char initial = testName.charAt( 0 );
checkInitialCharacter( initial, testName );
// Check the rest of the characters.
for( int i = 1; i < testName.length(); i++ )
{
final char internal = testName.charAt( i );
checkInternalCharacter( internal, testName );
}
}
/**
* Checks if the supplied character is permitted as an internal character.
* @throws Exception if the character is not permitted
*/
private void checkInternalCharacter( final char internal, final String
name )
throws Exception
{
if( !isValidInternalChar( internal ) )
{
final String message = REZ.getString(
"name.invalid-internal-char.error",
name,
describeValidInternalChars() );
throw new Exception( message );
}
}
/**
* Checks if the supplied character is permitted as an internal character.
* @throws Exception if the character is not permitted
*/
private void checkInitialCharacter( final char initial, final String name
)
throws Exception
{
if( !isValidInitialChar( initial ) )
{
final String message = REZ.getString(
"name.invalid-initial-char.error",
name,
describeValidInitialChars()
);
throw new Exception( message );
}
}
/**
* Checks the name for surrounding whitespace
* @throws Exception if surrounding whitespace is found
*/
private void checkSurroundingWhitespace( final String testName )
throws Exception
{
if( testName.length() == 0 )
{
return;
}
if( Character.isWhitespace( testName.charAt( 0 ) ) ||
Character.isWhitespace( testName.charAt( testName.length() - 1 )
) )
{
final String message =
REZ.getString( "name.enclosing-whitespace.error", testName );
throw new Exception( message );
}
}
/**
* Determines if a character is allowed as the first character in a name.
* Valid characters are Letters, Digits, and defined initial characters
("_").
* @param chr the character to be assessed
* @return <code>true</code> if the character can be the first character
of a name
*/
protected boolean isValidInitialChar( final char chr )
{
if( Character.isLetter( chr ) )
{
return true;
}
if( m_allowInitialDigit
&& Character.isDigit( chr ) )
{
return true;
}
if( m_additionalInitialCharacters.indexOf( chr ) != -1 )
{
return true;
}
return false;
}
/**
* Determines if a character is allowed as a non-initial character in a
name.
* Valid characters are Letters, Digits, whitespace, and defined
* internal characters ("_-.").
* @param chr the character to be assessed
* @return <code>true</code> if the character can be included in a name
*/
protected boolean isValidInternalChar( final char chr )
{
if( Character.isLetter( chr ) )
{
return true;
}
if( m_allowInternalDigits
&& Character.isDigit( chr ) )
{
return true;
}
if( m_allowInternalWhitespace
&& Character.isWhitespace( chr ) )
{
return true;
}
if( m_additionalInternalCharacters.indexOf( chr ) != -1 )
{
return true;
}
return false;
}
/**
* Builds a message detailing the valid initial characters.
*/
protected String describeValidInitialChars()
{
StringBuffer validChars = new StringBuffer( "letters" );
if( m_allowInitialDigit )
{
validChars.append( ", digits" );
}
validChars.append( ", and \"" );
validChars.append( m_additionalInitialCharacters );
validChars.append( "\"" );
return validChars.toString();
}
/**
* Builds a message detailing the valid internal characters.
*/
protected String describeValidInternalChars()
{
StringBuffer validChars = new StringBuffer( "letters" );
if( m_allowInternalDigits )
{
validChars.append( ", digits" );
}
if( m_allowInternalWhitespace )
{
validChars.append( ", whitespace" );
}
validChars.append( ", and \"" );
validChars.append( m_additionalInternalCharacters );
validChars.append( "\"" );
return validChars.toString();
}
/**
* @param allowSurroundingWhitespace
* specified if names are trimmed before checking
*/
public void setAllowSurroundingWhitespace( boolean
allowSurroundingWhitespace )
{
m_allowSurroundingWhitespace = allowSurroundingWhitespace;
}
/**
* @param allowInitialDigit
* specifies if digits are permitted as intial characters
*/
public void setAllowInitialDigit( boolean allowInitialDigit )
{
m_allowInitialDigit = allowInitialDigit;
}
/**
* @param additionalInitialCharacters
* extra characters to allow as initial characters.
*/
public void setAdditionalInitialCharacters( String
additionalInitialCharacters )
{
m_additionalInitialCharacters = additionalInitialCharacters;
}
/**
* @param allowInternalDigits
* specifies if digits are permitted as internal characters
*/
public void setAllowInternalDigits( boolean allowInternalDigits )
{
m_allowInternalDigits = allowInternalDigits;
}
/**
* @param allowInternalWhitespace
* specifies if whitespace is permitted internally in names
*/
public void setAllowInternalWhitespace( boolean allowInternalWhitespace )
{
m_allowInternalWhitespace = allowInternalWhitespace;
}
/**
* @param additionalInternalCharacters
* extra characters permitted in names
*/
public void setAdditionalInternalCharacters( String
additionalInternalCharacters )
{
m_additionalInternalCharacters = additionalInternalCharacters;
}
}
1.1
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model/NameValidator.java
Index: NameValidator.java
===================================================================
/*
* 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.myrmidon.interfaces.model;
/**
* Determines the validity of names used in projects.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Darrell DeBoer</a>
* @version $Revision: 1.1 $ $Date: 2002/03/11 06:07:24 $
*/
public interface NameValidator
{
/**
* Validates the supplied name, failing if it is not.
* @throws Exception is the supplied name is not valid.
*/
void validate( String name ) throws Exception;
}
1.1
jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model/Resources.properties
Index: Resources.properties
===================================================================
# Name validation
name.zero-char-name.error=Name "" is invalid, as it contains no characters.
name.enclosing-whitespace.error=Name "{0}" is invalid, as it contains
enclosing whitespace.
name.invalid-initial-char.error=Name "{0}" is invalid, as it begins with an
illegal character. Names can start with {1}.
name.invalid-internal-char.error=Name "{0}" is invalid, as it contains an
illegal character. Permitted name characters are {1}.
name.could-not-create.error=Could not valid name from "{0}".
1.4 +24 -1
jakarta-ant/proposal/myrmidon/src/testcases/org/apache/antlib/core/PropertyTest.java
Index: PropertyTest.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/proposal/myrmidon/src/testcases/org/apache/antlib/core/PropertyTest.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- PropertyTest.java 9 Mar 2002 10:13:02 -0000 1.3
+++ PropertyTest.java 11 Mar 2002 06:07:24 -0000 1.4
@@ -12,12 +12,13 @@
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.myrmidon.AbstractProjectTest;
import org.apache.myrmidon.LogMessageTracker;
+import org.apache.myrmidon.components.workspace.DefaultTaskContext;
/**
* Test cases for <property> task.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Adam Murdoch</a>
- * @version $Revision: 1.3 $ $Date: 2002/03/09 10:13:02 $
+ * @version $Revision: 1.4 $ $Date: 2002/03/11 06:07:24 $
*/
public class PropertyTest
extends AbstractProjectTest
@@ -89,6 +90,28 @@
executeTargetExpectError( projectFile, "too-many-values1", messages
);
executeTargetExpectError( projectFile, "too-many-values2", messages
);
executeTargetExpectError( projectFile, "too-many-values3", messages
);
+ }
+
+ /**
+ * Tests basic validation of property names.
+ */
+ public void testNameValidation() throws Exception
+ {
+ final File projectFile = getTestResource( "property.ant" );
+
+ final Resources contextResources
+ = ResourceManager.getPackageResources( DefaultTaskContext.class
);
+
+ // Invalid names
+ String[] messages = new String[]
+ {
+ null,
+ contextResources.getString( "bad-property-name.error" ),
+ null
+ };
+ executeTargetExpectError( projectFile, "bad-prop-name1", messages );
+ executeTargetExpectError( projectFile, "bad-prop-name2", messages );
+ executeTargetExpectError( projectFile, "bad-prop-name3", messages );
}
}
1.1
jakarta-ant/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/builder/DefaultProjectBuilderTest.java
Index: DefaultProjectBuilderTest.java
===================================================================
/*
* 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.myrmidon.components.builder;
import java.io.File;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.myrmidon.AbstractMyrmidonTest;
/**
* Test cases for [EMAIL PROTECTED] DefaultProjectBuilder}.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Darrell DeBoer</a>
* @version $Revision: 1.1 $ $Date: 2002/03/11 06:07:24 $
*/
public class DefaultProjectBuilderTest
extends AbstractMyrmidonTest
{
private final static Resources REZ
= ResourceManager.getPackageResources( DefaultProjectBuilder.class );
private DefaultProjectBuilder m_builder;
public DefaultProjectBuilderTest( String name )
{
super( name );
}
protected void setUp() throws Exception
{
super.setUp();
m_builder = new DefaultProjectBuilder();
m_builder.enableLogging( createLogger() );
}
/**
* Test validation of project and target names.
*/
public void testNameValidation() throws Exception
{
// Check bad project name
final File badProjectFile = getTestResource( "bad-project-name.ant" );
try
{
m_builder.build( badProjectFile.getAbsolutePath() );
fail();
}
catch( Exception e )
{
assertSameMessage( REZ.getString( "ant.project-bad-name.error" ),
e );
}
// Check bad target name
final File badTargetFile = getTestResource( "bad-target-name.ant" );
try
{
m_builder.build( badTargetFile.getAbsolutePath() );
fail();
}
catch( Exception e )
{
// TODO - check error message
}
}
}
1.1
jakarta-ant/proposal/myrmidon/src/testcases/org/apache/myrmidon/interfaces/model/DefaultNameValidatorTest.java
Index: DefaultNameValidatorTest.java
===================================================================
/*
* 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.myrmidon.interfaces.model;
import org.apache.myrmidon.AbstractMyrmidonTest;
/**
* TestCases for [EMAIL PROTECTED] DefaultNameValidator}.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Darrell DeBoer</a>
* @version $Revision: 1.1 $ $Date: 2002/03/11 06:07:24 $
*/
public class DefaultNameValidatorTest
extends AbstractMyrmidonTest
{
private DefaultNameValidator m_validator = new DefaultNameValidator();
public DefaultNameValidatorTest( String name )
{
super( name );
}
/**
* Test valid names for the default validator.
*/
public void testValidNames() throws Exception
{
testValid( "aName" );
testValid( "123456" );
testValid( "s p a ce s" );
testValid( "d-a-s-h-e-s-" );
testValid( "d.o.t.s." );
testValid( "_u_n_d_e_r_s_c_o_r_e_s" );
testValid( "a" );
testValid( "1" );
testValid( "_" );
}
/**
* Test invalid names for the default validator.
*/
public void testInvalidNames() throws Exception
{
testInvalid( "" );
testInvalid( " " );
testInvalid( " " );
testInvalid( " bad" );
testInvalid( "bad " );
testInvalid( " bad " );
testInvalid( "-dashfirst" );
testInvalid( ".dotfirst" );
testInvalid( "question?" );
}
/**
* Test that certain characters are disallowed in the default validator.
*/
public void testReservedChars() throws Exception
{
String reserved = "[EMAIL PROTECTED]&*()+=~`{}[]|\\/?<>,:;";
for( int pos = 0; pos < reserved.length(); pos++ )
{
char chr = reserved.charAt( pos );
testReservedChar( chr );
}
}
private void testReservedChar( char chr ) throws Exception
{
String test = "a" + String.valueOf( chr );
testInvalid( test );
}
/**
* Test validation using a restrictive set of validation rules.
*/
public void testStrictNames() throws Exception
{
m_validator = new DefaultNameValidator( false, false, "", false,
false, "." );
testValid( "name" );
testValid( "a" );
testValid( "yep.ok" );
testInvalid( "_nogood" );
testInvalid( "no_good" );
testInvalid( "nope1" );
testInvalid( "123" );
testInvalid( "not ok" );
}
private void testValid( String name )
{
try
{
m_validator.validate( name );
}
catch( Exception e )
{
fail( e.getMessage() );
}
}
private void testInvalid( String name )
{
try
{
m_validator.validate( name );
fail( "Name \"" + name + "\" should be invalid." );
}
catch( Exception e )
{
}
}
}
1.8 +0 -6 jakarta-ant/proposal/myrmidon/src/xdocs/todo.xml
Index: todo.xml
===================================================================
RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/xdocs/todo.xml,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- todo.xml 9 Mar 2002 10:31:31 -0000 1.7
+++ todo.xml 11 Mar 2002 06:07:24 -0000 1.8
@@ -209,12 +209,6 @@
<li>Fire ProjectListener events projectStarted() and
projectFinished()
events on start and finish of referenced projects,
adding indicator methods
to ProjectEvent.</li>
- <li>Validate project and target names in
DefaultProjectBuilder - reject dodgy
- names like "," or "", or " ". Probably want to reject
names that start or
- end with white-space (though internal whitespace is
probably fine). We also
- want to reserve certain punctuation characters like , :
? $ [ ] { } < >, etc for
- future use.</li>
- <li>Similarly, validate property names, using the same
rules.</li>
<li>Detect duplicate type names.</li>
<li>Add fully qualified type names, based on antlib name
and type shorthand name.
Allow these to be used in build files in addition to the
shorthand names.</li>
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>