aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java1101
1 files changed, 1101 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java
new file mode 100644
index 00000000..eceedeef
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/ComponentHelper.java
@@ -0,0 +1,1101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+
+import org.apache.tools.ant.launch.Launcher;
+import org.apache.tools.ant.taskdefs.Definer;
+import org.apache.tools.ant.taskdefs.Typedef;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Component creation and configuration.
+ *
+ * The class is based around handing component
+ * definitions in an AntTypeTable.
+ *
+ * The old task/type methods have been kept
+ * for backward compatibly.
+ * Project will just delegate its calls to this class.
+ *
+ * A very simple hook mechanism is provided that allows users to plug
+ * in custom code. It is also possible to replace the default behavior
+ * ( for example in an app embedding ant )
+ *
+ * @since Ant1.6
+ */
+public class ComponentHelper {
+ /** Map of component name to lists of restricted definitions */
+ private Map<String, List<AntTypeDefinition>> restrictedDefinitions = new HashMap<String, List<AntTypeDefinition>>();
+
+ /** Map from component name to anttypedefinition */
+ private final Hashtable<String, AntTypeDefinition> antTypeTable = new Hashtable<String, AntTypeDefinition>();
+
+ /** Map of tasks generated from antTypeTable */
+ private final Hashtable<String, Class<?>> taskClassDefinitions = new Hashtable<String, Class<?>>();
+
+ /** flag to rebuild taskClassDefinitions */
+ private boolean rebuildTaskClassDefinitions = true;
+
+ /** Map of types generated from antTypeTable */
+ private final Hashtable<String, Class<?>> typeClassDefinitions = new Hashtable<String, Class<?>>();
+
+ /** flag to rebuild typeClassDefinitions */
+ private boolean rebuildTypeClassDefinitions = true;
+
+ /** Set of namespaces that have been checked for antlibs */
+ private final HashSet<String> checkedNamespaces = new HashSet<String>();
+
+ /**
+ * Stack of antlib contexts used to resolve definitions while
+ * processing antlib
+ */
+ private Stack<String> antLibStack = new Stack<String>();
+
+ /** current antlib uri */
+ private String antLibCurrentUri = null;
+
+ /**
+ * this does not appear to be used anywhere in the Ant codebase
+ * even via its accessors
+ */
+ private ComponentHelper next;
+
+ /**
+ * Project that owns a component helper
+ */
+ private Project project;
+
+ /**
+ * Error string when the file taskdefs/defaults.properties cannot be found
+ */
+ private static final String ERROR_NO_TASK_LIST_LOAD = "Can't load default task list";
+
+ /**
+ * Error string when the typedefs/defaults.properties cannot be found
+ */
+ private static final String ERROR_NO_TYPE_LIST_LOAD = "Can't load default type list";
+
+ /**
+ * reference under which we register ourselves with a project -{@value}
+ */
+ public static final String COMPONENT_HELPER_REFERENCE = "ant.ComponentHelper";
+
+ /**
+ * string used to control build.syspath policy {@value}
+ */
+ private static final String BUILD_SYSCLASSPATH_ONLY = "only";
+
+ /**
+ * special name of ant's property task -{@value}. There is some
+ * contrived work here to enable this early.
+ */
+ private static final String ANT_PROPERTY_TASK = "property";
+
+ // {tasks, types}
+ private static Properties[] defaultDefinitions = new Properties[2];
+
+ /**
+ * Get the project.
+ * @return the project owner of this helper.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Find a project component for a specific project, creating
+ * it if it does not exist.
+ * @param project the project.
+ * @return the project component for a specific project.
+ */
+ public static ComponentHelper getComponentHelper(Project project) {
+ if (project == null) {
+ return null;
+ }
+ // Singleton for now, it may change ( per/classloader )
+ ComponentHelper ph = (ComponentHelper) project.getReference(COMPONENT_HELPER_REFERENCE);
+ if (ph != null) {
+ return ph;
+ }
+ ph = new ComponentHelper();
+ ph.setProject(project);
+
+ project.addReference(COMPONENT_HELPER_REFERENCE, ph);
+ return ph;
+ }
+
+ /**
+ * Creates a new ComponentHelper instance.
+ */
+ protected ComponentHelper() {
+ }
+
+ /**
+ * Set the next chained component helper.
+ *
+ * @param next the next chained component helper.
+ */
+ public void setNext(ComponentHelper next) {
+ this.next = next;
+ }
+
+ /**
+ * Get the next chained component helper.
+ *
+ * @return the next chained component helper.
+ */
+ public ComponentHelper getNext() {
+ return next;
+ }
+
+ /**
+ * Sets the project for this component helper.
+ *
+ * @param project the project for this helper.
+ */
+ public void setProject(Project project) {
+ this.project = project;
+// antTypeTable = new Hashtable<String, AntTypeDefinition>(project);
+ }
+
+ /**
+ * @return A copy of the CheckedNamespace.
+ */
+ private synchronized Set<String> getCheckedNamespace() {
+ @SuppressWarnings("unchecked")
+ final Set<String> result = (Set<String>) checkedNamespaces.clone();
+ return result;
+ }
+
+ /**
+ * @return A deep copy of the restrictredDefinition
+ */
+ private Map<String, List<AntTypeDefinition>> getRestrictedDefinition() {
+ final Map<String, List<AntTypeDefinition>> result = new HashMap<String, List<AntTypeDefinition>>();
+ synchronized (restrictedDefinitions) {
+ for (Map.Entry<String, List<AntTypeDefinition>> entry : restrictedDefinitions.entrySet()) {
+ List<AntTypeDefinition> entryVal = entry.getValue();
+ synchronized (entryVal) {
+ //copy the entryVal
+ entryVal = new ArrayList<AntTypeDefinition> (entryVal);
+ }
+ result.put(entry.getKey(), entryVal);
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Used with creating child projects. Each child
+ * project inherits the component definitions
+ * from its parent.
+ * @param helper the component helper of the parent project.
+ */
+ public void initSubProject(ComponentHelper helper) {
+ // add the types of the parent project
+ @SuppressWarnings("unchecked")
+ final Hashtable<String, AntTypeDefinition> typeTable = (Hashtable<String, AntTypeDefinition>) helper.antTypeTable.clone();
+ synchronized (antTypeTable) {
+ for (AntTypeDefinition def : typeTable.values()) {
+ antTypeTable.put(def.getName(), def);
+ }
+ }
+ // add the parsed namespaces of the parent project
+ Set<String> inheritedCheckedNamespace = helper.getCheckedNamespace();
+ synchronized (this) {
+ checkedNamespaces.addAll(inheritedCheckedNamespace);
+ }
+ Map<String, List<AntTypeDefinition>> inheritedRestrictedDef = helper.getRestrictedDefinition();
+ synchronized (restrictedDefinitions) {
+ restrictedDefinitions.putAll(inheritedRestrictedDef);
+ }
+ }
+
+ /**
+ * Factory method to create the components.
+ *
+ * This should be called by UnknownElement.
+ *
+ * @param ue The Unknown Element creating this component.
+ * @param ns Namespace URI. Also available as ue.getNamespace().
+ * @param componentType The component type,
+ * Also available as ue.getComponentName().
+ * @return the created component.
+ * @throws BuildException if an error occurs.
+ */
+ public Object createComponent(UnknownElement ue, String ns, String componentType)
+ throws BuildException {
+ Object component = createComponent(componentType);
+ if (component instanceof Task) {
+ Task task = (Task) component;
+ task.setLocation(ue.getLocation());
+ task.setTaskType(componentType);
+ task.setTaskName(ue.getTaskName());
+ task.setOwningTarget(ue.getOwningTarget());
+ task.init();
+ }
+ return component;
+ }
+
+ /**
+ * Create an object for a component.
+ *
+ * @param componentName the name of the component, if
+ * the component is in a namespace, the
+ * name is prefixed with the namespace uri and ":".
+ * @return the class if found or null if not.
+ */
+ public Object createComponent(String componentName) {
+ AntTypeDefinition def = getDefinition(componentName);
+ return def == null ? null : def.create(project);
+ }
+
+ /**
+ * Return the class of the component name.
+ *
+ * @param componentName the name of the component, if
+ * the component is in a namespace, the
+ * name is prefixed with the namespace uri and ":".
+ * @return the class if found or null if not.
+ */
+ public Class<?> getComponentClass(String componentName) {
+ AntTypeDefinition def = getDefinition(componentName);
+ return def == null ? null : def.getExposedClass(project);
+ }
+
+ /**
+ * Return the antTypeDefinition for a componentName.
+ * @param componentName the name of the component.
+ * @return the ant definition or null if not present.
+ */
+ public AntTypeDefinition getDefinition(String componentName) {
+ checkNamespace(componentName);
+ return antTypeTable.get(componentName);
+ }
+
+ /**
+ * This method is initialization code implementing the original ant component
+ * loading from /org/apache/tools/ant/taskdefs/default.properties
+ * and /org/apache/tools/ant/types/default.properties.
+ */
+ public void initDefaultDefinitions() {
+ initTasks();
+ initTypes();
+ new DefaultDefinitions(this).execute();
+ }
+
+ /**
+ * 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.
+ *
+ * @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) {
+ checkTaskClass(taskClass);
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(taskName);
+ def.setClassLoader(taskClass.getClassLoader());
+ def.setClass(taskClass);
+ def.setAdapterClass(TaskAdapter.class);
+ def.setClassName(taskClass.getName());
+ def.setAdaptToClass(Task.class);
+ updateDataTypeDefinition(def);
+ }
+
+ /**
+ * 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((Class[]) 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. Also, the returned table may be
+ * modified asynchronously.
+ *
+ * @return a map of from task name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getTaskDefinitions() {
+ synchronized (taskClassDefinitions) {
+ synchronized (antTypeTable) {
+ if (rebuildTaskClassDefinitions) {
+ taskClassDefinitions.clear();
+ for (Map.Entry<String, AntTypeDefinition> e : antTypeTable.entrySet()) {
+ final Class<?> clazz = e.getValue().getExposedClass(project);
+ if (clazz == null) {
+ continue;
+ }
+ if (Task.class.isAssignableFrom(clazz)) {
+ taskClassDefinitions.put(e.getKey(), e.getValue().getTypeClass(project));
+ }
+ }
+ rebuildTaskClassDefinitions = false;
+ }
+ }
+ }
+ return taskClassDefinitions;
+ }
+
+ /**
+ * Returns the current type definition hashtable. The returned hashtable is
+ * "live" and so should not be modified.
+ *
+ * @return a map of from type name to implementing class
+ * (String to Class).
+ */
+ public Hashtable<String, Class<?>> getDataTypeDefinitions() {
+ synchronized (typeClassDefinitions) {
+ synchronized (antTypeTable) {
+ if (rebuildTypeClassDefinitions) {
+ typeClassDefinitions.clear();
+ for (Map.Entry<String, AntTypeDefinition> e : antTypeTable.entrySet()) {
+ final Class<?> clazz = e.getValue().getExposedClass(project);
+ if (clazz == null) {
+ continue;
+ }
+ if (!Task.class.isAssignableFrom(clazz)) {
+ typeClassDefinitions.put(e.getKey(), e.getValue().getTypeClass(project));
+ }
+ }
+ rebuildTypeClassDefinitions = false;
+ }
+ }
+ }
+ return typeClassDefinitions;
+ }
+
+ /**
+ * This returns a list of restricted definitions for a name.
+ * The returned List is "live" and so should not be modified.
+ * Also, the returned list may be modified asynchronously.
+ * Any access must be guarded with a lock on the list itself.
+ *
+ * @param componentName the name to use.
+ * @return the list of restricted definitions for a particular name.
+ */
+ public List<AntTypeDefinition> getRestrictedDefinitions(String componentName) {
+ synchronized (restrictedDefinitions) {
+ return restrictedDefinitions.get(componentName);
+ }
+ }
+
+ /**
+ * 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) {
+ final AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(typeName);
+ def.setClass(typeClass);
+ updateDataTypeDefinition(def);
+ project.log(" +User datatype: " + typeName + " " + typeClass.getName(),
+ Project.MSG_DEBUG);
+ }
+
+ /**
+ * Describe <code>addDataTypeDefinition</code> method here.
+ *
+ * @param def an <code>AntTypeDefinition</code> value.
+ */
+ public void addDataTypeDefinition(AntTypeDefinition def) {
+ if (!def.isRestrict()) {
+ updateDataTypeDefinition(def);
+ } else {
+ updateRestrictedDefinition(def);
+ }
+ }
+
+ /**
+ * 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 datatype definition
+ * (String to {@link AntTypeDefinition}).
+ */
+ public Hashtable<String, AntTypeDefinition> getAntTypeTable() {
+ return antTypeTable;
+ }
+
+ /**
+ * Creates a new instance of a task.
+ *
+ * Called from Project.createTask(), which can be called by tasks.
+ *
+ * @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 && taskType.equals(ANT_PROPERTY_TASK)) {
+ // quick fix for Ant.java use of property before
+ // initializing the project
+ addTaskDefinition(ANT_PROPERTY_TASK, org.apache.tools.ant.taskdefs.Property.class);
+ task = createNewTask(taskType);
+ }
+ return task;
+ }
+
+ /**
+ * Creates a new instance of a task.
+ * @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 = getComponentClass(taskType);
+ if (c == null || !(Task.class.isAssignableFrom(c))) {
+ return null;
+ }
+ Object obj = createComponent(taskType);
+ if (obj == null) {
+ return null;
+ }
+ if (!(obj instanceof Task)) {
+ throw new BuildException("Expected a Task from '" + taskType
+ + "' but got an instance of " + obj.getClass().getName() + " instead");
+ }
+ Task task = (Task) obj;
+ task.setTaskType(taskType);
+
+ // set default value, can be changed by the user
+ task.setTaskName(taskType);
+
+ project.log(" +Task: " + taskType, Project.MSG_DEBUG);
+ return task;
+ }
+
+ /**
+ * 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 {
+ return createComponent(typeName);
+ }
+
+ /**
+ * Returns a description of the type of the given element.
+ * <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) {
+ return getElementName(element, false);
+ }
+
+ /**
+ * Returns a description of the type of the given element.
+ * <p>
+ * This is useful for logging purposes.
+ *
+ * @param o The element to describe.
+ * Must not be <code>null</code>.
+ * @param brief whether to use a brief description.
+ * @return a description of the element type.
+ *
+ * @since Ant 1.7
+ */
+ public String getElementName(Object o, boolean brief) {
+ // PR: I do not know what to do if the object class
+ // has multiple defines
+ // but this is for logging only...
+ Class<?> elementClass = o.getClass();
+ String elementClassname = elementClass.getName();
+ synchronized (antTypeTable) {
+ for (AntTypeDefinition def : antTypeTable.values()) {
+ if (elementClassname.equals(def.getClassName())
+ && (elementClass == def.getExposedClass(project))) {
+ String name = def.getName();
+ return brief ? name : "The <" + name + "> type";
+ }
+ }
+ }
+ return getUnmappedElementName(o.getClass(), brief);
+ }
+
+ /**
+ * Convenient way to get some element name even when you may not have a
+ * Project context.
+ * @param p The optional Project instance.
+ * @param o The element to describe.
+ * Must not be <code>null</code>.
+ * @param brief whether to use a brief description.
+ * @return a description of the element type.
+ * @since Ant 1.7
+ */
+ public static String getElementName(Project p, Object o, boolean brief) {
+ if (p == null) {
+ p = Project.getProject(o);
+ }
+ return p == null ? getUnmappedElementName(o.getClass(), brief) : getComponentHelper(p)
+ .getElementName(o, brief);
+ }
+
+ private static String getUnmappedElementName(Class<?> c, boolean brief) {
+ if (brief) {
+ String name = c.getName();
+ return name.substring(name.lastIndexOf('.') + 1);
+ }
+ return c.toString();
+ }
+
+ /**
+ * Check if definition is a valid definition--it may be a
+ * definition of an optional task that does not exist.
+ * @param def the definition to test.
+ * @return true if exposed type of definition is present.
+ */
+ private boolean validDefinition(AntTypeDefinition def) {
+ return !(def.getTypeClass(project) == null || def.getExposedClass(project) == null);
+ }
+
+ /**
+ * Check if two definitions are the same.
+ * @param def the new definition.
+ * @param old the old definition.
+ * @return true if the two definitions are the same.
+ */
+ private boolean sameDefinition(AntTypeDefinition def, AntTypeDefinition old) {
+ boolean defValid = validDefinition(def);
+ boolean sameValidity = (defValid == validDefinition(old));
+ //must have same validity; then if they are valid they must also be the same:
+ return sameValidity && (!defValid || def.sameDefinition(old, project));
+ }
+
+ /**
+ * update the restricted definition table with a new or
+ * modified definition.
+ */
+ private void updateRestrictedDefinition(AntTypeDefinition def) {
+ String name = def.getName();
+ List<AntTypeDefinition> list = null;
+ synchronized (restrictedDefinitions) {
+ list = restrictedDefinitions.get(name);
+ if (list == null) {
+ list = new ArrayList<AntTypeDefinition>();
+ restrictedDefinitions.put(name, list);
+ }
+ }
+ // Check if the classname is already present and remove it
+ // if it is
+ synchronized (list) {
+ for (Iterator<AntTypeDefinition> i = list.iterator(); i.hasNext();) {
+ AntTypeDefinition current = i.next();
+ if (current.getClassName().equals(def.getClassName())) {
+ i.remove();
+ break;
+ }
+ }
+ list.add(def);
+ }
+ }
+
+ /**
+ * Update the component definition table with a new or
+ * modified definition.
+ * @param def the definition to update or insert.
+ */
+ private void updateDataTypeDefinition(AntTypeDefinition def) {
+ String name = def.getName();
+ synchronized (antTypeTable) {
+ rebuildTaskClassDefinitions = true;
+ rebuildTypeClassDefinitions = true;
+ final AntTypeDefinition old = antTypeTable.get(name);
+ if (old != null) {
+ if (sameDefinition(def, old)) {
+ return;
+ }
+ Class<?> oldClass = old.getExposedClass(project);
+ boolean isTask = oldClass != null && Task.class.isAssignableFrom(oldClass);
+ project.log("Trying to override old definition of "
+ + (isTask ? "task " : "datatype ") + name, (def.similarDefinition(old,
+ project)) ? Project.MSG_VERBOSE : Project.MSG_WARN);
+ }
+ project.log(" +Datatype " + name + " " + def.getClassName(), Project.MSG_DEBUG);
+ antTypeTable.put(name, def);
+ }
+ }
+
+ /**
+ * Called at the start of processing an antlib.
+ * @param uri the uri that is associated with this antlib.
+ */
+ public void enterAntLib(String uri) {
+ antLibCurrentUri = uri;
+ antLibStack.push(uri);
+ }
+
+ /**
+ * @return the current antlib uri.
+ */
+ public String getCurrentAntlibUri() {
+ return antLibCurrentUri;
+ }
+
+ /**
+ * Called at the end of processing an antlib.
+ */
+ public void exitAntLib() {
+ antLibStack.pop();
+ antLibCurrentUri = (antLibStack.size() == 0) ? null : (String) antLibStack.peek();
+ }
+
+ /**
+ * Load ant's tasks.
+ */
+ private void initTasks() {
+ ClassLoader classLoader = getClassLoader(null);
+ Properties props = getDefaultDefinitions(false);
+ Enumeration<?> e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ String name = (String) e.nextElement();
+ String className = props.getProperty(name);
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(name);
+ def.setClassName(className);
+ def.setClassLoader(classLoader);
+ def.setAdaptToClass(Task.class);
+ def.setAdapterClass(TaskAdapter.class);
+ antTypeTable.put(name, def);
+ }
+ }
+
+ private ClassLoader getClassLoader(ClassLoader classLoader) {
+ String buildSysclasspath = project.getProperty(MagicNames.BUILD_SYSCLASSPATH);
+ if (project.getCoreLoader() != null
+ && !(BUILD_SYSCLASSPATH_ONLY.equals(buildSysclasspath))) {
+ classLoader = project.getCoreLoader();
+ }
+ return classLoader;
+ }
+
+ /**
+ * Load default task or type definitions - just the names,
+ * no class loading.
+ * Caches results between calls to reduce overhead.
+ * @param type true for typedefs, false for taskdefs
+ * @return a mapping from definition names to class names
+ * @throws BuildException if there was some problem loading
+ * or parsing the definitions list
+ */
+ private static synchronized Properties getDefaultDefinitions(boolean type)
+ throws BuildException {
+ int idx = type ? 1 : 0;
+ if (defaultDefinitions[idx] == null) {
+ String resource = type ? MagicNames.TYPEDEFS_PROPERTIES_RESOURCE
+ : MagicNames.TASKDEF_PROPERTIES_RESOURCE;
+ String errorString = type ? ERROR_NO_TYPE_LIST_LOAD : ERROR_NO_TASK_LIST_LOAD;
+ InputStream in = null;
+ try {
+ in = ComponentHelper.class.getResourceAsStream(resource);
+ if (in == null) {
+ throw new BuildException(errorString);
+ }
+ Properties p = new Properties();
+ p.load(in);
+ defaultDefinitions[idx] = p;
+ } catch (IOException e) {
+ throw new BuildException(errorString, e);
+ } finally {
+ FileUtils.close(in);
+ }
+ }
+ return defaultDefinitions[idx];
+ }
+
+ /**
+ * Load ant's datatypes.
+ */
+ private void initTypes() {
+ ClassLoader classLoader = getClassLoader(null);
+ Properties props = getDefaultDefinitions(true);
+ Enumeration<?> e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ String name = (String) e.nextElement();
+ String className = props.getProperty(name);
+ AntTypeDefinition def = new AntTypeDefinition();
+ def.setName(name);
+ def.setClassName(className);
+ def.setClassLoader(classLoader);
+ antTypeTable.put(name, def);
+ }
+ }
+
+ /**
+ * Called for each component name, check if the
+ * associated URI has been examined for antlibs.
+ * @param componentName the name of the component, which should include a URI
+ * prefix if it is in a namespace
+ */
+ private synchronized void checkNamespace(String componentName) {
+ String uri = ProjectHelper.extractUriFromComponentName(componentName);
+ if ("".equals(uri)) {
+ uri = ProjectHelper.ANT_CORE_URI;
+ }
+ if (!uri.startsWith(ProjectHelper.ANTLIB_URI)) {
+ return; // namespace that does not contain antlib
+ }
+ if (checkedNamespaces.contains(uri)) {
+ return; // Already processed
+ }
+ checkedNamespaces.add(uri);
+
+ if (antTypeTable.size() == 0) {
+ // Project instance doesn't know the tasks and types
+ // defined in defaults.properties, likely created by the
+ // user - without those definitions it cannot parse antlib
+ // files as taskdef, typedef and friends are unknown
+ initDefaultDefinitions();
+ }
+ Typedef definer = new Typedef();
+ definer.setProject(project);
+ definer.init();
+ definer.setURI(uri);
+ //there to stop error messages being "null"
+ definer.setTaskName(uri);
+ //if this is left out, bad things happen. like all build files break
+ //on the first element encountered.
+ definer.setResource(Definer.makeResourceFromURI(uri));
+ // a fishing expedition :- ignore errors if antlib not present
+ definer.setOnError(new Typedef.OnError(Typedef.OnError.POLICY_IGNORE));
+ definer.execute();
+ }
+
+ /**
+ * Handler called to do decent diagnosis on instantiation failure.
+ * @param componentName component name.
+ * @param type component type, used in error messages
+ * @return a string containing as much diagnostics info as possible.
+ */
+ public String diagnoseCreationFailure(String componentName, String type) {
+ StringWriter errorText = new StringWriter();
+ PrintWriter out = new PrintWriter(errorText);
+ out.println("Problem: failed to create " + type + " " + componentName);
+ //class of problem
+ boolean lowlevel = false;
+ boolean jars = false;
+ boolean definitions = false;
+ boolean antTask;
+ String home = System.getProperty(Launcher.USER_HOMEDIR);
+ File libDir = new File(home, Launcher.USER_LIBDIR);
+ String antHomeLib;
+ boolean probablyIDE = false;
+ String anthome = System.getProperty(MagicNames.ANT_HOME);
+ if (anthome != null) {
+ File antHomeLibDir = new File(anthome, "lib");
+ antHomeLib = antHomeLibDir.getAbsolutePath();
+ } else {
+ //running under an IDE that doesn't set ANT_HOME
+ probablyIDE = true;
+ antHomeLib = "ANT_HOME" + File.separatorChar + "lib";
+ }
+ StringBuffer dirListingText = new StringBuffer();
+ final String tab = " -";
+ dirListingText.append(tab);
+ dirListingText.append(antHomeLib);
+ dirListingText.append('\n');
+ if (probablyIDE) {
+ dirListingText.append(tab);
+ dirListingText.append("the IDE Ant configuration dialogs");
+ } else {
+ dirListingText.append(tab);
+ dirListingText.append(libDir);
+ dirListingText.append('\n');
+ dirListingText.append(tab);
+ dirListingText.append("a directory added on the command line with the -lib argument");
+ }
+ String dirListing = dirListingText.toString();
+
+ //look up the name
+ AntTypeDefinition def = getDefinition(componentName);
+ if (def == null) {
+ //not a known type
+ printUnknownDefinition(out, componentName, dirListing);
+ definitions = true;
+ } else {
+ //we are defined, so it is an instantiation problem
+ final String classname = def.getClassName();
+ antTask = classname.startsWith("org.apache.tools.ant.");
+ boolean optional = classname.startsWith("org.apache.tools.ant.taskdefs.optional");
+ optional |= classname.startsWith("org.apache.tools.ant.types.optional");
+
+ //start with instantiating the class.
+ Class<?> clazz = null;
+ try {
+ clazz = def.innerGetTypeClass();
+ } catch (ClassNotFoundException e) {
+ jars = true;
+ if (!optional) {
+ definitions = true;
+ }
+ printClassNotFound(out, classname, optional, dirListing);
+ } catch (NoClassDefFoundError ncdfe) {
+ jars = true;
+ printNotLoadDependentClass(out, optional, ncdfe, dirListing);
+ }
+ //here we successfully loaded the class or failed.
+ if (clazz != null) {
+ //success: proceed with more steps
+ try {
+ def.innerCreateAndSet(clazz, project);
+ //hey, there is nothing wrong with us
+ out.println("The component could be instantiated.");
+ } catch (NoSuchMethodException e) {
+ lowlevel = true;
+ out.println("Cause: The class " + classname
+ + " has no compatible constructor.");
+
+ } catch (InstantiationException e) {
+ lowlevel = true;
+ out.println("Cause: The class " + classname
+ + " is abstract and cannot be instantiated.");
+ } catch (IllegalAccessException e) {
+ lowlevel = true;
+ out.println("Cause: The constructor for " + classname
+ + " is private and cannot be invoked.");
+ } catch (InvocationTargetException ex) {
+ lowlevel = true;
+ Throwable t = ex.getTargetException();
+ out.println("Cause: The constructor threw the exception");
+ out.println(t.toString());
+ t.printStackTrace(out);
+ } catch (NoClassDefFoundError ncdfe) {
+ jars = true;
+ out.println("Cause: A class needed by class " + classname
+ + " cannot be found: ");
+ out.println(" " + ncdfe.getMessage());
+ out.println("Action: Determine what extra JAR files are"
+ + " needed, and place them in:");
+ out.println(dirListing);
+ }
+ }
+ out.println();
+ out.println("Do not panic, this is a common problem.");
+ if (definitions) {
+ out.println("It may just be a typographical error in the build file "
+ + "or the task/type declaration.");
+ }
+ if (jars) {
+ out.println("The commonest cause is a missing JAR.");
+ }
+ if (lowlevel) {
+ out.println("This is quite a low level problem, which may need "
+ + "consultation with the author of the task.");
+ if (antTask) {
+ out.println("This may be the Ant team. Please file a "
+ + "defect or contact the developer team.");
+ } else {
+ out.println("This does not appear to be a task bundled with Ant.");
+ out.println("Please take it up with the supplier of the third-party " + type
+ + ".");
+ out.println("If you have written it yourself, you probably have a bug to fix.");
+ }
+ } else {
+ out.println();
+ out.println("This is not a bug; it is a configuration problem");
+ }
+ }
+ out.flush();
+ out.close();
+ return errorText.toString();
+ }
+
+ /**
+ * Print unknown definition.forking
+ */
+ private void printUnknownDefinition(PrintWriter out, String componentName, String dirListing) {
+ boolean isAntlib = componentName.startsWith(MagicNames.ANTLIB_PREFIX);
+ String uri = ProjectHelper.extractUriFromComponentName(componentName);
+ out.println("Cause: The name is undefined.");
+ out.println("Action: Check the spelling.");
+ out.println("Action: Check that any custom tasks/types have been declared.");
+ out.println("Action: Check that any <presetdef>/<macrodef>"
+ + " declarations have taken place.");
+ if (uri.length() > 0) {
+ final List<AntTypeDefinition> matches = findTypeMatches(uri);
+ if (matches.size() > 0) {
+ out.println();
+ out.println("The definitions in the namespace " + uri + " are:");
+ for (AntTypeDefinition def : matches) {
+ String local = ProjectHelper.extractNameFromComponentName(def.getName());
+ out.println(" " + local);
+ }
+ } else {
+ out.println("No types or tasks have been defined in this namespace yet");
+ if (isAntlib) {
+ out.println();
+ out.println("This appears to be an antlib declaration. ");
+ out.println("Action: Check that the implementing library exists in one of:");
+ out.println(dirListing);
+ }
+ }
+ }
+ }
+
+ /**
+ * Print class not found.
+ */
+ private void printClassNotFound(PrintWriter out, String classname, boolean optional,
+ String dirListing) {
+ out.println("Cause: the class " + classname + " was not found.");
+ if (optional) {
+ out.println(" This looks like one of Ant's optional components.");
+ out.println("Action: Check that the appropriate optional JAR exists in");
+ out.println(dirListing);
+ } else {
+ out.println("Action: Check that the component has been correctly declared");
+ out.println(" and that the implementing JAR is in one of:");
+ out.println(dirListing);
+ }
+ }
+
+ /**
+ * Print could not load dependent class.
+ */
+ private void printNotLoadDependentClass(PrintWriter out, boolean optional,
+ NoClassDefFoundError ncdfe, String dirListing) {
+ out.println("Cause: Could not load a dependent class "
+ + ncdfe.getMessage());
+ if (optional) {
+ out.println(" It is not enough to have Ant's optional JARs");
+ out.println(" you need the JAR files that the" + " optional tasks depend upon.");
+ out.println(" Ant's optional task dependencies are" + " listed in the manual.");
+ } else {
+ out.println(" This class may be in a separate JAR" + " that is not installed.");
+ }
+ out.println("Action: Determine what extra JAR files are"
+ + " needed, and place them in one of:");
+ out.println(dirListing);
+ }
+
+ /**
+ * Create a list of all definitions that match a prefix, usually the URI
+ * of a library
+ * @param prefix prefix to match off
+ * @return the (possibly empty) list of definitions
+ */
+ private List<AntTypeDefinition> findTypeMatches(String prefix) {
+ final List<AntTypeDefinition> result = new ArrayList<AntTypeDefinition>();
+ synchronized (antTypeTable) {
+ for (AntTypeDefinition def : antTypeTable.values()) {
+ if (def.getName().startsWith(prefix)) {
+ result.add(def);
+ }
+ }
+ }
+ return result;
+ }
+}