KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > PropertyFile


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
19 package org.apache.tools.ant.taskdefs.optional;
20
21 import java.io.BufferedInputStream JavaDoc;
22 import java.io.BufferedOutputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileInputStream JavaDoc;
25 import java.io.FileOutputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.text.DateFormat JavaDoc;
28 import java.text.DecimalFormat JavaDoc;
29 import java.text.ParseException JavaDoc;
30 import java.text.SimpleDateFormat JavaDoc;
31 import java.util.Calendar JavaDoc;
32 import java.util.Date JavaDoc;
33 import java.util.Enumeration JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Properties JavaDoc;
37 import java.util.Vector JavaDoc;
38 import org.apache.tools.ant.BuildException;
39 import org.apache.tools.ant.Task;
40 import org.apache.tools.ant.util.FileUtils;
41 import org.apache.tools.ant.types.EnumeratedAttribute;
42
43 /**
44  *Modifies settings in a property file.
45  *
46  * <p>
47  *The following is an example of its usage:
48  * <ul>&lt;target name="setState"&gt;<br>
49  * <ul>&lt;property<br>
50  * <ul>name="header"<br>
51  * value="##Generated file - do not modify!"/&gt;<br>
52  * &lt;propertyfile file="apropfile.properties" comment="${header}"&gt;<br>
53  * &lt;entry key="product.version.major" type="int" value="5"/&gt;<br>
54  * &lt;entry key="product.version.minor" type="int" value="0"/&gt;<br>
55  * &lt;entry key="product.build.major" type="int" value="0" /&gt;<br>
56  * &lt;entry key="product.build.minor" type="int" operation="+" /&gt;<br>
57  * &lt;entry key="product.build.date" type="date" value="now" /&gt;<br>
58  * &lt;entry key="intSet" type="int" operation="=" value="681"/&gt;<br>
59  * &lt;entry key="intDec" type="int" operation="-"/&gt;<br>
60  * &lt;entry key="StringEquals" type="string" value="testValue"/&gt;<br>
61  * &lt;/propertyfile&gt;<br></ul>
62  * &lt;/target&gt;</ul><p>
63  *
64  *The &lt;propertyfile&gt; task must have:<br>
65  * <ul><li>file</li></ul>
66  *Other parameters are:<br>
67  * <ul><li>comment, key, operation, type and value (the final four being
68  * eliminated shortly)</li></ul>
69  *
70  *The &lt;entry&gt; task must have:<br>
71  * <ul><li>key</li></ul>
72  *Other parameters are:<br>
73  * <ul><li>operation</li>
74  * <li>type</li>
75  * <li>value</li>
76  * <li>default</li>
77  * <li>unit</li>
78  * </ul>
79  *
80  *If type is unspecified, it defaults to string
81  *
82  *Parameter values:<br>
83  * <ul><li>operation:</li>
84  * <ul><li>"=" (set -- default)</li>
85  * <li>"-" (dec)</li>
86  * <li>"+" (inc)</li>
87  *
88  * <li>type:</li>
89  * <ul><li>"int"</li>
90  * <li>"date"</li>
91  * <li>"string"</li></ul></ul>
92  *
93  * <li>value:</li>
94  * <ul><li>holds the default value, if the property
95  * was not found in property file</li>
96  * <li>"now" In case of type "date", the
97  * value "now" will be replaced by the current
98  * date/time and used even if a valid date was
99  * found in the property file.</li></ul>
100  *
101  *
102  *String property types can only use the "=" operation.
103  *Int property types can only use the "=", "-" or "+" operations.<p>
104  *
105  *The message property is used for the property file header, with "\\" being
106  *a newline delimiter character.
107  *
108  */

