5 import org.xml.sax.helpers.DefaultHandler;
7 import javax.xml.parsers.ParserConfigurationException;
8 import javax.xml.parsers.SAXParser;
9 import javax.xml.parsers.SAXParserFactory;
12 import mir.misc.ConfigException;
13 import mir.misc.Location;
16 * Configures a based on
19 * Based and inspired by ant's XmlConfigurator.java
20 * It's a simplified version of the ant parser with
21 * the addition of calling set* methods for defined
22 * classes as well as the inclusion of a method to
23 * add parameters (nested tags) that are required.
24 * (the addRequired method) in the config file.
26 * @author -mh <heckmann@hbe.ca>
30 public class XmlConfigurator {
32 private static SAXParserFactory parserFactory = null;
34 private SAXParser saxParser;
35 //private Project project;
36 private File configFile;
37 private File configFileParent;
38 private Locator locator;
40 //private SaxContext saxContext;
43 * Configures the Project with the contents of the specified XML file.
45 public static void configure(File configFile) throws ConfigException {
46 new XmlConfigurator(configFile).parse();
50 * Constructs a new Ant parser for the specified XML file.
52 private XmlConfigurator(File configFile) {
53 this.configFile = new File(configFile.getAbsolutePath());
54 configFileParent = new File(this.configFile.getParent());
58 * Parses the config file.
60 private void parse() throws ConfigException {
61 FileInputStream inputStream = null;
62 InputSource inputSource = null;
65 SAXParserFactory factory = SAXParserFactory.newInstance();
66 saxParser = factory.newSAXParser();
68 String uri = "file:" + configFile.getAbsolutePath().replace('\\', '/');
69 for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) {
70 uri = uri.substring(0, index) + "%23" + uri.substring(index+1);
73 inputStream = new FileInputStream(configFile);
74 inputSource = new InputSource(inputStream);
75 inputSource.setSystemId(uri);
76 saxParser.parse(inputSource, new RootHandler());
78 catch(ParserConfigurationException exc) {
79 throw new ConfigException("Parser has not been configured correctly", exc);
81 catch(SAXParseException exc) {
83 new Location(configFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
85 Throwable t = exc.getException();
86 if (t instanceof ConfigException) {
87 ConfigException be = (ConfigException) t;
88 if (be.getLocation() == Location.UNKNOWN_LOCATION) {
89 be.setLocation(location);
94 throw new ConfigException(exc.getMessage(), t, location);
96 catch(SAXException exc) {
97 Throwable t = exc.getException();
98 if (t instanceof ConfigException) {
99 throw (ConfigException) t;
101 throw new ConfigException(exc.getMessage(), t);
103 catch(FileNotFoundException exc) {
104 throw new ConfigException(exc);
106 catch(IOException exc) {
107 throw new ConfigException("Error reading config file", exc);
110 if (inputStream != null) {
114 catch (IOException ioe) {
122 * The common superclass for all sax event handlers in Ant. Basically
123 * throws an exception in each method, so subclasses should override
124 * what they can handle.
126 * Each type of xml element (task, target, etc) in ant will
127 * have its own subclass of AbstractHandler.
129 * In the constructor, this class takes over the handling of sax
130 * events from the parent handler, and returns
131 * control back to the parent in the endElement method.
133 private class AbstractHandler extends DefaultHandler {
134 protected ContentHandler parentHandler;
136 public AbstractHandler(ContentHandler parentHandler) {
137 this.parentHandler = parentHandler;
139 // Start handling SAX events
141 saxParser.getXMLReader().setContentHandler(this);
142 } catch (SAXException e) {
143 throw new ConfigException("Error getting XMLReader",e);
148 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
149 throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
152 public void characters(char[] buf, int start, int end) throws SAXParseException {
153 String s = new String(buf, start, end).trim();
155 if (s.length() > 0) {
156 throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
161 * Called when this element and all elements nested into it have been
164 protected void finished() {}
166 public void endElement() throws SAXException {
169 // Let parent resume handling SAX events
170 saxParser.getXMLReader().setContentHandler(parentHandler);
175 * Handler for the root element. It's only child must be the "mir" element.
177 private class RootHandler extends DefaultHandler {
180 * resolve file: URIs as relative to the build file.
182 public InputSource resolveEntity(String publicId,
186 if (systemId.startsWith("file:")) {
187 String path = systemId.substring(5);
188 int index = path.indexOf("file:");
190 // we only have to handle these for backward compatibility
191 // since they are in the FAQ.
192 while (index != -1) {
193 path = path.substring(0, index) + path.substring(index + 5);
194 index = path.indexOf("file:");
197 String entitySystemId = path;
198 index = path.indexOf("%23");
199 // convert these to #
200 while (index != -1) {
201 path = path.substring(0, index) + "#" + path.substring(index + 3);
202 index = path.indexOf("%23");
205 File file = new File(path);
206 if (!file.isAbsolute()) {
207 file = new File(configFileParent, path);
211 InputSource inputSource = new InputSource(new FileInputStream(file));
212 inputSource.setSystemId("file:" + entitySystemId);
214 } catch (FileNotFoundException fne) {
215 System.out.println(file.getAbsolutePath()+" could not be found");
218 // use default if not file or file not found
222 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
223 if (tag.equals("mir")) {
224 new MirHandler(this).init(tag, attrs);
226 throw new SAXParseException("Config file is not of expected XML type", locator);
230 public void setDocumentLocator(Locator locator) {
231 XmlConfigurator.this.locator = locator;
236 * Handler for the top level "project" element.
238 private class MirHandler extends AbstractHandler {
239 public MirHandler(ContentHandler parentHandler) {
240 super(parentHandler);
243 public void init(String tag, Attributes attrs) throws SAXParseException {
246 for (int i = 0; i < attrs.getLength(); i++) {
247 String key = attrs.getLocalName(i);
248 String value = attrs.getValue(i);
250 if (key.equals("name")) {
253 throw new SAXParseException("Unexpected attribute \"" + attrs.getLocalName(i) + "\"", locator);
258 throw new SAXParseException("The default attribute of \"name\" is required",
262 //MirConfig.setName(name);
266 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
267 if (name.equals("class")) {
268 handleClassdef(name, attrs);
270 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
274 private void handleClassdef(String name, Attributes attrs) throws SAXParseException {
275 (new ClassHandler(this)).init(name, attrs);
281 * Handler for "class" elements.
283 private class ClassHandler extends AbstractHandler {
287 public ClassHandler(ContentHandler parentHandler) {
288 super(parentHandler);
291 public void init(String tag, Attributes attrs) throws SAXParseException {
294 for (int i = 0; i < attrs.getLength(); i++) {
295 String key = attrs.getLocalName(i);
296 String value = attrs.getValue(i);
298 if (key.equals("name")) {
301 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
306 throw new SAXParseException("class element appears without a \"name\" attribute", locator);
310 classN=Class.forName(name);
311 } catch (ClassNotFoundException e) {
312 throw new ConfigException("Error invoking class: \""+name+
318 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
319 if (name.equals("property")) {
320 handleProperties(name, attrs);
322 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
326 private void handleProperties(String name, Attributes attrs) throws SAXParseException {
327 (new PropertiesHandler(this, classN )).init(name, attrs);
333 * Handler for all property elements.
335 private class PropertiesHandler extends AbstractHandler {
336 private Class classN;
338 public PropertiesHandler(ContentHandler parentHandler, Class classN) {
339 super(parentHandler);
341 this.classN = classN;
344 public void init(String tag, Attributes attrs) throws SAXParseException {
348 for (int i = 0; i < attrs.getLength(); i++) {
349 String key = attrs.getLocalName(i);
350 String v = attrs.getValue(i);
352 if (key.equals("name")) {
354 } else if (key.equals("value")) {
357 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
362 throw new SAXParseException("property element appears without a \"name\" attribute", locator);
365 throw new SAXParseException("property element appears without a \"value\" attribute", locator);
370 protected void finished() {
371 //do the setting here?
376 private static String capitalize(String name) {
377 if (name == null || name.length() == 0) {
380 char chars[] = name.toCharArray();
381 chars[0] = Character.toUpperCase(chars[0]);
382 return new String(chars);