/*
 *  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
 *
 *      https://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.taskdefs;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import org.apache.tools.ant.AntTypeDefinition;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.ComponentHelper;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.MagicNames;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.util.FileUtils;

Base class for Taskdef and Typedef - handles all the attributes for Typedef. The uri and class handling is handled by DefBase
Since:Ant 1.4
/** * Base class for Taskdef and Typedef - handles all * the attributes for Typedef. The uri and class * handling is handled by DefBase * * @since Ant 1.4 */
public abstract class Definer extends DefBase {
the extension of an antlib file for autoloading. {@value /antlib.xml}
/** * the extension of an antlib file for autoloading. * {@value /antlib.xml} */
private static final String ANTLIB_XML = "/antlib.xml"; private static final ThreadLocal<Map<URL, Location>> RESOURCE_STACK = ThreadLocal.withInitial(HashMap::new); private String name; private String classname; private File file; private String resource; private boolean restrict = false; private int format = Format.PROPERTIES; private boolean definerSet = false; private int onError = OnError.FAIL; private String adapter; private String adaptTo; private Class<?> adapterClass; private Class<?> adaptToClass;
Enumerated type for onError attribute
See Also:
  • EnumeratedAttribute
/** * Enumerated type for onError attribute * * @see EnumeratedAttribute */
public static class OnError extends EnumeratedAttribute {
Enumerated values
/** Enumerated values */
public static final int FAIL = 0, REPORT = 1, IGNORE = 2, FAIL_ALL = 3;
text value of onerror option "fail"
/** * text value of onerror option {@value} */
public static final String POLICY_FAIL = "fail";
text value of onerror option "report"
/** * text value of onerror option {@value} */
public static final String POLICY_REPORT = "report";
text value of onerror option "ignore"
/** * text value of onerror option {@value} */
public static final String POLICY_IGNORE = "ignore";
text value of onerror option "failall"
/** * text value of onerror option {@value} */
public static final String POLICY_FAILALL = "failall";
Constructor
/** * Constructor */
public OnError() { super(); }
Constructor using a string.
Params:
  • value – the value of the attribute
/** * Constructor using a string. * @param value the value of the attribute */
public OnError(String value) { setValue(value); }
get the values
Returns:an array of the allowed values for this attribute.
/** * get the values * @return an array of the allowed values for this attribute. */
@Override public String[] getValues() { return new String[] {POLICY_FAIL, POLICY_REPORT, POLICY_IGNORE, POLICY_FAILALL}; } }
Enumerated type for format attribute
See Also:
  • EnumeratedAttribute
/** * Enumerated type for format attribute * * @see EnumeratedAttribute */
public static class Format extends EnumeratedAttribute {
Enumerated values
/** Enumerated values */
public static final int PROPERTIES = 0, XML = 1;
get the values
Returns:an array of the allowed values for this attribute.
/** * get the values * @return an array of the allowed values for this attribute. */
@Override public String[] getValues() { return new String[] {"properties", "xml"}; } }
The restrict attribute. If this is true, only use this definition in add(X).
Params:
  • restrict – the value to set.
/** * The restrict attribute. * If this is true, only use this definition in add(X). * @param restrict the value to set. */
protected void setRestrict(boolean restrict) { this.restrict = restrict; }
What to do if there is an error in loading the class.
  • error - throw build exception
  • report - output at warning level
  • ignore - output at debug level
Params:
  • onError – an OnError value
/** * What to do if there is an error in loading the class. * <ul> * <li>error - throw build exception</li> * <li>report - output at warning level</li> * <li>ignore - output at debug level</li> * </ul> * * @param onError an <code>OnError</code> value */
public void setOnError(OnError onError) { this.onError = onError.getIndex(); }
Sets the format of the file or resource
Params:
  • format – the enumerated value - xml or properties
/** * Sets the format of the file or resource * @param format the enumerated value - xml or properties */
public void setFormat(Format format) { this.format = format.getIndex(); }
Returns:the name for this definition
/** * @return the name for this definition */
public String getName() { return name; }
Returns:the file containing definitions
/** * @return the file containing definitions */
public File getFile() { return file; }
Returns:the resource containing definitions
/** * @return the resource containing definitions */
public String getResource() { return resource; }
Run the definition.
Throws:
  • BuildException – if an error occurs
/** * Run the definition. * * @exception BuildException if an error occurs */
@Override public void execute() throws BuildException { ClassLoader al = createLoader(); if (!definerSet) { //we arent fully defined yet. this is an error unless //we are in an antlib, in which case the resource name is determined //automatically. //NB: URIs in the ant core package will be "" at this point. if (getURI() == null) { throw new BuildException( "name, file or resource attribute of " + getTaskName() + " is undefined", getLocation()); } if (getURI().startsWith(MagicNames.ANTLIB_PREFIX)) { //convert the URI to a resource String uri1 = getURI(); setResource(makeResourceFromURI(uri1)); } else { throw new BuildException( "Only antlib URIs can be located from the URI alone, not the URI '" + getURI() + "'"); } } 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) { throw new BuildException( "You must not specify classname together with file or resource.", getLocation()); } final Enumeration<URL> urls; if (file == null) { urls = resourceToURLs(al); } else { final URL url = fileToURL(); if (url == null) { return; } urls = Collections.enumeration(Collections.singleton(url)); } while (urls.hasMoreElements()) { URL url = urls.nextElement(); int fmt = this.format; if (url.getPath().toLowerCase(Locale.ENGLISH).endsWith(".xml")) { fmt = Format.XML; } if (fmt == Format.PROPERTIES) { loadProperties(al, url); break; } else if (RESOURCE_STACK.get().get(url) != null) { log("Warning: Recursive loading of " + url + " ignored" + " at " + getLocation() + " originally loaded at " + RESOURCE_STACK.get().get(url), Project.MSG_WARN); } else { try { RESOURCE_STACK.get().put(url, getLocation()); loadAntlib(al, url); } finally { RESOURCE_STACK.get().remove(url); } } } } }
This is where the logic to map from a URI to an antlib resource is kept.
Params:
  • uri – the xml namespace uri that to convert.
Returns:the name of a resource. It may not exist
/** * This is where the logic to map from a URI to an antlib resource * is kept. * @param uri the xml namespace uri that to convert. * @return the name of a resource. It may not exist */
public static String makeResourceFromURI(String uri) { String path = uri.substring(MagicNames.ANTLIB_PREFIX.length()); String resource; if (path.startsWith("//")) { //handle new style full paths to an antlib, in which //all but the forward slashes are allowed. resource = path.substring("//".length()); if (!resource.endsWith(".xml")) { //if we haven't already named an XML file, it gets antlib.xml resource += ANTLIB_XML; } } else { //convert from a package to a path resource = path.replace('.', '/') + ANTLIB_XML; } return resource; }
Convert a file to a file: URL.
Throws:
  • BuildException – if the file is missing/not a file and the policy requires failure at this point.
Returns:the URL, or null if it isn't valid and the active error policy is not to raise a fault
/** * Convert a file to a file: URL. * * @return the URL, or null if it isn't valid and the active error policy * is not to raise a fault * @throws BuildException if the file is missing/not a file and the * policy requires failure at this point. */
private URL fileToURL() { String message = null; if (!file.exists()) { message = "File " + file + " does not exist"; } if (message == null && !(file.isFile())) { message = "File " + file + " is not a file"; } if (message == null) { try { return FileUtils.getFileUtils().getFileURL(file); } catch (Exception ex) { message = "File " + file + " cannot use as URL: " + ex.toString(); } } // Here if there is an error switch (onError) { case OnError.FAIL_ALL: throw new BuildException(message); case OnError.FAIL: // Fall Through case OnError.REPORT: log(message, Project.MSG_WARN); break; case OnError.IGNORE: // log at a lower level log(message, Project.MSG_VERBOSE); break; default: // Ignore the problem break; } return null; } private Enumeration<URL> resourceToURLs(ClassLoader classLoader) { Enumeration<URL> ret; try { ret = classLoader.getResources(resource); } catch (IOException e) { throw new BuildException( "Could not fetch resources named " + resource, e, getLocation()); } if (!ret.hasMoreElements()) { String message = "Could not load definitions from resource " + resource + ". It could not be found."; switch (onError) { case OnError.FAIL_ALL: throw new BuildException(message); case OnError.FAIL: case OnError.REPORT: log(message, Project.MSG_WARN); break; case OnError.IGNORE: log(message, Project.MSG_VERBOSE); break; default: // Ignore the problem break; } } return ret; }
Load type definitions as properties from a URL.
Params:
  • al – the classloader to use
  • url – the url to get the definitions from
/** * 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) { try (InputStream is = url.openStream()) { if (is == null) { log("Could not load definitions from " + url, Project.MSG_WARN); return; } Properties props = new Properties(); props.load(is); for (String key : props.stringPropertyNames()) { name = key; classname = props.getProperty(name); addDefinition(al, name, classname); } } catch (IOException ex) { throw new BuildException(ex, getLocation()); } }
Load an antlib from a URL.
Params:
  • classLoader – the classloader to use.
  • url – the url to load the definitions from.
/** * Load an antlib from a URL. * * @param classLoader the classloader to use. * @param url the url to load the definitions from. */
private void loadAntlib(ClassLoader classLoader, URL url) { try { Antlib antlib = Antlib.createAntlib(getProject(), url, getURI()); antlib.setClassLoader(classLoader); antlib.setURI(getURI()); antlib.execute(); } catch (BuildException ex) { throw ProjectHelper.addLocationToBuildException( ex, getLocation()); } }
Name of the property file to load ant name/classname pairs from.
Params:
  • file – the file
/** * 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.
Params:
  • res – the resource to use
/** * 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; }
Antlib attribute, sets resource and uri. uri is set the antlib value and, resource is set to the antlib.xml resource in the classpath. For example antlib="antlib:org.acme.bland.cola" corresponds to uri="antlib:org.acme.bland.cola" resource="org/acme/bland/cola/antlib.xml". ASF Bugzilla Bug 31999
Params:
  • antlib – the value to set.
/** * Antlib attribute, sets resource and uri. * uri is set the antlib value and, resource is set * to the antlib.xml resource in the classpath. * For example antlib="antlib:org.acme.bland.cola" * corresponds to uri="antlib:org.acme.bland.cola" * resource="org/acme/bland/cola/antlib.xml". * ASF Bugzilla Bug 31999 * @param antlib the value to set. */
public void setAntlib(String antlib) { if (definerSet) { tooManyDefinitions(); } if (!antlib.startsWith("antlib:")) { throw new BuildException( "Invalid antlib attribute - it must start with antlib:"); } setURI(antlib); this.resource = antlib.substring("antlib:".length()).replace('.', '/') + "/antlib.xml"; definerSet = true; }
Name of the definition
Params:
  • name – the name of the definition
/** * 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.
Returns:the class name
/** * Returns the classname of the object we are defining. * May be <code>null</code>. * @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.
Params:
  • classname – the name of the class
/** * 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.
Params:
  • adapter – the name of the adapter class
/** * 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.
Params:
  • adapterClass – the class to use to adapt the definition class
/** * 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 adapter class.
Params:
  • adaptTo – the name of the adaptto class
/** * Set the classname of the class that the definition * must be compatible with, either directly or * by use of the adapter 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.
Params:
  • adaptToClass – the class for adaptor.
/** * Set the class for adaptToClass, to be * used by derived classes, used instead of * the adaptTo attribute. * * @param adaptToClass the class for adaptor. */
protected void setAdaptToClass(Class<?> adaptToClass) { this.adaptToClass = adaptToClass; }
Add a definition using the attributes of Definer
Params:
  • al – the ClassLoader to use
  • name – the name of the definition
  • classname – the classname of the definition
Throws:
/** * 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 { name = ProjectHelper.genComponentName(getURI(), name); if (onError != OnError.IGNORE) { cl = Class.forName(classname, true, al); } if (adapter != null) { adapterClass = Class.forName(adapter, true, al); } if (adaptTo != null) { adaptToClass = Class.forName(adaptTo, true, al); } AntTypeDefinition def = new AntTypeDefinition(); def.setName(name); def.setClassName(classname); def.setClass(cl); def.setAdapterClass(adapterClass); def.setAdaptToClass(adaptToClass); def.setRestrict(restrict); def.setClassLoader(al); if (cl != null) { def.checkClass(getProject()); } ComponentHelper.getComponentHelper(getProject()) .addDataTypeDefinition(def); } catch (ClassNotFoundException cnfe) { throw new BuildException( getTaskName() + " class " + classname + " cannot be found\n using the classloader " + al, cnfe, getLocation()); } catch (NoClassDefFoundError ncdfe) { throw new BuildException( getTaskName() + " A class needed by class " + classname + " cannot be found: " + ncdfe.getMessage() + "\n using the classloader " + al, ncdfe, getLocation()); } } catch (BuildException ex) { switch (onError) { case OnError.FAIL_ALL: 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); } } }
handle too many definitions by raising an exception.
Throws:
  • BuildException – always.
/** * handle too many definitions by raising an exception. * @throws BuildException always. */
private void tooManyDefinitions() { throw new BuildException( "Only one of the attributes name, file and resource can be set", getLocation()); } }