5 import java.lang.reflect.*;
6 import org.xml.sax.helpers.DefaultHandler;
8 import javax.xml.parsers.ParserConfigurationException;
9 import javax.xml.parsers.SAXParser;
10 import javax.xml.parsers.SAXParserFactory;
13 import mir.misc.ConfigException;
14 import mir.misc.Location;
17 * Configures a based on
20 * Based and inspired by ant's XmlConfigurator.java
21 * It's a simplified version of the ant parser with
22 * the addition of calling set* methods for defined
23 * classes as well as the inclusion of a method to
24 * add parameters (nested tags) that are required.
25 * (the addRequired method) in the config file.
27 * @author -mh <heckmann@hbe.ca>
31 public class XmlConfigurator {
33 private static SAXParserFactory parserFactory = null;
35 private SAXParser saxParser;
36 //private Project project;
37 private File configFile;
38 private File configFileParent;
39 private Locator locator;
41 private SaxContext saxContext;
43 XmlMatch requiredXmlMatch[]=new XmlMatch[256]; //maximum amount of rules
44 int requiredXmlMatchCount=0;
45 boolean matched[] = new boolean[256];
48 private static XmlConfigurator instance = new XmlConfigurator();
49 public static XmlConfigurator getInstance() { return instance; }
52 * Configures the Project with the contents of the specified XML file.
54 public void configure(File configFile) throws ConfigException {
55 setConfigFile(configFile);
60 * konstruktor. private so no one calls "new" on us.
62 private XmlConfigurator() {}
66 * Constructs a new Ant parser for the specified XML file.
68 private void setConfigFile(File configFile) {
69 this.configFile = new File(configFile.getAbsolutePath());
70 configFileParent = new File(this.configFile.getParent());
71 saxContext = new SaxContext();
75 * Parses the config file.
77 private void parse() throws ConfigException {
78 FileInputStream inputStream = null;
79 InputSource inputSource = null;
82 SAXParserFactory factory = SAXParserFactory.newInstance();
83 saxParser = factory.newSAXParser();
85 String uri = "file:" + configFile.getAbsolutePath().replace('\\', '/');
86 for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) {
87 uri = uri.substring(0, index) + "%23" + uri.substring(index+1);
90 inputStream = new FileInputStream(configFile);
91 inputSource = new InputSource(inputStream);
92 inputSource.setSystemId(uri);
93 saxParser.parse(inputSource, new RootHandler());
94 if(matchedCount < requiredXmlMatchCount) {
95 for( int i=0; i<requiredXmlMatchCount; i++) {
97 throw new ConfigException("Error parsing config file, missing required element: "+requiredXmlMatch[i].toString());
101 catch(ParserConfigurationException exc) {
102 throw new ConfigException("Parser has not been configured correctly", exc);
104 catch(SAXParseException exc) {
106 new Location(configFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
108 Throwable t = exc.getException();
109 if (t instanceof ConfigException) {
110 ConfigException be = (ConfigException) t;
111 if (be.getLocation() == Location.UNKNOWN_LOCATION) {
112 be.setLocation(location);
117 throw new ConfigException(exc.getMessage(), t, location);
119 catch(SAXException exc) {
120 Throwable t = exc.getException();
121 if (t instanceof ConfigException) {
122 throw (ConfigException) t;
124 throw new ConfigException(exc.getMessage(), t);
126 catch(FileNotFoundException exc) {
127 throw new ConfigException(exc);
129 catch(IOException exc) {
130 throw new ConfigException("Error reading config file", exc);
133 if (inputStream != null) {
137 catch (IOException ioe) {
145 * The common superclass for all sax event handlers in Ant. Basically
146 * throws an exception in each method, so subclasses should override
147 * what they can handle.
149 * Each type of xml element (task, target, etc) in ant will
150 * have its own subclass of AbstractHandler.
152 * In the constructor, this class takes over the handling of sax
153 * events from the parent handler, and returns
154 * control back to the parent in the endElement method.
156 private class AbstractHandler extends DefaultHandler {
157 protected ContentHandler parentHandler;
159 public AbstractHandler(ContentHandler parentHandler) {
160 this.parentHandler = parentHandler;
162 // Start handling SAX events
164 saxParser.getXMLReader().setContentHandler(this);
165 } catch (SAXException e) {
166 throw new ConfigException("Error getting XMLReader",e);
171 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
172 throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
175 public void characters(char[] buf, int start, int end) throws SAXParseException {
176 String s = new String(buf, start, end).trim();
178 if (s.length() > 0) {
179 throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
184 * Called when this element and all elements nested into it have been
187 protected void finished() {}
189 public void endElement(String uri, String tag, String qName) throws SAXException {
191 // Let parent resume handling SAX events
192 saxParser.getXMLReader().setContentHandler(parentHandler);
197 * Handler for the root element. It's only child must be the "mir" element.
199 private class RootHandler extends DefaultHandler {
202 * resolve file: URIs as relative to the build file.
204 public InputSource resolveEntity(String publicId,
208 if (systemId.startsWith("file:")) {
209 String path = systemId.substring(5);
210 int index = path.indexOf("file:");
212 // we only have to handle these for backward compatibility
213 // since they are in the FAQ.
214 while (index != -1) {
215 path = path.substring(0, index) + path.substring(index + 5);
216 index = path.indexOf("file:");
219 String entitySystemId = path;
220 index = path.indexOf("%23");
221 // convert these to #
222 while (index != -1) {
223 path = path.substring(0, index) + "#" + path.substring(index + 3);
224 index = path.indexOf("%23");
227 File file = new File(path);
228 if (!file.isAbsolute()) {
229 file = new File(configFileParent, path);
233 InputSource inputSource = new InputSource(new FileInputStream(file));
234 inputSource.setSystemId("file:" + entitySystemId);
236 } catch (FileNotFoundException fne) {
237 System.out.println(file.getAbsolutePath()+" could not be found");
240 // use default if not file or file not found
244 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
245 if (tag.equals("mir")) {
246 new MirHandler(this).init(tag, attrs);
248 throw new SAXParseException("Config file is not of expected XML type", locator);
252 public void setDocumentLocator(Locator locator) {
253 XmlConfigurator.this.locator = locator;
258 * Handler for the top level "project" element.
260 private class MirHandler extends AbstractHandler {
261 public MirHandler(ContentHandler parentHandler) {
262 super(parentHandler);
265 public void init(String tag, Attributes attrs) throws SAXParseException {
268 for (int i = 0; i < attrs.getLength(); i++) {
269 String key = attrs.getLocalName(i);
270 String value = attrs.getValue(i);
272 if (key.equals("name")) {
275 throw new SAXParseException("Unexpected attribute \"" + attrs.getLocalName(i) + "\"", locator);
280 throw new SAXParseException("The default attribute of \"name\" is required",
284 saxContext.push(tag);
285 matchedCount += checkRequiredTag(saxContext);
287 //MirConfig.setName(name);
291 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
292 if (name.equals("class")) {
293 handleClassdef(name, attrs);
295 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
299 public void finished() {
300 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
304 private void handleClassdef(String name, Attributes attrs) throws SAXParseException {
305 (new ClassHandler(this)).init(name, attrs);
311 * Handler for "class" elements.
313 private class ClassHandler extends AbstractHandler {
317 public ClassHandler(ContentHandler parentHandler) {
318 super(parentHandler);
321 public void init(String tag, Attributes attrs) throws SAXParseException {
324 for (int i = 0; i < attrs.getLength(); i++) {
325 String key = attrs.getLocalName(i);
326 String value = attrs.getValue(i);
328 if (key.equals("name")) {
331 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
336 throw new SAXParseException("class element appears without a \"name\" attribute", locator);
339 saxContext.push(tag+":"+name);
340 matchedCount += checkRequiredTag(saxContext);
342 classN=Class.forName(name);
343 } catch (ClassNotFoundException e) {
344 throw new ConfigException("Error invoking class: \""+name+
350 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
351 if (name.equals("property")) {
352 handleProperties(name, attrs);
354 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
358 public void finished() {
359 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
360 System.out.println("COUNT "+saxContext.getTagCount());
364 private void handleProperties(String name, Attributes attrs) throws SAXParseException {
365 (new PropertiesHandler(this, classN )).init(name, attrs);
371 * Handler for all property elements.
373 private class PropertiesHandler extends AbstractHandler {
374 private Class classN;
376 public PropertiesHandler(ContentHandler parentHandler, Class classN) {
377 super(parentHandler);
379 this.classN = classN;
382 public void init(String tag, Attributes attrs) throws SAXParseException {
386 for (int i = 0; i < attrs.getLength(); i++) {
387 String key = attrs.getLocalName(i);
388 String v = attrs.getValue(i);
390 if (key.equals("name")) {
392 } else if (key.equals("value")) {
395 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
400 throw new SAXParseException("property element appears without a \"name\" attribute", locator);
403 throw new SAXParseException("property element appears without a \"value\" attribute", locator);
405 saxContext.push(tag+":"+name);
406 matchedCount += checkRequiredTag(saxContext);
408 //finally try to set the property
410 setProperty(classN, name, value);
411 } catch (Exception e) {
412 throw new SAXParseException(e.toString(), locator);
416 protected void finished() {
417 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
418 System.out.println("COUNT "+saxContext.getTagCount());
424 public void addRequiredTag(String xmlPath) {
425 requiredXmlMatch[requiredXmlMatchCount]=new XmlMatch(xmlPath);
426 matched[requiredXmlMatchCount]=false;
427 requiredXmlMatchCount++;
430 private int checkRequiredTag(SaxContext ctx) {
432 for( int i=0; i<requiredXmlMatchCount; i++ ) {
433 if( requiredXmlMatch[i].match(ctx) ) {
442 private static String capitalize(String name) {
443 if (name == null || name.length() == 0) {
446 char chars[] = name.toCharArray();
447 chars[0] = Character.toUpperCase(chars[0]);
448 return new String(chars);
451 /** Find a method with the right name
452 * If found, call the method ( if param is int or boolean we'll convert
453 * value to the right type before) - that means you can have setDebug(1).
455 static void setProperty( Class classN, String name, String value )
458 String setter= "set" +capitalize(name);
461 Method methods[]=classN.getMethods();
462 Method setPropertyMethod=null;
464 // First, the ideal case - a setFoo( String ) method
465 for( int i=0; i< methods.length; i++ ) {
466 Class paramT[]=methods[i].getParameterTypes();
467 if( setter.equals( methods[i].getName() ) &&
468 paramT.length == 1 &&
469 "java.lang.String".equals( paramT[0].getName())) {
471 methods[i].invoke( null, new Object[] { value } );
476 // Try a setFoo ( int ) or ( boolean )
477 for( int i=0; i< methods.length; i++ ) {
479 if( setter.equals( methods[i].getName() ) &&
480 methods[i].getParameterTypes().length == 1) {
482 // match - find the type and invoke it
483 Class paramType=methods[i].getParameterTypes()[0];
484 Object params[]=new Object[1];
485 if ("java.lang.Integer".equals( paramType.getName()) ||
486 "int".equals( paramType.getName())) {
488 params[0]=new Integer(value);
489 } catch( NumberFormatException ex ) {ok=false;}
490 } else if ("java.lang.Boolean".equals( paramType.getName()) ||
491 "boolean".equals( paramType.getName())) {
492 params[0]=new Boolean(value);
494 throw new Exception("Unknown type " + paramType.getName() + "for property \""+name+"\"with value \""+value+"\"");
498 System.out.println("XXX: " + methods[i] + " " + classN + " " + params[0] );
499 methods[i].invoke( null, params );
505 //if we got this far it means we were not successful in setting the
507 throw new Exception("Count not find method \""+setter+"\" in Class \""+classN.getName()+"\" in order to set property \""+name+"\"");
509 } catch( SecurityException ex1 ) {
510 throw new Exception("SecurityException for " + classN.getName() + " " + name + "=" + value +")" );
511 //if( ctx.getDebug() > 1 ) ex1.printStackTrace();
512 } catch (IllegalAccessException iae) {
513 throw new Exception("IllegalAccessException for " + classN.getName() + " " + name + "=" + value +")" );
514 //if( ctx.getDebug() > 1 ) iae.printStackTrace();
515 } catch (InvocationTargetException ie) {
516 throw new Exception("InvocationTargetException for " + classN.getName() + " " + name + "=" + value +")" );
517 //if( ctx.getDebug() > 1 ) ie.printStackTrace();