Browse Source

modified Project.java and ComponentHelper.java in the antlib proposal to

reflect the last changes in the main branch


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274568 13f79535-47bb-0310-9956-ffa450edef68
master
Antoine Levy-Lambert 22 years ago
parent
commit
a571a7b97b
3 changed files with 1474 additions and 902 deletions
  1. +1
    -0
      proposal/sandbox/antlib/build.xml
  2. +649
    -0
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/ComponentHelper.java
  3. +824
    -902
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java

+ 1
- 0
proposal/sandbox/antlib/build.xml View File

@@ -23,6 +23,7 @@
<exclude name='org/apache/tools/ant/Project.class' />
<exclude name='org/apache/tools/ant/TaskAdapter.class' />
<exclude name='org/apache/tools/ant/taskdefs/Ant.class' />
<exclude name='org/apache/tools/ant/ComponentHelper.class' />
</fileset>
</copy>
</target>


+ 649
- 0
proposal/sandbox/antlib/src/main/org/apache/tools/ant/ComponentHelper.java View File

@@ -0,0 +1,649 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Ant" and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.tools.ant;

import org.apache.tools.ant.util.LazyHashtable;
import org.apache.tools.ant.util.WeakishReference;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
import java.io.InputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;

/**
* Component creation and configuration.
*
* This is cut&paste from Project.java of everything related to
* task/type management. Project will just delegate.
*
* A very simple hook mechnism is provided that allows users to plug
* in custom code. It is also possible to replace the default behavior
* ( for example in an app embeding ant )
*
* @author Costin Manolache
* @since Ant1.6
*/
public class ComponentHelper {
/** Map from data type names to implementing classes (String to Class). */
private Hashtable dataClassDefinitions;
/** Map from task names to implementing classes (String to Class). */
private Hashtable taskClassDefinitions;
/**
* Map from task names to vectors of created tasks
* (String to Vector of Task). This is used to invalidate tasks if
* the task definition changes.
*/
private Hashtable createdTasks = new Hashtable();


protected ComponentHelper next;
protected Project project;

/**
*/
public static ComponentHelper getComponentHelper(Project project) {
// Singleton for now, it may change ( per/classloader )
ComponentHelper ph=(ComponentHelper)project.getReference( "ant.ComponentHelper" );
if( ph!=null ) return ph;
ph=new ComponentHelper();
ph.setProject( project );

project.addReference( "ant.ComponentHelper",ph );
return ph;
}

protected ComponentHelper() {
}

public void setNext( ComponentHelper next ) {
this.next=next;
}

public ComponentHelper getNext() {
return next;
}

public void setProject(Project project) {
this.project = project;
dataClassDefinitions= new AntTaskTable(project, false);
taskClassDefinitions= new AntTaskTable(project, true);
}

/** Factory method to create the components.
*
* This should be called by UnknownElement.
*
* @param ue The component helper has access via ue to the entire XML tree.
* @param ns Namespace. Also available as ue.getNamespace()
* @param taskName The element name. Also available as ue.getTag()
* @return
* @throws BuildException
*/
public Object createComponent( UnknownElement ue,
String ns,
String taskName )
throws BuildException
{
Object component=null;

// System.out.println("Fallback to project default " + taskName );
// Can't create component. Default is to use the old methods in project.

// This policy is taken from 1.5 ProjectHelper. In future the difference between
// task and type should disapear.
if( project.getDataTypeDefinitions().get(taskName) != null ) {
// This is the original policy in ProjectHelper. The 1.5 version of UnkwnonwElement
// used to try first to create a task, and if it failed tried a type. In 1.6 the diff
// should disapear.
component = this.createDataType(taskName);
if( component!=null ) return component;
}

// from UnkwnonwElement.createTask. The 'top level' case is removed, we're
// allways lazy
component = this.createTask(taskName);

return component;
}

/** Initialization code - implementing the original ant component
* loading from /org/apache/tools/ant/taskdefs/default.properties
* and .../types/default.properties
*
* @throws BuildException
*/
public void initDefaultDefinitions() throws BuildException {
project.loadDefinitions();
}

/**
* Adds a new task definition to the project.
* Attempting to override an existing definition with an
* equivalent one (i.e. with the same classname) results in
* a verbose log message. Attempting to override an existing definition
* with a different one results in a warning log message and
* invalidates any tasks which have already been created with the
* old definition.
*
* @param taskName The name of the task to add.
* Must not be <code>null</code>.
* @param taskClass The full name of the class implementing the task.
* Must not be <code>null</code>.
*
* @exception BuildException if the class is unsuitable for being an Ant
* task. An error level message is logged before
* this exception is thrown.
*
* @see #checkTaskClass(Class)
*/
public void addTaskDefinition(String taskName, Class taskClass)
throws BuildException {
Class old = (Class) taskClassDefinitions.get(taskName);
if (null != old) {
if (old.equals(taskClass)) {
// project.log("Ignoring override for task " + taskName
// + ", it is already defined by the same class.",
// Project.MSG_VERBOSE);
return;
} else {
int logLevel = Project.MSG_WARN;
if (old.getName().equals(taskClass.getName())) {
ClassLoader oldLoader = old.getClassLoader();
ClassLoader newLoader = taskClass.getClassLoader();
// system classloader on older JDKs can be null
if (oldLoader != null
&& newLoader != null
&& oldLoader instanceof AntClassLoader
&& newLoader instanceof AntClassLoader
&& ((AntClassLoader) oldLoader).getClasspath()
.equals(((AntClassLoader) newLoader).getClasspath())
) {
// same classname loaded from the same
// classpath components
logLevel = Project.MSG_VERBOSE;
}
}

project.log("Trying to override old definition of task " + taskName,
logLevel);
invalidateCreatedTasks(taskName);
}
}

String msg = " +User task: " + taskName + " " + taskClass.getName();
project.log(msg, Project.MSG_DEBUG);
checkTaskClass(taskClass);
taskClassDefinitions.put(taskName, taskClass);
}

/**
* Checks whether or not a class is suitable for serving as Ant task.
* Ant task implementation classes must be public, concrete, and have
* a no-arg constructor.
*
* @param taskClass The class to be checked.
* Must not be <code>null</code>.
*
* @exception BuildException if the class is unsuitable for being an Ant
* task. An error level message is logged before
* this exception is thrown.
*/
public void checkTaskClass(final Class taskClass) throws BuildException {
if (!Modifier.isPublic(taskClass.getModifiers())) {
final String message = taskClass + " is not public";
project.log(message, Project.MSG_ERR);
throw new BuildException(message);
}
if (Modifier.isAbstract(taskClass.getModifiers())) {
final String message = taskClass + " is abstract";
project.log(message, Project.MSG_ERR);
throw new BuildException(message);
}
try {
taskClass.getConstructor(null);
// don't have to check for public, since
// getConstructor finds public constructors only.
} catch (NoSuchMethodException e) {
final String message = "No public no-arg constructor in "
+ taskClass;
project.log(message, Project.MSG_ERR);
throw new BuildException(message);
}
if (!Task.class.isAssignableFrom(taskClass)) {
TaskAdapter.checkTaskClass(taskClass, project);
}
}

/**
* Returns the current task definition hashtable. The returned hashtable is
* "live" and so should not be modified.
*
* @return a map of from task name to implementing class
* (String to Class).
*/
public Hashtable getTaskDefinitions() {
return taskClassDefinitions;
}

/**
* Adds a new datatype definition.
* Attempting to override an existing definition with an
* equivalent one (i.e. with the same classname) results in
* a verbose log message. Attempting to override an existing definition
* with a different one results in a warning log message, but the
* definition is changed.
*
* @param typeName The name of the datatype.
* Must not be <code>null</code>.
* @param typeClass The full name of the class implementing the datatype.
* Must not be <code>null</code>.
*/
public void addDataTypeDefinition(String typeName, Class typeClass) {
synchronized(dataClassDefinitions) {
Class old = (Class) dataClassDefinitions.get(typeName);
if (null != old) {
if (old.equals(typeClass)) {
// project.log("Ignoring override for datatype " + typeName
// + ", it is already defined by the same class.",
// Project.MSG_VERBOSE);
return;
} else {
project.log("Trying to override old definition of datatype "
+ typeName, Project.MSG_WARN);
}
}
dataClassDefinitions.put(typeName, typeClass);
}
String msg = " +User datatype: " + typeName + " "
+ typeClass.getName();
project.log(msg, Project.MSG_DEBUG);
}

/**
* Returns the current datatype definition hashtable. The returned
* hashtable is "live" and so should not be modified.
*
* @return a map of from datatype name to implementing class
* (String to Class).
*/
public Hashtable getDataTypeDefinitions() {
return dataClassDefinitions;
}

/**
* Creates a new instance of a task, adding it to a list of
* created tasks for later invalidation. This causes all tasks
* to be remembered until the containing project is removed
*
* Called from Project.createTask(), which can be called by tasks.
* The method should be deprecated, as it doesn't support ns and libs.
*
* @param taskType The name of the task to create an instance of.
* Must not be <code>null</code>.
*
* @return an instance of the specified task, or <code>null</code> if
* the task name is not recognised.
*
* @exception BuildException if the task name is recognised but task
* creation fails.
*/
public Task createTask(String taskType) throws BuildException {
Task task=createNewTask(taskType);
if(task!=null) {
addCreatedTask(taskType, task);
}
return task;
}

/**
* Creates a new instance of a task. This task is not
* cached in the createdTasks list.
* @since ant1.6
* @param taskType The name of the task to create an instance of.
* Must not be <code>null</code>.
*
* @return an instance of the specified task, or <code>null</code> if
* the task name is not recognised.
*
* @exception BuildException if the task name is recognised but task
* creation fails.
*/
private Task createNewTask(String taskType) throws BuildException {
Class c = (Class) taskClassDefinitions.get(taskType);

if (c == null) {
return null;
}

try {
Object o = c.newInstance();
if ( project != null ) {
project.setProjectReference( o );
}
Task task = null;
if (o instanceof Task) {
task = (Task) o;
} else {
// "Generic" Bean - use the setter pattern
// and an Adapter
TaskAdapter taskA = new TaskAdapter();
taskA.setProxy(o);
if ( project != null ) {
project.setProjectReference( taskA );
}
task = taskA;
}
task.setProject( project );
task.setTaskType(taskType);

// set default value, can be changed by the user
task.setTaskName(taskType);

String msg = " +Task: " + taskType;
project.log (msg, Project.MSG_DEBUG);
return task;
} catch (Throwable t) {
System.out.println("task CL=" + c.getClassLoader());
String msg = "Could not create task of type: "
+ taskType + " due to " + t;
throw new BuildException(msg, t);
}
}

/**
* Keeps a record of all tasks that have been created so that they
* can be invalidated if a new task definition overrides the current one.
*
* @param type The name of the type of task which has been created.
* Must not be <code>null</code>.
*
* @param task The freshly created task instance.
* Must not be <code>null</code>.
*/
private void addCreatedTask(String type, Task task) {
synchronized (createdTasks) {
Vector v = (Vector) createdTasks.get(type);
if (v == null) {
v = new Vector();
createdTasks.put(type, v);
}
v.addElement(WeakishReference.createReference(task));
}
}

/**
* Mark tasks as invalid which no longer are of the correct type
* for a given taskname.
*
* @param type The name of the type of task to invalidate.
* Must not be <code>null</code>.
*/
private void invalidateCreatedTasks(String type) {
synchronized (createdTasks) {
Vector v = (Vector) createdTasks.get(type);
if (v != null) {
Enumeration enum = v.elements();
while (enum.hasMoreElements()) {
WeakishReference ref=
(WeakishReference) enum.nextElement();
Task t = (Task) ref.get();
//being a weak ref, it may be null by this point
if(t!=null) {
t.markInvalid();
}
}
v.removeAllElements();
createdTasks.remove(type);
}
}
}

/**
* Creates a new instance of a data type.
*
* @param typeName The name of the data type to create an instance of.
* Must not be <code>null</code>.
*
* @return an instance of the specified data type, or <code>null</code> if
* the data type name is not recognised.
*
* @exception BuildException if the data type name is recognised but
* instance creation fails.
*/
public Object createDataType(String typeName) throws BuildException {
Class c = (Class) dataClassDefinitions.get(typeName);

if (c == null) {
return null;
}

try {
java.lang.reflect.Constructor ctor = null;
boolean noArg = false;
// DataType can have a "no arg" constructor or take a single
// Project argument.
try {
ctor = c.getConstructor(new Class[0]);
noArg = true;
} catch (NoSuchMethodException nse) {
ctor = c.getConstructor(new Class[] {Project.class});
noArg = false;
}

Object o = null;
if (noArg) {
o = ctor.newInstance(new Object[0]);
} else {
o = ctor.newInstance(new Object[] {project});
}
if ( project != null ) {
project.setProjectReference( o );
}
String msg = " +DataType: " + typeName;
project.log(msg, Project.MSG_DEBUG);
return o;
} catch (java.lang.reflect.InvocationTargetException ite) {
Throwable t = ite.getTargetException();
String msg = "Could not create datatype of type: "
+ typeName + " due to " + t;
throw new BuildException(msg, t);
} catch (Throwable t) {
String msg = "Could not create datatype of type: "
+ typeName + " due to " + t;
throw new BuildException(msg, t);
}
}

/**
* Returns a description of the type of the given element, with
* special handling for instances of tasks and data types.
* <p>
* This is useful for logging purposes.
*
* @param element The element to describe.
* Must not be <code>null</code>.
*
* @return a description of the element type
*
* @since Ant 1.6
*/
public String getElementName(Object element) {
Hashtable elements = taskClassDefinitions;
Class elementClass = element.getClass();
String typeName = "task";
if (!elements.contains(elementClass)) {
elements = dataClassDefinitions;
typeName = "data type";
if (!elements.contains(elementClass)) {
elements = null;
}
}

if (elements != null) {
Enumeration e = elements.keys();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();
Class clazz = (Class) elements.get(name);
if (elementClass.equals(clazz)) {
return "The <" + name + "> " + typeName;
}
}
}

return "Class " + elementClass.getName();
}


