2 * Copyright (C) 2001, 2002 The Mir-coders group
4 * This file is part of Mir.
6 * Mir is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Mir is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Mir; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * In addition, as a special exception, The Mir-coders gives permission to link
21 * the code of this program with the com.oreilly.servlet library, any library
22 * licensed under the Apache Software License, The Sun (tm) Java Advanced
23 * Imaging library (JAI), The Sun JIMI library (or with modified versions of
24 * the above that use the same license as the above), and distribute linked
25 * combinations including the two. You must obey the GNU General Public
26 * License in all respects for all of the code used other than the above
27 * mentioned libraries. If you modify this file, you may extend this exception
28 * to your version of the file, but you are not obligated to do so. If you do
29 * not wish to do so, delete this exception statement from your version.
35 import java.io.FileInputStream;
36 import java.io.FileNotFoundException;
37 import java.io.IOException;
38 import java.lang.reflect.InvocationTargetException;
39 import java.lang.reflect.Method;
41 import javax.xml.parsers.ParserConfigurationException;
42 import javax.xml.parsers.SAXParser;
43 import javax.xml.parsers.SAXParserFactory;
45 import mir.misc.ConfigException;
46 import mir.misc.Location;
48 import org.xml.sax.Attributes;
49 import org.xml.sax.ContentHandler;
50 import org.xml.sax.InputSource;
51 import org.xml.sax.Locator;
52 import org.xml.sax.SAXException;
53 import org.xml.sax.SAXParseException;
54 import org.xml.sax.helpers.DefaultHandler;
57 * Configures a based on
60 * Based and inspired by ant's XmlConfigurator.java
61 * It's a simplified version of the ant parser with
62 * the addition of calling set* methods for defined
63 * classes as well as the inclusion of a method to
64 * add parameters (nested tags) that are required.
65 * (the addRequired method) in the config file.
66 * that part is from tomcat.
68 * much code is stolen from ant ProjectHelper.java.
70 * @author -mh <heckmann@hbe.ca>
74 public class XmlConfigurator {
76 private static SAXParserFactory parserFactory = null;
78 private SAXParser saxParser;
79 //private Project project;
80 private File configFile;
81 private File configFileParent;
82 private Locator locator;
84 private SaxContext saxContext;
86 XmlMatch requiredXmlMatch[]=new XmlMatch[256]; //maximum amount of rules
87 int requiredXmlMatchCount=0;
88 boolean matched[] = new boolean[256];
91 XmlMatch mustComeFirstMatch[]=new XmlMatch[256]; //maximum amount of rules
92 int comeFirstMatchCount=0;
94 Property comesFirstArr[]=new Property[128];
95 int comesFirstCount=0;
97 Property propertyArr[]=new Property[128];
100 private static XmlConfigurator instance = new XmlConfigurator();
101 public static XmlConfigurator getInstance() { return instance; }
104 * Configures the Project with the contents of the specified XML file.
106 public void configure(File configFile) throws ConfigException {
107 setConfigFile(configFile);
112 * konstruktor. private so no one calls "new" on us.
114 private XmlConfigurator() {}
118 * Constructs a new Ant parser for the specified XML file.
120 private void setConfigFile(File configFile) {
121 this.configFile = new File(configFile.getAbsolutePath());
122 configFileParent = new File(this.configFile.getParent());
123 saxContext = new SaxContext();
127 * Parses the config file.
129 private void parse() throws ConfigException {
130 FileInputStream inputStream = null;
131 InputSource inputSource = null;
134 SAXParserFactory factory = SAXParserFactory.newInstance();
135 saxParser = factory.newSAXParser();
137 String uri = "file:" + configFile.getAbsolutePath().replace('\\', '/');
138 for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) {
139 uri = uri.substring(0, index) + "%23" + uri.substring(index+1);
142 inputStream = new FileInputStream(configFile);
143 inputSource = new InputSource(inputStream);
144 inputSource.setSystemId(uri);
145 saxParser.parse(inputSource, new RootHandler());
146 if(matchedCount < requiredXmlMatchCount) {
147 for( int i=0; i<requiredXmlMatchCount; i++) {
149 throw new ConfigException("Error parsing config file, missing required element: "+requiredXmlMatch[i].toString());
153 for(int i=0; i<comesFirstCount;i++) {
154 comesFirstArr[i].set();
156 for(int i=0; i<propertyCount;i++) {
157 propertyArr[i].set();
158 System.out.println("about to set: "+i);
160 } catch (Exception e) {
161 throw new SAXParseException(e.toString(), locator);
164 catch(ParserConfigurationException exc) {
165 throw new ConfigException("Parser has not been configured correctly", exc);
167 catch(SAXParseException exc) {
169 new Location(configFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
171 Throwable t = exc.getException();
172 if (t instanceof ConfigException) {
173 ConfigException be = (ConfigException) t;
174 if (be.getLocation() == Location.UNKNOWN_LOCATION) {
175 be.setLocation(location);
180 throw new ConfigException(exc.getMessage(), t, location);
182 catch(SAXException exc) {
183 Throwable t = exc.getException();
184 if (t instanceof ConfigException) {
185 throw (ConfigException) t;
187 throw new ConfigException(exc.getMessage(), t);
189 catch(FileNotFoundException exc) {
190 throw new ConfigException(exc);
192 catch(IOException exc) {
193 throw new ConfigException("Error reading config file", exc);
196 if (inputStream != null) {
200 catch (IOException ioe) {
208 * The common superclass for all sax event handlers in Ant. Basically
209 * throws an exception in each method, so subclasses should override
210 * what they can handle.
212 * Each type of xml element (task, target, etc) in ant will
213 * have its own subclass of AbstractHandler.
215 * In the constructor, this class takes over the handling of sax
216 * events from the parent handler, and returns
217 * control back to the parent in the endElement method.
219 private class AbstractHandler extends DefaultHandler {
220 protected ContentHandler parentHandler;
222 public AbstractHandler(ContentHandler parentHandler) {
223 this.parentHandler = parentHandler;
225 // Start handling SAX events
227 saxParser.getXMLReader().setContentHandler(this);
228 } catch (SAXException e) {
229 throw new ConfigException("Error getting XMLReader",e);
234 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
235 throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
238 public void characters(char[] buf, int start, int end) throws SAXParseException {
239 String s = new String(buf, start, end).trim();
241 if (s.length() > 0) {
242 throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
247 * Called when this element and all elements nested into it have been
250 protected void finished() {}
252 public void endElement(String uri, String tag, String qName) throws SAXException {
254 // Let parent resume handling SAX events
255 saxParser.getXMLReader().setContentHandler(parentHandler);
260 * Handler for the root element. It's only child must be the "mir" element.
262 private class RootHandler extends DefaultHandler {
265 * resolve file: URIs as relative to the build file.
267 public InputSource resolveEntity(String publicId,
271 if (systemId.startsWith("file:")) {
272 String path = systemId.substring(5);
273 int index = path.indexOf("file:");
275 // we only have to handle these for backward compatibility
276 // since they are in the FAQ.
277 while (index != -1) {
278 path = path.substring(0, index) + path.substring(index + 5);
279 index = path.indexOf("file:");
282 String entitySystemId = path;
283 index = path.indexOf("%23");
284 // convert these to #
285 while (index != -1) {
286 path = path.substring(0, index) + "#" + path.substring(index + 3);
287 index = path.indexOf("%23");
290 File file = new File(path);
291 if (!file.isAbsolute()) {
292 file = new File(configFileParent, path);
296 InputSource inputSource = new InputSource(new FileInputStream(file));
297 inputSource.setSystemId("file:" + entitySystemId);
299 } catch (FileNotFoundException fne) {
300 System.out.println(file.getAbsolutePath()+" could not be found");
303 // use default if not file or file not found
307 public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
308 if (tag.equals("mir")) {
309 new MirHandler(this).init(tag, attrs);
311 throw new SAXParseException("Config file is not of expected XML type", locator);
315 public void setDocumentLocator(Locator locator) {
316 XmlConfigurator.this.locator = locator;
321 * Handler for the top level "project" element.
323 private class MirHandler extends AbstractHandler {
324 public MirHandler(ContentHandler parentHandler) {
325 super(parentHandler);
328 public void init(String tag, Attributes attrs) throws SAXParseException {
331 for (int i = 0; i < attrs.getLength(); i++) {
332 String key = attrs.getLocalName(i);
333 String value = attrs.getValue(i);
335 if (key.equals("name")) {
338 throw new SAXParseException("Unexpected attribute \"" + attrs.getLocalName(i) + "\"", locator);
343 throw new SAXParseException("The default attribute of \"name\" is required",
347 saxContext.push(tag);
348 matchedCount += checkRequiredTag(saxContext);
350 //MirConfig.setName(name);
354 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
355 if (name.equals("class")) {
356 handleClassdef(name, attrs);
358 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
362 public void finished() {
363 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
367 private void handleClassdef(String name, Attributes attrs) throws SAXParseException {
368 (new ClassHandler(this)).init(name, attrs);
374 * Handler for "class" elements.
376 private class ClassHandler extends AbstractHandler {
380 public ClassHandler(ContentHandler parentHandler) {
381 super(parentHandler);
384 public void init(String tag, Attributes attrs) throws SAXParseException {
387 for (int i = 0; i < attrs.getLength(); i++) {
388 String key = attrs.getLocalName(i);
389 String value = attrs.getValue(i);
391 if (key.equals("name")) {
394 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
399 throw new SAXParseException("class element appears without a \"name\" attribute", locator);
402 saxContext.push(tag+":"+name);
403 matchedCount += checkRequiredTag(saxContext);
405 classN=Class.forName(name, false, this.getClass().getClassLoader());
406 } catch (ClassNotFoundException e) {
407 throw new ConfigException("Error invoking class: \""+name+
413 public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
414 if (name.equals("property")) {
415 handleProperties(name, attrs);
417 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
421 public void finished() {
422 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
423 System.out.println("COUNT "+saxContext.getTagCount());
427 private void handleProperties(String name, Attributes attrs) throws SAXParseException {
428 (new PropertiesHandler(this, classN )).init(name, attrs);
434 * Handler for all property elements.
436 private class PropertiesHandler extends AbstractHandler {
437 private Class classN;
439 public PropertiesHandler(ContentHandler parentHandler, Class classN) {
440 super(parentHandler);
442 this.classN = classN;
445 public void init(String tag, Attributes attrs) throws SAXParseException {
449 for (int i = 0; i < attrs.getLength(); i++) {
450 String key = attrs.getLocalName(i);
451 String v = attrs.getValue(i);
453 if (key.equals("name")) {
455 } else if (key.equals("value")) {
458 throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
463 throw new SAXParseException("property element appears without a \"name\" attribute", locator);
466 throw new SAXParseException("property element appears without a \"value\" attribute", locator);
468 saxContext.push(tag+":"+name);
469 matchedCount += checkRequiredTag(saxContext);
471 //finally add it to the lists
472 //to be processed later
473 if (checkComesFirstTag(saxContext)) {
474 comesFirstArr[comesFirstCount]=new Property(classN, name, value);
477 propertyArr[propertyCount]=new Property(classN, name, value);
482 protected void finished() {
483 System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
484 System.out.println("COUNT "+saxContext.getTagCount());
490 public void addComesFirstTag(String xmlPath) {
491 mustComeFirstMatch[comeFirstMatchCount]=new XmlMatch(xmlPath);
492 comeFirstMatchCount++;
495 private boolean checkComesFirstTag(SaxContext ctx) {
496 for( int i=0; i<comeFirstMatchCount; i++ ) {
497 if( mustComeFirstMatch[i].match(ctx) ) {
504 public void addRequiredTag(String xmlPath) {
505 requiredXmlMatch[requiredXmlMatchCount]=new XmlMatch(xmlPath);
506 matched[requiredXmlMatchCount]=false;
507 requiredXmlMatchCount++;
510 private int checkRequiredTag(SaxContext ctx) {
512 for( int i=0; i<requiredXmlMatchCount; i++ ) {
513 if( requiredXmlMatch[i].match(ctx) ) {
522 private static String capitalize(String name) {
523 if (name == null || name.length() == 0) {
526 char chars[] = name.toCharArray();
527 chars[0] = Character.toUpperCase(chars[0]);
528 return new String(chars);
531 private class Property {
536 public Property( Class classN, String name, String value) {
542 /** Find a method with the right name
543 * If found, call the method ( if param is int or boolean we'll convert
544 * value to the right type before) - that means you can have setDebug(1).
546 public void set() throws Exception {
548 String setter= "set" +capitalize(name);
551 Method methods[]=classN.getMethods();
552 Method setPropertyMethod=null;
554 // First, the ideal case - a setFoo( String ) method
555 for( int i=0; i< methods.length; i++ ) {
556 Class paramT[]=methods[i].getParameterTypes();
557 if( setter.equals( methods[i].getName() ) &&
558 paramT.length == 1 &&
559 "java.lang.String".equals( paramT[0].getName())) {
561 methods[i].invoke( null, new Object[] { value } );
566 // Try a setFoo ( int ), (float) or ( boolean )
567 for( int i=0; i< methods.length; i++ ) {
569 if( setter.equals( methods[i].getName() ) &&
570 methods[i].getParameterTypes().length == 1) {
572 // match - find the type and invoke it
573 Class paramType=methods[i].getParameterTypes()[0];
574 Object params[]=new Object[1];
575 if ("java.lang.Integer".equals( paramType.getName()) ||
576 "int".equals( paramType.getName())) {
578 params[0]=new Integer(value);
579 } catch( NumberFormatException ex ) {ok=false;}
580 } else if ("java.lang.Float".equals( paramType.getName()) ||
581 "float".equals( paramType.getName())) {
583 params[0]=new Float(value);
584 } catch( NumberFormatException ex ) {ok=false;}
585 } else if ("java.lang.Boolean".equals( paramType.getName()) ||
586 "boolean".equals( paramType.getName())) {
587 params[0]=new Boolean(value);
589 throw new Exception("Unknown type " + paramType.getName() + "for property \""+name+"\"with value \""+value+"\"");
593 System.out.println("XXX: " + methods[i] + " " + classN + " " + params[0] );
594 methods[i].invoke( null, params );
600 //if we got this far it means we were not successful in setting the
602 throw new Exception("Count not find method \""+setter+"\" in Class \""+classN.getName()+"\" in order to set property \""+name+"\"");
604 } catch( SecurityException ex1 ) {
605 throw new Exception("SecurityException for " + classN.getName() + " " + name + "=" + value +")" );
606 //if( ctx.getDebug() > 1 ) ex1.printStackTrace();
607 } catch (IllegalAccessException iae) {
608 throw new Exception("IllegalAccessException for " + classN.getName() + " " + name + "=" + value +")" );
609 //if( ctx.getDebug() > 1 ) iae.printStackTrace();
610 } catch (InvocationTargetException ie) {
611 throw new Exception("InvocationTargetException for " + classN.getName() + " " + name + "=" + value +")" );
612 //if( ctx.getDebug() > 1 ) ie.printStackTrace();