@@ -54,7 +54,6 @@
package org.apache.tools.ant;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
@@ -97,8 +96,8 @@ public class SymbolTable {
* from that defined in the calling Project.
* @param p the calling project
*/
public SymbolTable(SymbolTable st ) {
parentTable = st ;
public SymbolTable(Project p ) {
parentTable = p.getSymbols() ;
}
/**
@@ -109,54 +108,6 @@ public class SymbolTable {
this.project = p;
}
/**
* Get the specified loader for the project.
* @param name the name of the loader
* @return the corresponding ANT classloader
*/
private AntClassLoader getLoader(String name) {
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null && parentTable != null) {
return parentTable.getLoader(name);
}
return cl;
}
/**
* Add the specified class-path to a loader.
* If the loader is defined in an ancestor project then a new
* classloader inheritin from the one already existing
* will be created, otherwise the path willbe added to the existing
* ClassLoader.
* @param name the name of the loader to use.
* @param clspath the path to be added to the classloader
*/
public ClassLoader addToLoader(String name, Path clspath) {
// Find if the loader is already defined in the current project
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null) {
// Is it inherited from the calling project
if (parentTable != null) {
cl = parentTable.getLoader(name);
}
cl = new AntClassLoader(cl, project, clspath, true);
loaders.put(name, cl);
}
else {
// Add additional path to the existing definition
String[] pathElements = clspath.list();
for (int i = 0; i < pathElements.length; ++i) {
try {
cl.addPathElement(pathElements[i]);
}
catch (BuildException e) {
// ignore path elements invalid relative to the project
}
}
}
return cl;
}
/**
* Find all the roles supported by a Class
* on this symbol table.
@@ -182,13 +133,13 @@ public class SymbolTable {
list.addElement(role);
}
}
if (parentTable != null) parentTable. findRoles(clz, list);
if (parentTable != null) findRoles(clz, list);
}
/**
* Get the Role definition
* @param role the name of the role
* @return the Role description
* @return the method used to support objects on this role
*/
public Role getRole(String role) {
Role r = (Role) roles.get(role);
@@ -220,6 +171,112 @@ public class SymbolTable {
return (old != null);
}
/**
* Verify if the interface is valid.
* @param clz the interface to validate
* @return the method defined by the interface
*/
private Method validInterface(Class clz) {
Method m[] = clz.getDeclaredMethods();
if (m.length == 1
&& java.lang.Void.TYPE.equals(m[0].getReturnType())) {
Class args[] = m[0].getParameterTypes();
if (args.length == 1
&& !java.lang.String.class.equals(args[0])
&& !args[0].isArray()
&& !args[0].isPrimitive()) {
return m[0];
}
else {
throw new BuildException("Invalid role interface method in: "
+ clz.getName());
}
}
else {
throw new BuildException("More than one method on role interface");
}
}
/**
* Verify if the adapter is valid with respect to the interface.
* @param clz the class adapter to validate
* @param mtd the method whose only argument must match
* @return the static method to use for validating adaptees
*/
private Method validAdapter(Class clz, Method mtd) {
if (clz == null) return null;
checkClass(clz);
if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) {
String msg = "Adapter " + clz.getName() +
" is incompatible with role interface " +
mtd.getDeclaringClass().getName();
throw new BuildException(msg);
}
String msg = "Class " + clz.getName() + " is not an adapter: ";
if (!RoleAdapter.class.isAssignableFrom(clz)) {
throw new BuildException(msg + "does not implement RoleAdapter");
}
try {
Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS);
if (!Modifier.isStatic(chk.getModifiers())) {
throw new BuildException(msg + "checkClass() is not static");
}
return chk;
}
catch(NoSuchMethodException nme){
throw new BuildException(msg + "checkClass() not found", nme);
}
}
/**
* Get the specified loader for the project.
* @param name the name of the loader
* @return the corresponding ANT classloader
*/
private AntClassLoader getLoader(String name) {
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null && parentTable != null) {
return parentTable.getLoader(name);
}
return cl;
}
/**
* Add the specified class-path to a loader.
* If the loader is defined in an ancestor project then a new
* classloader inheritin from the one already existing
* will be created, otherwise the path willbe added to the existing
* ClassLoader.
* @param name the name of the loader to use.
* @param clspath the path to be added to the classloader
*/
public ClassLoader addToLoader(String name, Path clspath) {
// Find if the loader is already defined in the current project
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null) {
// Is it inherited from the calling project
if (parentTable != null) {
cl = parentTable.getLoader(name);
}
cl = new AntClassLoader(cl, project, clspath, true);
loaders.put(name, cl);
}
else {
// Add additional path to the existing definition
String[] pathElements = clspath.list();
for (int i = 0; i < pathElements.length; ++i) {
try {
cl.addPathElement(pathElements[i]);
}
catch (BuildException e) {
// ignore path elements invalid relative to the project
}
}
}
return cl;
}
/**
* Add a new type of element to a role.
* @param role the role for this Class.
@@ -234,13 +291,13 @@ public class SymbolTable {
throw new BuildException("Unknown role: " + role);
}
// Check if it is already defined
Factory old = get(role, name);
Class old = get(role, name);
if (old != null) {
if (old.getOriginalClass(). equals(clz)) {
if (old.equals(clz)) {
project.log("Ignoring override for "+ role + " " + name
+ ", it is already defined by the same class.",
project.MSG_VERBOSE);
return old.getOriginalClass() ;
return old;
}
else {
project.log("Trying to override old definition of " +
@@ -248,33 +305,26 @@ public class SymbolTable {
project.MSG_WARN);
}
}
Factory f = checkClass(clz);
checkClass(clz);
// Check that the Class is compatible with the role definition
f = r.verifyAdaptability(role, f );
r.verifyAdaptability(role, clz );
// Record the new type
Hashtable defTable = (Hashtable)defs.get(role);
if (defTable == null) {
defTable = new Hashtable();
defs.put(role, defTable);
}
defTable.put(name, f);
String msg =
" +User " + role + ": " + name + " " + clz.getName();
project.log(msg, project.MSG_DEBUG);
return (old != null ? old.getOriginalClass() : null);
defTable.put(name, clz);
return old;
}
/**
* Checks a class, whether it is suitable for serving in ANT.
* @return the factory to use when instantiating the class
* @throws BuildException and logs as Project.MSG_ERR for
* conditions, that will cause execution to fail.
*/
Factory checkClass(final Class clz) // Package on purpose
void checkClass(final Class clz)
throws BuildException {
if (clz == null) return null;
if(!Modifier.isPublic(clz.getModifiers())) {
final String message = clz + " is not public";
project.log(message, Project.MSG_ERR);
@@ -292,37 +342,8 @@ public class SymbolTable {
// getConstructor finds public constructors only.
try {
clz.getConstructor(new Class[0]);
return new Factory(){
public Object create(Project p) {
try {
return clz.newInstance();
}
catch(Exception e) {
throw new BuildException(e);
}
}
public Class getOriginalClass() {
return clz;
}
};
} catch (NoSuchMethodException nse) {
final Constructor c =
clz.getConstructor(new Class[] {Project.class});
return new Factory(){
public Object create(Project p) {
try {
return c.newInstance(new Object[]{p});
}
catch(Exception e) {
throw new BuildException(e);
}
}
public Class getOriginalClass() {
return clz;
}
};
clz.getConstructor(new Class[] {Project.class});
}
} catch(NoSuchMethodException e) {
final String message =
@@ -338,11 +359,11 @@ public class SymbolTable {
* @param name the name of the element to sea
* @return the Class implementation
*/
public Factory get(String role, String name) {
public Class get(String role, String name) {
Hashtable defTable = (Hashtable)defs.get(role);
if (defTable != null) {
Factory f = (Factory )defTable.get(name);
if (f != null) return f ;
Class clz = (Class )defTable.get(name);
if (clz != null) return clz ;
}
if (parentTable != null) {
return parentTable.get(role, name);
@@ -351,12 +372,19 @@ public class SymbolTable {
}
/**
* Get a Hashtable that is usable for manipulating elements on Role.
* @param role the role of the elements in the table
* Get a Hashtable that is usable for manipulating Tasks,
* @return a Hashtable that delegates to the Symbol table.
*/
Hashtable getDefinitions(String role) { // package scope on purpose
return new SymbolHashtable(role);
public Hashtable getTaskDefinitions() {
return new SymbolHashtable("task");
}
/**
* Get a Hashtable that is usable for manipulating Datatypes,
* @return a Hashtable that delegates to the Symbol table.
*/
public Hashtable getDataTypeDefinitions() {
return new SymbolHashtable("datatype");
}
/**
@@ -374,43 +402,16 @@ public class SymbolTable {
}
public synchronized Object get(Object key) {
Factory f = SymbolTable.this.get(role, (String)key);
return (f == null? null : f.getOriginalClass());
return SymbolTable.this.get(role, (String)key);
}
}
/**
* Factory for creating ANT objects.
* Class objects are not instanciated directly but through a Factory
* which is able to resolve issues such as proxys and such.
*/
public static interface Factory {
/**
* Creates an object for the Role
* @param the project in which it is created
* @return the instantiated object with a proxy if necessary
*/
public Object create(Project p);
/**
* Creates an object for the Role, adapted if necessary
* for a particular interface.
*/
// public Object adaptFor(Class clz, Project p, Object o);
/**
* The original class of the object without proxy.
*/
public Class getOriginalClass();
}
/**
* The definition of a role
*/
public class Role {
private Method interfaceMethod;
private Method adapterVerifier;
private Factory adapterFactory;
/**
* Creates a new Role object
@@ -419,7 +420,6 @@ public class SymbolTable {
*/
Role(Class roleClz, Class adapterClz) {
interfaceMethod = validInterface(roleClz);
adapterFactory = checkClass(adapterClz);
adapterVerifier = validAdapter(adapterClz, interfaceMethod);
}
@@ -433,11 +433,12 @@ public class SymbolTable {
/**
* Instantiate a new adapter for this role.
*/
public RoleAdapter createAdapter(Project p ) {
if (adapterFactory == null) return null;
public RoleAdapter createAdapter() {
if (adapterVerifier == null) return null;
try {
return (RoleAdapter) adapterFactory.create(p);
return (RoleAdapter)
adapterVerifier.getDeclaringClass().newInstance();
}
catch(BuildException be) {
throw be;
@@ -450,12 +451,11 @@ public class SymbolTable {
/**
* Verify if the class can be adapted to use by the role
* @param role the name of the role to verify
* @param f the factory for the class to verify
* @param clz the class to verify
*/
public Factory verifyAdaptability(String role, final Factory f) {
final Class clz = f.getOriginalClass();
public void verifyAdaptability(String role, Class clz) {
if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) {
return f ;
return;
}
if (adapterVerifier == null) {
String msg = "Class " + clz.getName() +
@@ -464,18 +464,8 @@ public class SymbolTable {
}
try {
try {
adapterVerifier.invoke(null, new Object[]{clz, project});
return new Factory(){
public Object create(Project p) {
RoleAdapter ra = createAdapter(p);
ra.setProxy(f.create(p));
return ra;
}
public Class getOriginalClass() {
return clz;
}
};
adapterVerifier.invoke(null,
new Object[]{clz, project});
}
catch (InvocationTargetException ite) {
throw ite.getTargetException();
@@ -497,63 +487,5 @@ public class SymbolTable {
public boolean isImplementedBy(Class clz) {
return interfaceMethod.getDeclaringClass().isAssignableFrom(clz);
}
/**
* Verify if the interface is valid.
* @param clz the interface to validate
* @return the method defined by the interface
*/
private Method validInterface(Class clz) {
Method m[] = clz.getDeclaredMethods();
if (m.length == 1
&& java.lang.Void.TYPE.equals(m[0].getReturnType())) {
Class args[] = m[0].getParameterTypes();
if (args.length == 1
&& !java.lang.String.class.equals(args[0])
&& !args[0].isArray()
&& !args[0].isPrimitive()) {
return m[0];
}
else {
throw new BuildException("Invalid role interface method in: "
+ clz.getName());
}
}
else {
throw new BuildException("More than one method on role interface");
}
}
/**
* Verify if the adapter is valid with respect to the interface.
* @param clz the class adapter to validate
* @param mtd the method whose only argument must match
* @return the static method to use for validating adaptees
*/
private Method validAdapter(Class clz, Method mtd) {
if (clz == null) return null;
if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) {
String msg = "Adapter " + clz.getName() +
" is incompatible with role interface " +
mtd.getDeclaringClass().getName();
throw new BuildException(msg);
}
String msg = "Class " + clz.getName() + " is not an adapter: ";
if (!RoleAdapter.class.isAssignableFrom(clz)) {
throw new BuildException(msg + "does not implement RoleAdapter");
}
try {
Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS);
if (!Modifier.isStatic(chk.getModifiers())) {
throw new BuildException(msg + "checkClass() is not static");
}
return chk;
}
catch(NoSuchMethodException nme){
throw new BuildException(msg + "checkClass() not found", nme);
}
}
}
}