109 public class PropertyFile extends Task {
110
111     /* ========================================================================
112     *
113     * Instance variables.
114     */

115
116     // Use this to prepend a message to the properties file
117
private String JavaDoc comment;
118
119     private Properties JavaDoc properties;
120     private File JavaDoc propertyfile;
121
122     private Vector JavaDoc entries = new Vector JavaDoc();
123
124     /* ========================================================================
125     *
126     * Constructors
127     */

128
129     /* ========================================================================
130     *
131     * Methods
132     */

133
134     /**
135      * Execute the task.
136      * @throws BuildException on error.
137      */

138     public void execute() throws BuildException {
139         checkParameters();
140         readFile();
141         executeOperation();
142         writeFile();
143     }
144
145     /**
146      * The entry nested element.
147      * @return an entry nested element to be configured.
148      */

149     public Entry createEntry() {
150         Entry e = new Entry();
151         entries.addElement(e);
152         return e;
153     }
154
155     private void executeOperation() throws BuildException {
156         for (Enumeration JavaDoc e = entries.elements(); e.hasMoreElements();) {
157             Entry entry = (Entry) e.nextElement();
158             entry.executeOn(properties);
159         }
160     }
161
162     private void readFile() throws BuildException {
163         // Create the PropertyFile
164
properties = new Properties JavaDoc();
165         try {
166             if (propertyfile.exists()) {
167                 log("Updating property file: "
168                     + propertyfile.getAbsolutePath());
169                 FileInputStream JavaDoc fis = null;
170                 try {
171                     fis = new FileInputStream JavaDoc(propertyfile);
172                     BufferedInputStream JavaDoc bis = new BufferedInputStream JavaDoc(fis);
173                     properties.load(bis);
174                 } finally {
175                     if (fis != null) {
176                         fis.close();
177                     }
178                 }
179             } else {
180                 log("Creating new property file: "
181                     + propertyfile.getAbsolutePath());
182                 FileOutputStream JavaDoc out = null;
183                 try {
184                     out = new FileOutputStream JavaDoc(propertyfile.getAbsolutePath());
185                     out.flush();
186                 } finally {
187                     if (out != null) {
188                         out.close();
189                     }
190                 }
191             }
192         } catch (IOException JavaDoc ioe) {
193             throw new BuildException(ioe.toString());
194         }
195     }
196
197     private void checkParameters() throws BuildException {
198         if (!checkParam(propertyfile)) {
199             throw new BuildException("file token must not be null.", getLocation());
200         }
201     }
202
203     /**
204      * Location of the property file to be edited; required.
205      * @param file the property file.
206      */

207     public void setFile(File JavaDoc file) {
208         propertyfile = file;
209     }
210
211     /**
212      * optional header comment for the file
213      * @param hdr the string to use for the comment.
214      */

215     public void setComment(String JavaDoc hdr) {
216         comment = hdr;
217     }
218
219     private void writeFile() throws BuildException {
220         BufferedOutputStream JavaDoc bos = null;
221         try {
222             bos = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(propertyfile));
223             properties.store(bos, comment);
224         } catch (IOException JavaDoc ioe) {
225             throw new BuildException(ioe, getLocation());
226         } finally {
227             FileUtils.close(bos);
228         }
229     }
230
231     private boolean checkParam(File JavaDoc param) {
232         return !(param == null);
233     }
234
235     /**
236      * Instance of this class represents nested elements of
237      * a task propertyfile.
238      */

