Browse Source

enhancement to the optional task PropertyFile.

Operations can now be applied to all types. The now and never operations
have become values for the = operation.

Submitted by:	Thomas Christen <chr@active.ch>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268576 13f79535-47bb-0310-9956-ffa450edef68
master
Conor MacNeill 24 years ago
parent
commit
ba37bf7991
2 changed files with 403 additions and 186 deletions
  1. +52
    -10
      docs/propertyfile.html
  2. +351
    -176
      src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java

+ 52
- 10
docs/propertyfile.html View File

@@ -10,10 +10,11 @@
<p>by</p>
<!-- Names are in alphabetical order, on last name -->
<ul>
<li>Thomas Christen (<a href="mailto:chr@active.ch">chr@active.ch</a>)</li>
<li>Jeremy Mawson (<a href="mailto:jem@loftinspace.com.au">jem@loftinspace.com/au</a>)</li>
</ul>

<p>Version 1.0 - 2000/11/02</p>
<p>Version 1.1 - 2001/01/28</p>
<hr>
<h2>Table of Contents</h2>
<ul>
@@ -53,7 +54,7 @@ are lost. Work is being done to make this task a bit more "human friendly".

<h3>Parameters specified as nested elements</h3>
<h4>Entry</h4>
<p>Use nested <code>&lt;entry&gt;</code>
<p>Use nested <code>&lt;entry&gt;</code>
elements to specify actual modifcations to the property file itself
<table border="1" cellpadding="2" cellspacing="0">
<tr>
@@ -68,17 +69,31 @@ elements to specify actual modifcations to the property file itself
</tr>
<tr>
<td valign="top">value</td>
<td valign="top">Value to set for the key the property name/value pair</td>
<td valign="top">Value to set (=), to add (+) or subtract (-)</td>
<td valign="top" align="center">Yes</td>
</tr>
<tr>
<td valign="top">type</td>
<td valign="top">Manipulate the value as type: int, date</td>
<td valign="top" align="center">No, but must be used if opertion attribute used</td>
<td valign="top">Regard the value as : int, date or string (default)</td>
<td valign="top" align="center">No</td>
</tr>
<tr>
<td valign="top">operation</td>
<td valign="top">If type is date, this cane be "now" or "never". If the type is int, only "-" and "+" may be used.</td>
<td valign="top">"+" or "=" (default) for all datatypes<br>"-" (for date and int only).<br>
</td>
<td valign="top" align="center">No</td>
</tr>
<tr>
<td valign="top">default</td>
<td valign="top">Initial value to set for a property if it is not
already defined in the property file.<br>
For type date, two additional keywordsw are allowed: "now" or "never".</td>
<td valign="top" align="center">No</td>
</tr>
<tr>
<td valign="top">pattern</td>
<td valign="top">For int and date type only. If present, Values will
be paresed and formated accordingly.</td>
<td valign="top" align="center">No</td>
</tr>
</table>
@@ -94,7 +109,9 @@ After running, the file would now look like
<ul>#Thu Nov 02 23:41:47 EST 2000<br>
akey=avalue<br>
adate=2000/11/02 23\:41<br>
anint=1<br></ul>
anint=1<br>
formated.int=0014<br>
formated.date=028 17\:34<br></ul>
</p>
The slashes conform to the expectations of the Properties class. The file will be stored in a manner so that each character is examined and escaped if necessary. Note that the original comment is now lost. Please keep this in mind when running this task against heavily commented properties files. It may be best to have a commented version in the source tree, copy it to a deployment area, and then run the modifications on the copy. Future versions of PropertyFile will hopefully eliminate this shortcoming.
</p>
@@ -103,10 +120,35 @@ The slashes conform to the expectations of the Properties class. The file will
file="my.properties"
comment"My properties" &gt;
&lt;entry key="akey" value="avalue" /&gt;
&lt;entry key="adate" type="date" operation="now"/&gt;
&lt;entry key="adate" type="date" value="now"/&gt;
&lt;entry key="anint" type="int" operation="+"/&gt;
&lt;entry key="formated.int" type="int" default="0013" operation="+" pattern="0000"/&gt;
&lt;entry key="formated.date" type="date" value="now" pattern="DDD HH:mm"/&gt;
&lt;/propertyfile&gt;
</pre></blockquote>

