/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-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
*
OnError value
*/
public void setOnError(OnError onError) {
this.onError = onError.getIndex();
}
/**
* @param reverseLoader if true a delegated loader will take precedence over
* the parent
* @deprecated stop using this attribute
* @ant.attribute ignore="true"
*/
public void setReverseLoader(boolean reverseLoader) {
this.cpDelegate.setReverseLoader(reverseLoader);
log("The reverseloader attribute is DEPRECATED. It will be removed",
Project.MSG_WARN);
}
/**
* @return the name for this definition
*/
public String getName() {
return name;
}
/**
* @return the class path path for this definition
*/
public Path getClasspath() {
return cpDelegate.getClasspath();
}
/**
* @return the file containing definitions
*/
public File getFile() {
return file;
}
/**
* @return the resource containing definitions
*/
public String getResource() {
return resource;
}
/**
* @return the reverse loader attribute of the classpath delegate.
*/
public boolean isReverseLoader() {
return cpDelegate.isReverseLoader();
}
/**
* Returns the loader id of the class path Delegate.
* @return the loader id
*/
public String getLoaderId() {
return cpDelegate.getClassLoadId();
}
/**
* Returns the class path id of the class path delegate.
* @return the class path id
*/
public String getClasspathId() {
return cpDelegate.getClassLoadId();
}
/**
* Set the classpath to be used when searching for component being defined
*
* @param classpath an Ant Path object containing the classpath.
*/
public void setClasspath(Path classpath) {
this.cpDelegate.setClasspath(classpath);
}
/**
* Create the classpath to be used when searching for component being
* defined
* @return the classpath of the this definition
*/
public Path createClasspath() {
return this.cpDelegate.createClasspath();
}
/**
* reference to a classpath to use when loading the files.
* To actually share the same loader, set loaderref as well
* @param r the reference to the classpath
*/
public void setClasspathRef(Reference r) {
this.cpDelegate.setClasspathref(r);
}
/**
* Use the reference to locate the loader. If the loader is not
* found, taskdef will use the specified classpath and register it
* with the specified name.
*
* This allow multiple taskdef/typedef to use the same class loader,
* so they can be used together. It eliminate the need to
* put them in the CLASSPATH.
*
* @param r the reference to locate the loader.
* @since Ant 1.5
*/
public void setLoaderRef(Reference r) {
this.cpDelegate.setLoaderRef(r);
}
/**
* Run the definition.
*
* @exception BuildException if an error occurs
*/
public void execute() throws BuildException {
ClassLoader al = createLoader();
if (!definerSet) {
throw new BuildException(
"name, file or resource attribute of "
+ getTaskName() + " is undefined", getLocation());
}
if (name != null) {
if (classname == null) {
throw new BuildException(
"classname attribute of " + getTaskName() + " element "
+ "is undefined", getLocation());
}
addDefinition(al, name, classname);
} else {
if (classname != null) {
String msg = "You must not specify classname "
+ "together with file or resource.";
throw new BuildException(msg, getLocation());
}
URL url = null;
if (file != null) {
url = fileToURL();
}
if (resource != null) {
url = resourceToURL(al);
}
if (url == null) {
return;
}
loadProperties(al, url);
}
}
private URL fileToURL() {
if (!(file.exists())) {
log("File " + file + " does not exist", Project.MSG_WARN);
return null;
}
if (!(file.isFile())) {
log("File " + file + " is not a file", Project.MSG_WARN);
return null;
}
try {
return file.toURL();
} catch (Exception ex) {
log("File " + file + " cannot use as URL: "
+ ex.toString(), Project.MSG_WARN);
return null;
}
}
private URL resourceToURL(ClassLoader classLoader) {
URL ret = classLoader.getResource(resource);
if (ret == null) {
if (onError != OnError.IGNORE) {
log("Could not load definitions from resource "
+ resource + ". It could not be found.",
Project.MSG_WARN);
}
}
return ret;
}
/**
* Load type definitions as properties from a url.
*
* @param al the classloader to use
* @param url the url to get the definitions from
*/
protected void loadProperties(ClassLoader al, URL url) {
InputStream is = null;
try {
is = url.openStream();
if (is == null) {
log("Could not load definitions from " + url,
Project.MSG_WARN);
return;
}
Properties props = new Properties();
props.load(is);
Enumeration keys = props.keys();
while (keys.hasMoreElements()) {
name = ((String) keys.nextElement());
classname = props.getProperty(name);
addDefinition(al, name, classname);
}
} catch (IOException ex) {
throw new BuildException(ex, getLocation());
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
}
/**
* create a classloader for this definition
* @return the classloader from the cpDelegate
*/
protected ClassLoader createLoader() {
if (internalClassLoader != null) {
return internalClassLoader;
}
ClassLoader al = this.cpDelegate.getClassLoader();
// need to load Task via system classloader or the new
// task we want to define will never be a Task but always
// be wrapped into a TaskAdapter.
((AntClassLoader) al).addSystemPackageRoot("org.apache.tools.ant");
return al;
}
/**
* Name of the property file to load
* ant name/classname pairs from.
* @param file the file
*/
public void setFile(File file) {
if (definerSet) {
tooManyDefinitions();
}
definerSet = true;
this.file = file;
}
/**
* Name of the property resource to load
* ant name/classname pairs from.
* @param res the resource to use
*/
public void setResource(String res) {
if (definerSet) {
tooManyDefinitions();
}
definerSet = true;
this.resource = res;
}
/**
* Name of the definition
* @param name the name of the definition
*/
public void setName(String name) {
if (definerSet) {
tooManyDefinitions();
}
definerSet = true;
this.name = name;
}
/**
* Returns the classname of the object we are defining.
* May be null.
* @return the class name
*/
public String getClassname() {
return classname;
}
/**
* The full class name of the object being defined.
* Required, unless file or resource have
* been specified.
* @param classname the name of the class
*/
public void setClassname(String classname) {
this.classname = classname;
}
/**
* Set the class name of the adapter class.
* An adapter class is used to proxy the
* definition class. It is used if the
* definition class is not assignable to
* the adaptto class, or if the adaptto
* class is not present.
*
* @param adapter the name of the adapter class
*/
public void setAdapter(String adapter) {
this.adapter = adapter;
}
/**
* Set the adapter class.
*
* @param adapterClass the class to use to adapt the definition class
*/
protected void setAdapterClass(Class adapterClass) {
this.adapterClass = adapterClass;
}
/**
* Set the classname of the class that the definition
* must be compatible with, either directly or
* by use of the adapeter class.
*
* @param adaptTo the name of the adaptto class
*/
public void setAdaptTo(String adaptTo) {
this.adaptTo = adaptTo;
}
/**
* Set the class for adaptToClass, to be
* used by derived classes, used instead of
* the adaptTo attribute.
*
* @param adaptToClass the class for adapto.
*/
protected void setAdaptToClass(Class adaptToClass) {
this.adaptToClass = adaptToClass;
}
/**
* Set the class loader, overrides the cpDelagate
* classloader.
*
* @param classLoader a ClassLoader value
*/
protected void setInternalClassLoader(ClassLoader classLoader) {
this.internalClassLoader = classLoader;
}
/**
* @see org.apache.tools.ant.Task#init()
* @since Ant 1.6
*/
public void init() throws BuildException {
this.cpDelegate = ClasspathUtils.getDelegate(this);
super.init();
}
/**
* Add a definition using the attributes of Definer
*
* @param al the ClassLoader to use
* @param name the name of the definition
* @param classname the classname of the definition
* @exception BuildException if an error occurs
*/
protected void addDefinition(ClassLoader al, String name, String classname)
throws BuildException {
Class cl = null;
try {
try {
if (onError != OnError.IGNORE) {
cl = al.loadClass(classname);
AntClassLoader.initializeClass(cl);
}
if (adapter != null) {
adapterClass = al.loadClass(adapter);
AntClassLoader.initializeClass(adapterClass);
}
if (adaptTo != null) {
adaptToClass = al.loadClass(adaptTo);
AntClassLoader.initializeClass(adaptToClass);
}
AntTypeDefinition def = new AntTypeDefinition();
def.setName(name);
def.setClassName(classname);
def.setClass(cl);
def.setAdapterClass(adapterClass);
def.setAdaptToClass(adaptToClass);
def.setClassLoader(al);
if (cl != null) {
def.checkClass(getProject());
}
ComponentHelper.getComponentHelper(getProject())
.addDataTypeDefinition(def);
} catch (ClassNotFoundException cnfe) {
String msg = getTaskName() + " class " + classname
+ " cannot be found";
throw new BuildException(msg, cnfe, getLocation());
} catch (NoClassDefFoundError ncdfe) {
String msg = getTaskName() + "A class needed by class "
+ classname + " cannot be found: " + ncdfe.getMessage();
throw new BuildException(msg, ncdfe, location);
}
} catch (BuildException ex) {
switch (onError) {
case OnError.FAIL:
throw ex;
case OnError.REPORT:
log(ex.getLocation() + "Warning: " + ex.getMessage(),
Project.MSG_WARN);
break;
default:
log(ex.getLocation() + ex.getMessage(),
Project.MSG_DEBUG);
}
}
}
private void tooManyDefinitions() {
throw new BuildException(
"Only one of the attributes name,file,resource"
+ " can be set", getLocation());
}
}