new mir Configuration file parser. not used yet. it works very nicely though,
[mir.git] / source / mir / xml / XmlConfigurator.java
1 package mir.xml;
2
3 import java.io.*;
4 import java.util.*;
5 import org.xml.sax.helpers.DefaultHandler;
6 import org.xml.sax.*;
7 import javax.xml.parsers.ParserConfigurationException;
8 import javax.xml.parsers.SAXParser;
9 import javax.xml.parsers.SAXParserFactory;
10
11
12 import mir.misc.ConfigException;
13 import mir.misc.Location;
14
15 /**
16  * Configures a based on
17  * a XML config file.
18  *
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.
25  *
26  * @author -mh <heckmann@hbe.ca>
27  * @version 2001.10.21
28  */
29
30 public class XmlConfigurator {
31
32     private static SAXParserFactory parserFactory = null;
33
34     private SAXParser saxParser;
35     //private Project project;
36     private File configFile;
37     private File configFileParent;
38     private Locator locator;
39
40     //private SaxContext saxContext;
41
42     /**
43      * Configures the Project with the contents of the specified XML file.
44      */
45     public static void configure(File configFile) throws ConfigException {
46         new XmlConfigurator(configFile).parse();
47     }
48
49     /**
50      * Constructs a new Ant parser for the specified XML file.
51      */
52     private XmlConfigurator(File configFile) {
53         this.configFile = new File(configFile.getAbsolutePath());
54         configFileParent = new File(this.configFile.getParent());
55     }
56
57     /**
58      * Parses the config file.
59      */
60     private void parse() throws ConfigException {
61         FileInputStream inputStream = null;
62         InputSource inputSource = null;
63         
64         try {
65             SAXParserFactory factory = SAXParserFactory.newInstance();
66             saxParser = factory.newSAXParser();
67                 
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);
71             }
72             
73             inputStream = new FileInputStream(configFile);
74             inputSource = new InputSource(inputStream);
75             inputSource.setSystemId(uri);
76             saxParser.parse(inputSource, new RootHandler());
77         }
78         catch(ParserConfigurationException exc) {
79             throw new ConfigException("Parser has not been configured correctly", exc);
80         }
81         catch(SAXParseException exc) {
82             Location location =
83                 new Location(configFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
84
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);
90                 }
91                 throw be;
92             }
93             
94             throw new ConfigException(exc.getMessage(), t, location);
95         }
96         catch(SAXException exc) {
97             Throwable t = exc.getException();
98             if (t instanceof ConfigException) {
99                 throw (ConfigException) t;
100             }
101             throw new ConfigException(exc.getMessage(), t);
102         }
103         catch(FileNotFoundException exc) {
104             throw new ConfigException(exc);
105         }
106         catch(IOException exc) {
107             throw new ConfigException("Error reading config file", exc);
108         }
109         finally {
110             if (inputStream != null) {
111                 try {
112                     inputStream.close();
113                 }
114                 catch (IOException ioe) {
115                     // ignore this
116                 }
117             }
118         }
119     }
120
121     /**
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.
125      *
126      * Each type of xml element (task, target, etc) in ant will
127      * have its own subclass of AbstractHandler.
128      *
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.
132      */
133     private class AbstractHandler extends DefaultHandler {
134         protected ContentHandler parentHandler;
135
136         public AbstractHandler(ContentHandler parentHandler) {
137             this.parentHandler = parentHandler;
138
139             // Start handling SAX events
140             try {
141                 saxParser.getXMLReader().setContentHandler(this);
142             } catch (SAXException e) {
143                 throw new ConfigException("Error getting XMLReader",e);
144             }
145                 
146         }
147
148         public void startElement(String uri, String tag, String qName, Attributes attrs) throws SAXParseException {
149             throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
150         }
151
152         public void characters(char[] buf, int start, int end) throws SAXParseException {
153             String s = new String(buf, start, end).trim();
154
155             if (s.length() > 0) {
156                 throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
157             }
158         }
159
160         /**
161          * Called when this element and all elements nested into it have been
162          * handeled.
163          */
164         protected void finished() {}
165
166         public void endElement() throws SAXException {
167
168             finished();
169             // Let parent resume handling SAX events
170             saxParser.getXMLReader().setContentHandler(parentHandler);
171         }
172     }
173
174     /**
175      * Handler for the root element. It's only child must be the "mir" element.
176      */
177     private class RootHandler extends DefaultHandler {
178
179         /**
180          * resolve file: URIs as relative to the build file.
181          */
182         public InputSource resolveEntity(String publicId,
183                                          String systemId) {
184         
185         
186             if (systemId.startsWith("file:")) {
187                 String path = systemId.substring(5);
188                 int index = path.indexOf("file:");
189                 
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:");
195                 }
196                 
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");
203                 }
204
205                 File file = new File(path);
206                 if (!file.isAbsolute()) {
207                     file = new File(configFileParent, path);
208                 }
209                 
210                 try {
211                     InputSource inputSource = new InputSource(new FileInputStream(file));
212                     inputSource.setSystemId("file:" + entitySystemId);
213                     return inputSource;
214                 } catch (FileNotFoundException fne) {
215                     System.out.println(file.getAbsolutePath()+" could not be found");
216                 }
217             }
218             // use default if not file or file not found
219             return null;
220         }
221
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);
225             } else {
226                 throw new SAXParseException("Config file is not of expected XML type", locator);
227             }
228         }
229
230         public void setDocumentLocator(Locator locator) {
231             XmlConfigurator.this.locator = locator;
232         }
233     }
234
235     /**
236      * Handler for the top level "project" element.
237      */
238     private class MirHandler extends AbstractHandler {
239         public MirHandler(ContentHandler parentHandler) {
240             super(parentHandler);
241         }
242
243         public void init(String tag, Attributes attrs) throws SAXParseException {
244             String name = null;
245
246             for (int i = 0; i < attrs.getLength(); i++) {
247                 String key = attrs.getLocalName(i);
248                 String value = attrs.getValue(i);
249
250                 if (key.equals("name")) {
251                     name = value;
252                 } else {
253                     throw new SAXParseException("Unexpected attribute \"" + attrs.getLocalName(i) + "\"", locator);
254                 }
255             }
256
257             if (name == null) {
258                 throw new SAXParseException("The default attribute of \"name\" is required", 
259                                             locator);
260             }
261             
262             //MirConfig.setName(name);
263
264         }
265
266         public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
267             if (name.equals("class")) {
268                 handleClassdef(name, attrs);
269             } else {
270                 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
271             }
272         }
273
274         private void handleClassdef(String name, Attributes attrs) throws SAXParseException {
275             (new ClassHandler(this)).init(name, attrs);
276         }
277
278     }
279
280     /**
281      * Handler for "class" elements.
282      */
283     private class ClassHandler extends AbstractHandler {
284
285         Class classN;
286
287         public ClassHandler(ContentHandler parentHandler) {
288             super(parentHandler);
289         }
290
291         public void init(String tag, Attributes attrs) throws SAXParseException {
292             String name = null;
293
294             for (int i = 0; i < attrs.getLength(); i++) {
295                 String key = attrs.getLocalName(i);
296                 String value = attrs.getValue(i);
297
298                 if (key.equals("name")) {
299                     name = value;
300                 } else {
301                     throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
302                 }
303             }
304
305             if (name == null) {
306                 throw new SAXParseException("class element appears without a \"name\" attribute", locator);
307             }
308
309             try {
310                 classN=Class.forName(name);
311             } catch (ClassNotFoundException e) {
312                 throw new ConfigException("Error invoking class: \""+name+
313                     "\"",e);
314             }
315
316         }
317
318         public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXParseException {
319             if (name.equals("property")) {
320                 handleProperties(name, attrs);
321             } else {
322                 throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
323             }
324         }
325
326         private void handleProperties(String name, Attributes attrs) throws SAXParseException {
327             (new PropertiesHandler(this, classN )).init(name, attrs);
328         }
329
330     }
331
332     /**
333      * Handler for all property elements.
334      */
335     private class PropertiesHandler extends AbstractHandler {
336         private Class classN;
337
338         public PropertiesHandler(ContentHandler parentHandler, Class classN) {
339             super(parentHandler);
340
341             this.classN = classN;
342         }
343
344         public void init(String tag, Attributes attrs) throws SAXParseException {
345             String name=null;
346             String value=null;
347
348             for (int i = 0; i < attrs.getLength(); i++) {
349                 String key = attrs.getLocalName(i);
350                 String v = attrs.getValue(i);
351
352                 if (key.equals("name")) {
353                     name = v;
354                 } else if (key.equals("value")) {
355                     value = v; 
356                 } else {
357                     throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
358                 }
359             }
360
361             if (name == null) {
362                 throw new SAXParseException("property element appears without a \"name\" attribute", locator);
363             }
364             if (value == null) {
365                 throw new SAXParseException("property element appears without a \"value\" attribute", locator);
366             }
367             /////
368         }
369
370         protected void finished() {
371           //do the setting here?
372         }
373
374     }
375
376     private static String capitalize(String name) {
377         if (name == null || name.length() == 0) {
378             return name;
379         }
380         char chars[] = name.toCharArray();
381         chars[0] = Character.toUpperCase(chars[0]);
382         return new String(chars);
383     }
384
385 }