</blockquote></pre>
<p>
To produce dates relative from today :
<pre><blockquote>&lt;propertyfile
file="my.properties"
comment"My properties" &gt;
&lt;entry key="formated.date-1"
type="date" default="now" pattern="DDD"
operation="-" value="1"/&gt;
&lt;entry key="formated.tomorrow"
type="date" default="now" pattern="DDD"
operation="+" value="1"/&gt;
&lt;/propertyfile&gt;
</blockquote></pre>
</p>
<p>
Concatenation of strings :
<pre><blockquote>&lt;propertyfile
file="my.properties"
comment"My properties" &gt;
&lt;entry key="progress" default="" operation="+" value="."/&gt;
&lt;/propertyfile&gt;
</blockquote></pre>
Each time called, a "." will be appended to "progress"
</p>
</body>
</html>

+ 351
- 176
src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java View File

@@ -1,7 +1,7 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -56,10 +56,12 @@
package org.apache.tools.ant.taskdefs.optional;

import org.apache.tools.ant.*;
import org.apache.tools.ant.types.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.text.*;

/**
*PropertyFile task uses java.util.Properties to modify integer, String and
@@ -69,7 +71,7 @@ import java.util.*;
*The following is an example of its usage:
* <ul>&lt;target name="setState"&gt;<br>
* <ul>&lt;property<br>
* <ul>name="header"<br>
* <ul>name="header"<br>
* value="##Generated file - do not modify!"/&gt;<br>
* &lt;propertyfile file="apropfile.properties" comment="${header}"&gt;<br>
* &lt;entry key="product.version.major" type="int" value="5"/&gt;<br>
@@ -77,7 +79,7 @@ import java.util.*;
* &lt;entry key="product.build.major" type="int" value="0" /&gt;<br>
* &lt;entry key="product.build.minor" type="int" operation="+" /&gt;<br>
* &lt;entry key="product.build.date" type="date" operation="now" /&gt;<br>
* &lt;entry key="intInc" type="int" operation="=" value="681"/&gt;<br>
* &lt;entry key="intSet" type="int" operation="=" value="681"/&gt;<br>
* &lt;entry key="intDec" type="int" operation="-"/&gt;<br>
* &lt;entry key="NeverDate" type="date" operation="never"/&gt;<br>
* &lt;entry key="StringEquals" type="string" value="testValue"/&gt;<br>
@@ -93,7 +95,10 @@ import java.util.*;
*The &lt;entry&gt; task must have:<br>
* <ul><li>key</li></ul>
*Other parameters are:<br>
* <ul><li>key, operation, type</li></ul>
* <ul><li>operation</li>
* <li>type</li>
* <li>value</li>
* <li>offset</li></ul>
*
*If type is unspecified, it defaults to string
*
@@ -102,14 +107,25 @@ import java.util.*;
* <ul><li>"=" (set -- default)</li>
* <li>"-" (dec)</li>
* <li>"+" (inc)</li>
* <li>"now" (date and time)</li>
* <li>"never" (empty string)</li></ul>
*
* <li>type:</li>
* <ul><li>"int"</li>
* <li>"date"</li>
* <li>"string"</li></ul></ul>
*
* <li>value:</li>
* <ul><li>holds the default value, if the property
* was not found in property file</li>
* <li>"now" In case of type "date", the
* value "now" will be replaced by the current
* date/time and used even if a valid date was
* found in the property file.</li></ul>
*
* <li>offset:<br>valid for "-" or "+", the offset (default
* set to 1) will be added or subtracted from "int" or
* "date" type value.</li>
* </ul>
*
*String property types can only use the "=" operation.
*Date property types can only use the "never" or "now" operations.
*Int property types can only use the "=", "-" or "+" operations.<p>
@@ -117,9 +133,10 @@ import java.util.*;
*The message property is used for the property file header, with "\\" being
*a newline delimiter charater.
*
* @author Jeremy Mawson <jem@loftinspace.com.au>
* @author Thomas Christen <a href="mailto:chr@active.ch">chr@active.ch</a>
* @author Jeremy Mawson <a href="mailto:jem@loftinspace.com.au>jem@loftinspace.com.au</a>
*/
public class PropertyFile extends Task
public class PropertyFile extends Task
{

/* ========================================================================
@@ -140,9 +157,8 @@ public class PropertyFile extends Task

private Properties m_properties;
private File m_propertyfile;
private Vector entries = new Vector();
private Entry mainEntry = new Entry();

/* ========================================================================
*
@@ -154,7 +170,7 @@ public class PropertyFile extends Task
* Methods
*/

public void execute() throws BuildException
public void execute() throws BuildException
{
checkParameters();
readFile();
@@ -162,36 +178,35 @@ public class PropertyFile extends Task
writeFile();
}

public Entry createEntry()
public Entry createEntry()
{
Entry e = new Entry();
entries.addElement(e);
return e;
}
private void executeOperation() throws BuildException
private void executeOperation() throws BuildException
{
// mainEntry.executeOn(m_properties);
for (Enumeration e = entries.elements(); e.hasMoreElements(); )
for (Enumeration e = entries.elements(); e.hasMoreElements(); )
{
Entry entry = (Entry)e.nextElement();
entry.executeOn(m_properties);
}
}

private void readFile() throws BuildException
private void readFile() throws BuildException
{
// Create the PropertyFile
m_properties = new Properties();
try
try
{
if (m_propertyfile.exists())
if (m_propertyfile.exists())
{
log("Updating property file: "+m_propertyfile.getAbsolutePath());
m_properties.load(new BufferedInputStream(
new FileInputStream(m_propertyfile)));
}
else
else
{
log("Creating new property file: "+
m_propertyfile.getAbsolutePath());
@@ -200,85 +215,40 @@ public class PropertyFile extends Task
out.close();
}
}
catch(IOException ioe)
catch(IOException ioe)
{
throw new BuildException(ioe.toString());
}
}
private void checkParameters() throws BuildException
private void checkParameters() throws BuildException
{
if (!checkParam(m_propertyfile))
if (!checkParam(m_propertyfile))
{
throw new BuildException("file token must not be null.", location);
}
}
public void setOperation(String op)
{
mainEntry.setOperation(op);
}

public void setType(String type)
{
mainEntry.setType(type);
}

public void setValue(String value)
{
mainEntry.setValue(value);
}

public void setKey(String key)
{
mainEntry.setKey(key);
}

public void setFile(String file)
public void setFile(File file)
{
m_propertyfile = new File(file);
m_propertyfile = file;
}

public void setComment(String hdr)
public void setComment(String hdr)
{
m_comment = hdr;
}

private void writeFile() throws BuildException
private void writeFile() throws BuildException
{
BufferedOutputStream bos = null;
try
try
{
bos = new BufferedOutputStream(new FileOutputStream(m_propertyfile));

// Write the message if we have one.
// if (m_comment != null)
// {
// // FIXME: would like to use \n as the newline rather than \\.
// StringTokenizer tok = new StringTokenizer(m_comment, "\\");
// while (tok.hasMoreTokens())
// {
// bos.write("# ".getBytes());
// bos.write(((String)tok.nextToken()).getBytes());
// bos.write(NEWLINE.getBytes());
// }
// bos.write(NEWLINE.getBytes());
// bos.flush();
// }
// Enumeration enumValues = m_properties.elements();
// Enumeration enumKeys = m_properties.keys();
// while (enumKeys.hasMoreElements())
// {
// bos.write(((String)enumKeys.nextElement()).getBytes());
// bos.write("=".getBytes());
// bos.write(((String)enumValues.nextElement()).getBytes());
// bos.write(NEWLINE.getBytes());
// bos.flush();
// }

// Properties.store is not available in JDK 1.1
Method m =
Properties.class.getMethod("store",
Method m =
Properties.class.getMethod("store",
new Class[] {
OutputStream.class,
String.class}
@@ -294,7 +264,7 @@ public class PropertyFile extends Task
// impossible
throw new BuildException(iae, location);
}
catch (IOException ioe)
catch (IOException ioe)
{
throw new BuildException(ioe, location);
}
@@ -310,155 +280,360 @@ public class PropertyFile extends Task
/*
* Returns whether the given parameter has been defined.
*/
private boolean checkParam(String param)
private boolean checkParam(String param)
{
return !((param == null) || (param.equals("null")));
}

private boolean checkParam(File param)
private boolean checkParam(File param)
{
return !(param == null);
}
public static class Entry

/**
* Instance of this class represents nested elements of
* a task propertyfile.
*/
public static class Entry
{
// Property types
private static final String INTEGER_TYPE = "int";
private static final String DATE_TYPE = "date";
private static final String STRING_TYPE = "string";
// Property type operations
private static final String INCREMENT_OPER = "+";
private static final String DECREMENT_OPER = "-";
private static final String EQUALS_OPER = "=";

// Special values
private static final String NOW_VALUE = "now";
private static final String NULL_VALUE = "never";

static final String NOW_VALUE_ = "now";
static final String NULL_VALUE_ = "never";

private static final int DEFAULT_INT_VALUE = 1;
private static final GregorianCalendar
DEFAULT_DATE_VALUE = new GregorianCalendar();

private String m_key;
private String m_type = null;
private String m_operation = null;
private String m_value;
private int m_intValue = DEFAULT_INT_VALUE;
private GregorianCalendar m_dateValue = DEFAULT_DATE_VALUE;
public void setKey(String value)
private String m_key = null;
private int m_type = Type.STRING_TYPE;
private int m_operation = Operation.EQUALS_OPER;
private String m_value ="1";
private String m_default = null;
private String m_pattern = null;
public void setKey(String value)
{
this.m_key = value;
}
public void setValue(String value)
public void setValue(String value)
{
this.m_value = value;
this.setOperation(EQUALS_OPER);
}
public void setOperation(String value)
public void setOperation(Operation value)
{
this.m_operation = value;
int newOperation = Operation.toOperation(value.getValue());
if (newOperation == Operation.NOW_VALUE) {
this.m_operation = Operation.EQUALS_OPER;
this.setValue(this.NOW_VALUE_);
}
else if (newOperation == Operation.NULL_VALUE) {
this.m_operation = Operation.EQUALS_OPER;
this.setValue(this.NULL_VALUE_);
}
else {
this.m_operation = newOperation;
}
}
public void setType(String value)
public void setType(Type value)
{

this.m_type = value;
this.m_type = Type.toType(value.getValue());
}
public void setDefault(String value)
{
this.m_default = value;
}
public void setPattern(String value)
{
this.m_pattern = value;
}

protected void executeOn(Properties props) throws BuildException
protected void executeOn(Properties props) throws BuildException
{
// Fork off process per the operation type requested
checkParameters();

// m_type may be null because it wasn't set
try {
if (m_type.equals(INTEGER_TYPE))
{
executeInteger((String)props.get(m_key));
}
else if (m_type.equals(DATE_TYPE))
{
executeDate((String)props.get(m_key));
}
else if (m_type.equals(STRING_TYPE))
{
}
else
{
throw new BuildException("Unknown operation type: "+m_type+"");
}
if (m_type == Type.INTEGER_TYPE)
{
executeInteger((String)props.get(m_key));
}
else if (m_type == Type.DATE_TYPE)
{
executeDate((String)props.get(m_key));
}
else if (m_type == Type.STRING_TYPE)
{
executeString((String)props.get(m_key));
}
else
{
throw new BuildException("Unknown operation type: "+m_type+"");
}
} catch (NullPointerException npe) {
// Default to string type
// which means do nothing
npe.printStackTrace();
}
// Insert as a string by default
props.put(m_key, m_value);

}
/*
* Continue execution for Date values
* TODO: Modify for different locales and formats

/**
* Handle operations for type <code>date</code>.
*
* @param oldValue the current value read from the property file or
* <code>null</code> if the <code>key</code> was
* not contained in the property file.
*/
private void executeDate(String oldValue) throws BuildException
private void executeDate(String oldValue) throws BuildException
{
StringBuffer dateString = new StringBuffer();
GregorianCalendar value = new GregorianCalendar();
GregorianCalendar newValue = new GregorianCalendar();

if (m_pattern == null) m_pattern = "yyyy/MM/dd HH:mm";
DateFormat fmt = new SimpleDateFormat(m_pattern);

if (m_value != null) {
if (NOW_VALUE_.equals(m_value.toLowerCase())) {
value.setTime(new Date());
}
else if (NULL_VALUE_.equals(m_value.toLowerCase())) {
value = null;
}
else {
try {
value.setTime(fmt.parse(m_value));
}
catch (Exception ex) {
// obviously not a date, try a simple int
try {
int offset = Integer.parseInt(m_value);
value.clear();
value.set(Calendar.DAY_OF_YEAR, offset);
}
catch (Exception ex_) {
value.clear();
value.set(Calendar.DAY_OF_YEAR, 1);
}
}

}
}

// If value is defined then interpret what's given
if (m_operation.equals(NULL_VALUE))
{
m_dateValue = null;
// special case
if (m_default != null &&
NOW_VALUE_.equals(m_default.toLowerCase()) &&
(m_operation == Operation.INCREMENT_OPER ||
m_operation == Operation.DECREMENT_OPER) ) {
oldValue = null;
}

if (oldValue != null) {
try {
newValue.setTime(fmt.parse(oldValue));
}
catch (ParseException pe) { /* swollow */ }
}
else {
if (m_default != null) {
if (NOW_VALUE_.equals(m_default.toLowerCase())) {
newValue.setTime(new Date());
}
else if (NULL_VALUE_.equals(m_default.toLowerCase())) {
newValue = null;
}
else {
try {
newValue.setTime(fmt.parse(m_default));
}
catch (ParseException pe) { /* swollow */ }
}
}
}

if (m_operation == Operation.EQUALS_OPER) {
newValue = value;
}
else if (m_operation == Operation.INCREMENT_OPER) {
newValue.add(Calendar.SECOND, value.get(Calendar.SECOND));
newValue.add(Calendar.MINUTE, value.get(Calendar.MINUTE));
newValue.add(Calendar.HOUR_OF_DAY, value.get(Calendar.HOUR_OF_DAY));
newValue.add(Calendar.DAY_OF_YEAR, value.get(Calendar.DAY_OF_YEAR));
}
else if (m_operation == Operation.DECREMENT_OPER) {
newValue.add(Calendar.SECOND, -1 * value.get(Calendar.SECOND));
newValue.add(Calendar.MINUTE, -1 * value.get(Calendar.MINUTE));
newValue.add(Calendar.HOUR_OF_DAY, -1 * value.get(Calendar.HOUR_OF_DAY));
newValue.add(Calendar.DAY_OF_YEAR, -1 * value.get(Calendar.DAY_OF_YEAR));
}
if (newValue != null) {
m_value = fmt.format(newValue.getTime());
}
else {
m_value = "";
}
else
{
Date now = new Date();
m_dateValue.setTime(now);
dateString.append(m_dateValue.get(Calendar.YEAR));
dateString.append("/");
dateString.append((m_dateValue.get(Calendar.MONTH)+1 < 10) ? "0" : "");
dateString.append(m_dateValue.get(Calendar.MONTH)+1);
dateString.append("/");
dateString.append((m_dateValue.get(Calendar.DATE) < 10) ? "0" : "");
dateString.append(m_dateValue.get(Calendar.DATE));
dateString.append(" ");
dateString.append((m_dateValue.get(Calendar.HOUR_OF_DAY) < 10) ? "0" : "");
dateString.append(m_dateValue.get(Calendar.HOUR_OF_DAY));
dateString.append(":");
dateString.append((m_dateValue.get(Calendar.MINUTE) < 10) ? "0" : "");
dateString.append(m_dateValue.get(Calendar.MINUTE));
m_value = dateString.toString();
}

m_value = dateString.toString();
}


/*
* Continue execution for int values
/**
* Handle operations for type <code>int</code>.
*
* @param oldValue the current value read from the property file or
* <code>null</code> if the <code>key</code> was
* not contained in the property file.
*/
private void executeInteger(String oldValue) throws BuildException
private void executeInteger(String oldValue) throws BuildException
{
String newValue = "";
int currentValue = 0;
try
{
currentValue = new Integer(oldValue).intValue();
int value = 0;
int newValue = 0;

DecimalFormat fmt = (m_pattern != null) ? new DecimalFormat(m_pattern)
: new DecimalFormat();

if (m_value != null) {
try {
value = fmt.parse(m_value).intValue();
}
catch (NumberFormatException nfe) { /* swollow */ }
catch (ParseException pe) { /* swollow */ }
}
catch (NumberFormatException nfe)
{
// Do nothing
if (oldValue != null) {
try {
newValue = fmt.parse(oldValue).intValue();
}
catch (NumberFormatException nfe) { /* swollow */ }
catch (ParseException pe) { /* swollow */ }
}
else if (m_default != null) {
try {
newValue = fmt.parse(m_default).intValue();
}
catch (NumberFormatException nfe) { /* swollow */ }
catch (ParseException pe) { /* swollow */ }
}

if (m_operation.equals(INCREMENT_OPER))
{
currentValue++;
m_value = new String(""+currentValue);
if (m_operation == Operation.EQUALS_OPER) {
newValue = value;
}
else if (m_operation.equals(DECREMENT_OPER))
{
currentValue--;
m_value = new String(""+currentValue);
else if (m_operation == Operation.INCREMENT_OPER) {
newValue += value;
}
else if (m_operation == Operation.DECREMENT_OPER) {
newValue -= value;
}
m_value = fmt.format(newValue);
}

/**
* Handle operations for type <code>string</code>.
*
* @param oldValue the current value read from the property file or
* <code>null</code> if the <code>key</code> was
* not contained in the property file.
*/
private void executeString(String oldValue) throws BuildException
{
String value = "";
String newValue = "";

if (m_value != null) {
value = m_value;
}
if (oldValue != null) {
newValue = oldValue;
}
else if (m_default != null) {
newValue = m_default;
}

if (m_operation == Operation.EQUALS_OPER) {
newValue = value;
}
else if (m_operation == Operation.INCREMENT_OPER) {
newValue += value;
}
m_value = newValue;
}

/**
* Check if parameter combinations can be supported
*/
private void checkParameters() throws BuildException {
if (m_type == Type.STRING_TYPE &&
m_operation == Operation.DECREMENT_OPER) {
throw new BuildException("- is not suported for string properties (key:" + m_key + ")");
}
if (m_value == null) {
throw new BuildException("value is mandatory (key:" + m_key + ")");
}
if (m_key == null) {
throw new BuildException("key is mandatory");
}
if (m_type == Type.STRING_TYPE &&
m_pattern != null) {
throw new BuildException("pattern is not suported for string properties (key:" + m_key + ")");
}
}

/**
* Enumerated attribute with the values "+", "-", "=", "now" and "never".
*/
public static class Operation extends EnumeratedAttribute {

// Property type operations
public static final int INCREMENT_OPER = 0;
public static final int DECREMENT_OPER = 1;
public static final int EQUALS_OPER = 2;

// Special values
public static final int NOW_VALUE = 3;
public static final int NULL_VALUE = 4;

public String[] getValues() {
return new String[] {"+", "-", "=", NOW_VALUE_, NULL_VALUE_};
}

public static int toOperation(String oper) {
if ("+".equals(oper)) {
return INCREMENT_OPER;
}
else if ("-".equals(oper)) {
return DECREMENT_OPER;
}
else if (NOW_VALUE_.equals(oper)) {
return NOW_VALUE;
}
else if (NULL_VALUE_.equals(oper)) {
return NULL_VALUE;
}
return EQUALS_OPER;
}
}

/**
* Enumerated attribute with the values "int", "date" and "string".
*/
public static class Type extends EnumeratedAttribute {

// Property types
public static final int INTEGER_TYPE = 0;
public static final int DATE_TYPE = 1;
public static final int STRING_TYPE = 2;

public String[] getValues() {
return new String[] {"int", "date", "string"};
}

public static int toType(String type) {
if ("int".equals(type)) {
return INTEGER_TYPE;
}
else if ("date".equals(type)) {
return DATE_TYPE;
}
return STRING_TYPE;
}
}
}
}

Loading…
Cancel
Save