239     public static class Entry {
240         private static final int DEFAULT_INT_VALUE = 0;
241         private static final String JavaDoc DEFAULT_DATE_VALUE = "now";
242         private static final String JavaDoc DEFAULT_STRING_VALUE = "";
243
244         private String JavaDoc key = null;
245         private int type = Type.STRING_TYPE;
246         private int operation = Operation.EQUALS_OPER;
247         private String JavaDoc value = null;
248         private String JavaDoc defaultValue = null;
249         private String JavaDoc newValue = null;
250         private String JavaDoc pattern = null;
251         private int field = Calendar.DATE;
252
253         /**
254          * Name of the property name/value pair
255          * @param value the key.
256          */

257         public void setKey(String JavaDoc value) {
258             this.key = value;
259         }
260
261         /**
262          * Value to set (=), to add (+) or subtract (-)
263          * @param value the value.
264          */

265         public void setValue(String JavaDoc value) {
266             this.value = value;
267         }
268
269         /**
270          * operation to apply.
271          * &quot;+&quot; or &quot;=&quot;
272          *(default) for all datatypes; &quot;-&quot; for date and int only)\.
273          * @param value the operation enumerated value.
274          */

275         public void setOperation(Operation value) {
276             this.operation = Operation.toOperation(value.getValue());
277         }
278
279         /**
280          * Regard the value as : int, date or string (default)
281          * @param value the type enumerated value.
282          */

283         public void setType(Type value) {
284             this.type = Type.toType(value.getValue());
285         }
286
287         /**
288          * Initial value to set for a property if it is not
289          * already defined in the property file.
290          * For type date, an additional keyword is allowed: &quot;now&quot;
291          * @param value the default value.
292          */

293         public void setDefault(String JavaDoc value) {
294             this.defaultValue = value;
295         }
296
297         /**
298          * For int and date type only. If present, Values will
299          * be parsed and formatted accordingly.
300          * @param value the pattern to use.
301          */

302         public void setPattern(String JavaDoc value) {
303             this.pattern = value;
304         }
305
306         /**
307          * The unit of the value to be applied to date +/- operations.
308          * Valid Values are:
309          * <ul>
310          * <li>millisecond</li>
311          * <li>second</li>
312          * <li>minute</li>
313          * <li>hour</li>
314          * <li>day (default)</li>
315          * <li>week</li>
316          * <li>month</li>
317          * <li>year</li>
318          * </ul>
319          * This only applies to date types using a +/- operation.
320          * @param unit the unit enumerated value.
321          * @since Ant 1.5
322          */

323         public void setUnit(PropertyFile.Unit unit) {
324             field = unit.getCalendarField();
325         }
326
327         /**
328          * Apply the nested element to the properties.
329          * @param props the properties to apply the entry on.
330          * @throws BuildException if there is an error.
331          */

332         protected void executeOn(Properties JavaDoc props) throws BuildException {
333             checkParameters();
334
335             // type may be null because it wasn't set
336
String JavaDoc oldValue = (String JavaDoc) props.get(key);
337             try {
338                 if (type == Type.INTEGER_TYPE) {
339                     executeInteger(oldValue);
340                 } else if (type == Type.DATE_TYPE) {
341                     executeDate(oldValue);
342                 } else if (type == Type.STRING_TYPE) {
343                     executeString(oldValue);
344                 } else {
345                     throw new BuildException("Unknown operation type: "
346                         + type);
347                 }
348             } catch (NullPointerException JavaDoc npe) {
349                 // Default to string type
350
// which means do nothing
351
npe.printStackTrace();
352             }
353
354             if (newValue == null) {
355                 newValue = "";
356             }
357
358             // Insert as a string by default
359
props.put(key, newValue);
360         }
361
362         /**
363         * Handle operations for type <code>date</code>.
364         *
365         * @param oldValue the current value read from the property file or
366         * <code>null</code> if the <code>key</code> was
367         * not contained in the property file.
368         */

369         private void executeDate(String JavaDoc oldValue) throws BuildException {
370             Calendar JavaDoc currentValue = Calendar.getInstance();
371
372             if (pattern == null) {
373               pattern = "yyyy/MM/dd HH:mm";
374             }
375             DateFormat JavaDoc fmt = new SimpleDateFormat JavaDoc(pattern);
376
377             String JavaDoc currentStringValue = getCurrentValue(oldValue);
378             if (currentStringValue == null) {
379                 currentStringValue = DEFAULT_DATE_VALUE;
380             }
381
382             if ("now".equals(currentStringValue)) {
383                 currentValue.setTime(new Date JavaDoc());
384             } else {
385                 try {
386                     currentValue.setTime(fmt.parse(currentStringValue));
387                 } catch (ParseException JavaDoc pe) {
388                     // swallow
389
}
390             }
391
392             if (operation != Operation.EQUALS_OPER) {
393                 int offset = 0;
394                 try {
395                     offset = Integer.parseInt(value);
396                     if (operation == Operation.DECREMENT_OPER) {
397                         offset = -1 * offset;
398                     }
399                 } catch (NumberFormatException JavaDoc e) {
400                     throw new BuildException("Value not an integer on " + key);
401                 }
402                 currentValue.add(field, offset);
403             }
404
405             newValue = fmt.format(currentValue.getTime());
406         }
407
408
409         /**
410         * Handle operations for type <code>int</code>.
411         *
412         * @param oldValue the current value read from the property file or
413         * <code>null</code> if the <code>key</code> was
414         * not contained in the property file.
415         */

416         private void executeInteger(String JavaDoc oldValue) throws BuildException {
417             int currentValue = DEFAULT_INT_VALUE;
418             int newV = DEFAULT_INT_VALUE;
419
420
421             DecimalFormat JavaDoc fmt = (pattern != null) ? new DecimalFormat JavaDoc(pattern)
422                                                     : new DecimalFormat JavaDoc();
423             try {
424                 String JavaDoc curval = getCurrentValue(oldValue);
425                 if (curval != null) {
426                     currentValue = fmt.parse(curval).intValue();
427                 } else {
428                     currentValue = 0;
429                 }
430             } catch (NumberFormatException JavaDoc nfe) {
431                 // swallow
432
} catch (ParseException JavaDoc pe) {
433                 // swallow
434
}
435
436             if (operation == Operation.EQUALS_OPER) {
437                 newV = currentValue;
438             } else {
439                 int operationValue = 1;
440                 if (value != null) {
441                     try {
442                         operationValue = fmt.parse(value).intValue();
443                     } catch (NumberFormatException JavaDoc nfe) {
444                         // swallow
445
} catch (ParseException JavaDoc pe) {
446                         // swallow
447
}
448                 }
449
450                 if (operation == Operation.INCREMENT_OPER) {
451                     newV = currentValue + operationValue;
452                 } else if (operation == Operation.DECREMENT_OPER) {
453                     newV = currentValue - operationValue;
454                 }
455             }
456
457             this.newValue = fmt.format(newV);
458         }
459
460         /**
461         * Handle operations for type <code>string</code>.
462         *
463         * @param oldValue the current value read from the property file or
464         * <code>null</code> if the <code>key</code> was
465         * not contained in the property file.
466         */

467         private void executeString(String JavaDoc oldValue) throws BuildException {
468             String JavaDoc newV = DEFAULT_STRING_VALUE;
469
470             String JavaDoc currentValue = getCurrentValue(oldValue);
471
472             if (currentValue == null) {
473                 currentValue = DEFAULT_STRING_VALUE;
474             }
475
476             if (operation == Operation.EQUALS_OPER) {
477                 newV = currentValue;
478             } else if (operation == Operation.INCREMENT_OPER) {
479                 newV = currentValue + value;
480             }
481             this.newValue = newV;
482         }
483
484         /**
485          * Check if parameter combinations can be supported
486          * @todo make sure the 'unit' attribute is only specified on date
487          * fields
488          */

489         private void checkParameters() throws BuildException {
490             if (type == Type.STRING_TYPE
491                 && operation == Operation.DECREMENT_OPER) {
492                 throw new BuildException("- is not supported for string "
493                     + "properties (key:" + key + ")");
494             }
495             if (value == null && defaultValue == null) {
496                 throw new BuildException("\"value\" and/or \"default\" "
497                     + "attribute must be specified (key:" + key + ")");
498             }
499             if (key == null) {
500                 throw new BuildException("key is mandatory");
501             }
502             if (type == Type.STRING_TYPE && pattern != null) {
503                 throw new BuildException("pattern is not supported for string "
504                     + "properties (key:" + key + ")");
505             }
506         }
507
508         private String JavaDoc getCurrentValue(String JavaDoc oldValue) {
509             String JavaDoc ret = null;
510             if (operation == Operation.EQUALS_OPER) {
511                 // If only value is specified, the property is set to it
512
// regardless of its previous value.
513
if (value != null && defaultValue == null) {
514                     ret = value;
515                 }
516
517                 // If only default is specified and the property previously
518
// existed in the property file, it is unchanged.
519
if (value == null && defaultValue != null && oldValue != null) {
520                     ret = oldValue;
521                 }
522
523                 // If only default is specified and the property did not
524
// exist in the property file, the property is set to default.
525
if (value == null && defaultValue != null && oldValue == null) {
526                     ret = defaultValue;
527                 }
528
529                 // If value and default are both specified and the property
530
// previously existed in the property file, the property
531
// is set to value.
532
if (value != null && defaultValue != null && oldValue != null) {
533                     ret = value;
534                 }
535
536                 // If value and default are both specified and the property
537
// did not exist in the property file, the property is set
538
// to default.
539
if (value != null && defaultValue != null && oldValue == null) {
540                     ret = defaultValue;
541                 }
542             } else {
543                 ret = (oldValue == null) ? defaultValue : oldValue;
544             }
545
546             return ret;
547         }
548
549         /**
550          * Enumerated attribute with the values "+", "-", "="
551          */

552         public static class Operation extends EnumeratedAttribute {
553
554             // Property type operations
555
/** + */
556             public static final int INCREMENT_OPER = 0;
557             /** - */
558             public static final int DECREMENT_OPER = 1;
559             /** = */
560             public static final int EQUALS_OPER = 2;
561
562             /** {@inheritDoc}. */
563             public String JavaDoc[] getValues() {
564                 return new String JavaDoc[] {"+", "-", "="};
565             }
566
567             /**
568              * Convert string to index.
569              * @param oper the string to convert.
570              * @return the index.
571              */

572             public static int toOperation(String JavaDoc oper) {
573                 if ("+".equals(oper)) {
574                     return INCREMENT_OPER;
575                 } else if ("-".equals(oper)) {
576                     return DECREMENT_OPER;
577                 }
578                 return EQUALS_OPER;
579             }
580         }
581
582         /**
583          * Enumerated attribute with the values "int", "date" and "string".
584          */

585         public static class Type extends EnumeratedAttribute {
586
587             // Property types
588
/** int */
589             public static final int INTEGER_TYPE = 0;
590             /** date */
591             public static final int DATE_TYPE = 1;
592             /** string */
593             public static final int STRING_TYPE = 2;
594
595             /** {@inheritDoc} */
596             public String JavaDoc[] getValues() {
597                 return new String JavaDoc[] {"int", "date", "string"};
598             }
599
600             /**
601              * Convert string to index.
602              * @param type the string to convert.
603              * @return the index.
604              */

605             public static int toType(String JavaDoc type) {
606                 if ("int".equals(type)) {
607                     return INTEGER_TYPE;
608                 } else if ("date".equals(type)) {
609                     return DATE_TYPE;
610                 }
611                 return STRING_TYPE;
612             }
613         }
614     }
615
616     /**
617      * Borrowed from Tstamp
618      * @todo share all this time stuff across many tasks as a datetime datatype
619      * @since Ant 1.5
620      */

