From: mh Date: Tue, 23 Oct 2001 20:31:32 +0000 (+0000) Subject: new mir Configuration file parser. not used yet. it works very nicely though, X-Git-Tag: prexmlproducerconfig~325 X-Git-Url: http://erislabs.org.uk/gitweb/?a=commitdiff_plain;h=ed555a087df9a68d3b3160ac8b036c0ae3e64b10;p=mir.git new mir Configuration file parser. not used yet. it works very nicely though, handles errors in the XML file, etc. I stil need to add in the addRequired XML Path stuff and the Path matching stuff. --- diff --git a/source/mir/misc/ConfigException.java b/source/mir/misc/ConfigException.java new file mode 100755 index 00000000..e8a3b1bd --- /dev/null +++ b/source/mir/misc/ConfigException.java @@ -0,0 +1,174 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package mir.misc; + + +import java.io.*; + +/** + * Signals an error condition during a build. + * + * @author James Duncan Davidson + */ +public class ConfigException extends RuntimeException { + + /** Exception that might have caused this one. */ + private Throwable cause; + + /** Location in the build file where the exception occured */ + private Location location = Location.UNKNOWN_LOCATION; + + /** + * Constructs a build exception with no descriptive information. + */ + public ConfigException() { + super(); + } + + /** + * Constructs an exception with the given descriptive message. + * @param msg Description of or information about the exception. + */ + public ConfigException(String msg) { + super(msg); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause. + * @param msg Description of or information about the exception. + * @param cause Throwable that might have cause this one. + */ + public ConfigException(String msg, Throwable cause) { + super(msg); + this.cause = cause; + } + + /** + * Constructs an exception with the given message and exception as + * a root cause and a location in a file. + * @param msg Description of or information about the exception. + * @param cause Exception that might have cause this one. + * @param location Location in the project file where the error occured. + */ + public ConfigException(String msg, Throwable cause, Location location) { + this(msg, cause); + this.location = location; + } + + /** + * Constructs an exception with the given exception as a root cause. + * @param cause Exception that might have caused this one. + */ + public ConfigException(Throwable cause) { + super(cause.toString()); + this.cause = cause; + } + + /** + * Constructs an exception with the given descriptive message and a location + * in a file. + * @param msg Description of or information about the exception. + * @param location Location in the project file where the error occured. + */ + public ConfigException(String msg, Location location) { + super(msg); + this.location = location; + } + + /** + * Constructs an exception with the given exception as + * a root cause and a location in a file. + * @param cause Exception that might have cause this one. + * @param location Location in the project file where the error occured. + */ + public ConfigException(Throwable cause, Location location) { + this(cause); + this.location = location; + } + + /** + * Returns the nested exception. + */ + public Throwable getException() { + return cause; + } + + /** + * Returns the location of the error and the error message. + */ + public String toString() { + return location.toString() + getMessage(); + } + + /** + * Sets the file location where the error occured. + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Returns the file location where the error occured. + */ + public Location getLocation() { + return location; + } + + // Override stack trace methods to show original cause: + public void printStackTrace() { + printStackTrace(System.err); + } + +} diff --git a/source/mir/misc/Location.java b/source/mir/misc/Location.java new file mode 100755 index 00000000..13954a5a --- /dev/null +++ b/source/mir/misc/Location.java @@ -0,0 +1,111 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package mir.misc; + +/** + * Stores the file name and line number in a file. + */ +public class Location { + private String fileName; + private int lineNumber; + private int columnNumber; + + public static final Location UNKNOWN_LOCATION = new Location(); + + /** + * Creates an "unknown" location. + */ + private Location() { + this(null, 0, 0); + } + + /** + * Creates a location consisting of a file name but no line number. + */ + public Location(String fileName) { + this(fileName, 0, 0); + } + + /** + * Creates a location consisting of a file name and line number. + */ + public Location(String fileName, int lineNumber, int columnNumber) { + this.fileName = fileName; + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + /** + * Returns the file name, line number and a trailing space. An error + * message can be appended easily. For unknown locations, returns + * an empty string. + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + + if (fileName != null) { + buf.append(fileName); + + if (lineNumber != 0) { + buf.append(":"); + buf.append(lineNumber); + } + + buf.append(": "); + } + + return buf.toString(); + } +} diff --git a/source/mir/xml/XmlConfigurator.java b/source/mir/xml/XmlConfigurator.java new file mode 100755 index 00000000..a67a7c4a --- /dev/null +++ b/source/mir/xml/XmlConfigurator.java @@ -0,0 +1,385 @@ +package mir.xml; + +import java.io.*; +import java.util.*; +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.ConfigException; +import mir.misc.Location; + +/** + * Configures a based on + * a XML config file. + * + * Based and inspired by ant's XmlConfigurator.java + * It's a simplified version of the ant parser with + * the addition of calling set* methods for defined + * classes as well as the inclusion of a method to + * add parameters (nested tags) that are required. + * (the addRequired method) in the config file. + * + * @author -mh + * @version 2001.10.21 + */ + +public class XmlConfigurator { + + private static SAXParserFactory parserFactory = null; + + private SAXParser saxParser; + //private Project project; + private File configFile; + private File configFileParent; + private Locator locator; + + //private SaxContext saxContext; + + /** + * Configures the Project with the contents of the specified XML file. + */ + public static void configure(File configFile) throws ConfigException { + new XmlConfigurator(configFile).parse(); + } + + /** + * Constructs a new Ant parser for the specified XML file. + */ + private XmlConfigurator(File configFile) { + this.configFile = new File(configFile.getAbsolutePath()); + configFileParent = new File(this.configFile.getParent()); + } + + /** + * Parses the config file. + */ + private void parse() throws ConfigException { + FileInputStream inputStream = null; + InputSource inputSource = null; + + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + saxParser = factory.newSAXParser(); + + String uri = "file:" + configFile.getAbsolutePath().replace('\\', '/'); + for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) { + uri = uri.substring(0, index) + "%23" + uri.substring(index+1); + } + + inputStream = new FileInputStream(configFile); + inputSource = new InputSource(inputStream); + inputSource.setSystemId(uri); + saxParser.parse(inputSource, new RootHandler()); + } + catch(ParserConfigurationException exc) { + throw new ConfigException("Parser has not been configured correctly", exc); + } + catch(SAXParseException exc) { + Location location = + new Location(configFile.toString(), exc.getLineNumber(), exc.getColumnNumber()); + + Throwable t = exc.getException(); + if (t instanceof ConfigException) { + ConfigException be = (ConfigException) t; + if (be.getLocation() == Location.UNKNOWN_LOCATION) { + be.setLocation(location); + } + throw be; + } + + throw new ConfigException(exc.getMessage(), t, location); + } + catch(SAXException exc) { + Throwable t = exc.getException(); + if (t instanceof ConfigException) { + throw (ConfigException) t; + } + throw new ConfigException(exc.getMessage(), t); + } + catch(FileNotFoundException exc) { + throw new ConfigException(exc); + } + catch(IOException exc) { + throw new ConfigException("Error reading config file", exc); + } + finally { + if (inputStream != null) { + try { + inputStream.close(); + } + catch (IOException ioe) { + // ignore this + } + } + } + } + + /** + * The common superclass for all sax event handlers in Ant. Basically + * throws an exception in each method, so subclasses should override + * what they can handle. + * + * Each type of xml element (task, target, etc) in ant will + * have its own subclass of AbstractHandler. + * + * In the constructor, this class takes over the handling of sax + * events from the parent handler, and returns + * control back to the parent in the endElement method. + */ + private class AbstractHandler extends DefaultHandler { + protected ContentHandler parentHandler; + + public AbstractHandler(ContentHandler parentHandler) { + this.parentHandler = parentHandler; + + // Start handling SAX events + try { + saxParser.getXMLReader().setContentHandler(this); + } catch (SAXException e) { + throw new ConfigException("Error getting XMLReader",e); + } + + } + + public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException { + throw new SAXParseException("Unexpected element \"" + tag + "\"", locator); + } + + public void characters(char[] buf, int start, int end) throws SAXParseException { + String s = new String(buf, start, end).trim(); + + if (s.length() > 0) { + throw new SAXParseException("Unexpected text \"" + s + "\"", locator); + } + } + + /** + * Called when this element and all elements nested into it have been + * handeled. + */ + protected void finished() {} + + public void endElement() throws SAXException { + + finished(); + // Let parent resume handling SAX events + saxParser.getXMLReader().setContentHandler(parentHandler); + } + } + + /** + * Handler for the root element. It's only child must be the "mir" element. + */ + private class RootHandler extends DefaultHandler { + + /** + * resolve file: URIs as relative to the build file. + */ + public InputSource resolveEntity(String publicId, + String systemId) { + + + if (systemId.startsWith("file:")) { + String path = systemId.substring(5); + int index = path.indexOf("file:"); + + // we only have to handle these for backward compatibility + // since they are in the FAQ. + while (index != -1) { + path = path.substring(0, index) + path.substring(index + 5); + index = path.indexOf("file:"); + } + + String entitySystemId = path; + index = path.indexOf("%23"); + // convert these to # + while (index != -1) { + path = path.substring(0, index) + "#" + path.substring(index + 3); + index = path.indexOf("%23"); + } + + File file = new File(path); + if (!file.isAbsolute()) { + file = new File(configFileParent, path); + } + + try { + InputSource inputSource = new InputSource(new FileInputStream(file)); + inputSource.setSystemId("file:" + entitySystemId); + return inputSource; + } catch (FileNotFoundException fne) { + System.out.println(file.getAbsolutePath()+" could not be found"); + } + } + // use default if not file or file not found + return null; + } + + public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException { + if (tag.equals("mir")) { + new MirHandler(this).init(tag, attrs); + } else { + throw new SAXParseException("Config file is not of expected XML type", locator); + } + } + + public void setDocumentLocator(Locator locator) { + XmlConfigurator.this.locator = locator; + } + } + + /** + * Handler for the top level "project" element. + */ + private class MirHandler extends AbstractHandler { + public MirHandler(ContentHandler parentHandler) { + super(parentHandler); + } + + public void init(String tag, Attributes attrs) throws SAXParseException { + String name = null; + + for (int i = 0; i < attrs.getLength(); i++) { + String key = attrs.getLocalName(i); + String value = attrs.getValue(i); + + if (key.equals("name")) { + name = value; + } else { + throw new SAXParseException("Unexpected attribute \"" + attrs.getLocalName(i) + "\"", locator); + } + } + + if (name == null) { + throw new SAXParseException("The default attribute of \"name\" is required", + locator); + } + + //MirConfig.setName(name); + + } + + public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException { + if (name.equals("class")) { + handleClassdef(name, attrs); + } else { + throw new SAXParseException("Unexpected element \"" + name + "\"", locator); + } + } + + private void handleClassdef(String name, Attributes attrs) throws SAXParseException { + (new ClassHandler(this)).init(name, attrs); + } + + } + + /** + * Handler for "class" elements. + */ + private class ClassHandler extends AbstractHandler { + + Class classN; + + public ClassHandler(ContentHandler parentHandler) { + super(parentHandler); + } + + public void init(String tag, Attributes attrs) throws SAXParseException { + String name = null; + + for (int i = 0; i < attrs.getLength(); i++) { + String key = attrs.getLocalName(i); + String value = attrs.getValue(i); + + if (key.equals("name")) { + name = value; + } else { + throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator); + } + } + + if (name == null) { + throw new SAXParseException("class element appears without a \"name\" attribute", locator); + } + + try { + classN=Class.forName(name); + } catch (ClassNotFoundException e) { + throw new ConfigException("Error invoking class: \""+name+ + "\"",e); + } + + } + + public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException { + if (name.equals("property")) { + handleProperties(name, attrs); + } else { + throw new SAXParseException("Unexpected element \"" + name + "\"", locator); + } + } + + private void handleProperties(String name, Attributes attrs) throws SAXParseException { + (new PropertiesHandler(this, classN )).init(name, attrs); + } + + } + + /** + * Handler for all property elements. + */ + private class PropertiesHandler extends AbstractHandler { + private Class classN; + + public PropertiesHandler(ContentHandler parentHandler, Class classN) { + super(parentHandler); + + this.classN = classN; + } + + public void init(String tag, Attributes attrs) throws SAXParseException { + String name=null; + String value=null; + + for (int i = 0; i < attrs.getLength(); i++) { + String key = attrs.getLocalName(i); + String v = attrs.getValue(i); + + if (key.equals("name")) { + name = v; + } else if (key.equals("value")) { + value = v; + } else { + throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator); + } + } + + if (name == null) { + throw new SAXParseException("property element appears without a \"name\" attribute", locator); + } + if (value == null) { + throw new SAXParseException("property element appears without a \"value\" attribute", locator); + } + ///// + } + + protected void finished() { + //do the setting here? + } + + } + + private static String capitalize(String name) { + if (name == null || name.length() == 0) { + return name; + } + char chars[] = name.toCharArray(); + chars[0] = Character.toUpperCase(chars[0]); + return new String(chars); + } + +}