bb3e39a5c0858228619d76be353200731cbf5e40
[mir.git] / source / mir / config / ConfigReader.java
1 /*
2  * Copyright (C) 2001, 2002  The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
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.
10  *
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.
15  *
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
19  *
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.
30  */
31
32 package  mir.config;\r
33 \r
34 import java.io.*;\r
35 import java.util.*;\r
36 import java.lang.System;\r
37 import org.xml.sax.helpers.DefaultHandler;\r
38 import org.xml.sax.*;\r
39 import javax.xml.parsers.ParserConfigurationException;\r
40 import javax.xml.parsers.SAXParser;\r
41 import javax.xml.parsers.SAXParserFactory;\r
42 \r
43 import mir.config.exceptions.*;\r
44 import mir.misc.Location;\r
45 \r
46 public class ConfigReader {\r
47   final static String propertyTagName="property";\r
48   final static String propertyNameAttribute="name";\r
49   final static String propertyValueAttribute="value";\r
50   final static String defineTagName="define";\r
51   final static String defineNameAttribute="name";\r
52   final static String defineValueAttribute="value";\r
53   final static String includeTagName="include";\r
54   final static String includeFileAttribute="file";\r
55 \r
56   public ConfigReader() {\r
57     super();\r
58   };\r
59 \r
60   public void parseFile(String aFileName, ConfigNodeBuilder aRootNode) throws ConfigException {\r
61 \r
62     try {\r
63       SAXParserFactory parserFactory = SAXParserFactory.newInstance();\r
64 \r
65       parserFactory.setNamespaceAware(false);\r
66       parserFactory.setValidating(true);\r
67 \r
68 \r
69       ConfigReaderHandler handler = new ConfigReaderHandler(aRootNode, parserFactory);\r
70 \r
71       handler.includeFile(aFileName);\r
72     }\r
73     catch (Throwable e) {\r
74       if (e instanceof SAXParseException && ((SAXParseException) e).getException() instanceof ConfigException) {\r
75         throw (ConfigException) ((SAXParseException) e).getException();\r
76       }\r
77       else {\r
78         e.printStackTrace();\r
79         throw new ConfigException( e.getMessage() );\r
80       }\r
81     }\r
82   }\r
83 \r
84   private class ConfigReaderHandler extends DefaultHandler {\r
85     ConfigNodeBuilder builder;\r
86     Stack nodeStack;\r
87     Locator locator;\r
88     DefinesManager definesManager;\r
89     int level;\r
90     Stack includeFileStack;\r
91     SAXParserFactory parserFactory;\r
92 \r
93     public ConfigReaderHandler(ConfigNodeBuilder aBuilder, SAXParserFactory aParserFactory) {\r
94       super();\r
95 \r
96       builder=aBuilder;\r
97       nodeStack=new Stack();\r
98       includeFileStack=new Stack();\r
99       definesManager=new DefinesManager();\r
100       parserFactory=aParserFactory;\r
101       level=0;\r
102     }\r
103 \r
104     public String getLocatorDescription(Locator aLocator) {\r
105       return aLocator.getPublicId()+" ("+aLocator.getLineNumber()+")";\r
106     }\r
107 \r
108     public void setDocumentLocator(Locator aLocator) {\r
109       locator=aLocator;\r
110     }\r
111 \r
112     private void includeFile(String aFileName) throws ConfigException, SAXParseException, SAXException {\r
113       File file;\r
114       SAXParser parser;\r
115       InputSource inputSource;\r
116       System.err.println("about to include "+aFileName);\r
117 \r
118       try {\r
119         if (!includeFileStack.empty())\r
120           file = new File(new File((String) includeFileStack.peek()).getParent(), aFileName);\r
121         else\r
122           file = new File(aFileName);\r
123 \r
124         System.err.println("about to include "+file.getCanonicalPath());\r
125 \r
126         if (includeFileStack.contains(file.getCanonicalPath())) {\r
127           throw new ConfigException("recursive inclusion of file "+file.getCanonicalPath(), getLocatorDescription(locator));\r
128         }\r
129 \r
130         parser=parserFactory.newSAXParser();\r
131 \r
132         inputSource = new InputSource(new FileInputStream(file));\r
133         inputSource.setPublicId(file.getCanonicalPath());\r
134 \r
135         includeFileStack.push(file.getCanonicalPath());\r
136         try {\r
137           parser.parse(inputSource, this);\r
138         }\r
139         finally {\r
140           includeFileStack.pop();\r
141         }\r
142       }\r
143       catch (ParserConfigurationException e) {\r
144         throw new ConfigException("Internal exception while including \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator));\r
145       }\r
146       catch (SAXParseException e) {\r
147         throw e;\r
148       }\r
149       catch (ConfigException e) {\r
150         throw e;\r
151       }\r
152       catch (FileNotFoundException e) {\r
153         throw new ConfigException("Include file \""+aFileName+"\" not found: "+e.getMessage(), e, getLocatorDescription(locator));\r
154       }\r
155       catch (IOException e) {\r
156         throw new ConfigException("unable to open include file \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator));\r
157       }\r
158 \r
159     }\r
160 \r
161     public void startElement(String aUri, String aTag, String aQualifiedName, Attributes anAttributes) throws SAXException {\r
162       nodeStack.push(builder);\r
163       level++;\r
164       try {\r
165         if (builder==null) {\r
166           throw new ConfigException("define, include and property tags cannot have content", getLocatorDescription(locator));\r
167         }\r
168         if (aQualifiedName.equals(propertyTagName)) {\r
169           String name=anAttributes.getValue(propertyNameAttribute);\r
170           String value=anAttributes.getValue(propertyValueAttribute);\r
171 \r
172           if (name==null) {\r
173             throw new ConfigException("property has no name attribute", getLocatorDescription(locator));\r
174           }\r
175           else\r
176           if (value==null) {\r
177             throw new ConfigException("property \""+name+"\" has no value attribute", getLocatorDescription(locator));\r
178           }\r
179 \r
180           builder.addProperty(name, definesManager.resolve(value, getLocatorDescription(locator)), value, getLocatorDescription(locator));\r
181           builder=null;\r
182 \r
183         }\r
184         else if (aQualifiedName.equals(defineTagName)) {\r
185           String name=anAttributes.getValue(defineNameAttribute);\r
186           String value=anAttributes.getValue(defineValueAttribute);\r
187 \r
188           if (name==null) {\r
189             throw new ConfigException("define has no name attribute", getLocatorDescription(locator));\r
190           }\r
191           else\r
192           if (value==null) {\r
193             throw new ConfigException("define \""+name+"\" has no value attribute", getLocatorDescription(locator));\r
194           }\r
195 \r
196           definesManager.addDefine(name, definesManager.resolve(value, getLocatorDescription(locator)));\r
197           builder=null;\r
198         }\r
199         else if (aQualifiedName.equals(includeTagName)) {\r
200           String fileName=anAttributes.getValue(includeFileAttribute);\r
201 \r
202           if (fileName==null) {\r
203             throw new ConfigException("include has no file attribute", getLocatorDescription(locator));\r
204           }\r
205 \r
206           includeFile(definesManager.resolve(fileName, getLocatorDescription(locator)));\r
207           builder=null;\r
208         }\r
209         else\r
210         {\r
211           builder=builder.makeSubNode(aQualifiedName, getLocatorDescription(locator));\r
212         }\r
213       }\r
214       catch (ConfigException e) {\r
215         throw new SAXParseException(e.getMessage(), locator, e);\r
216       }\r
217     }\r
218 \r
219     public void endElement(String aUri, String aTag, String aQualifiedName) throws SAXParseException {\r
220       builder=(ConfigNodeBuilder) nodeStack.pop();\r
221       level--;\r
222     }\r
223 \r
224     public void characters(char[] aBuffer, int aStart, int anEnd) throws SAXParseException {\r
225       String text = new String(aBuffer, aStart, anEnd).trim();\r
226       if ( text.length() > 0) {\r
227         throw new SAXParseException("Text not allowed", locator, new ConfigException("text not allowed", getLocatorDescription(locator)));\r
228       }\r
229     }\r
230   }\r
231 \r
232   private class DefinesManager {\r
233     Map defines;\r
234 \r
235     public DefinesManager() {\r
236       defines = new HashMap();\r
237     }\r
238 \r
239     public void addDefine(String aName, String anExpression) {\r
240       defines.put(aName, anExpression);\r
241     }\r
242 \r
243     public String resolve(String anExpression, String aLocation) throws ConfigException {\r
244       int previousPosition = 0;\r
245       int position;\r
246       int endOfNamePosition;\r
247       String name;\r
248 \r
249       StringBuffer result = new StringBuffer();\r
250 \r
251       while ((position=anExpression.indexOf("$", previousPosition))>=0) {\r
252         result.append(anExpression.substring(previousPosition, position));\r
253 \r
254         if (position>=anExpression.length()-1) {\r
255           result.append(anExpression.substring(position, anExpression.length()));\r
256           previousPosition=anExpression.length();\r
257         }\r
258         else\r
259         {\r
260           if (anExpression.charAt(position+1) == '{') {\r
261             endOfNamePosition=anExpression.indexOf('}', position);\r
262             if (endOfNamePosition>=0) {\r
263               name=anExpression.substring(position+2, endOfNamePosition);\r
264               if (defines.containsKey(name)) {\r
265                 result.append((String) defines.get(name));\r
266                 previousPosition=endOfNamePosition+1;\r
267               }\r
268               else {\r
269                 throw new ConfigDefineNotKnownException("Variable \""+name+"\" not defined", aLocation);\r
270               }\r
271             }\r
272             else {\r
273                 throw new ConfigException("Missing }", aLocation);\r
274             }\r
275 \r
276           }\r
277           else\r
278           {\r
279             previousPosition=position+2;\r
280             result.append(anExpression.charAt(position+1));\r
281           }\r
282         }\r
283       }\r
284       result.append(anExpression.substring(previousPosition, anExpression.length()));\r
285 \r
286       return result.toString();\r
287     }\r
288   }\r
289 }\r
290 \r
291 \r