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;
42 XmlMatch requiredXmlMatch[]=new XmlMatch[256]; //maximum amount of rules
43 int requiredXmlMatchCount=0;
44 boolean matched[] = new boolean[256];
47 private static XmlConfigurator instance = new XmlConfigurator();
48 public static XmlConfigurator getInstance() { return instance; }
51 * Configures the Project with the contents of the specified XML file.
53 public void configure(File configFile) throws ConfigException {
54 setConfigFile(configFile);
59 * konstruktor. private so no one calls "new" on us.
61 private XmlConfigurator() {}
65 * Constructs a new Ant parser for the specified XML file.
67 private void setConfigFile(File configFile) {
68 this.configFile = new File(configFile.getAbsolutePath());
69 configFileParent = new File(this.configFile.getParent());
70 saxContext = new SaxContext();
74 * Parses the config file.
76 private void parse() throws ConfigException {
77 FileInputStream inputStream = null;
78 InputSource inputSource = null;
81 SAXParserFactory factory = SAXParserFactory.newInstance();
82 saxParser = factory.newSAXParser();
84 String uri = "file:" + configFile.getAbsolutePath().replace('\\', '/');
85 for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) {
86 uri = uri.substring(0, index) + "%23" + uri.substring(index+1);
89 inputStream = new FileInputStream(configFile);
90 inputSource = new InputSource(inputStream);
91 inputSource.setSystemId(uri);
92 saxParser.parse(inputSource, new RootHandler());
93 if(matchedCount < requiredXmlMatchCount) {
94 for( int i=0; i<requiredXmlMatchCount; i++) {
96 throw new ConfigException("Error parsing config file, missing required element: "+requiredXmlMatch[i].toString());
100 catch(ParserConfigurationException exc) {
101 throw new ConfigException("Parser has not been configured correctly", exc);
103 catch(SAXParseException exc) {
105 new Location(configFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
107 Throwable t = exc.getException();
108 if (t instanceof ConfigException) {
109 ConfigException be = (ConfigException) t;
110 if (be.getLocation() == Location.UNKNOWN_LOCATION) {
111 be.setLocation(location);
116 throw new ConfigException(exc.getMessage(), t, location);
118 catch(SAXException exc) {
119 Throwable t = exc.getException();
120 if (t instanceof ConfigException) {
121 throw (ConfigException) t;
123 throw new ConfigException(exc.getMessage(), t);
125 catch(FileNotFoundException exc) {
126 throw new ConfigException(exc);
128 catch(IOException exc) {
129 throw new ConfigException("Error reading config file", exc);
132 if (inputStream != null) {
136 catch (IOException ioe) {
144 * The common superclass for all sax event handlers in Ant. Basically
145 * throws an exception in each method, so subclasses should override
146 * what they can handle.
148 * Each type of xml element (task, target, etc) in ant will
149 * have its own subclass of AbstractHandler.
151 * In the constructor, this class takes over the handling of sax
152 * events from the parent handler, and returns
153 * control back to the parent in the endElement method.
155 private class AbstractHandler extends DefaultHandler {
156 protected ContentHandler parentHandler;
158 public AbstractHandler(ContentHandler parentHandler) {
159 this.parentHandler = parentHandler;
161 // Start handling SAX events
163 saxParser.getXMLReader().setContentHandler(this);
164 } catch (SAXException e) {
165 throw new ConfigException("Error getting XMLReader",e);
170 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
171 throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
174 public void characters(char[] buf, int start, int end) throws SAXParseException {
175 String s = new String(buf, start, end).trim();
177 if (s.length() > 0) {
178 throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
183 * Called when this element and all elements nested into it have been
186 protected void finished() {}
188 public void endElement(String uri, String tag, String qName) throws SAXException {
190 // Let parent resume handling SAX events
191 saxParser.getXMLReader().setContentHandler(parentHandler);
196 * Handler for the root element. It's only child must be the "mir" element.
198 private class RootHandler extends DefaultHandler {
201 * resolve file: URIs as relative to the build file.
203 public InputSource resolveEntity(String publicId,
207 if (systemId.startsWith("file:")) {
208 String path = systemId.substring(5);
209 int index = path.indexOf("file:");
211 // we only have to handle these for backward compatibility
212 // since they are in the FAQ.
213 while (index != -1) {
214 path = path.substring(0, index) + path.substring(index + 5);
215 index = path.indexOf("file:");
218 String entitySystemId = path;
219 index = path.indexOf("%23");
220 // convert these to #
221 while (index != -1) {
222 path = path.substring(0, index) + "#" + path.substring(index + 3);
223 index = path.indexOf("%23");
226 File file = new File(path);
227 if (!file.isAbsolute()) {
228 file = new File(configFileParent, path);
232 InputSource inputSource = new InputSource(new FileInputStream(file));
233 inputSource.setSystemId("file:" + entitySystemId);
235 } catch (FileNotFoundException fne) {
236 System.out.println(file.getAbsolutePath()+" could not be found");
239 // use default if not file or file not found
243 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
244 if (tag.equals("mir")) {
245 new MirHandler(this).init(tag, attrs);
247 throw new SAXParseException("Config file is not of expected XML type", locator);
251 public void setDocumentLocator(Locator locator) {
252 XmlConfigurator.this.locator = locator;
257 * Handler for the top level "project" element.
259 private class MirHandler extends AbstractHandler {
260 public MirHandler(ContentHandler parentHandler) {
261 super(parentHandler);
264 public void init(String tag, Attributes attrs) throws SAXParseException {
267 for (int i = 0; i < attrs.getLength(); i++) {
268 String key = attrs.getLocalName(i);
269 String value = attrs.getValue(i);
271 if (key.equals("name")) {
274 throw new SAXParseException("Unexpected attribute \"" + attrs.getLocalName(i) + "\"", locator);
279 throw new SAXParseException("The default attribute of \"name\" is required",
283 saxContext.push(tag);
284 matchedCount += checkRequiredTag(saxContext);
286 //MirConfig.setName(name);
290 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
291 if (name.equals("class")) {
292 handleClassdef(name, attrs);
294 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
298 public void finished() {
299 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
303 private void handleClassdef(String name, Attributes attrs) throws SAXParseException {
304 (new ClassHandler(this)).init(name, attrs);
310 * Handler for "class" elements.
312 private class ClassHandler extends AbstractHandler {
316 public ClassHandler(ContentHandler parentHandler) {
317 super(parentHandler);
320 public void init(String tag, Attributes attrs) throws SAXParseException {
323 for (int i = 0; i < attrs.getLength(); i++) {
324 String key = attrs.getLocalName(i);
325 String value = attrs.getValue(i);
327 if (key.equals("name")) {
330 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
335 throw new SAXParseException("class element appears without a \"name\" attribute", locator);
338 saxContext.push(tag+":"+name);
339 matchedCount += checkRequiredTag(saxContext);
341 classN=Class.forName(name);
342 } catch (ClassNotFoundException e) {
343 throw new ConfigException("Error invoking class: \""+name+
349 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
350 if (name.equals("property")) {
351 handleProperties(name, attrs);
353 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
357 public void finished() {
358 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
359 System.out.println("COUNT "+saxContext.getTagCount());
363 private void handleProperties(String name, Attributes attrs) throws SAXParseException {
364 (new PropertiesHandler(this, classN )).init(name, attrs);
370 * Handler for all property elements.
372 private class PropertiesHandler extends AbstractHandler {
373 private Class classN;
375 public PropertiesHandler(ContentHandler parentHandler, Class classN) {
376 super(parentHandler);
378 this.classN = classN;
381 public void init(String tag, Attributes attrs) throws SAXParseException {
385 for (int i = 0; i < attrs.getLength(); i++) {
386 String key = attrs.getLocalName(i);
387 String v = attrs.getValue(i);
389 if (key.equals("name")) {
391 } else if (key.equals("value")) {
394 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
399 throw new SAXParseException("property element appears without a \"name\" attribute", locator);
402 throw new SAXParseException("property element appears without a \"value\" attribute", locator);
404 saxContext.push(tag+":"+name);
405 matchedCount += checkRequiredTag(saxContext);
409 protected void finished() {
410 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
411 System.out.println("COUNT "+saxContext.getTagCount());
417 public void addRequiredTag(String xmlPath) {
418 requiredXmlMatch[requiredXmlMatchCount]=new XmlMatch(xmlPath);
419 matched[requiredXmlMatchCount]=false;
420 requiredXmlMatchCount++;
423 private int checkRequiredTag(SaxContext ctx) {
425 for( int i=0; i<requiredXmlMatchCount; i++ ) {
426 if( requiredXmlMatch[i].match(ctx) ) {
435 private static String capitalize(String name) {
436 if (name == null || name.length() == 0) {
439 char chars[] = name.toCharArray();
440 chars[0] = Character.toUpperCase(chars[0]);
441 return new String(chars);