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.util.Hashtable;
   21   import java.util.Vector;
   22   import java.util.Enumeration;
   23   
   24   /* ISSUES:
   25    - ns param. It could be used to provide "namespaces" for properties, which
   26    may be more flexible.
   27    - Object value. In ant1.5 String is used for Properties - but it would be nice
   28    to support generic Objects (the property remains immutable - you can't change
   29    the associated object). This will also allow JSP-EL style setting using the
   30    Object if an attribute contains only the property (name="${property}" could
   31    avoid Object->String->Object conversion)
   32    - Currently we "chain" only for get and set property (probably most users
   33    will only need that - if they need more they can replace the top helper).
   34    Need to discuss this and find if we need more.
   35    */
   36   
   37   /** NOT FINAL. API MAY CHANGE
   38    *
   39    * Deals with properties - substitution, dynamic properties, etc.
   40    *
   41    * This is the same code as in Ant1.5. The main addition is the ability
   42    * to chain multiple PropertyHelpers and to replace the default.
   43    *
   44    * @since Ant 1.6
   45    */
   46   public class PropertyHelper {
   47   
   48       private Project project;
   49       private PropertyHelper next;
   50   
   51       /** Project properties map (usually String to String). */
   52       private Hashtable properties = new Hashtable();
   53   
   54       /**
   55        * Map of "user" properties (as created in the Ant task, for example).
   56        * Note that these key/value pairs are also always put into the
   57        * project properties, so only the project properties need to be queried.
   58        * Mapping is String to String.
   59        */
   60       private Hashtable userProperties = new Hashtable();
   61   
   62       /**
   63        * Map of inherited "user" properties - that are those "user"
   64        * properties that have been created by tasks and not been set
   65        * from the command line or a GUI tool.
   66        * Mapping is String to String.
   67        */
   68       private Hashtable inheritedProperties = new Hashtable();
   69   
   70       /**
   71        * Default constructor.
   72        */
   73       protected PropertyHelper() {
   74       }
   75   
   76       //override facility for subclasses to put custom hashtables in
   77   
   78       // --------------------  Hook management  --------------------
   79   
   80       /**
   81        * Set the project for which this helper is performing property resolution.
   82        *
   83        * @param p the project instance.
   84        */
   85       public void setProject(Project p) {
   86           this.project = p;
   87       }
   88   
   89       /**
   90        * Get this PropertyHelper's Project.
   91        * @return Project
   92        */
   93       public Project getProject() {
   94           return project;
   95       }
   96   
   97       /** There are 2 ways to hook into property handling:
   98        *  - you can replace the main PropertyHelper. The replacement is required
   99        * to support the same semantics (of course :-)
  100        *
  101        *  - you can chain a property helper capable of storing some properties.
  102        *  Again, you are required to respect the immutability semantics (at
  103        *  least for non-dynamic properties)
  104        *
  105        * @param next the next property helper in the chain.
  106        */
  107       public void setNext(PropertyHelper next) {
  108           this.next = next;
  109       }
  110   
  111       /**
  112        * Get the next property helper in the chain.
  113        *
  114        * @return the next property helper.
  115        */
  116       public PropertyHelper getNext() {
  117           return next;
  118       }
  119   
  120       /**
  121        * Factory method to create a property processor.
  122        * Users can provide their own or replace it using "ant.PropertyHelper"
  123        * reference. User tasks can also add themselves to the chain, and provide
  124        * dynamic properties.
  125        *
  126        * @param project the project for which the property helper is required.
  127        *
  128        * @return the project's property helper.
  129        */
  130       public static synchronized PropertyHelper getPropertyHelper(Project project) {
  131           PropertyHelper helper
  132                   = (PropertyHelper) project.getReference(MagicNames.REFID_PROPERTY_HELPER);
  133           if (helper != null) {
  134               return helper;
  135           }
  136           helper = new PropertyHelper();
  137           helper.setProject(project);
  138   
  139           project.addReference(MagicNames.REFID_PROPERTY_HELPER, helper);
  140           return helper;
  141       }
  142   
  143       // --------------------  Methods to override  --------------------
  144   
  145       /**
  146        * Sets a property. Any existing property of the same name
  147        * is overwritten, unless it is a user property. Will be called
  148        * from setProperty().
  149        *
  150        * If all helpers return false, the property will be saved in
  151        * the default properties table by setProperty.
  152        *
  153        * @param ns   The namespace that the property is in (currently
  154        *             not used.
  155        * @param name The name of property to set.
  156        *             Must not be <code>null</code>.
  157        * @param value The new value of the property.
  158        *              Must not be <code>null</code>.
  159        * @param inherited True if this property is inherited (an [sub]ant[call] property).
  160        * @param user      True if this property is a user property.
  161        * @param isNew     True is this is a new property.
  162        * @return true if this helper has stored the property, false if it
  163        *    couldn't. Each helper should delegate to the next one (unless it
  164        *    has a good reason not to).
  165        */
  166       public boolean setPropertyHook(String ns, String name,
  167                                      Object value,
  168                                      boolean inherited, boolean user,
  169                                      boolean isNew) {
  170           if (getNext() != null) {
  171               boolean subst = getNext().setPropertyHook(ns, name, value, inherited, user, isNew);
  172               // If next has handled the property
  173               if (subst) {
  174                   return true;
  175               }
  176           }
  177           return false;
  178       }
  179   
  180       /** Get a property. If all hooks return null, the default
  181        * tables will be used.
  182        *
  183        * @param ns namespace of the sought property.
  184        * @param name name of the sought property.
  185        * @param user True if this is a user property.
  186        * @return The property, if returned by a hook, or null if none.
  187        */
  188       public Object getPropertyHook(String ns, String name, boolean user) {
  189           if (getNext() != null) {
  190               Object o = getNext().getPropertyHook(ns, name, user);
  191               if (o != null) {
  192                   return o;
  193               }
  194           }
  195           // Experimental/Testing, will be removed
  196           if (name.startsWith("toString:")) {
  197               name = name.substring("toString:".length());
  198               Object v = project.getReference(name);
  199               return (v == null) ? null : v.toString();
  200           }
  201           return null;
  202       }
  203   
  204       // -------------------- Optional methods   --------------------
  205       // You can override those methods if you want to optimize or
  206       // do advanced things (like support a special syntax).
  207       // The methods do not chain - you should use them when embedding ant
  208       // (by replacing the main helper)
  209   
  210       /**
  211        * Parses a string containing <code>${xxx}</code> style property
  212        * references into two lists. The first list is a collection
  213        * of text fragments, while the other is a set of string property names.
  214        * <code>null</code> entries in the first list indicate a property
  215        * reference from the second list.
  216        *
  217        * It can be overridden with a more efficient or customized version.
  218        *
  219        * @param value     Text to parse. Must not be <code>null</code>.
  220        * @param fragments List to add text fragments to.
  221        *                  Must not be <code>null</code>.
  222        * @param propertyRefs List to add property names to.
  223        *                     Must not be <code>null</code>.
  224        *
  225        * @exception BuildException if the string contains an opening
  226        *                           <code>${</code> without a closing
  227        *                           <code>}</code>
  228        */
  229       public void parsePropertyString(String value, Vector fragments,
  230                                       Vector propertyRefs) throws BuildException {
  231           parsePropertyStringDefault(value, fragments, propertyRefs);
  232       }
  233   
  234       /**
  235        * Replaces <code>${xxx}</code> style constructions in the given value
  236        * with the string value of the corresponding data types.
  237        *
  238        * @param ns    The namespace for the property.
  239        * @param value The string to be scanned for property references.
  240        *              May be <code>null</code>, in which case this
  241        *              method returns immediately with no effect.
  242        * @param keys  Mapping (String to String) of property names to their
  243        *              values. If <code>null</code>, only project properties will
  244        *              be used.
  245        *
  246        * @exception BuildException if the string contains an opening
  247        *                           <code>${</code> without a closing
  248        *                           <code>}</code>
  249        * @return the original string with the properties replaced, or
  250        *         <code>null</code> if the original string is <code>null</code>.
  251        */
  252       public String replaceProperties(String ns, String value, Hashtable keys) throws BuildException {
  253           if (value == null || value.indexOf('$') == -1) {
  254               return value;
  255           }
  256           Vector fragments = new Vector();
  257           Vector propertyRefs = new Vector();
  258           parsePropertyString(value, fragments, propertyRefs);
  259   
  260           StringBuffer sb = new StringBuffer();
  261           Enumeration i = fragments.elements();
  262           Enumeration j = propertyRefs.elements();
  263   
  264           while (i.hasMoreElements()) {
  265               String fragment = (String) i.nextElement();
  266               if (fragment == null) {
  267                   String propertyName = (String) j.nextElement();
  268                   Object replacement = null;
  269   
  270                   // try to get it from the project or keys
  271                   // Backward compatibility
  272                   if (keys != null) {
  273                       replacement = keys.get(propertyName);
  274                   }
  275                   if (replacement == null) {
  276                       replacement = getProperty(ns, propertyName);
  277                   }
  278                   if (replacement == null) {
  279                       project.log("Property \"" + propertyName
  280                               + "\" has not been set", Project.MSG_VERBOSE);
  281                   }
  282                   fragment = (replacement != null)
  283                           ? replacement.toString() : "${" + propertyName + "}";
  284               }
  285               sb.append(fragment);
  286           }
  287           return sb.toString();
  288       }
  289   
  290       // -------------------- Default implementation  --------------------
  291       // Methods used to support the default behavior and provide backward
  292       // compatibility. Some will be deprecated, you should avoid calling them.
  293   
  294       /** Default implementation of setProperty. Will be called from Project.
  295        *  This is the original 1.5 implementation, with calls to the hook
  296        *  added.
  297        *  @param ns      The namespace for the property (currently not used).
  298        *  @param name    The name of the property.
  299        *  @param value   The value to set the property to.
  300        *  @param verbose If this is true output extra log messages.
  301        *  @return true if the property is set.
  302        */
  303       public synchronized boolean setProperty(String ns, String name,
  304                                               Object value, boolean verbose) {
  305           // user (CLI) properties take precedence
  306           if (null != userProperties.get(name)) {
  307               if (verbose) {
  308                   project.log("Override ignored for user property \"" + name
  309                           + "\"", Project.MSG_VERBOSE);
  310               }
  311               return false;
  312           }
  313   
  314           boolean done = setPropertyHook(ns, name, value, false, false, false);
  315           if (done) {
  316               return true;
  317           }
  318   
  319           if (null != properties.get(name) && verbose) {
  320               project.log("Overriding previous definition of property \"" + name
  321                       + "\"", Project.MSG_VERBOSE);
  322           }
  323   
  324           if (verbose) {
  325               project.log("Setting project property: " + name + " -> "
  326                       + value, Project.MSG_DEBUG);
  327           }
  328           if (name != null && value != null) {
  329               properties.put(name, value);
  330           }
  331           return true;
  332       }
  333   
  334       /**
  335        * Sets a property if no value currently exists. If the property
  336        * exists already, a message is logged and the method returns with
  337        * no other effect.
  338        *
  339        * @param ns   The namespace for the property (currently not used).
  340        * @param name The name of property to set.
  341        *             Must not be <code>null</code>.
  342        * @param value The new value of the property.
  343        *              Must not be <code>null</code>.
  344        * @since Ant 1.6
  345        */
  346       public synchronized void setNewProperty(String ns, String name,
  347                                               Object value) {
  348           if (null != properties.get(name)) {
  349               project.log("Override ignored for property \"" + name
  350                       + "\"", Project.MSG_VERBOSE);
  351               return;
  352           }
  353           boolean done = setPropertyHook(ns, name, value, false, false, true);
  354           if (done) {
  355               return;
  356           }
  357           project.log("Setting project property: " + name + " -> "
  358                   + value, Project.MSG_DEBUG);
  359           if (name != null && value != null) {
  360               properties.put(name, value);
  361           }
  362       }
  363   
  364       /**
  365        * Sets a user property, which cannot be overwritten by
  366        * set/unset property calls. Any previous value is overwritten.
  367        * @param ns   The namespace for the property (currently not used).
  368        * @param name The name of property to set.
  369        *             Must not be <code>null</code>.
  370        * @param value The new value of the property.
  371        *              Must not be <code>null</code>.
  372        */
  373       public synchronized void setUserProperty(String ns, String name,
  374                                                Object value) {
  375           project.log("Setting ro project property: " + name + " -> "
  376                   + value, Project.MSG_DEBUG);
  377           userProperties.put(name, value);
  378   
  379           boolean done = setPropertyHook(ns, name, value, false, true, false);
  380           if (done) {
  381               return;
  382           }
  383           properties.put(name, value);
  384       }
  385   
  386       /**
  387        * Sets an inherited user property, which cannot be overwritten by set/unset
  388        * property calls. Any previous value is overwritten. Also marks
  389        * these properties as properties that have not come from the
  390        * command line.
  391        *
  392        * @param ns   The namespace for the property (currently not used).
  393        * @param name The name of property to set.
  394        *             Must not be <code>null</code>.
  395        * @param value The new value of the property.
  396        *              Must not be <code>null</code>.
  397        */
  398       public synchronized void setInheritedProperty(String ns, String name,
  399                                                     Object value) {
  400           inheritedProperties.put(name, value);
  401   
  402           project.log("Setting ro project property: " + name + " -> "
  403                   + value, Project.MSG_DEBUG);
  404           userProperties.put(name, value);
  405   
  406           boolean done = setPropertyHook(ns, name, value, true, false, false);
  407           if (done) {
  408               return;
  409           }
  410           properties.put(name, value);
  411       }
  412   
  413       // -------------------- Getting properties  --------------------
  414   
  415       /**
  416        * Returns the value of a property, if it is set.  You can override
  417        * this method in order to plug your own storage.
  418        *
  419        * @param ns   The namespace for the property (currently not used).
  420        * @param name The name of the property.
  421        *             May be <code>null</code>, in which case
  422        *             the return value is also <code>null</code>.
  423        * @return the property value, or <code>null</code> for no match
  424        *         or if a <code>null</code> name is provided.
  425        */
  426       public synchronized Object getProperty(String ns, String name) {
  427           if (name == null) {
  428               return null;
  429           }
  430           Object o = getPropertyHook(ns, name, false);
  431           if (o != null) {
  432               return o;
  433           }
  434           return properties.get(name);
  435       }
  436       /**
  437        * Returns the value of a user property, if it is set.
  438        *
  439        * @param ns   The namespace for the property (currently not used).
  440        * @param name The name of the property.
  441        *             May be <code>null</code>, in which case
  442        *             the return value is also <code>null</code>.
  443        * @return the property value, or <code>null</code> for no match
  444        *         or if a <code>null</code> name is provided.
  445        */
  446       public synchronized Object getUserProperty(String ns, String name) {
  447           if (name == null) {
  448               return null;
  449           }
  450           Object o = getPropertyHook(ns, name, true);
  451           if (o != null) {
  452               return o;
  453           }
  454           return userProperties.get(name);
  455       }
  456   
  457       // -------------------- Access to property tables  --------------------
  458       // This is used to support ant call and similar tasks. It should be
  459       // deprecated, it is possible to use a better (more efficient)
  460       // mechanism to preserve the context.
  461   
  462       /**
  463        * Returns a copy of the properties table.
  464        * @return a hashtable containing all properties (including user properties).
  465        */
  466       public Hashtable getProperties() {
  467           //avoid concurrent modification:
  468           synchronized (properties) {
  469               return new Hashtable(properties);
  470           }
  471           // There is a better way to save the context. This shouldn't
  472           // delegate to next, it's for backward compatibility only.
  473       }
  474   
  475       /**
  476        * Returns a copy of the user property hashtable
  477        * @return a hashtable containing just the user properties
  478        */
  479       public Hashtable getUserProperties() {
  480           //avoid concurrent modification:
  481           synchronized (userProperties) {
  482               return new Hashtable(userProperties);
  483           }
  484       }
  485   
  486       /**
  487        * special back door for subclasses, internal access to the hashtables
  488        * @return the live hashtable of all properties
  489        */
  490       protected Hashtable getInternalProperties() {
  491           return properties;
  492       }
  493   
  494       /**
  495        * special back door for subclasses, internal access to the hashtables
  496        *
  497        * @return the live hashtable of user properties
  498        */
  499       protected Hashtable getInternalUserProperties() {
  500           return userProperties;
  501       }
  502   
  503       /**
  504        * special back door for subclasses, internal access to the hashtables
  505        *
  506        * @return the live hashtable inherited properties
  507        */
  508       protected Hashtable getInternalInheritedProperties() {
  509           return inheritedProperties;
  510       }
  511   
  512       /**
  513        * Copies all user properties that have not been set on the
  514        * command line or a GUI tool from this instance to the Project
  515        * instance given as the argument.
  516        *
  517        * <p>To copy all "user" properties, you will also have to call
  518        * {@link #copyUserProperties copyUserProperties}.</p>
  519        *
  520        * @param other the project to copy the properties to.  Must not be null.
  521        *
  522        * @since Ant 1.6
  523        */
  524       public void copyInheritedProperties(Project other) {
  525           //avoid concurrent modification:
  526           synchronized (inheritedProperties) {
  527               Enumeration e = inheritedProperties.keys();
  528               while (e.hasMoreElements()) {
  529                   String arg = e.nextElement().toString();
  530                   if (other.getUserProperty(arg) != null) {
  531                       continue;
  532                   }
  533                   Object value = inheritedProperties.get(arg);
  534                   other.setInheritedProperty(arg, value.toString());
  535               }
  536           }
  537       }
  538   
  539       /**
  540        * Copies all user properties that have been set on the command
  541        * line or a GUI tool from this instance to the Project instance
  542        * given as the argument.
  543        *
  544        * <p>To copy all "user" properties, you will also have to call
  545        * {@link #copyInheritedProperties copyInheritedProperties}.</p>
  546        *
  547        * @param other the project to copy the properties to.  Must not be null.
  548        *
  549        * @since Ant 1.6
  550        */
  551       public void copyUserProperties(Project other) {
  552           //avoid concurrent modification:
  553           synchronized (userProperties) {
  554               Enumeration e = userProperties.keys();
  555               while (e.hasMoreElements()) {
  556                   Object arg = e.nextElement();
  557                   if (inheritedProperties.containsKey(arg)) {
  558                       continue;
  559                   }
  560                   Object value = userProperties.get(arg);
  561                   other.setUserProperty(arg.toString(), value.toString());
  562               }
  563           }
  564       }
  565   
  566       // -------------------- Property parsing  --------------------
  567       // Moved from ProjectHelper. You can override the static method -
  568       // this is used for backward compatibility (for code that calls
  569       // the parse method in ProjectHelper).
  570   
  571       /** Default parsing method. It is here only to support backward compatibility
  572        * for the static ProjectHelper.parsePropertyString().
  573        */
  574       static void parsePropertyStringDefault(String value, Vector fragments, Vector propertyRefs)
  575               throws BuildException {
  576           int prev = 0;
  577           int pos;
  578           //search for the next instance of $ from the 'prev' position
  579           while ((pos = value.indexOf("$", prev)) >= 0) {
  580   
  581               //if there was any text before this, add it as a fragment
  582               //TODO, this check could be modified to go if pos>prev;
  583               //seems like this current version could stick empty strings
  584               //into the list
  585               if (pos > 0) {
  586                   fragments.addElement(value.substring(prev, pos));
  587               }
  588               //if we are at the end of the string, we tack on a $
  589               //then move past it
  590               if (pos == (value.length() - 1)) {
  591                   fragments.addElement("$");
  592                   prev = pos + 1;
  593               } else if (value.charAt(pos + 1) != '{') {
  594                   //peek ahead to see if the next char is a property or not
  595                   //not a property: insert the char as a literal
  596                   /*
  597                   fragments.addElement(value.substring(pos + 1, pos + 2));
  598                   prev = pos + 2;
  599                   */
  600                   if (value.charAt(pos + 1) == '$') {
  601                       //backwards compatibility two $ map to one mode
  602                       fragments.addElement("$");
  603                       prev = pos + 2;
  604                   } else {
  605                       //new behaviour: $X maps to $X for all values of X!='$'
  606                       fragments.addElement(value.substring(pos, pos + 2));
  607                       prev = pos + 2;
  608                   }
  609               } else {
  610                   //property found, extract its name or bail on a typo
  611                   int endName = value.indexOf('}', pos);
  612                   if (endName < 0) {
  613                       throw new BuildException("Syntax error in property: " + value);
  614                   }
  615                   String propertyName = value.substring(pos + 2, endName);
  616                   fragments.addElement(null);
  617                   propertyRefs.addElement(propertyName);
  618                   prev = endName + 1;
  619               }
  620           }
  621           //no more $ signs found
  622           //if there is any tail to the file, append it
  623           if (prev < value.length()) {
  624               fragments.addElement(value.substring(prev));
  625           }
  626       }
  627   }

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