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