private static class AntTaskTable extends LazyHashtable {
Project project;
Properties props;
boolean tasks=false;

public AntTaskTable( Project p, boolean tasks ) {
this.project=p;
this.tasks=tasks;
}

public void addDefinitions( Properties props ) {
this.props=props;
}

protected void initAll( ) {
if( initAllDone ) {
return;
}
project.log("InitAll", Project.MSG_DEBUG);
if( props==null ) {
return;
}
Enumeration enum = props.propertyNames();
while (enum.hasMoreElements()) {
String key = (String) enum.nextElement();
Class taskClass=getTask( key );
if( taskClass!=null ) {
// This will call a get() and a put()
if( tasks ) {
project.addTaskDefinition(key, taskClass);
} else {
project.addDataTypeDefinition(key, taskClass );
}
}
}
initAllDone=true;
}

protected Class getTask(String key) {
if( props==null ) {
return null; // for tasks loaded before init()
}
String value=props.getProperty(key);
if( value==null) {
//project.log( "No class name for " + key, Project.MSG_VERBOSE );
return null;
}
try {
Class taskClass=null;
if( project.getCoreLoader() != null &&
!("only".equals(project.getProperty("build.sysclasspath")))) {
try {
project.log("Loading with the core loader " + value,
Project.MSG_DEBUG);
taskClass=project.getCoreLoader().loadClass(value);
if( taskClass != null ) {
return taskClass;
}
} catch( Exception ex ) {
//ignore
}
}
taskClass = Class.forName(value);
return taskClass;
} catch (NoClassDefFoundError ncdfe) {
project.log("Could not load a dependent class ("
+ ncdfe.getMessage() + ") for task "
+ key, Project.MSG_DEBUG);
} catch (ClassNotFoundException cnfe) {
project.log("Could not load class (" + value
+ ") for task " + key, Project.MSG_DEBUG);
}
return null;
}

// Hashtable implementation
public Object get( Object key ) {
Object orig=super.get( key );
if( orig!= null ) {
return orig;
}
if( ! (key instanceof String) ) {
return null;
}
project.log("Get task " + key, Project.MSG_DEBUG );
Object taskClass=getTask( (String) key);
if( taskClass != null) {
super.put( key, taskClass );
}
return taskClass;
}

public boolean containsKey( Object key ) {
return get( key ) != null;
}

}
}

+ 824
- 902
proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java
File diff suppressed because it is too large
View File


Loading…
Cancel
Save