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.
26 * that part is from tomcat.
28 * much code is stolen from ant ProjectHelper.java.
30 * @author -mh <heckmann@hbe.ca>
34 public class XmlConfigurator {
36 private static SAXParserFactory parserFactory = null;
38 private SAXParser saxParser;
39 //private Project project;
40 private File configFile;
41 private File configFileParent;
42 private Locator locator;
44 private SaxContext saxContext;
46 XmlMatch requiredXmlMatch[]=new XmlMatch[256]; //maximum amount of rules
47 int requiredXmlMatchCount=0;
48 boolean matched[] = new boolean[256];
51 XmlMatch mustComeFirstMatch[]=new XmlMatch[256]; //maximum amount of rules
52 int comeFirstMatchCount=0;
54 Property comesFirstArr[]=new Property[128];
55 int comesFirstCount=0;
57 Property propertyArr[]=new Property[128];
60 private static XmlConfigurator instance = new XmlConfigurator();
61 public static XmlConfigurator getInstance() { return instance; }
64 * Configures the Project with the contents of the specified XML file.
66 public void configure(File configFile) throws ConfigException {
67 setConfigFile(configFile);
72 * konstruktor. private so no one calls "new" on us.
74 private XmlConfigurator() {}
78 * Constructs a new Ant parser for the specified XML file.
80 private void setConfigFile(File configFile) {
81 this.configFile = new File(configFile.getAbsolutePath());
82 configFileParent = new File(this.configFile.getParent());
83 saxContext = new SaxContext();
87 * Parses the config file.
89 private void parse() throws ConfigException {
90 FileInputStream inputStream = null;
91 InputSource inputSource = null;
94 SAXParserFactory factory = SAXParserFactory.newInstance();
95 saxParser = factory.newSAXParser();
97 String uri = "file:" + configFile.getAbsolutePath().replace('\\', '/');
98 for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) {
99 uri = uri.substring(0, index) + "%23" + uri.substring(index+1);
102 inputStream = new FileInputStream(configFile);
103 inputSource = new InputSource(inputStream);
104 inputSource.setSystemId(uri);
105 saxParser.parse(inputSource, new RootHandler());
106 if(matchedCount < requiredXmlMatchCount) {
107 for( int i=0; i<requiredXmlMatchCount; i++) {
109 throw new ConfigException("Error parsing config file, missing required element: "+requiredXmlMatch[i].toString());
113 for(int i=0; i<comesFirstCount;i++) {
114 comesFirstArr[i].set();
116 for(int i=0; i<propertyCount;i++) {
117 propertyArr[i].set();
118 System.out.println("about to set: "+i);
120 } catch (Exception e) {
121 throw new SAXParseException(e.toString(), locator);
124 catch(ParserConfigurationException exc) {
125 throw new ConfigException("Parser has not been configured correctly", exc);
127 catch(SAXParseException exc) {
129 new Location(configFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
131 Throwable t = exc.getException();
132 if (t instanceof ConfigException) {
133 ConfigException be = (ConfigException) t;
134 if (be.getLocation() == Location.UNKNOWN_LOCATION) {
135 be.setLocation(location);
140 throw new ConfigException(exc.getMessage(), t, location);
142 catch(SAXException exc) {
143 Throwable t = exc.getException();
144 if (t instanceof ConfigException) {
145 throw (ConfigException) t;
147 throw new ConfigException(exc.getMessage(), t);
149 catch(FileNotFoundException exc) {
150 throw new ConfigException(exc);
152 catch(IOException exc) {
153 throw new ConfigException("Error reading config file", exc);
156 if (inputStream != null) {
160 catch (IOException ioe) {
168 * The common superclass for all sax event handlers in Ant. Basically
169 * throws an exception in each method, so subclasses should override
170 * what they can handle.
172 * Each type of xml element (task, target, etc) in ant will
173 * have its own subclass of AbstractHandler.
175 * In the constructor, this class takes over the handling of sax
176 * events from the parent handler, and returns
177 * control back to the parent in the endElement method.
179 private class AbstractHandler extends DefaultHandler {
180 protected ContentHandler parentHandler;
182 public AbstractHandler(ContentHandler parentHandler) {
183 this.parentHandler = parentHandler;
185 // Start handling SAX events
187 saxParser.getXMLReader().setContentHandler(this);
188 } catch (SAXException e) {
189 throw new ConfigException("Error getting XMLReader",e);
194 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
195 throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
198 public void characters(char[] buf, int start, int end) throws SAXParseException {
199 String s = new String(buf, start, end).trim();
201 if (s.length() > 0) {
202 throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
207 * Called when this element and all elements nested into it have been
210 protected void finished() {}
212 public void endElement(String uri, String tag, String qName) throws SAXException {
214 // Let parent resume handling SAX events
215 saxParser.getXMLReader().setContentHandler(parentHandler);
220 * Handler for the root element. It's only child must be the "mir" element.
222 private class RootHandler extends DefaultHandler {
225 * resolve file: URIs as relative to the build file.
227 public InputSource resolveEntity(String publicId,
231 if (systemId.startsWith("file:")) {
232 String path = systemId.substring(5);
233 int index = path.indexOf("file:");
235 // we only have to handle these for backward compatibility
236 // since they are in the FAQ.
237 while (index != -1) {
238 path = path.substring(0, index) + path.substring(index + 5);
239 index = path.indexOf("file:");
242 String entitySystemId = path;
243 index = path.indexOf("%23");
244 // convert these to #
245 while (index != -1) {
246 path = path.substring(0, index) + "#" + path.substring(index + 3);
247 index = path.indexOf("%23");
250 File file = new File(path);
251 if (!file.isAbsolute()) {
252 file = new File(configFileParent, path);
256 InputSource inputSource = new InputSource(new FileInputStream(file));
257 inputSource.setSystemId("file:" + entitySystemId);
259 } catch (FileNotFoundException fne) {
260 System.out.println(file.getAbsolutePath()+" could not be found");
263 // use default if not file or file not found
267 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
268 if (tag.equals("mir")) {
269 new MirHandler(this).init(tag, attrs);
271 throw new SAXParseException("Config file is not of expected XML type", locator);
275 public void setDocumentLocator(Locator locator) {
276 XmlConfigurator.this.locator = locator;
281 * Handler for the top level "project" element.
283 private class MirHandler extends AbstractHandler {
284 public MirHandler(ContentHandler parentHandler) {
285 super(parentHandler);
288 public void init(String tag, Attributes attrs) throws SAXParseException {
291 for (int i = 0; i < attrs.getLength(); i++) {
292 String key = attrs.getLocalName(i);
293 String value = attrs.getValue(i);
295 if (key.equals("name")) {
298 throw new SAXParseException("Unexpected attribute \"" + attrs.getLocalName(i) + "\"", locator);
303 throw new SAXParseException("The default attribute of \"name\" is required",
307 saxContext.push(tag);
308 matchedCount += checkRequiredTag(saxContext);
310 //MirConfig.setName(name);
314 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
315 if (name.equals("class")) {
316 handleClassdef(name, attrs);
318 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
322 public void finished() {
323 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
327 private void handleClassdef(String name, Attributes attrs) throws SAXParseException {
328 (new ClassHandler(this)).init(name, attrs);
334 * Handler for "class" elements.
336 private class ClassHandler extends AbstractHandler {
340 public ClassHandler(ContentHandler parentHandler) {
341 super(parentHandler);
344 public void init(String tag, Attributes attrs) throws SAXParseException {
347 for (int i = 0; i < attrs.getLength(); i++) {
348 String key = attrs.getLocalName(i);
349 String value = attrs.getValue(i);
351 if (key.equals("name")) {
354 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
359 throw new SAXParseException("class element appears without a \"name\" attribute", locator);
362 saxContext.push(tag+":"+name);
363 matchedCount += checkRequiredTag(saxContext);
365 classN=Class.forName(name, false, this.getClass().getClassLoader());
366 } catch (ClassNotFoundException e) {
367 throw new ConfigException("Error invoking class: \""+name+
373 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
374 if (name.equals("property")) {
375 handleProperties(name, attrs);
377 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
381 public void finished() {
382 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
383 System.out.println("COUNT "+saxContext.getTagCount());
387 private void handleProperties(String name, Attributes attrs) throws SAXParseException {
388 (new PropertiesHandler(this, classN )).init(name, attrs);
394 * Handler for all property elements.
396 private class PropertiesHandler extends AbstractHandler {
397 private Class classN;
399 public PropertiesHandler(ContentHandler parentHandler, Class classN) {
400 super(parentHandler);
402 this.classN = classN;
405 public void init(String tag, Attributes attrs) throws SAXParseException {
409 for (int i = 0; i < attrs.getLength(); i++) {
410 String key = attrs.getLocalName(i);
411 String v = attrs.getValue(i);
413 if (key.equals("name")) {
415 } else if (key.equals("value")) {
418 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
423 throw new SAXParseException("property element appears without a \"name\" attribute", locator);
426 throw new SAXParseException("property element appears without a \"value\" attribute", locator);
428 saxContext.push(tag+":"+name);
429 matchedCount += checkRequiredTag(saxContext);
431 //finally add it to the lists
432 //to be processed later
433 if (checkComesFirstTag(saxContext)) {
434 comesFirstArr[comesFirstCount]=new Property(classN, name, value);
437 propertyArr[propertyCount]=new Property(classN, name, value);
442 protected void finished() {
443 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
444 System.out.println("COUNT "+saxContext.getTagCount());
450 public void addComesFirstTag(String xmlPath) {
451 mustComeFirstMatch[comeFirstMatchCount]=new XmlMatch(xmlPath);
452 comeFirstMatchCount++;
455 private boolean checkComesFirstTag(SaxContext ctx) {
456 for( int i=0; i<comeFirstMatchCount; i++ ) {
457 if( mustComeFirstMatch[i].match(ctx) ) {
464 public void addRequiredTag(String xmlPath) {
465 requiredXmlMatch[requiredXmlMatchCount]=new XmlMatch(xmlPath);
466 matched[requiredXmlMatchCount]=false;
467 requiredXmlMatchCount++;
470 private int checkRequiredTag(SaxContext ctx) {
472 for( int i=0; i<requiredXmlMatchCount; i++ ) {
473 if( requiredXmlMatch[i].match(ctx) ) {
482 private static String capitalize(String name) {
483 if (name == null || name.length() == 0) {
486 char chars[] = name.toCharArray();
487 chars[0] = Character.toUpperCase(chars[0]);
488 return new String(chars);
491 private class Property {
496 public Property( Class classN, String name, String value) {
502 /** Find a method with the right name
503 * If found, call the method ( if param is int or boolean we'll convert
504 * value to the right type before) - that means you can have setDebug(1).
506 public void set() throws Exception {
508 String setter= "set" +capitalize(name);
511 Method methods[]=classN.getMethods();
512 Method setPropertyMethod=null;
514 // First, the ideal case - a setFoo( String ) method
515 for( int i=0; i< methods.length; i++ ) {
516 Class paramT[]=methods[i].getParameterTypes();
517 if( setter.equals( methods[i].getName() ) &&
518 paramT.length == 1 &&
519 "java.lang.String".equals( paramT[0].getName())) {
521 methods[i].invoke( null, new Object[] { value } );
526 // Try a setFoo ( int ), (float) or ( boolean )
527 for( int i=0; i< methods.length; i++ ) {
529 if( setter.equals( methods[i].getName() ) &&
530 methods[i].getParameterTypes().length == 1) {
532 // match - find the type and invoke it
533 Class paramType=methods[i].getParameterTypes()[0];
534 Object params[]=new Object[1];
535 if ("java.lang.Integer".equals( paramType.getName()) ||
536 "int".equals( paramType.getName())) {
538 params[0]=new Integer(value);
539 } catch( NumberFormatException ex ) {ok=false;}
540 } else if ("java.lang.Float".equals( paramType.getName()) ||
541 "float".equals( paramType.getName())) {
543 params[0]=new Float(value);
544 } catch( NumberFormatException ex ) {ok=false;}
545 } else if ("java.lang.Boolean".equals( paramType.getName()) ||
546 "boolean".equals( paramType.getName())) {
547 params[0]=new Boolean(value);
549 throw new Exception("Unknown type " + paramType.getName() + "for property \""+name+"\"with value \""+value+"\"");
553 System.out.println("XXX: " + methods[i] + " " + classN + " " + params[0] );
554 methods[i].invoke( null, params );
560 //if we got this far it means we were not successful in setting the
562 throw new Exception("Count not find method \""+setter+"\" in Class \""+classN.getName()+"\" in order to set property \""+name+"\"");
564 } catch( SecurityException ex1 ) {
565 throw new Exception("SecurityException for " + classN.getName() + " " + name + "=" + value +")" );
566 //if( ctx.getDebug() > 1 ) ex1.printStackTrace();
567 } catch (IllegalAccessException iae) {
568 throw new Exception("IllegalAccessException for " + classN.getName() + " " + name + "=" + value +")" );
569 //if( ctx.getDebug() > 1 ) iae.printStackTrace();
570 } catch (InvocationTargetException ie) {
571 throw new Exception("InvocationTargetException for " + classN.getName() + " " + name + "=" + value +")" );
572 //if( ctx.getDebug() > 1 ) ie.printStackTrace();