621     public static class Unit extends EnumeratedAttribute {
622
623         private static final String JavaDoc MILLISECOND = "millisecond";
624         private static final String JavaDoc SECOND = "second";
625         private static final String JavaDoc MINUTE = "minute";
626         private static final String JavaDoc HOUR = "hour";
627         private static final String JavaDoc DAY = "day";
628         private static final String JavaDoc WEEK = "week";
629         private static final String JavaDoc MONTH = "month";
630         private static final String JavaDoc YEAR = "year";
631
632         private static final String JavaDoc[] UNITS
633             = {MILLISECOND, SECOND, MINUTE, HOUR,
634                DAY, WEEK, MONTH, YEAR };
635
636         private Map JavaDoc calendarFields = new HashMap JavaDoc();
637
638         /** no arg constructor */
639         public Unit() {
640             calendarFields.put(MILLISECOND,
641                                     new Integer JavaDoc(Calendar.MILLISECOND));
642             calendarFields.put(SECOND, new Integer JavaDoc(Calendar.SECOND));
643             calendarFields.put(MINUTE, new Integer JavaDoc(Calendar.MINUTE));
644             calendarFields.put(HOUR, new Integer JavaDoc(Calendar.HOUR_OF_DAY));
645             calendarFields.put(DAY, new Integer JavaDoc(Calendar.DATE));
646             calendarFields.put(WEEK, new Integer JavaDoc(Calendar.WEEK_OF_YEAR));
647             calendarFields.put(MONTH, new Integer JavaDoc(Calendar.MONTH));
648             calendarFields.put(YEAR, new Integer JavaDoc(Calendar.YEAR));
649         }
650
651         /**
652          * Convert the value to a Calendar field index.
653          * @return the calander value.
654          */

655         public int getCalendarField() {
656             String JavaDoc key = getValue().toLowerCase();
657             Integer JavaDoc i = (Integer JavaDoc) calendarFields.get(key);
658             return i.intValue();
659         }
660
661         /** {@inheritDoc}. */
662         public String JavaDoc[] getValues() {
663             return UNITS;
664         }
665     }
666 }
667
Popular Tags