package org.apache.tools.ant; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import org.apache.tools.ant.helper.ProjectHelper2; import org.apache.tools.ant.util.LoaderUtils; /** * Repository of {@link ProjectHelper} found in the classpath or via * some System properties. *

See the ProjectHelper documentation in the manual.

* * @since Ant 1.8.0 */ public class ProjectHelperRepository { private static final String DEBUG_PROJECT_HELPER_REPOSITORY = "ant.project-helper-repo.debug"; // The message log level is not accessible here because everything // is instanciated statically private static final boolean DEBUG = "true".equals(System.getProperty(DEBUG_PROJECT_HELPER_REPOSITORY)); private static ProjectHelperRepository instance = new ProjectHelperRepository(); private List/* */ helpers = new ArrayList(); private static final Class[] NO_CLASS = new Class[0]; private static final Object[] NO_OBJECT = new Object[0]; public static ProjectHelperRepository getInstance() { return instance; } private ProjectHelperRepository() { collectProjectHelpers(); } private void collectProjectHelpers() { // First, try the system property ProjectHelper projectHelper = getProjectHelperBySystemProperty(); registerProjectHelper(projectHelper); // A JDK1.3 'service' ( like in JAXP ). That will plug a helper // automatically if in CLASSPATH, with the right META-INF/services. try { ClassLoader classLoader = LoaderUtils.getContextClassLoader(); if (classLoader != null) { Enumeration resources = classLoader.getResources(ProjectHelper.SERVICE_ID); while (resources.hasMoreElements()) { URL resource = (URL) resources.nextElement(); projectHelper = getProjectHelperByService(resource.openStream()); registerProjectHelper(projectHelper); } } InputStream systemResource = ClassLoader.getSystemResourceAsStream(ProjectHelper.SERVICE_ID); if (systemResource != null) { projectHelper = getProjectHelperByService(systemResource); registerProjectHelper(projectHelper); } } catch (Exception e) { System.err.println("Unable to load ProjectHelper from service " + ProjectHelper.SERVICE_ID + " (" + e.getClass().getName() + ": " + e.getMessage() + ")"); if (DEBUG) { e.printStackTrace(System.err); } } // last but not least, ant default project helper projectHelper = new ProjectHelper2(); registerProjectHelper(projectHelper); } private void registerProjectHelper(ProjectHelper projectHelper) { if (projectHelper == null) { return; } if (DEBUG) { System.out.println("ProjectHelper " + projectHelper.getClass().getName() + " registered."); } try { helpers.add(projectHelper.getClass().getConstructor(NO_CLASS)); } catch (NoSuchMethodException nse) { // impossible to get here throw new BuildException("Couldn't find no-arg constructor in " + projectHelper.getClass().getName()); } } private ProjectHelper getProjectHelperBySystemProperty() { String helperClass = System.getProperty(ProjectHelper.HELPER_PROPERTY); try { if (helperClass != null) { return newHelper(helperClass); } } catch (SecurityException e) { System.err.println("Unable to load ProjectHelper class \"" + helperClass + " specified in system property " + ProjectHelper.HELPER_PROPERTY + " (" + e.getMessage() + ")"); if (DEBUG) { e.printStackTrace(System.err); } } return null; } private ProjectHelper getProjectHelperByService(InputStream is) { try { // This code is needed by EBCDIC and other strange systems. // It's a fix for bugs reported in xerces InputStreamReader isr; try { isr = new InputStreamReader(is, "UTF-8"); } catch (java.io.UnsupportedEncodingException e) { isr = new InputStreamReader(is); } BufferedReader rd = new BufferedReader(isr); String helperClassName = rd.readLine(); rd.close(); if (helperClassName != null && !"".equals(helperClassName)) { return newHelper(helperClassName); } } catch (Exception e) { System.out.println("Unable to load ProjectHelper from service " + ProjectHelper.SERVICE_ID + " (" + e.getMessage() + ")"); if (DEBUG) { e.printStackTrace(System.err); } } return null; } /** * Creates a new helper instance from the name of the class. It'll * first try the thread class loader, then Class.forName() will * load from the same loader that loaded this class. * * @param helperClass * The name of the class to create an instance of. Must not be * null. * * @return a new instance of the specified class. * * @exception BuildException * if the class cannot be found or cannot be appropriate * instantiated. */ private ProjectHelper newHelper(String helperClass) throws BuildException { ClassLoader classLoader = LoaderUtils.getContextClassLoader(); try { Class clazz = null; if (classLoader != null) { try { clazz = classLoader.loadClass(helperClass); } catch (ClassNotFoundException ex) { // try next method } } if (clazz == null) { clazz = Class.forName(helperClass); } return ((ProjectHelper) clazz.newInstance()); } catch (Exception e) { throw new BuildException(e); } } /** * Get the helper that will be able to parse the specified file. The helper * will be chosen among the ones found in the classpath * * @return the first ProjectHelper that fit the requirement (never null). */ public ProjectHelper getProjectHelper(File buildFile) throws BuildException { Iterator it = getHelpers(); while (it.hasNext()) { ProjectHelper helper = (ProjectHelper) it.next(); if (helper.supportsBuildFile(buildFile)) { if (DEBUG) { System.out.println("ProjectHelper " + helper.getClass().getName() + " selected for the file " + buildFile); } return helper; } } throw new RuntimeException("BUG: at least the ProjectHelper2 should " + "have supported the file " + buildFile); } /** * Get an iterator on the list of project helpers configured. The iterator * will always return at least one element as there will always be the * default project helper configured. * * @return an iterator of {@link ProjectHelper} */ public Iterator getHelpers() { return new ConstructingIterator(helpers.iterator()); } private static class ConstructingIterator implements Iterator { private final Iterator nested; ConstructingIterator(Iterator nested) { this.nested = nested; } public boolean hasNext() { return nested.hasNext(); } public Object next() { Constructor c = (Constructor) nested.next(); try { return c.newInstance(NO_OBJECT); } catch (Exception e) { throw new BuildException("Failed to invoke no-arg constructor" + " on " + c.getName()); } } public void remove() { throw new UnsupportedOperationException("remove is not supported"); } } }