Home » apache-ant-1.7.1-src » org.apache.tools » ant » [javadoc | source]
    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    *
   17    */
   18   package org.apache.tools.ant;
   19   
   20   import java.io.BufferedReader;
   21   import java.io.File;
   22   import java.io.InputStream;
   23   import java.io.InputStreamReader;
   24   import java.util.Hashtable;
   25   import java.util.Locale;
   26   import java.util.Vector;
   27   
   28   import org.xml.sax.AttributeList;
   29   
   30   import org.apache.tools.ant.helper.ProjectHelper2;
   31   import org.apache.tools.ant.util.LoaderUtils;
   32   
   33   /**
   34    * Configures a Project (complete with Targets and Tasks) based on
   35    * a XML build file. It'll rely on a plugin to do the actual processing
   36    * of the xml file.
   37    *
   38    * This class also provide static wrappers for common introspection.
   39    *
   40    * All helper plugins must provide backward compatibility with the
   41    * original ant patterns, unless a different behavior is explicitly
   42    * specified. For example, if namespace is used on the <project> tag
   43    * the helper can expect the entire build file to be namespace-enabled.
   44    * Namespaces or helper-specific tags can provide meta-information to
   45    * the helper, allowing it to use new ( or different policies ).
   46    *
   47    * However, if no namespace is used the behavior should be exactly
   48    * identical with the default helper.
   49    *
   50    */
   51   public class ProjectHelper {
   52       /** The URI for ant name space */
   53       public static final String ANT_CORE_URI    = "antlib:org.apache.tools.ant";
   54   
   55       /** The URI for antlib current definitions */
   56       public static final String ANT_CURRENT_URI      = "ant:current";
   57   
   58       /** The URI for defined types/tasks - the format is antlib:<package> */
   59       public static final String ANTLIB_URI     = "antlib:";
   60   
   61       /** Polymorphic attribute  */
   62       public static final String ANT_TYPE = "ant-type";
   63   
   64       /**
   65        * Name of JVM system property which provides the name of the
   66        * ProjectHelper class to use.
   67        */
   68       public static final String HELPER_PROPERTY = MagicNames.PROJECT_HELPER_CLASS;
   69   
   70       /**
   71        * The service identifier in jars which provide Project Helper
   72        * implementations.
   73        */
   74       public static final String SERVICE_ID = MagicNames.PROJECT_HELPER_SERVICE;
   75   
   76       /**
   77        * name of project helper reference that we add to a project
   78        */
   79       public static final String PROJECTHELPER_REFERENCE = MagicNames.REFID_PROJECT_HELPER;
   80   
   81       /**
   82        * Configures the project with the contents of the specified XML file.
   83        *
   84        * @param project The project to configure. Must not be <code>null</code>.
   85        * @param buildFile An XML file giving the project's configuration.
   86        *                  Must not be <code>null</code>.
   87        *
   88        * @exception BuildException if the configuration is invalid or cannot be read
   89        */
   90       public static void configureProject(Project project, File buildFile) throws BuildException {
   91           ProjectHelper helper = ProjectHelper.getProjectHelper();
   92           project.addReference(PROJECTHELPER_REFERENCE, helper);
   93           helper.parse(project, buildFile);
   94       }
   95   
   96       /** Default constructor */
   97       public ProjectHelper() {
   98       }
   99   
  100       // -------------------- Common properties  --------------------
  101       // The following properties are required by import ( and other tasks
  102       // that read build files using ProjectHelper ).
  103   
  104       // A project helper may process multiple files. We'll keep track
  105       // of them - to avoid loops and to allow caching. The caching will
  106       // probably accelerate things like <antCall>.
  107       // The key is the absolute file, the value is a processed tree.
  108       // Since the tree is composed of UE and RC - it can be reused !
  109       // protected Hashtable processedFiles=new Hashtable();
  110   
  111       private Vector importStack = new Vector();
  112   
  113       // Temporary - until we figure a better API
  114       /** EXPERIMENTAL WILL_CHANGE
  115        *
  116        */
  117   //    public Hashtable getProcessedFiles() {
  118   //        return processedFiles;
  119   //    }
  120   
  121       /** EXPERIMENTAL WILL_CHANGE
  122        *  Import stack.
  123        *  Used to keep track of imported files. Error reporting should
  124        *  display the import path.
  125        *
  126        * @return the stack of import source objects.
  127        */
  128       public Vector getImportStack() {
  129           return importStack;
  130       }
  131   
  132       // --------------------  Parse method  --------------------
  133       /**
  134        * Parses the project file, configuring the project as it goes.
  135        *
  136        * @param project The project for the resulting ProjectHelper to configure.
  137        *                Must not be <code>null</code>.
  138        * @param source The source for XML configuration. A helper must support
  139        *               at least File, for backward compatibility. Helpers may
  140        *               support URL, InputStream, etc or specialized types.
  141        *
  142        * @since Ant1.5
  143        * @exception BuildException if the configuration is invalid or cannot
  144        *                           be read
  145        */
  146       public void parse(Project project, Object source) throws BuildException {
  147           throw new BuildException("ProjectHelper.parse() must be implemented "
  148               + "in a helper plugin " + this.getClass().getName());
  149       }
  150   
  151       /**
  152        * Discovers a project helper instance. Uses the same patterns
  153        * as JAXP, commons-logging, etc: a system property, a JDK1.3
  154        * service discovery, default.
  155        *
  156        * @return a ProjectHelper, either a custom implementation
  157        * if one is available and configured, or the default implementation
  158        * otherwise.
  159        *
  160        * @exception BuildException if a specified helper class cannot
  161        * be loaded/instantiated.
  162        */
  163       public static ProjectHelper getProjectHelper() throws BuildException {
  164           // Identify the class loader we will be using. Ant may be
  165           // in a webapp or embedded in a different app
  166           ProjectHelper helper = null;
  167   
  168           // First, try the system property
  169           String helperClass = System.getProperty(HELPER_PROPERTY);
  170           try {
  171               if (helperClass != null) {
  172                   helper = newHelper(helperClass);
  173               }
  174           } catch (SecurityException e) {
  175               System.out.println("Unable to load ProjectHelper class \""
  176                   + helperClass + " specified in system property "
  177                   + HELPER_PROPERTY);
  178           }
  179   
  180           // A JDK1.3 'service' ( like in JAXP ). That will plug a helper
  181           // automatically if in CLASSPATH, with the right META-INF/services.
  182           if (helper == null) {
  183               try {
  184                   ClassLoader classLoader = LoaderUtils.getContextClassLoader();
  185                   InputStream is = null;
  186                   if (classLoader != null) {
  187                       is = classLoader.getResourceAsStream(SERVICE_ID);
  188                   }
  189                   if (is == null) {
  190                       is = ClassLoader.getSystemResourceAsStream(SERVICE_ID);
  191                   }
  192                   if (is != null) {
  193                       // This code is needed by EBCDIC and other strange systems.
  194                       // It's a fix for bugs reported in xerces
  195                       InputStreamReader isr;
  196                       try {
  197                           isr = new InputStreamReader(is, "UTF-8");
  198                       } catch (java.io.UnsupportedEncodingException e) {
  199                           isr = new InputStreamReader(is);
  200                       }
  201                       BufferedReader rd = new BufferedReader(isr);
  202   
  203                       String helperClassName = rd.readLine();
  204                       rd.close();
  205   
  206                       if (helperClassName != null && !"".equals(helperClassName)) {
  207                           helper = newHelper(helperClassName);
  208                       }
  209                   }
  210               } catch (Exception ex) {
  211                   System.out.println("Unable to load ProjectHelper from service " + SERVICE_ID);
  212               }
  213           }
  214           return helper == null ? new ProjectHelper2() : helper;
  215       }
  216   
  217       /**
  218        * Creates a new helper instance from the name of the class.
  219        * It'll first try the thread class loader, then Class.forName()
  220        * will load from the same loader that loaded this class.
  221        *
  222        * @param helperClass The name of the class to create an instance
  223        *                    of. Must not be <code>null</code>.
  224        *
  225        * @return a new instance of the specified class.
  226        *
  227        * @exception BuildException if the class cannot be found or
  228        * cannot be appropriate instantiated.
  229        */
  230       private static ProjectHelper newHelper(String helperClass)
  231           throws BuildException {
  232           ClassLoader classLoader = LoaderUtils.getContextClassLoader();
  233           try {
  234               Class clazz = null;
  235               if (classLoader != null) {
  236                   try {
  237                       clazz = classLoader.loadClass(helperClass);
  238                   } catch (ClassNotFoundException ex) {
  239                       // try next method
  240                   }
  241               }
  242               if (clazz == null) {
  243                   clazz = Class.forName(helperClass);
  244               }
  245               return ((ProjectHelper) clazz.newInstance());
  246           } catch (Exception e) {
  247               throw new BuildException(e);
  248           }
  249       }
  250   
  251       /**
  252        * JDK1.1 compatible access to the context class loader. Cut & paste from JAXP.
  253        *
  254        * @deprecated since 1.6.x.
  255        *             Use LoaderUtils.getContextClassLoader()
  256        *
  257        * @return the current context class loader, or <code>null</code>
  258        * if the context class loader is unavailable.
  259        */
  260       public static ClassLoader getContextClassLoader() {
  261           return LoaderUtils.isContextLoaderAvailable() ? LoaderUtils.getContextClassLoader() : null;
  262       }
  263   
  264       // -------------------- Static utils, used by most helpers ----------------
  265   
  266       /**
  267        * Configures an object using an introspection handler.
  268        *
  269        * @param target The target object to be configured.
  270        *               Must not be <code>null</code>.
  271        * @param attrs  A list of attributes to configure within the target.
  272        *               Must not be <code>null</code>.
  273        * @param project The project containing the target.
  274        *                Must not be <code>null</code>.
  275        *
  276        * @deprecated since 1.6.x.
  277        *             Use IntrospectionHelper for each property.
  278        *
  279        * @exception BuildException if any of the attributes can't be handled by
  280        *                           the target
  281        */
  282       public static void configure(Object target, AttributeList attrs,
  283                                    Project project) throws BuildException {
  284           if (target instanceof TypeAdapter) {
  285               target = ((TypeAdapter) target).getProxy();
  286           }
  287           IntrospectionHelper ih = IntrospectionHelper.getHelper(project, target.getClass());
  288   
  289           for (int i = 0, length = attrs.getLength(); i < length; i++) {
  290               // reflect these into the target
  291               String value = replaceProperties(project, attrs.getValue(i), project.getProperties());
  292               try {
  293                   ih.setAttribute(project, target, attrs.getName(i).toLowerCase(Locale.US), value);
  294               } catch (BuildException be) {
  295                   // id attribute must be set externally
  296                   if (!attrs.getName(i).equals("id")) {
  297                       throw be;
  298                   }
  299               }
  300           }
  301       }
  302   
  303       /**
  304        * Adds the content of #PCDATA sections to an element.
  305        *
  306        * @param project The project containing the target.
  307        *                Must not be <code>null</code>.
  308        * @param target  The target object to be configured.
  309        *                Must not be <code>null</code>.
  310        * @param buf A character array of the text within the element.
  311        *            Will not be <code>null</code>.
  312        * @param start The start element in the array.
  313        * @param count The number of characters to read from the array.
  314        *
  315        * @exception BuildException if the target object doesn't accept text
  316        */
  317       public static void addText(Project project, Object target, char[] buf,
  318           int start, int count) throws BuildException {
  319           addText(project, target, new String(buf, start, count));
  320       }
  321   
  322       /**
  323        * Adds the content of #PCDATA sections to an element.
  324        *
  325        * @param project The project containing the target.
  326        *                Must not be <code>null</code>.
  327        * @param target  The target object to be configured.
  328        *                Must not be <code>null</code>.
  329        * @param text    Text to add to the target.
  330        *                May be <code>null</code>, in which case this
  331        *                method call is a no-op.
  332        *
  333        * @exception BuildException if the target object doesn't accept text
  334        */
  335       public static void addText(Project project, Object target, String text)
  336           throws BuildException {
  337   
  338           if (text == null) {
  339               return;
  340           }
  341           if (target instanceof TypeAdapter) {
  342               target = ((TypeAdapter) target).getProxy();
  343           }
  344           IntrospectionHelper.getHelper(project, target.getClass()).addText(project, target, text);
  345       }
  346   
  347       /**
  348        * Stores a configured child element within its parent object.
  349        *
  350        * @param project Project containing the objects.
  351        *                May be <code>null</code>.
  352        * @param parent  Parent object to add child to.
  353        *                Must not be <code>null</code>.
  354        * @param child   Child object to store in parent.
  355        *                Should not be <code>null</code>.
  356        * @param tag     Name of element which generated the child.
  357        *                May be <code>null</code>, in which case
  358        *                the child is not stored.
  359        */
  360       public static void storeChild(Project project, Object parent, Object child, String tag) {
  361           IntrospectionHelper ih = IntrospectionHelper.getHelper(project, parent.getClass());
  362           ih.storeElement(project, parent, child, tag);
  363       }
  364   
  365       /**
  366        * Replaces <code>${xxx}</code> style constructions in the given value with
  367        * the string value of the corresponding properties.
  368        *
  369        * @param project The project containing the properties to replace.
  370        *                Must not be <code>null</code>.
  371        *
  372        * @param value The string to be scanned for property references.
  373        *              May be <code>null</code>.
  374        *
  375        * @exception BuildException if the string contains an opening
  376        *                           <code>${</code> without a closing
  377        *                           <code>}</code>
  378        * @return the original string with the properties replaced, or
  379        *         <code>null</code> if the original string is <code>null</code>.
  380        *
  381        * @deprecated since 1.6.x.
  382        *             Use project.replaceProperties().
  383        * @since 1.5
  384        */
  385        public static String replaceProperties(Project project, String value) throws BuildException {
  386           // needed since project properties are not accessible
  387            return project.replaceProperties(value);
  388        }
  389   
  390       /**
  391        * Replaces <code>${xxx}</code> style constructions in the given value
  392        * with the string value of the corresponding data types.
  393        *
  394        * @param project The container project. This is used solely for
  395        *                logging purposes. Must not be <code>null</code>.
  396        * @param value The string to be scanned for property references.
  397        *              May be <code>null</code>, in which case this
  398        *              method returns immediately with no effect.
  399        * @param keys  Mapping (String to String) of property names to their
  400        *              values. Must not be <code>null</code>.
  401        *
  402        * @exception BuildException if the string contains an opening
  403        *                           <code>${</code> without a closing
  404        *                           <code>}</code>
  405        * @return the original string with the properties replaced, or
  406        *         <code>null</code> if the original string is <code>null</code>.
  407        * @deprecated since 1.6.x.
  408        *             Use PropertyHelper.
  409        */
  410        public static String replaceProperties(Project project, String value, Hashtable keys)
  411                throws BuildException {
  412           PropertyHelper ph = PropertyHelper.getPropertyHelper(project);
  413           return ph.replaceProperties(null, value, keys);
  414       }
  415   
  416       /**
  417        * Parses a string containing <code>${xxx}</code> style property
  418        * references into two lists. The first list is a collection
  419        * of text fragments, while the other is a set of string property names.
  420        * <code>null</code> entries in the first list indicate a property
  421        * reference from the second list.
  422        *
  423        * @param value     Text to parse. Must not be <code>null</code>.
  424        * @param fragments List to add text fragments to.
  425        *                  Must not be <code>null</code>.
  426        * @param propertyRefs List to add property names to.
  427        *                     Must not be <code>null</code>.
  428        *
  429        * @deprecated since 1.6.x.
  430        *             Use PropertyHelper.
  431        * @exception BuildException if the string contains an opening
  432        *                           <code>${</code> without a closing <code>}</code>
  433        */
  434       public static void parsePropertyString(String value, Vector fragments, Vector propertyRefs)
  435               throws BuildException {
  436           PropertyHelper.parsePropertyStringDefault(value, fragments, propertyRefs);
  437       }
  438   
  439       /**
  440        * Map a namespaced {uri,name} to an internal string format.
  441        * For BC purposes the names from the ant core uri will be
  442        * mapped to "name", other names will be mapped to
  443        * uri + ":" + name.
  444        * @param uri   The namepace URI
  445        * @param name  The localname
  446        * @return      The stringified form of the ns name
  447        */
  448       public static String genComponentName(String uri, String name) {
  449           if (uri == null || uri.equals("") || uri.equals(ANT_CORE_URI)) {
  450               return name;
  451           }
  452           return uri + ":" + name;
  453       }
  454   
  455       /**
  456        * extract a uri from a component name
  457        *
  458        * @param componentName  The stringified form for {uri, name}
  459        * @return               The uri or "" if not present
  460        */
  461       public static String extractUriFromComponentName(String componentName) {
  462           if (componentName == null) {
  463               return "";
  464           }
  465           int index = componentName.lastIndexOf(':');
  466           if (index == -1) {
  467               return "";
  468           }
  469           return componentName.substring(0, index);
  470       }
  471   
  472       /**
  473        * extract the element name from a component name
  474        *
  475        * @param componentName  The stringified form for {uri, name}
  476        * @return               The element name of the component
  477        */
  478       public static String extractNameFromComponentName(String componentName) {
  479           int index = componentName.lastIndexOf(':');
  480           if (index == -1) {
  481               return componentName;
  482           }
  483           return componentName.substring(index + 1);
  484       }
  485   
  486       /**
  487        * Add location to build exception.
  488        * @param ex the build exception, if the build exception
  489        *           does not include
  490        * @param newLocation the location of the calling task (may be null)
  491        * @return a new build exception based in the build exception with
  492        *         location set to newLocation. If the original exception
  493        *         did not have a location, just return the build exception
  494        */
  495       public static BuildException addLocationToBuildException(
  496               BuildException ex, Location newLocation) {
  497           if (ex.getLocation() == null || ex.getMessage() == null) {
  498               return ex;
  499           }
  500           String errorMessage
  501               = "The following error occurred while executing this line:"
  502               + System.getProperty("line.separator")
  503               + ex.getLocation().toString()
  504               + ex.getMessage();
  505           if (newLocation == null) {
  506               return new BuildException(errorMessage, ex);
  507           }
  508           return new BuildException(errorMessage, ex, newLocation);
  509       }
  510   }

Save This Page
Home » apache-ant-1.7.1-src » org.apache.tools » ant » [javadoc | source]