Update of /cvsroot/larm/larm/src/java/larm/framework/config
In directory sc8-pr-cvs1:/tmp/cvs-serv23925/src/java/larm/framework/config
Added Files:
ConfigList.java Configurable.java Configuration.java
Log Message:
- Big bad update, reorganization, etc.
--- NEW FILE: ConfigList.java ---
package larm.framework.config;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* ConfigList
* encapsulates org.xml.dom.NodeList. returned by @see{Configuration.getSubConfigList()}
*
* @author
* @version $Id: ConfigList.java,v 1.1 2003/07/29 15:11:40 otis Exp $
*/
public class ConfigList
{
NodeList nl;
Node root;
protected ConfigList(NodeList nodes, Node rootNode)
{
nl = nodes;
root = rootNode;
}
/**
* @return the number of Configuration objects contained in this list;
*/
public int length()
{
return nl.getLength();
}
/**
* returns a configuration item [will be created from a NodeList object
* when this method is called]
* @param index index of the item. Must not exceed @see{#getLength()}
* @return the configuration
*/
public Configuration item(int index)
{
return new Configuration(nl.item(index), root);
}
}
--- NEW FILE: Configurable.java ---
package larm.framework.config;
/**
* Configurable
*
* @author
* @version $Id: Configurable.java,v 1.1 2003/07/29 15:11:40 otis Exp $
*/
public interface Configurable
{
void configure(Configuration conf);
}
--- NEW FILE: Configuration.java ---
package larm.framework.config;
import java.io.IOException;
import java.io.Reader;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Configuration encapsulates an XML file and provides an API similar to the
* java.util.Properties class while retaining the benefits of a hierarchical
* configuration file. Node contents are accessed via
* XPath expressions. Configuration doesn't expose any XML APIs. XPath Node lists
* are encapsulated via @see{ConfigList}.
*
* @author
* @version $Id: Configuration.java,v 1.1 2003/07/29 15:11:40 otis Exp $
*/
public class Configuration
{
/**
* the node represented by this configuration.
*/
private Node doc;
/**
* root node for evaluating absolute XPath expressions
*/
private Node rootNode;
protected NodeList getNodes(String xpath) throws IllegalArgumentException
{
try
{
return XPathAPI.selectNodeList(doc, xpath);
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
}
protected Node getThisNode()
{
return doc;
}
protected Node getNode(String xpath) throws IllegalArgumentException
{
try
{
return XPathAPI.selectSingleNode(doc, xpath);
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
}
public boolean contains(String xpath)
{
try
{
return XPathAPI.selectSingleNode(doc, xpath) != null;
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
}
/**
* resolves strings like
* <ol>
* <li>"foo ${my.property} bar". my.property is read
* from the systems properties and inserted into the string.
* <li>"foo $${/my/xpath} bar". /my/path must be a valid xpath argument
* that can be translated into a string. It is resolved using the configuration's
* root element
* </ol>
* resolveProperty works recursively: If a property contains another property
* placeholder, up to 10 levels of recursion will be resolved
* @param prop
* @param recurse
* @return
*/
protected String resolveProperty(String prop, int recurse)
{
char[] c = new char[prop.length()];
prop.getChars(0,prop.length(),c,0);
int l = c.length;
StringBuffer s = new StringBuffer(l * 2);
final int CHAR = 0;
final int DOLLAR = 1;
final int DOLLAR2 = 2;
int start = 0;
final int PROPERTY = 3;
final int XPATH = 4;
StringBuffer v = new StringBuffer();
int mode = CHAR;
for(int i = 0; i<c.length; i++)
{
char ac = c[i];
switch(mode)
{
case CHAR:
if(ac == '$')
{
mode = DOLLAR;
start = i;
}
else
{
s.append(ac);
}
break;
case DOLLAR:
if(ac == '{')
{
mode = PROPERTY;
}
else if(ac == '$')
{
mode = DOLLAR2;
}
else
{
mode = CHAR;
s.append('$');
s.append(ac);
}
break;
case DOLLAR2:
if(ac == '{')
{
mode = XPATH;
}
else
{
mode = CHAR;
s.append('$');
s.append('$');
}
break;
case PROPERTY:
if(ac == '}')
{
// got Java property. resolve it.
String varName = new String(c,start + 2,i-start-2);
String value = getJavaPropertyAsStringDontResolve(varName);
if(recurse == 0)
{
throw new IllegalStateException("recursion limit reached while resolving " + varName + " -> " + value);
}
String p = resolveProperty(value,recurse-1);
s.append(p);
mode = CHAR;
}
case XPATH:
if(ac == '}')
{
// got XPath expression. resolve it.
String varName = new String(c,start + 2,i-start-2);
String value = getPropertyAsStringDontResolve(rootNode,varName);
if(recurse == 0)
{
throw new IllegalStateException("recursion limit reached while resolving " + varName + " -> " + value);
}
String p = resolveProperty(value,recurse-1);
s.append(p);
mode = CHAR;
}
}
}
if(mode != CHAR)
{
s.append(c,start,l-start);
}
return s.toString();
}
protected String getJavaPropertyAsStringDontResolve(String varName)
{
return System.getProperty(varName, "");
}
public String getPropertyAsStringDontResolve(String xpath) throws IllegalArgumentException
{
return getPropertyAsStringDontResolve(doc, xpath);
}
protected String getPropertyAsStringDontResolve(Node root, String xpath) throws IllegalArgumentException
{
try
{
return XPathAPI.eval(root, "string(" + xpath + ")").str();
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
}
/**
* returns a property as a string or null if the property was not set
* @param xpath an XPath expression like '/foo[@bar="value"]'
* @return a string, empty if expression didn't exist
* @throws IllegalArgumentException if there was a problem with the XPath
* @throws IllegalStateException if properties couldn't be resolved (e.g. recursion level reached)
*/
public String getProperty(String xpath) throws IllegalArgumentException, IllegalStateException
{
return resolveProperty(getPropertyAsStringDontResolve(doc,xpath), 10);
}
/**
* returns a property as a string or the default value if the property wasn't set
* @param xpath an XPath expression like '/foo[@bar="value"]'
* @return a string
* @throws IllegalArgumentException if there was a problem with the XPath
*/
public String getProperty(String xpath, String def) throws IllegalArgumentException
{
String p = getProperty(xpath);
return !"".equals(p) ? p : def;
}
/**
* returns a property as a long or 0 if it was not set
* @param xpath an XPath expression like './foo[@bar="value"]'
* @return the value or 0
* @throws IllegalArgumentException if there was a problem with the XPath
*/
public long getPropertyAsLong(String xpath) throws IllegalArgumentException, NumberFormatException
{
return getPropertyAsLong(xpath, 0);
}
/**
* returns a property as a long or the default value if it was not set
* @param xpath an XPath expression like './foo[@bar="value"]'
* @return the value
* @throws IllegalArgumentException if there was a problem with the XPath
*/
public long getPropertyAsLong(String xpath, long def) throws IllegalArgumentException, NumberFormatException
{
try
{
return Long.parseLong(XPathAPI.eval(doc, "string(" + xpath + ")").str());
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
catch(NullPointerException npe)
{
return def;
}
}
/**
* returns a property as a double or 0 if it was not set
* @param xpath an XPath expression like './foo[@bar="value"]'
* @return the value
* @throws IllegalArgumentException if there was a problem with the XPath
*/
public double getPropertyAsDouble(String xpath) throws IllegalArgumentException
{
try
{
return Double.parseDouble(XPathAPI.eval(doc, "string(" + xpath + ")").str());
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
}
/**
* returns a property as a boolean or the default value if it was not set
* @param xpath an XPath expression like './foo[@bar="value"]'
* @param deflt the default value
* @return the value
* @throws IllegalArgumentException if there was a problem with the XPath
*/
public boolean getPropertyAsBoolean(String xpath, boolean deflt) throws IllegalArgumentException
{
try
{
return Boolean.valueOf(XPathAPI.eval(doc, "string(" + xpath + ")").str()).booleanValue();
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
catch(NullPointerException e)
{
return deflt;
}
}
/**
* returns a property as a long value indicating a number of bytes or the default
* value if the property was not set.
* The input string must conform to (NUMBER (BYTES|KB|MB|GB))+
* with BYTES= EMPTY|b|byte|bytes,
* KB = k|kb|kbyte|kbytes|kilobyte|kilobytes
* MB = m|mb|mbyte|mbytes|megabyte|megabytes
* GB = g|gb|gbyte|gbytes|gigabyte|gigabytes
* and EMPTY = the empty string<p>
* example
* <ul>
* <li>"2000" (2000 bytes)
* <li>"10 kb" (or "10 k", "1 kbyte", "10 kbytes", "1 kilobyte", "10 kilobytes")
* <li>"3.4 mb" (or "m", "mbyte", "mbytes", "megabyte", "megabytes")
* <li>"0.3 gb" (or "g", "gbyte", "gbytes", "gigabyte", "gigabytes")
* </ul>
* which are all resolved to their byte values (we say k = kb = kilo = 1024
* although this is not perfectly correct since kilo = 1000; the same applies
* to mb or gb)<p>
* Tokens are separated by whitespace
* @param xpath an XPath expression like './foo[@bar="value"]'
* @param deflt the default value
* @return the value
* @throws IllegalArgumentException if there was a problem with the XPath
*/
public long getPropertyAsNrOfBytes(String xpath, long deflt) throws IllegalArgumentException
{
try
{
return parseNrOfBytes(XPathAPI.eval(doc, "string(" + xpath + ")").str());
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
catch(NullPointerException e)
{
return deflt;
}
}
private long parseNrOfSeconds(String s)
{
StringTokenizer t = new StringTokenizer(s);
long ret = 0;
while(t.hasMoreTokens())
{
double t1 = Double.parseDouble(t.nextToken());
int mult = 1;
if(t.hasMoreTokens())
{
String type = t.nextToken().toLowerCase();
if("d".equals(type) || "day".equals(type) || "days".equals(type))
{
mult = 3600 * 24;
}
if("h".equals(type) || "hours".equals(type))
{
mult = 3600;
}
else if("m".equals(type) || "min".equals(type) || "mins".equals(type) || "minutes".equals(type))
{
mult = 60;
}
else if(!("s".equals(type) || "sec".equals(type) || "seconds".equals(type)))
{
throw new IllegalArgumentException("s|sec|seconds|m|min|mins|minutes|h|hours expected. (Argument was: '" + type + "')");
}
}
ret += (long)(t1 * mult);
}
return ret;
}
protected long parseNrOfBytes(String s)
{
StringTokenizer t = new StringTokenizer(s);
long ret = 0;
while(t.hasMoreTokens())
{
double t1 = Double.parseDouble(t.nextToken());
long mult = 1;
if(t.hasMoreTokens())
{
String type = t.nextToken().toLowerCase();
if("k".equals(type) || "kb".equals(type) || "kbyte".equals(type) || "kbytes".equals(type))
{
mult = 1024;
}
else if("mb".equals(type) || "mbyte".equals(type) || "mbytes".equals(type) || "megabyte".equals(type) || "megabytes".equals(type))
{
mult = 1024 * 1024;
}
else if("gb".equals(type) || "gbyte".equals(type) || "gbytes".equals(type) || "gigabyte".equals(type) || "gigabytes".equals(type))
{
mult = 1024 * 1024 * 1024;
}
else
{
throw new IllegalArgumentException("k|kb|kbyte|kbytes|mb|mbyte|mbytes|megabyte|megabytes|gb|gbyte|gbytes|gigabytes expected. (Argument was: '" + type + "')");
}
}
ret += (long)(t1 * mult);
}
return ret;
}
/**
* returns a property as a long value indicating a number of seconds or the default
* value if the property was not set.
* The input string must conform to (NUMBER (SECS|MINS|HOURS|DAYS))+
* with SECS= EMPTY|s|sec|secs|second|seconds,
* MINS = m|min|mins|minutes and
* HOURS = h|hour|hours
* DAYS = d|day|days
* where EMPTY is the empty string. Tokens are separated by whitespace
* examples:
* <ul>
* <li>"30" (30 seconds)
* <li>"2 mins 10 secs"
* <li>"3.5 hours"
* <li>"1 day"
* </ul>
* @param xpath
* @return the value in seconds
*/
protected long getPropertyAsNrOfSeconds(String xpath, long dfault) throws IllegalArgumentException
{
try
{
return parseNrOfSeconds(XPathAPI.eval(doc, "string(" + xpath + ")").str());
}
catch(TransformerException e)
{
throw new IllegalArgumentException("illegal argument: '" + xpath + "' (root cause: " + e + ")");
}
catch(NullPointerException n)
{
return dfault;
}
}
/**
* returns a configuration object representing the sub-graph of the given XML
* tree
* @param xpath an expression denoting a node that contains a sub-tree.
* @return the configuration or null if the node doesn't exist
* @throws IllegalArgumentException if x
*/
public Configuration getSubConfig(String xpath) throws IllegalArgumentException
{
Node n = getNode(xpath);
if(n == null)
{
//return null;
throw new IllegalArgumentException("expected tag '" + xpath + "' in configuration file");
}
return new Configuration(n, rootNode);
}
/**
* returns a list of Configuration objects representing sub-graphs of the
* given XML tree, specified by an xPath expression
* @param xpath an expression denoting a list of nodes that contain a sub-tree.
* @return the configuration or null if the node doesn't exist
* @throws IllegalArgumentException if x
*/
public ConfigList getSubConfigList(String xpath) throws IllegalArgumentException
{
NodeList nl = getNodes(xpath);
if(nl == null)
{
throw new IllegalArgumentException("node '" + xpath + "' does not exist");
}
return new ConfigList(nl, rootNode);
}
protected Configuration(Node node, Node root)
{
this.doc = node;
this.rootNode = root;
}
/**
* Constructor. Node will be the root node
* @param node root node
*/
public Configuration(Node node)
{
doc = rootNode = node;
}
/**
* Constructor. config must contain a valid XML file.
* @param config the XML file this Configuration represents.
* @throws IOException
* @throws SAXException
*/
public Configuration(Reader config) throws IOException, SAXException
{
try
{
// inspired by http://cafeconleche.org/books/xmljava/chapters/ch16s06.html
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
InputSource data = new InputSource(config);
doc = rootNode = builder.parse(data);
}
catch(ParserConfigurationException e)
{
throw new RuntimeException("Couldn't initialize parser", e);
}
}
}
|