Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 631   Methods: 20
NCLOC: 269   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
PropertyHelper.java 67.5% 82.3% 95% 78.5%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant;
 56   
 
 57   
 import org.apache.tools.ant.helper.*;
 58   
 
 59   
 import java.util.*;
 60   
 
 61   
 import org.xml.sax.AttributeList;
 62   
 import org.xml.sax.Attributes;
 63   
 import org.xml.sax.helpers.AttributeListImpl;
 64   
 import org.xml.sax.helpers.AttributesImpl;
 65   
 
 66   
 /* ISSUES:
 67   
  - ns param. It could be used to provide "namespaces" for properties, which
 68   
  may be more flexible.
 69   
  - Object value. In ant1.5 String is used for Properties - but it would be nice
 70   
  to support generic Objects ( the property remains imutable - you can't change
 71   
  the associated object ). This will also allow JSP-EL style setting using the
 72   
  Object if an attribute contains only the property ( name="${property}" could
 73   
  avoid Object->String->Object conversion )
 74   
  - Currently we "chain" only for get and set property ( probably most users
 75   
  will only need that - if they need more they can replace the top helper ).
 76   
  Need to discuss this and find if we need more.
 77   
  */
 78   
 
 79   
 /** NOT FINAL. API MAY CHANGE
 80   
  *
 81   
  * Deals with properties - substitution, dynamic properties, etc.
 82   
  *
 83   
  * This is the same code as in Ant1.5. The main addition is the ability
 84   
  * to chain multiple PropertyHelpers and to replace the default.
 85   
  *
 86   
  * @since Ant 1.6
 87   
  * @author Costin Manolache
 88   
  */
 89   
 public class PropertyHelper {
 90   
 
 91   
     protected Project project;
 92   
     protected PropertyHelper next;
 93   
 
 94   
     /** Project properties map (usually String to String). */
 95   
     protected Hashtable properties = new Hashtable();
 96   
     /**
 97   
      * Map of "user" properties (as created in the Ant task, for example).
 98   
      * Note that these key/value pairs are also always put into the
 99   
      * project properties, so only the project properties need to be queried.
 100   
      * Mapping is String to String.
 101   
      */
 102   
     protected Hashtable userProperties = new Hashtable();
 103   
     /**
 104   
      * Map of inherited "user" properties - that are those "user"
 105   
      * properties that have been created by tasks and not been set
 106   
      * from the command line or a GUI tool.
 107   
      * Mapping is String to String.
 108   
      */
 109   
     protected Hashtable inheritedProperties = new Hashtable();
 110   
 
 111  683
     protected PropertyHelper() {
 112   
     }
 113   
 
 114   
     // --------------------  Hook management  --------------------
 115   
 
 116  683
     public void setProject(Project p ) {
 117  683
         this.project=p;
 118   
     }
 119   
 
 120   
     /** There are 2 ways to hook into property handling:
 121   
      *  - you can replace the main PropertyHelper. The replacement is required
 122   
      * to support the same semantics ( of course :-)
 123   
      *
 124   
      *  - you can chain a property helper capable of storing some properties.
 125   
      *  Again, you are required to respect the immutability semantics ( at
 126   
      *  least for non-dynamic properties )
 127   
      *
 128   
      * @param next
 129   
      */
 130  0
     public void setNext( PropertyHelper next ) {
 131  0
         this.next=next;
 132   
     }
 133   
 
 134  82053
     public PropertyHelper getNext() {
 135  82053
         return next;
 136   
     }
 137   
 
 138   
     /** Factory method to create a property processor.
 139   
      *  Users can provide their own or replace it using "ant.PropertyHelper"
 140   
      *  reference. User tasks can also add themself to the chain, and provide
 141   
      *  dynamic properties.
 142   
      */
 143  87268
     public static PropertyHelper getPropertyHelper(Project project) {
 144  87268
         PropertyHelper ph=(PropertyHelper)project.getReference( "ant.PropertyHelper" );
 145  86585
         if( ph!=null ) return ph;
 146  683
         ph=new PropertyHelper();
 147  683
         ph.setProject( project );
 148   
 
 149  683
         project.addReference( "ant.PropertyHelper",ph );
 150  683
         return ph;
 151   
     }
 152   
 
 153   
     // --------------------  Methods to override  --------------------
 154   
 
 155   
     /**
 156   
      * Sets a property. Any existing property of the same name
 157   
      * is overwritten, unless it is a user property. Will be called
 158   
      * from setProperty().
 159   
      *
 160   
      * If all helpers return false, the property will be saved in
 161   
      * the default properties table by setProperty.
 162   
      *
 163   
      * @param name The name of property to set.
 164   
      *             Must not be <code>null</code>.
 165   
      * @param value The new value of the property.
 166   
      *              Must not be <code>null</code>.
 167   
      * @return true if this helper has stored the property, false if it
 168   
      *    couldn't. Each helper should delegate to the next one ( unless it
 169   
      *    has a good reason not to ).
 170   
      */
 171  34719
     public boolean setPropertyHook(String ns, String name,
 172   
                                    Object value,
 173   
                                    boolean inherited, boolean user,
 174   
                                    boolean isNew)
 175   
     {
 176  34719
         if( getNext()!=null ) {
 177  0
             boolean subst=getNext().setPropertyHook(ns, name, value,
 178   
                     inherited, user, isNew);
 179   
             // If next has handled the property
 180  0
             if( subst ) {
 181  0
                 return true;
 182   
             }
 183   
         }
 184   
 
 185  34719
         return false;
 186   
     }
 187   
 
 188   
     /** Get a property. If all hooks return null, the default
 189   
      * tables will be used.
 190   
      *
 191   
      * @param ns
 192   
      * @param name
 193   
      * @return
 194   
      */
 195  47334
     public Object getPropertyHook(String ns, String name, boolean user) {
 196  47334
         if( getNext() != null ) {
 197  0
             Object o=getNext().getPropertyHook(ns, name, user);
 198  0
             if( o!= null ) return o;
 199   
         }
 200   
         // Experimental/Testing, will be removed
 201  47334
         if( name.startsWith( "toString:" )) {
 202  0
             name=name.substring( "toString:".length());
 203  0
             Object v=project.getReference( name );
 204  0
             if( v==null ) return null;
 205  0
             return v.toString();
 206   
         }
 207   
 
 208   
 
 209  47334
         return null;
 210   
     }
 211   
 
 212   
     // -------------------- Optional methods   --------------------
 213   
     // You can override those methods if you want to optimize or
 214   
     // do advanced things ( like support a special syntax ).
 215   
     // The methods do not chain - you should use them when embedding ant
 216   
     // ( by replacing the main helper )
 217   
 
 218   
     /**
 219   
      * Parses a string containing <code>${xxx}</code> style property
 220   
      * references into two lists. The first list is a collection
 221   
      * of text fragments, while the other is a set of string property names.
 222   
      * <code>null</code> entries in the first list indicate a property
 223   
      * reference from the second list.
 224   
      *
 225   
      * It can be overriden with a more efficient or customized version.
 226   
      *
 227   
      * @param value     Text to parse. Must not be <code>null</code>.
 228   
      * @param fragments List to add text fragments to.
 229   
      *                  Must not be <code>null</code>.
 230   
      * @param propertyRefs List to add property names to.
 231   
      *                     Must not be <code>null</code>.
 232   
      *
 233   
      * @exception BuildException if the string contains an opening
 234   
      *                           <code>${</code> without a closing
 235   
      *                           <code>}</code>
 236   
      */
 237  6144
     public void parsePropertyString(String value, Vector fragments,
 238   
                                     Vector propertyRefs)
 239   
         throws BuildException
 240   
     {
 241  6144
         parsePropertyStringDefault(value, fragments, propertyRefs);
 242   
     }
 243   
 
 244   
     /**
 245   
      * Replaces <code>${xxx}</code> style constructions in the given value
 246   
      * with the string value of the corresponding data types.
 247   
      *
 248   
      * @param value The string to be scanned for property references.
 249   
      *              May be <code>null</code>, in which case this
 250   
      *              method returns immediately with no effect.
 251   
      * @param keys  Mapping (String to String) of property names to their
 252   
      *              values. If <code>null</code>, only project properties will
 253   
      *              be used.
 254   
      *
 255   
      * @exception BuildException if the string contains an opening
 256   
      *                           <code>${</code> without a closing
 257   
      *                           <code>}</code>
 258   
      * @return the original string with the properties replaced, or
 259   
      *         <code>null</code> if the original string is <code>null</code>.
 260   
      */
 261  6144
     public String replaceProperties(String ns, String value,
 262   
                                     Hashtable keys)
 263   
             throws BuildException
 264   
     {
 265  6144
         if (value == null) {
 266  0
             return null;
 267   
         }
 268   
 
 269  6144
         Vector fragments = new Vector();
 270  6144
         Vector propertyRefs = new Vector();
 271  6144
         parsePropertyString(value, fragments, propertyRefs);
 272   
 
 273  6144
         StringBuffer sb = new StringBuffer();
 274  6144
         Enumeration i = fragments.elements();
 275  6144
         Enumeration j = propertyRefs.elements();
 276   
 
 277  6144
         while (i.hasMoreElements()) {
 278  6637
             String fragment = (String) i.nextElement();
 279  6637
             if (fragment == null) {
 280  1275
                 String propertyName = (String) j.nextElement();
 281  1275
                 Object replacement=null;
 282   
 
 283   
                 // try to get it from the project or keys
 284   
                 // Backward compatibility
 285  1275
                 if( keys!=null ) {
 286  0
                     replacement=keys.get(propertyName);
 287   
                 }
 288  1275
                 if( replacement==null ) {
 289  1275
                     replacement=getProperty(ns, propertyName);
 290   
                 }
 291   
 
 292  1275
                 if (replacement == null ) {
 293  26
                     project.log("Property ${" + propertyName
 294   
                             + "} has not been set", Project.MSG_VERBOSE);
 295   
                 }
 296  1275
                 fragment = (replacement!=null)
 297   
                         ? replacement.toString()
 298   
                         : "${" + propertyName + "}";
 299   
             }
 300  6637
             sb.append(fragment);
 301   
         }
 302   
 
 303  6144
         return sb.toString();
 304   
     }
 305   
 
 306   
     // -------------------- Default implementation  --------------------
 307   
     // Methods used to support the default behavior and provide backward
 308   
     // compatibility. Some will be deprecated, you should avoid calling them.
 309   
 
 310   
 
 311   
     /** Default implementation of setProperty. Will be called from Project.
 312   
      *  This is the original 1.5 implementation, with calls to the hook
 313   
      *  added.
 314   
      */
 315  30143
     public synchronized boolean setProperty(String ns, String name,
 316   
                                             Object value, boolean verbose)
 317   
     {
 318   
         // user ( CLI ) properties take precedence
 319  30143
         if (null != userProperties.get(name)) {
 320  8
             if( verbose ) {
 321  0
                 project.log("Override ignored for user property " + name,
 322   
                         Project.MSG_VERBOSE);
 323   
             }
 324  8
             return false;
 325   
         }
 326   
 
 327  30135
         boolean done=this.setPropertyHook(ns, name, value, false, false, false);
 328  30135
         if( done ) {
 329  0
             return true;
 330   
         }
 331   
 
 332  30135
         if (null != properties.get(name) && verbose) {
 333  30
             project.log("Overriding previous definition of property " + name,
 334   
                     Project.MSG_VERBOSE);
 335   
         }
 336   
 
 337  30135
         if( verbose ) {
 338  127
             project.log("Setting project property: " + name + " -> " +
 339   
                     value, Project.MSG_DEBUG);
 340   
         }
 341  30135
         properties.put(name, value);
 342  30135
         return true;
 343   
     }
 344   
 
 345   
     /**
 346   
      * Sets a property if no value currently exists. If the property
 347   
      * exists already, a message is logged and the method returns with
 348   
      * no other effect.
 349   
      *
 350   
      * @param name The name of property to set.
 351   
      *             Must not be <code>null</code>.
 352   
      * @param value The new value of the property.
 353   
      *              Must not be <code>null</code>.
 354   
      * @since Ant 1.6
 355   
      */
 356  2782
     public synchronized void setNewProperty(String ns, String name,
 357   
                                             Object value)
 358   
     {
 359  2782
         if (null != properties.get(name)) {
 360  21
             project.log("Override ignored for property " + name,
 361   
                     Project.MSG_VERBOSE);
 362  21
             return;
 363   
         }
 364   
 
 365  2761
         boolean done=this.setPropertyHook(ns, name, value, false, true, false);
 366  2761
         if( done ) {
 367  0
             return;
 368   
         }
 369   
 
 370  2761
         project.log("Setting project property: " + name + " -> " +
 371   
                 value, Project.MSG_DEBUG);
 372  2761
         if( name!= null && value!=null ) {
 373  2761
             properties.put(name, value);
 374   
         }
 375   
     }
 376   
 
 377   
     /**
 378   
      * Sets a user property, which cannot be overwritten by
 379   
      * set/unset property calls. Any previous value is overwritten.
 380   
      * @param name The name of property to set.
 381   
      *             Must not be <code>null</code>.
 382   
      * @param value The new value of the property.
 383   
      *              Must not be <code>null</code>.
 384   
      */
 385  1810
     public synchronized void setUserProperty(String ns, String name,
 386   
                                              Object value)
 387   
     {
 388  1810
         project.log("Setting ro project property: " + name + " -> " +
 389   
                 value, Project.MSG_DEBUG);
 390  1810
         userProperties.put(name, value);
 391   
 
 392  1810
         boolean done=this.setPropertyHook(ns, name, value, false, false, true);
 393  1810
         if( done ) {
 394  0
             return;
 395   
         }
 396  1810
         properties.put(name, value);
 397   
     }
 398   
 
 399   
     /**
 400   
      * Sets a user property, which cannot be overwritten by set/unset
 401   
      * property calls. Any previous value is overwritten. Also marks
 402   
      * these properties as properties that have not come from the
 403   
      * command line.
 404   
      *
 405   
      * @param name The name of property to set.
 406   
      *             Must not be <code>null</code>.
 407   
      * @param value The new value of the property.
 408   
      *              Must not be <code>null</code>.
 409   
      */
 410  13
     public synchronized void setInheritedProperty(String ns, String name,
 411   
                                                   Object value)
 412   
     {
 413  13
         inheritedProperties.put(name, value);
 414   
 
 415  13
         project.log("Setting ro project property: " + name + " -> " +
 416   
                 value, Project.MSG_DEBUG);
 417  13
         userProperties.put(name, value);
 418   
 
 419  13
         boolean done=this.setPropertyHook(ns, name, value, true, false, false);
 420  13
         if( done ) {
 421  0
             return;
 422   
         }
 423  13
         properties.put(name, value);
 424   
     }
 425   
 
 426   
     // -------------------- Getting properties  --------------------
 427   
 
 428   
     /**
 429   
      * Returns the value of a property, if it is set.  You can override
 430   
      * this method in order to plug your own storage.
 431   
      *
 432   
      * @param name The name of the property.
 433   
      *             May be <code>null</code>, in which case
 434   
      *             the return value is also <code>null</code>.
 435   
      * @return the property value, or <code>null</code> for no match
 436   
      *         or if a <code>null</code> name is provided.
 437   
      */
 438  47323
     public Object getProperty(String ns, String name) {
 439  47323
         if (name == null) {
 440  0
             return null;
 441   
         }
 442   
 
 443  47323
         Object o=getPropertyHook(ns, name, false);
 444  47323
         if( o!= null ) {
 445  0
             return o;
 446   
         }
 447   
 
 448  47323
         return properties.get(name);
 449   
     }
 450   
     /**
 451   
      * Returns the value of a user property, if it is set.
 452   
      *
 453   
      * @param name The name of the property.
 454   
      *             May be <code>null</code>, in which case
 455   
      *             the return value is also <code>null</code>.
 456   
      * @return the property value, or <code>null</code> for no match
 457   
      *         or if a <code>null</code> name is provided.
 458   
      */
 459  11
     public Object getUserProperty(String ns, String name) {
 460  11
         if (name == null) {
 461  0
             return null;
 462   
         }
 463  11
         Object o=getPropertyHook(ns, name, true);
 464  11
         if( o!= null ) {
 465  0
             return o;
 466   
         }
 467  11
         return  userProperties.get(name);
 468   
     }
 469   
 
 470   
 
 471   
     // -------------------- Access to property tables  --------------------
 472   
     // This is used to support ant call and similar tasks. It should be
 473   
     // deprecated, it is possible to use a better ( more efficient )
 474   
     // mechanism to preserve the context.
 475   
 
 476   
     // TODO: do we need to delegate ?
 477   
 
 478   
     /**
 479   
      * Returns a copy of the properties table.
 480   
      * @return a hashtable containing all properties
 481   
      *         (including user properties).
 482   
      */
 483  215
     public Hashtable getProperties() {
 484  215
         Hashtable propertiesCopy = new Hashtable();
 485   
 
 486  215
         Enumeration e = properties.keys();
 487  215
         while (e.hasMoreElements()) {
 488  28836
             Object name = e.nextElement();
 489  28836
             Object value = properties.get(name);
 490  28836
             propertiesCopy.put(name, value);
 491   
         }
 492   
 
 493   
         // There is a better way to save the context. This shouldn't
 494   
         // delegate to next, it's for backward compat only.
 495   
 
 496  215
         return propertiesCopy;
 497   
     }
 498   
 
 499   
     /**
 500   
      * Returns a copy of the user property hashtable
 501   
      * @return a hashtable containing just the user properties
 502   
      */
 503  2
     public Hashtable getUserProperties() {
 504  2
         Hashtable propertiesCopy = new Hashtable();
 505   
 
 506  2
         Enumeration e = userProperties.keys();
 507  2
         while (e.hasMoreElements()) {
 508  6
             Object name = e.nextElement();
 509  6
             Object value = properties.get(name);
 510  6
             propertiesCopy.put(name, value);
 511   
         }
 512   
 
 513  2
         return propertiesCopy;
 514   
     }
 515   
 
 516   
     /**
 517   
      * Copies all user properties that have not been set on the
 518   
      * command line or a GUI tool from this instance to the Project
 519   
      * instance given as the argument.
 520   
      *
 521   
      * <p>To copy all "user" properties, you will also have to call
 522   
      * {@link #copyUserProperties copyUserProperties}.</p>
 523   
      *
 524   
      * @param other the project to copy the properties to.  Must not be null.
 525   
      *
 526   
      * @since Ant 1.6
 527   
      */
 528  50
     public void copyInheritedProperties(Project other) {
 529  50
         Enumeration e = inheritedProperties.keys();
 530  50
         while (e.hasMoreElements()) {
 531  2
             String arg = e.nextElement().toString();
 532  2
             if (other.getUserProperty(arg) != null) {
 533  2
                 continue;
 534   
             }
 535  0
             Object value = inheritedProperties.get(arg);
 536  0
             other.setInheritedProperty(arg, value.toString());
 537   
         }
 538   
     }
 539   
 
 540   
     /**
 541   
      * Copies all user properties that have been set on the command
 542   
      * line or a GUI tool from this instance to the Project instance
 543   
      * given as the argument.
 544   
      *
 545   
      * <p>To copy all "user" properties, you will also have to call
 546   
      * {@link #copyInheritedProperties copyInheritedProperties}.</p>
 547   
      *
 548   
      * @param other the project to copy the properties to.  Must not be null.
 549   
      *
 550   
      * @since Ant 1.6
 551   
      */
 552  50
     public void copyUserProperties(Project other) {
 553  50
         Enumeration e = userProperties.keys();
 554  50
         while (e.hasMoreElements()) {
 555  149
             Object arg = e.nextElement();
 556  149
             if (inheritedProperties.containsKey(arg)) {
 557  2
                 continue;
 558   
             }
 559  147
             Object value = userProperties.get(arg);
 560  147
             other.setUserProperty(arg.toString(), value.toString());
 561   
         }
 562   
     }
 563   
 
 564   
     // -------------------- Property parsing  --------------------
 565   
     // Moved from ProjectHelper. You can override the static method -
 566   
     // this is used for backward compatibility ( for code that calls
 567   
     // the parse method in ProjectHelper ).
 568   
 
 569   
     /** Default parsing method. It is here only to support backward compat
 570   
      * for the static ProjectHelper.parsePropertyString().
 571   
      */
 572  6267
     static void parsePropertyStringDefault(String value, Vector fragments,
 573   
                                     Vector propertyRefs)
 574   
         throws BuildException
 575   
     {
 576  6267
         int prev = 0;
 577  6267
         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  1322
             if (pos > 0) {
 586  72
                 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  1322
             if (pos == (value.length() - 1)) {
 591  3
                 fragments.addElement("$");
 592  3
                 prev = pos + 1;
 593  1319
             } 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  38
                 if (value.charAt(pos + 1) == '$') {
 601   
                     //backwards compatibility two $ map to one mode
 602  33
                     fragments.addElement("$");
 603  33
                     prev = pos + 2;
 604   
                 } else {
 605   
                     //new behaviour: $X maps to $X for all values of X!='$'
 606  5
                     fragments.addElement(value.substring(pos, pos + 2));
 607  5
                     prev = pos + 2;
 608   
                 }
 609   
 
 610   
             } else {
 611   
                 //property found, extract its name or bail on a typo
 612  1281
                 int endName = value.indexOf('}', pos);
 613  1281
                 if (endName < 0) {
 614  0
                     throw new BuildException("Syntax error in property: "
 615   
                                                  + value);
 616   
                 }
 617  1281
                 String propertyName = value.substring(pos + 2, endName);
 618  1281
                 fragments.addElement(null);
 619  1281
                 propertyRefs.addElement(propertyName);
 620  1281
                 prev = endName + 1;
 621   
             }
 622   
         }
 623   
         //no more $ signs found
 624   
         //if there is any tail to the file, append it
 625  6267
         if (prev < value.length()) {
 626  5370
             fragments.addElement(value.substring(prev));
 627   
         }
 628   
     }
 629   
 
 630   
 }
 631