Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 691   Methods: 30
NCLOC: 366   Classes: 5
 
 Source file Conditionals Statements Methods TOTAL
PropertyFile.java 69.4% 81.2% 96.7% 79.8%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-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.taskdefs.optional;
 56   
 
 57   
 import java.io.BufferedInputStream;
 58   
 import java.io.BufferedOutputStream;
 59   
 import java.io.File;
 60   
 import java.io.FileInputStream;
 61   
 import java.io.FileOutputStream;
 62   
 import java.io.IOException;
 63   
 import java.io.OutputStream;
 64   
 import java.lang.reflect.InvocationTargetException;
 65   
 import java.lang.reflect.Method;
 66   
 import java.text.DateFormat;
 67   
 import java.text.DecimalFormat;
 68   
 import java.text.ParseException;
 69   
 import java.text.SimpleDateFormat;
 70   
 import java.util.Calendar;
 71   
 import java.util.Date;
 72   
 import java.util.Enumeration;
 73   
 import java.util.Hashtable;
 74   
 import java.util.Properties;
 75   
 import java.util.Vector;
 76   
 import org.apache.tools.ant.BuildException;
 77   
 import org.apache.tools.ant.Task;
 78   
 import org.apache.tools.ant.types.EnumeratedAttribute;
 79   
 
 80   
 /**
 81   
  *Modifies settings in a property file.
 82   
  *
 83   
  * <p>
 84   
  *The following is an example of its usage:
 85   
  *    <ul>&lt;target name="setState"&gt;<br>
 86   
  *    <ul>&lt;property<br>
 87   
  *        <ul>name="header"<br>
 88   
  *        value="##Generated file - do not modify!"/&gt;<br>
 89   
  *      &lt;propertyfile file="apropfile.properties" comment="${header}"&gt;<br>
 90   
  *        &lt;entry key="product.version.major" type="int"  value="5"/&gt;<br>
 91   
  *        &lt;entry key="product.version.minor" type="int"  value="0"/&gt;<br>
 92   
  *        &lt;entry key="product.build.major"   type="int"  value="0" /&gt;<br>
 93   
  *        &lt;entry key="product.build.minor"   type="int"  operation="+" /&gt;<br>
 94   
  *        &lt;entry key="product.build.date"    type="date" value="now" /&gt;<br>
 95   
  *        &lt;entry key="intSet" type="int" operation="=" value="681"/&gt;<br>
 96   
  *        &lt;entry key="intDec" type="int" operation="-"/&gt;<br>
 97   
  *        &lt;entry key="StringEquals" type="string" value="testValue"/&gt;<br>
 98   
  *     &lt;/propertyfile&gt;<br></ul>
 99   
  *   &lt;/target&gt;</ul><p>
 100   
  *
 101   
  *The &lt;propertyfile&gt; task must have:<br>
 102   
  *    <ul><li>file</li></ul>
 103   
  *Other parameters are:<br>
 104   
  *    <ul><li>comment, key, operation, type and value (the final four being eliminated shortly)</li></ul>
 105   
  *
 106   
  *The &lt;entry&gt; task must have:<br>
 107   
  *    <ul><li>key</li></ul>
 108   
  *Other parameters are:<br>
 109   
  *    <ul><li>operation</li>
 110   
  *        <li>type</li>
 111   
  *        <li>value</li>
 112   
  *        <li>default</li>
 113   
  *        <li>unit</li>
 114   
  *    </ul>
 115   
  *
 116   
  *If type is unspecified, it defaults to string
 117   
  *
 118   
  *Parameter values:<br>
 119   
  *    <ul><li>operation:</li>
 120   
  *        <ul><li>"=" (set -- default)</li>
 121   
  *        <li>"-" (dec)</li>
 122   
  *        <li>"+" (inc)</li>
 123   
  *
 124   
  *    <li>type:</li>
 125   
  *        <ul><li>"int"</li>
 126   
  *        <li>"date"</li>
 127   
  *        <li>"string"</li></ul></ul>
 128   
  *
 129   
  *    <li>value:</li>
 130   
  *      <ul><li>holds the default value, if the property
 131   
  *              was not found in property file</li>
 132   
  *          <li>"now" In case of type "date", the
 133   
  *              value "now" will be replaced by the current
 134   
  *              date/time and used even if a valid date was
 135   
  *              found in the property file.</li></ul>
 136   
  *
 137   
  *
 138   
  *String property types can only use the "=" operation.
 139   
  *Int property types can only use the "=", "-" or "+" operations.<p>
 140   
  *
 141   
  *The message property is used for the property file header, with "\\" being
 142   
  *a newline delimiter character.
 143   
  *
 144   
  * @author Thomas Christen <a href="mailto:chr@active.ch">chr@active.ch</a>
 145   
  * @author Jeremy Mawson <a href="mailto:jem@loftinspace.com.au">jem@loftinspace.com.au</a>
 146   
  * @author Erik Hatcher <a href="mailto:ehatcher@apache.org">ehatcher@apache.org</a>
 147   
  */
 148   
 public class PropertyFile extends Task {
 149   
 
 150   
     /* ========================================================================
 151   
     *
 152   
     * Instance variables.
 153   
     */
 154   
 
 155   
     // Use this to prepend a message to the properties file
 156   
     private String              comment;
 157   
 
 158   
     private Properties          properties;
 159   
     private File                propertyfile;
 160   
 
 161   
     private Vector entries = new Vector();
 162   
 
 163   
     /* ========================================================================
 164   
     *
 165   
     * Constructors
 166   
     */
 167   
 
 168   
     /* ========================================================================
 169   
     *
 170   
     * Methods
 171   
     */
 172   
 
 173  2
     public void execute() throws BuildException {
 174  2
         checkParameters();
 175  2
         readFile();
 176  2
         executeOperation();
 177  2
         writeFile();
 178   
     }
 179   
 
 180  15
     public Entry createEntry() {
 181  15
         Entry e = new Entry();
 182  15
         entries.addElement(e);
 183  15
         return e;
 184   
     }
 185   
 
 186  2
     private void executeOperation() throws BuildException {
 187  2
         for (Enumeration e = entries.elements(); e.hasMoreElements();) {
 188  15
             Entry entry = (Entry) e.nextElement();
 189  15
             entry.executeOn(properties);
 190   
         }
 191   
     }
 192   
 
 193  2
     private void readFile() throws BuildException {
 194   
         // Create the PropertyFile
 195  2
         properties = new Properties();
 196  2
         try {
 197  2
             if (propertyfile.exists()) {
 198  2
                 log("Updating property file: " 
 199   
                     + propertyfile.getAbsolutePath());
 200  2
                 FileInputStream fis = null;
 201  2
                 try {
 202  2
                     fis = new FileInputStream(propertyfile);
 203  2
                     BufferedInputStream bis = new BufferedInputStream(fis);
 204  2
                     properties.load(bis);
 205   
                 } finally {
 206  2
                     if (fis != null) {
 207  2
                         fis.close();
 208   
                     }
 209   
                 }
 210   
             } else {
 211  0
                 log("Creating new property file: " 
 212   
                     + propertyfile.getAbsolutePath());
 213  0
                 FileOutputStream out = null;
 214  0
                 try {
 215  0
                     out = new FileOutputStream(propertyfile.getAbsolutePath());
 216  0
                     out.flush();
 217   
                 } finally {
 218  0
                     if (out != null) {
 219  0
                         out.close();
 220   
                     }
 221   
                 }
 222   
             }
 223   
         } catch (IOException ioe) {
 224  0
             throw new BuildException(ioe.toString());
 225   
         }
 226   
     }
 227   
 
 228  2
     private void checkParameters() throws BuildException {
 229  2
         if (!checkParam(propertyfile)) {
 230  0
             throw new BuildException("file token must not be null.", getLocation());
 231   
         }
 232   
     }
 233   
 
 234   
     /**
 235   
      * Location of the property file to be edited; required.
 236   
      */
 237  2
     public void setFile(File file) {
 238  2
         propertyfile = file;
 239   
     }
 240   
 
 241   
     /**
 242   
      * optional header comment for the file
 243   
      */
 244  1
     public void setComment(String hdr) {
 245  1
         comment = hdr;
 246   
     }
 247   
 
 248  2
     private void writeFile() throws BuildException {
 249  2
         BufferedOutputStream bos = null;
 250  2
         try {
 251  2
             bos = new BufferedOutputStream(new FileOutputStream(propertyfile));
 252   
 
 253   
             // Properties.store is not available in JDK 1.1
 254  2
             Method m =
 255   
                 Properties.class.getMethod("store",
 256   
                                            new Class[] {
 257   
                                                OutputStream.class,
 258   
                                                String.class});
 259  2
             m.invoke(properties, new Object[] {bos, comment});
 260   
 
 261   
         } catch (NoSuchMethodException nsme) {
 262  0
             properties.save(bos, comment);
 263   
         } catch (InvocationTargetException ite) {
 264  0
             Throwable t = ite.getTargetException();
 265  0
             throw new BuildException(t, getLocation());
 266   
         } catch (IllegalAccessException iae) {
 267   
             // impossible
 268  0
             throw new BuildException(iae, getLocation());
 269   
         } catch (IOException ioe) {
 270  0
             throw new BuildException(ioe, getLocation());
 271   
         } finally {
 272  2
             if (bos != null) {
 273  2
                 try {
 274  2
                     bos.close();
 275   
                 } catch (IOException ioex) {}
 276   
             }
 277   
         }
 278   
     }
 279   
 
 280   
     /**
 281   
     * Returns whether the given parameter has been defined.
 282   
     * @todo IDEA is saying this method is never used - remove?
 283   
     */
 284  0
     private boolean checkParam(String param) {
 285  0
         return !((param == null) || (param.equals("null")));
 286   
     }
 287   
 
 288  2
     private boolean checkParam(File param) {
 289  2
         return !(param == null);
 290   
     }
 291   
 
 292   
     /**
 293   
      * Instance of this class represents nested elements of
 294   
      * a task propertyfile.
 295   
      */
 296   
     public static class Entry {
 297   
         private static final int DEFAULT_INT_VALUE = 0;
 298   
         private static final String DEFAULT_DATE_VALUE = "now";
 299   
         private static final String DEFAULT_STRING_VALUE = "";
 300   
 
 301   
         private String              key = null;
 302   
         private int                 type = Type.STRING_TYPE;
 303   
         private int                 operation = Operation.EQUALS_OPER;
 304   
         private String              value = null;
 305   
         private String              defaultValue = null;
 306   
         private String              pattern = null;
 307   
         private int                 field = Calendar.DATE;
 308   
 
 309   
         /**
 310   
          * Name of the property name/value pair
 311   
          */
 312  15
         public void setKey(String value) {
 313  15
             this.key = value;
 314   
         }
 315   
         
 316   
         /** 
 317   
          * Value to set (=), to add (+) or subtract (-)
 318   
          */
 319  10
         public void setValue(String value) {
 320  10
             this.value = value;
 321   
         }
 322   
         
 323   
         /**
 324   
          * operation to apply. 
 325   
          * &quot;+&quot; or &quot;=&quot; 
 326   
          *(default) for all datatypes; &quot;-&quot; for date and int only)\.
 327   
          */
 328  7
         public void setOperation(Operation value) {
 329  7
             this.operation = Operation.toOperation(value.getValue());
 330   
         }
 331   
         
 332   
         /**
 333   
          * Regard the value as : int, date or string (default)
 334   
          */
 335  9
         public void setType(Type value) {
 336  9
             this.type = Type.toType(value.getValue());
 337   
         }
 338   
         
 339   
         /**
 340   
          * Initial value to set for a property if it is not
 341   
          * already defined in the property file.
 342   
          * For type date, an additional keyword is allowed: &quot;now&quot;
 343   
          */
 344   
                      
 345  9
         public void setDefault(String value) {
 346  9
             this.defaultValue = value;
 347   
         }
 348   
         
 349   
         /**
 350   
          * For int and date type only. If present, Values will
 351   
          * be parsed and formatted accordingly.
 352   
          */
 353  2
         public void setPattern(String value) {
 354  2
             this.pattern = value;
 355   
         }
 356   
         
 357   
         /**
 358   
          * The unit of the value to be applied to date +/- operations.
 359   
          *            Valid Values are:
 360   
          *            <ul>
 361   
          *               <li>millisecond</li>
 362   
          *               <li>second</li>
 363   
          *               <li>minute</li>
 364   
          *               <li>hour</li>
 365   
          *               <li>day (default)</li>
 366   
          *               <li>week</li>
 367   
          *               <li>month</li>
 368   
          *               <li>year</li>
 369   
          *            </ul>
 370   
          *            This only applies to date types using a +/- operation.        
 371   
          * @since Ant 1.5
 372   
          */
 373  1
         public void setUnit(PropertyFile.Unit unit) {
 374  1
             field = unit.getCalendarField();
 375   
         }
 376   
 
 377  15
         protected void executeOn(Properties props) throws BuildException {
 378  15
             checkParameters();
 379   
 
 380   
             // type may be null because it wasn't set
 381  15
             String oldValue = (String) props.get(key);
 382  15
             try {
 383  15
                 if (type == Type.INTEGER_TYPE) {
 384  5
                     executeInteger(oldValue);
 385  10
                 } else if (type == Type.DATE_TYPE) {
 386  4
                     executeDate(oldValue);
 387  6
                 } else if (type == Type.STRING_TYPE) {
 388  6
                     executeString(oldValue);
 389   
                 } else {
 390  0
                     throw new BuildException("Unknown operation type: "
 391   
                         + type);
 392   
                 }
 393   
             } catch (NullPointerException npe) {
 394   
                 // Default to string type
 395   
                 // which means do nothing
 396  1
                 npe.printStackTrace();
 397   
             }
 398   
             
 399  15
             if (value == null) {
 400  0
                 value = "";
 401   
             }
 402   
             
 403   
             // Insert as a string by default
 404  15
             props.put(key, value);
 405   
         }
 406   
 
 407   
         /**
 408   
         * Handle operations for type <code>date</code>.
 409   
         *
 410   
         * @param oldValue the current value read from the property file or
 411   
         *                 <code>null</code> if the <code>key</code> was
 412   
         *                 not contained in the property file.
 413   
         */
 414  4
         private void executeDate(String oldValue) throws BuildException {
 415  4
             Calendar currentValue = Calendar.getInstance();
 416   
 
 417  4
             if (pattern == null) {
 418  2
               pattern = "yyyy/MM/dd HH:mm";
 419   
             }
 420  4
             DateFormat fmt = new SimpleDateFormat(pattern);
 421   
 
 422  4
             String currentStringValue = getCurrentValue(oldValue);
 423  4
             if (currentStringValue == null) {
 424  0
                 currentStringValue = DEFAULT_DATE_VALUE;
 425   
             }
 426   
             
 427  4
             if ("now".equals(currentStringValue)) {
 428  0
                 currentValue.setTime(new Date());
 429   
             } else {
 430  4
                 try {
 431  4
                     currentValue.setTime(fmt.parse(currentStringValue));
 432   
                 } catch (ParseException pe)  { 
 433   
                     // swallow 
 434   
                 }
 435   
             }
 436   
             
 437  4
             if (operation != Operation.EQUALS_OPER) {
 438  2
                 int offset = 0;
 439  2
                 try {
 440  2
                     offset = Integer.parseInt(value);
 441  2
                     if (operation == Operation.DECREMENT_OPER) {
 442  1
                         offset = -1 * offset;
 443   
                     }
 444   
                 } catch (NumberFormatException e) {
 445  0
                     throw new BuildException("Value not an integer on " + key);
 446   
                 }
 447  2
                 currentValue.add(field, offset);
 448   
             }
 449   
 
 450  4
             value = fmt.format(currentValue.getTime());
 451   
         }
 452   
 
 453   
 
 454   
         /**
 455   
         * Handle operations for type <code>int</code>.
 456   
         *
 457   
         * @param oldValue the current value read from the property file or
 458   
         *                 <code>null</code> if the <code>key</code> was
 459   
         *                 not contained in the property file.
 460   
         */
 461  5
         private void executeInteger(String oldValue) throws BuildException {
 462  5
             int currentValue = DEFAULT_INT_VALUE;
 463  5
             int newValue  = DEFAULT_INT_VALUE;
 464   
 
 465   
 
 466  5
             DecimalFormat fmt = (pattern != null) ? new DecimalFormat(pattern)
 467   
                                                     : new DecimalFormat();
 468  5
             try {
 469  5
                 currentValue = fmt.parse(getCurrentValue(oldValue)).intValue();
 470   
             } catch (NumberFormatException nfe) {
 471   
                 // swallow
 472   
             } catch (ParseException pe)  {
 473   
                 // swallow
 474   
             }
 475   
             
 476  4
             if (operation == Operation.EQUALS_OPER) {
 477  2
                 newValue = currentValue;
 478   
             } else {
 479  2
                 int operationValue = 1;
 480  2
                 if (value != null) {
 481  1
                     try {
 482  1
                         operationValue = fmt.parse(value).intValue();
 483   
                     } catch (NumberFormatException nfe) {
 484   
                         // swallow
 485   
                     } catch (ParseException pe)  {
 486   
                         // swallow
 487   
                     }
 488   
                 }
 489   
 
 490  2
                 if (operation == Operation.INCREMENT_OPER) {
 491  2
                     newValue = currentValue + operationValue;
 492  0
                 } else if (operation == Operation.DECREMENT_OPER) {
 493  0
                     newValue = currentValue - operationValue;
 494   
                 }
 495   
             }
 496   
 
 497  4
             value = fmt.format(newValue);
 498   
         }
 499   
         
 500   
         /**
 501   
         * Handle operations for type <code>string</code>.
 502   
         *
 503   
         * @param oldValue the current value read from the property file or
 504   
         *                 <code>null</code> if the <code>key</code> was
 505   
         *                 not contained in the property file.
 506   
         */
 507  6
         private void executeString(String oldValue) throws BuildException {
 508  6
             String newValue  = DEFAULT_STRING_VALUE;
 509   
 
 510  6
             String currentValue = getCurrentValue(oldValue);
 511   
             
 512  6
             if (currentValue == null) {
 513  1
                 currentValue = DEFAULT_STRING_VALUE;
 514   
             }
 515   
             
 516  6
             if (operation == Operation.EQUALS_OPER) {
 517  4
                 newValue = currentValue;
 518  2
             } else if (operation == Operation.INCREMENT_OPER) {
 519  2
                 newValue = currentValue + value;
 520   
             }
 521  6
             value = newValue;
 522   
         }
 523   
         
 524   
         /**
 525   
          * Check if parameter combinations can be supported
 526   
          * @todo make sure the 'unit' attribute is only specified on date
 527   
          *      fields
 528   
          */
 529  15
         private void checkParameters() throws BuildException {
 530  15
             if (type == Type.STRING_TYPE &&
 531   
                 operation == Operation.DECREMENT_OPER) {
 532  0
                 throw new BuildException("- is not suported for string " 
 533   
                     + "properties (key:" + key + ")");
 534   
             }
 535  15
             if (value == null && defaultValue == null) {
 536  0
                 throw new BuildException("\"value\" and/or \"default\" " 
 537   
                     + "attribute must be specified (key:" + key + ")");
 538   
             }
 539  15
             if (key == null) {
 540  0
                 throw new BuildException("key is mandatory");
 541   
             }
 542  15
             if (type == Type.STRING_TYPE &&
 543   
                 pattern != null) {
 544  0
                 throw new BuildException("pattern is not suported for string " 
 545   
                     + "properties (key:" + key + ")");
 546   
             }
 547   
         }
 548   
 
 549  15
         private String getCurrentValue(String oldValue) {
 550  15
             String ret = null;
 551  15
             if (operation == Operation.EQUALS_OPER) {
 552   
                 // If only value is specified, the property is set to it
 553   
                 // regardless of its previous value. 
 554  8
                 if (value != null && defaultValue == null) {
 555  4
                     ret = value;
 556   
                 }
 557   
                 
 558   
                 // If only default is specified and the property previously
 559   
                 // existed in the property file, it is unchanged. 
 560  8
                 if (value == null && defaultValue != null && oldValue != null) {
 561  1
                     ret = oldValue;
 562   
                 }
 563   
 
 564   
                 // If only default is specified and the property did not
 565   
                 // exist in the property file, the property is set to default.
 566  8
                 if (value == null && defaultValue != null && oldValue == null) {
 567  3
                     ret = defaultValue;
 568   
                 }
 569   
                 
 570   
                 // If value and default are both specified and the property
 571   
                 // previously existed in the property file, the property
 572   
                 // is set to value.
 573  8
                 if (value != null && defaultValue != null && oldValue != null) {
 574  0
                     ret = value;
 575   
                 }
 576   
 
 577   
                 // If value and default are both specified and the property
 578   
                 // did not exist in the property file, the property is set
 579   
                 // to default. 
 580  8
                 if (value != null && defaultValue != null && oldValue == null) {
 581  0
                     ret = defaultValue;
 582   
                 }
 583   
             } else {
 584  7
                 ret = (oldValue == null) ? defaultValue : oldValue;
 585   
             }
 586   
             
 587  15
             return ret;
 588   
         }
 589   
         
 590   
         /**
 591   
          * Enumerated attribute with the values "+", "-", "="
 592   
          */
 593   
         public static class Operation extends EnumeratedAttribute {
 594   
 
 595   
             // Property type operations
 596   
             public static final int INCREMENT_OPER =   0;
 597   
             public static final int DECREMENT_OPER =   1;
 598   
             public static final int EQUALS_OPER =      2;
 599   
 
 600  7
             public String[] getValues() {
 601  7
                 return new String[] {"+", "-", "="};
 602   
             }
 603   
 
 604  7
             public static int toOperation(String oper) {
 605  7
                 if ("+".equals(oper)) {
 606  6
                     return INCREMENT_OPER;
 607  1
                 } else if ("-".equals(oper)) {
 608  1
                     return DECREMENT_OPER;
 609   
                 }
 610  0
                 return EQUALS_OPER;
 611   
             }
 612   
         }
 613   
 
 614   
         /**
 615   
          * Enumerated attribute with the values "int", "date" and "string".
 616   
          */
 617   
         public static class Type extends EnumeratedAttribute {
 618   
 
 619   
             // Property types
 620   
             public static final int INTEGER_TYPE =     0;
 621   
             public static final int DATE_TYPE =        1;
 622   
             public static final int STRING_TYPE =      2;
 623   
 
 624  9
             public String[] getValues() {
 625  9
                 return new String[] {"int", "date", "string"};
 626   
             }
 627   
 
 628  9
             public static int toType(String type) {
 629  9
                 if ("int".equals(type)) {
 630  5
                     return INTEGER_TYPE;
 631  4
                 } else if ("date".equals(type)) {
 632  4
                     return DATE_TYPE;
 633   
                 }
 634  0
                 return STRING_TYPE;
 635   
             }
 636   
         }
 637   
     }
 638   
     
 639   
     /**
 640   
      * Borrowed from Tstamp
 641   
      * @todo share all this time stuff across many tasks as a datetime datatype
 642   
      * @since Ant 1.5
 643   
      */
 644   
     public static class Unit extends EnumeratedAttribute {
 645   
 
 646   
         private static final String MILLISECOND = "millisecond";
 647   
         private static final String SECOND = "second";
 648   
         private static final String MINUTE = "minute";
 649   
         private static final String HOUR = "hour";
 650   
         private static final String DAY = "day";
 651   
         private static final String WEEK = "week";
 652   
         private static final String MONTH = "month";
 653   
         private static final String YEAR = "year";
 654   
 
 655   
         private static final String[] units = {
 656   
                                                 MILLISECOND,
 657   
                                                 SECOND,
 658   
                                                 MINUTE,
 659   
                                                 HOUR,
 660   
                                                 DAY,
 661   
                                                 WEEK,
 662   
                                                 MONTH,
 663   
                                                 YEAR
 664   
                                               };
 665   
 
 666   
         private Hashtable calendarFields = new Hashtable();
 667   
 
 668  1
         public Unit() {
 669  1
             calendarFields.put(MILLISECOND,
 670   
                                     new Integer(Calendar.MILLISECOND));
 671  1
             calendarFields.put(SECOND, new Integer(Calendar.SECOND));
 672  1
             calendarFields.put(MINUTE, new Integer(Calendar.MINUTE));
 673  1
             calendarFields.put(HOUR, new Integer(Calendar.HOUR_OF_DAY));
 674  1
             calendarFields.put(DAY, new Integer(Calendar.DATE));
 675  1
             calendarFields.put(WEEK, new Integer(Calendar.WEEK_OF_YEAR));
 676  1
             calendarFields.put(MONTH, new Integer(Calendar.MONTH));
 677  1
             calendarFields.put(YEAR, new Integer(Calendar.YEAR));
 678   
         }
 679   
 
 680  1
         public int getCalendarField() {
 681  1
             String key = getValue().toLowerCase();
 682  1
             Integer i = (Integer) calendarFields.get(key);
 683  1
             return i.intValue();
 684   
         }
 685   
 
 686  1
         public String[] getValues() {
 687  1
             return units;
 688   
         }
 689   
     }
 690   
 }
 691