From: zapata Date: Sun, 17 Mar 2002 02:32:47 +0000 (+0000) Subject: initial checkin of the new xml-style configuration classes X-Git-Tag: prexmlproducerconfig~291 X-Git-Url: http://erislabs.org.uk/gitweb/?a=commitdiff_plain;h=973a63c00a383f568fed2e37be1f4a46a72d124a;p=mir.git initial checkin of the new xml-style configuration classes --- diff --git a/source/mir/config/ConfigChecker.java b/source/mir/config/ConfigChecker.java new file mode 100755 index 00000000..7d52eb01 --- /dev/null +++ b/source/mir/config/ConfigChecker.java @@ -0,0 +1,130 @@ +package mir.config; + +import java.util.*; + +public class ConfigChecker { + public final static int STRING = 0; + public final static int INTEGER = 1; + public final static int BOOLEAN = 2; + public final static int DOUBLE = 3; + public final static int PATH = 4; +// public final static int ABSOLUTEPATH = 5; +// public final static int ABSOLUTEURL = 6; + + private Node rootNode; + + public Node getRootNode() { + return rootNode; + } + + public ConfigChecker() { + super(); + + rootNode = new Node(); + } + + public void check(ConfigNode aNode) throws ConfigException { + getRootNode().check(aNode); + } + + public class Node { + + private Map subNodes; + private Vector constraints; + + public Node() { + subNodes = new HashMap(); + constraints = new Vector(); + } + + public Node getSubNode(String aName) { + Node subNode = (Node) subNodes.get(aName); + + if (subNode==null) { + subNode = new Node(); + subNodes.put(aName, subNode); + } + + return subNode; + } + + public void addExistenceConstraint(String aPropertyName) { + constraints.add(new ExistenceConstraint(aPropertyName)); + } + + public void addTypeConstraint(String aPropertyName, int aType) { + constraints.add(new TypeConstraint(aPropertyName, aType)); + } + + public void addExistenceAndTypeConstraint(String aPropertyName, int aType) { + addExistenceConstraint(aPropertyName); + addTypeConstraint(aPropertyName, aType); + } + + public void check(ConfigNode aNode) throws ConfigException { + Iterator iterator; + + iterator=constraints.iterator(); + while (iterator.hasNext()) { + ((Constraint) iterator.next()).check(aNode); + } + + iterator=subNodes.keySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Map.Entry) iterator.next(); + ((Node) entry.getValue()).check(aNode.getSubNode((String) entry.getKey())); + } + + } + + private class Constraint { + protected String propertyName; + + Constraint(String aPropertyName) { + propertyName=aPropertyName; + } + + public void check(ConfigNode aNode) throws ConfigException { + }; + } + + private class ExistenceConstraint extends Constraint { + ExistenceConstraint(String aPropertyName) { + super(aPropertyName); + } + + public void check(ConfigNode aNode) throws ConfigException { + aNode.getRequiredStringProperty(propertyName); + }; + } + + private class TypeConstraint extends Constraint { + private int type; + + TypeConstraint(String aPropertyName, int aType) { + super(aPropertyName); + + type=aType; + } + + public void check(ConfigNode aNode) throws ConfigException { + switch(type) { + case INTEGER: + aNode.getOptionalIntegerProperty(propertyName, new Integer(0)); + break; + case STRING: + aNode.getOptionalStringProperty(propertyName, ""); + break; + case DOUBLE: + aNode.getOptionalDoubleProperty(propertyName, new Double(0.0)); + break; + case BOOLEAN: + aNode.getOptionalBooleanProperty(propertyName, Boolean.FALSE); + break; + default: + throw new ConfigException("Invalid value for type in type constraint: "+new Integer(type).toString()); + } + } + } + } +} diff --git a/source/mir/config/ConfigNode.java b/source/mir/config/ConfigNode.java new file mode 100755 index 00000000..a65ee909 --- /dev/null +++ b/source/mir/config/ConfigNode.java @@ -0,0 +1,17 @@ +package mir.config; + +import java.util.*; + +public interface ConfigNode { + public String getLocationDescription(); + + public ConfigNode getSubNode(String aSubNodeName); + public Boolean getRequiredBooleanProperty(String aPropertyName) throws ConfigException; + public Integer getRequiredIntegerProperty(String aPropertyName) throws ConfigException; + public String getRequiredStringProperty(String aPropertyName) throws ConfigException; + public Double getRequiredDoubleProperty(String aPropertyName) throws ConfigException; + public Boolean getOptionalBooleanProperty(String aPropertyName, Boolean aDefaultValue) throws ConfigException; + public Integer getOptionalIntegerProperty(String aPropertyName, Integer aDefaultValue) throws ConfigException; + public String getOptionalStringProperty(String aPropertyName, String aDefaultValue) throws ConfigException; + public Double getOptionalDoubleProperty(String aPropertyName, Double aDefaultValue) throws ConfigException; +} diff --git a/source/mir/config/ConfigNodeBuilder.java b/source/mir/config/ConfigNodeBuilder.java new file mode 100755 index 00000000..063dbe51 --- /dev/null +++ b/source/mir/config/ConfigNodeBuilder.java @@ -0,0 +1,8 @@ +package mir.config; + +import java.util.*; + +public interface ConfigNodeBuilder { + public ConfigNodeBuilder makeSubNode(String aName, String aLocationDescription); + public void addProperty(String aName, String aValue, String aValueDescription, String aLocationDescription); +} \ No newline at end of file diff --git a/source/mir/config/ConfigReader.java b/source/mir/config/ConfigReader.java new file mode 100755 index 00000000..bbcdeeea --- /dev/null +++ b/source/mir/config/ConfigReader.java @@ -0,0 +1,260 @@ +package mir.config; + +import java.io.*; +import java.util.*; +import java.lang.System; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.*; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import mir.misc.Location; + + +public class ConfigReader { + final static String propertyTagName="property"; + final static String propertyNameAttribute="name"; + final static String propertyValueAttribute="value"; + final static String defineTagName="define"; + final static String defineNameAttribute="name"; + final static String defineValueAttribute="value"; + final static String includeTagName="include"; + final static String includeFileAttribute="file"; + + public ConfigReader() { + super(); + }; + + public void parseFile(String aFileName, ConfigNodeBuilder aRootNode) throws ConfigException { + + try { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + + parserFactory.setNamespaceAware(false); + parserFactory.setValidating(true); + + + ConfigReaderHandler handler = new ConfigReaderHandler(aRootNode, parserFactory); + + handler.includeFile(aFileName); + } + catch (Throwable e) { + if (e instanceof SAXParseException && ((SAXParseException) e).getException() instanceof ConfigException) { + throw (ConfigException) ((SAXParseException) e).getException(); + } + else { + e.printStackTrace(); + throw new ConfigException( e.getMessage() ); + } + } + } + + private class ConfigReaderHandler extends DefaultHandler { + ConfigNodeBuilder builder; + Stack nodeStack; + Locator locator; + DefinesManager definesManager; + int level; + Stack includeFileStack; + SAXParserFactory parserFactory; + + public ConfigReaderHandler(ConfigNodeBuilder aBuilder, SAXParserFactory aParserFactory) { + super(); + + builder=aBuilder; + nodeStack=new Stack(); + includeFileStack=new Stack(); + definesManager=new DefinesManager(); + parserFactory=aParserFactory; + level=0; + } + + public String getLocatorDescription(Locator aLocator) { + return aLocator.getPublicId()+" ("+aLocator.getLineNumber()+")"; + } + + public void setDocumentLocator(Locator aLocator) { + locator=aLocator; + } + + private void includeFile(String aFileName) throws ConfigException, SAXParseException, SAXException { + File file; + SAXParser parser; + InputSource inputSource; + System.err.println("about to include "+aFileName); + + try { + if (!includeFileStack.empty()) + file = new File(new File((String) includeFileStack.peek()).getParent(), aFileName); + else + file = new File(aFileName); + + System.err.println("about to include "+file.getCanonicalPath()); + + if (includeFileStack.contains(file.getCanonicalPath())) { + throw new ConfigException("recursive inclusion of file "+file.getCanonicalPath(), getLocatorDescription(locator)); + } + + parser=parserFactory.newSAXParser(); + + inputSource = new InputSource(new FileInputStream(file)); + inputSource.setPublicId(file.getCanonicalPath()); + + includeFileStack.push(file.getCanonicalPath()); + try { + parser.parse(inputSource, this); + } + finally { + includeFileStack.pop(); + } + } + catch (ParserConfigurationException e) { + throw new ConfigException("Internal exception while including \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator)); + } + catch (SAXParseException e) { + throw e; + } + catch (ConfigException e) { + throw e; + } + catch (FileNotFoundException e) { + throw new ConfigException("Include file \""+aFileName+"\" not found: "+e.getMessage(), e, getLocatorDescription(locator)); + } + catch (IOException e) { + throw new ConfigException("unable to open include file \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator)); + } + + } + + public void startElement(String aUri, String aTag, String aQualifiedName, Attributes anAttributes) throws SAXException { + nodeStack.push(builder); + level++; + try { + if (builder==null) { + throw new ConfigException("define, include and property tags cannot have content", getLocatorDescription(locator)); + } + if (aQualifiedName.equals(propertyTagName)) { + String name=anAttributes.getValue(propertyNameAttribute); + String value=anAttributes.getValue(propertyValueAttribute); + + if (name==null) { + throw new ConfigException("property has no name attribute", getLocatorDescription(locator)); + } + else + if (value==null) { + throw new ConfigException("property \""+name+"\" has no value attribute", getLocatorDescription(locator)); + } + + builder.addProperty(name, definesManager.resolve(value, getLocatorDescription(locator)), value, getLocatorDescription(locator)); + builder=null; + + } + else if (aQualifiedName.equals(defineTagName)) { + String name=anAttributes.getValue(defineNameAttribute); + String value=anAttributes.getValue(defineValueAttribute); + + if (name==null) { + throw new ConfigException("define has no name attribute", getLocatorDescription(locator)); + } + else + if (value==null) { + throw new ConfigException("define \""+name+"\" has no value attribute", getLocatorDescription(locator)); + } + + definesManager.addDefine(name, definesManager.resolve(value, getLocatorDescription(locator))); + builder=null; + } + else if (aQualifiedName.equals(includeTagName)) { + String fileName=anAttributes.getValue(includeFileAttribute); + + if (fileName==null) { + throw new ConfigException("include has no file attribute", getLocatorDescription(locator)); + } + + includeFile(definesManager.resolve(fileName, getLocatorDescription(locator))); + builder=null; + } + else + { + builder=builder.makeSubNode(aQualifiedName, getLocatorDescription(locator)); + } + } + catch (ConfigException e) { + throw new SAXParseException(e.getMessage(), locator, e); + } + } + + public void endElement(String aUri, String aTag, String aQualifiedName) throws SAXParseException { + builder=(ConfigNodeBuilder) nodeStack.pop(); + level--; + } + + public void characters(char[] aBuffer, int aStart, int anEnd) throws SAXParseException { + String text = new String(aBuffer, aStart, anEnd).trim(); + if ( text.length() > 0) { + throw new SAXParseException("Text not allowed", locator, new ConfigException("text not allowed", getLocatorDescription(locator))); + } + } + } + + private class DefinesManager { + Map defines; + + public DefinesManager() { + defines = new HashMap(); + } + + public void addDefine(String aName, String anExpression) { + defines.put(aName, anExpression); + } + + public String resolve(String anExpression, String aLocation) throws ConfigException { + int previousPosition = 0; + int position; + int endOfNamePosition; + String name; + + StringBuffer result = new StringBuffer(); + + while ((position=anExpression.indexOf("$", previousPosition))>=0) { + result.append(anExpression.substring(previousPosition, position)); + + if (position>=anExpression.length()-1) { + result.append(anExpression.substring(position, anExpression.length())); + previousPosition=anExpression.length(); + } + else + { + if (anExpression.charAt(position+1) == '{') { + endOfNamePosition=anExpression.indexOf('}', position); + if (endOfNamePosition>=0) { + name=anExpression.substring(position+2, endOfNamePosition); + if (defines.containsKey(name)) { + result.append((String) defines.get(name)); + previousPosition=endOfNamePosition+1; + } + else { + throw new ConfigDefineNotKnownException("Variable \""+name+"\" not defined", aLocation); + } + } + else { + throw new ConfigException("Missing }", aLocation); + } + + } + else + { + previousPosition=position+2; + result.append(anExpression.charAt(position+1)); + } + } + } + result.append(anExpression.substring(previousPosition, anExpression.length())); + + return result.toString(); + } + } +} + + diff --git a/source/mir/config/ConfigSimpleNode.java b/source/mir/config/ConfigSimpleNode.java new file mode 100755 index 00000000..4a02a250 --- /dev/null +++ b/source/mir/config/ConfigSimpleNode.java @@ -0,0 +1,229 @@ +package mir.config; + +import java.util.*; + +public class ConfigSimpleNode implements ConfigNode, ConfigNodeBuilder { + private Map properties; + private Map subNodes; + private String locationDescription; + private String path; + + public ConfigSimpleNode() { + this("", ""); + } + + public ConfigSimpleNode(String aLocationDescription) { + this("", aLocationDescription); + } + + public ConfigSimpleNode(String aPath, String aLocationDescription) { + super (); + + path = aPath; + locationDescription = aLocationDescription; + properties = new HashMap(); + subNodes = new HashMap(); + } + +// ConfigNodeBuilder helpers: + + private String makeSubNodePath(String aSubNode) { + if (path!=null && path.length()>0) + return path+"/"+aSubNode; + else + return aSubNode; + } + + private String makePropertyPath(String aProperty) { + if (path!=null && path.length()>0) + return path+"/"+aProperty; + else + return aProperty; + } + + public ConfigNodeBuilder mimicSubNode(String aName, String aLocationDescription) { + ConfigNodeBuilder result = new ConfigSimpleNode(makeSubNodePath(aName), aLocationDescription); + + return result; + } + +// ConfigNodeBuilder methods: + + public ConfigNodeBuilder makeSubNode(String aName, String aLocationDescription) { + if (subNodes.containsKey(aName)) { + return (ConfigNodeBuilder) subNodes.get(aName); + } + else { + ConfigNodeBuilder result = mimicSubNode(aName, aLocationDescription); + subNodes.put(aName, result); + + return result; + } + } + + public void addProperty(String aName, String aValue, String anUnexpandedValue, String aLocationDescription) { + properties.put(aName, new property(aValue, anUnexpandedValue, aLocationDescription, makePropertyPath(aName))); + } + +// ConfigNode helpers + + public boolean hasProperty(String aPropertyName) { + return properties.containsKey(aPropertyName); + } + + public property getProperty(String aPropertyName) { + return (property) properties.get(aPropertyName); + } + + private property getRequiredProperty(String aPropertyName) throws ConfigMissingPropertyException { + if (!hasProperty(aPropertyName)) { + throw new ConfigMissingPropertyException("required property \""+aPropertyName+"\" not found", getLocationDescription()); + } + + return getProperty(aPropertyName); + } + + +// ConfigNode methods: + + public String getLocationDescription() { + return getPath()+" ("+locationDescription+")"; + }; + + public String getPath() { + return path; + }; + + + public ConfigNode getSubNode(String aSubNodeName) { + if (subNodes.containsKey(aSubNodeName)) { + return (ConfigNode) subNodes.get(aSubNodeName); + } + else + { + return (ConfigNode) mimicSubNode(aSubNodeName, locationDescription); + } + } + + public Boolean getRequiredBooleanProperty(String aPropertyName) throws ConfigMissingPropertyException, ConfigInvalidPropertyTypeException { + return getRequiredProperty(aPropertyName).interpretAsBoolean(); + } + + public Integer getRequiredIntegerProperty(String aPropertyName) throws ConfigMissingPropertyException, ConfigInvalidPropertyTypeException { + return getRequiredProperty(aPropertyName).interpretAsInteger(); + } + + public String getRequiredStringProperty(String aPropertyName) throws ConfigMissingPropertyException, ConfigInvalidPropertyTypeException { + return getRequiredProperty(aPropertyName).interpretAsString(); + } + + public Double getRequiredDoubleProperty(String aPropertyName) throws ConfigMissingPropertyException, ConfigInvalidPropertyTypeException { + return getRequiredProperty(aPropertyName).interpretAsDouble(); + } + + + public Boolean getOptionalBooleanProperty(String aPropertyName, Boolean aDefaultValue) throws ConfigInvalidPropertyTypeException { + if (!hasProperty(aPropertyName)) { + return aDefaultValue; + } + else { + return getProperty(aPropertyName).interpretAsBoolean(); + } + } + + public Integer getOptionalIntegerProperty(String aPropertyName, Integer aDefaultValue) throws ConfigInvalidPropertyTypeException { + if (!hasProperty(aPropertyName)) { + return aDefaultValue; + } + else { + return getProperty(aPropertyName).interpretAsInteger(); + } + } + + public String getOptionalStringProperty(String aPropertyName, String aDefaultValue) throws ConfigInvalidPropertyTypeException { + if (!hasProperty(aPropertyName)) { + return aDefaultValue; + } + else { + return getProperty(aPropertyName).interpretAsString(); + } + } + + public Double getOptionalDoubleProperty(String aPropertyName, Double aDefaultValue) throws ConfigInvalidPropertyTypeException { + if (!hasProperty(aPropertyName)) { + return aDefaultValue; + } + else { + return getProperty(aPropertyName).interpretAsDouble(); + } + } + +// property helper class + + private class property { + private String value; + private String unexpandedValue; + private String path; + private String locationDescription; + + public property( String aValue, String anUnexpandedValue, String aLocationDescription, String aPath ) { + value = aValue; + unexpandedValue = anUnexpandedValue; + locationDescription = aLocationDescription; + path = aPath; + } + + public String getValue() { + return value; + } + + public String getUnexpandedValue() { + return unexpandedValue; + } + + public String getPath() { + return path; + } + + public String getLocationDescription() { + return getPath()+" ("+locationDescription+")"; + } + + public String getValueDescription() { + return "\""+value+"\" (\""+unexpandedValue+"\")"; + } + + public Boolean interpretAsBoolean() throws ConfigInvalidPropertyTypeException { + if (value.equals("1")) + return Boolean.TRUE; + else if (value.equals("0")) + return Boolean.FALSE; + else + throw new ConfigInvalidPropertyTypeException(getValueDescription() + " is not a boolean", getLocationDescription()); + } + + public String interpretAsString() throws ConfigInvalidPropertyTypeException { + return value; + } + + public Integer interpretAsInteger() throws ConfigInvalidPropertyTypeException { + try { + return Integer.valueOf(value); + } + catch (Throwable e) { + throw new ConfigInvalidPropertyTypeException("\""+value+"\" (\""+unexpandedValue+"\") is not an integer", getLocationDescription()); + } + } + + public Double interpretAsDouble() throws ConfigInvalidPropertyTypeException { + try { + return Double.valueOf(value); + } + catch (Throwable e) { + throw new ConfigInvalidPropertyTypeException("\""+value+"\" (\""+unexpandedValue+"\") is not a double", getLocationDescription()); + } + } + } +} + + diff --git a/source/mir/config/MirConfiguration.java b/source/mir/config/MirConfiguration.java new file mode 100755 index 00000000..e3730dff --- /dev/null +++ b/source/mir/config/MirConfiguration.java @@ -0,0 +1,27 @@ +package mir.config; + +//import java.net.*; +//import java.io.*; +//import java.util.*; +//import java.lang.*; + +public class MirConfiguration { + private ConfigNode rootNode; + + public MirConfiguration(ConfigNode aRootNode) { + super(); + rootNode = aRootNode; + } + + public MirConfiguration(String aFileName) throws ConfigException { + super(); + rootNode = new ConfigSimpleNode(); + + (new ConfigReader()).parseFile(aFileName,(ConfigNodeBuilder) rootNode); + } + + public ConfigNode getRootNode() { + return rootNode; + } +} + diff --git a/source/mir/config/exceptions/ConfigDefineNotKnownException.java b/source/mir/config/exceptions/ConfigDefineNotKnownException.java new file mode 100755 index 00000000..a527d345 --- /dev/null +++ b/source/mir/config/exceptions/ConfigDefineNotKnownException.java @@ -0,0 +1,7 @@ +package mir.config; + +public class ConfigDefineNotKnownException extends ConfigException { + public ConfigDefineNotKnownException(String aMessage, String aLocation) { + super (aMessage, aLocation); + } +} diff --git a/source/mir/config/exceptions/ConfigException.java b/source/mir/config/exceptions/ConfigException.java new file mode 100755 index 00000000..5b3b5725 --- /dev/null +++ b/source/mir/config/exceptions/ConfigException.java @@ -0,0 +1,23 @@ +package mir.config; + +import java.io.*; + +public class ConfigException extends Exception { + private String locationDescription; + private Throwable cause; + + public ConfigException (String aMessage, Throwable aCause, String aLocationDescription) { + super ("Configuration error at "+aLocationDescription+": "+aMessage); + + locationDescription = aLocationDescription; + cause = aCause; + } + + public ConfigException (String aMessage, String aLocationDescription) { + this (aMessage, (Throwable) null, aLocationDescription); + } + + public ConfigException (String aMessage) { + this (aMessage, (Throwable) null, "?"); + } +} diff --git a/source/mir/config/exceptions/ConfigInvalidPropertyTypeException.java b/source/mir/config/exceptions/ConfigInvalidPropertyTypeException.java new file mode 100755 index 00000000..6ea5a901 --- /dev/null +++ b/source/mir/config/exceptions/ConfigInvalidPropertyTypeException.java @@ -0,0 +1,7 @@ +package mir.config; + +public class ConfigInvalidPropertyTypeException extends ConfigException { + public ConfigInvalidPropertyTypeException(String aMessage, String aLocation) { + super (aMessage, aLocation); + } +} diff --git a/source/mir/config/exceptions/ConfigMissingPropertyException.java b/source/mir/config/exceptions/ConfigMissingPropertyException.java new file mode 100755 index 00000000..af059ef2 --- /dev/null +++ b/source/mir/config/exceptions/ConfigMissingPropertyException.java @@ -0,0 +1,7 @@ +package mir.config; + +public class ConfigMissingPropertyException extends ConfigException { + public ConfigMissingPropertyException(String aMessage, String aLocation) { + super (aMessage, aLocation); + } +}