source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

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