6b53f8e1bf583628830f8d17eb5f946cec26696d
[mir.git] / source / mir / util / XMLReader.java
1 /*\r
2  * Copyright (C) 2001, 2002  The Mir-coders group\r
3  *\r
4  * This file is part of Mir.\r
5  *\r
6  * Mir is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * Mir is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with Mir; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  *\r
20  * In addition, as a special exception, The Mir-coders gives permission to link\r
21  * the code of this program with the com.oreilly.servlet library, any library\r
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced\r
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of\r
24  * the above that use the same license as the above), and distribute linked\r
25  * combinations including the two.  You must obey the GNU General Public\r
26  * License in all respects for all of the code used other than the above\r
27  * mentioned libraries.  If you modify this file, you may extend this exception\r
28  * to your version of the file, but you are not obligated to do so.  If you do\r
29  * not wish to do so, delete this exception statement from your version.\r
30  */\r
31 \r
32 package mir.util;\r
33 \r
34 import java.io.*;\r
35 import java.util.*;\r
36 \r
37 import javax.xml.parsers.ParserConfigurationException;\r
38 import javax.xml.parsers.SAXParser;\r
39 import javax.xml.parsers.SAXParserFactory;\r
40 \r
41 import org.xml.sax.Attributes;\r
42 import org.xml.sax.InputSource;\r
43 import org.xml.sax.Locator;\r
44 import org.xml.sax.SAXException;\r
45 import org.xml.sax.SAXParseException;\r
46 import org.xml.sax.helpers.DefaultHandler;\r
47 \r
48 import multex.Failure;\r
49 import multex.Exc;\r
50 \r
51 public class XMLReader {\r
52   private Locator locator;\r
53   private String filename;\r
54 \r
55   public void parseFile(String aFileName, SectionHandler aRootHandler, List aUsedFiles) throws XMLReaderFailure, XMLReaderExc {\r
56     try {\r
57       SAXParserFactory parserFactory = SAXParserFactory.newInstance();\r
58 \r
59       parserFactory.setNamespaceAware(false);\r
60       parserFactory.setValidating(true);\r
61 \r
62       XMLReaderHandler handler = new XMLReaderHandler(parserFactory, aRootHandler, aUsedFiles);\r
63 \r
64       handler.includeFile(aFileName);\r
65     }\r
66     catch (Throwable e) {\r
67       Throwable t = getRootCause(e);\r
68 \r
69       if (t instanceof XMLReaderExc) {\r
70         ((XMLReaderExc) t).setLocation(filename, locator.getLineNumber(), locator.getColumnNumber());\r
71         throw (XMLReaderExc) t;\r
72       }\r
73 \r
74       if (t instanceof XMLReaderFailure) {\r
75         throw (XMLReaderFailure) t;\r
76       }\r
77 \r
78       throw new XMLReaderFailure(t);\r
79     }\r
80   }\r
81 \r
82   private Throwable getRootCause(Throwable t) {\r
83     if (t instanceof SAXParseException && ((SAXParseException) t).getException()!=null) {\r
84       return getRootCause(((SAXParseException) t).getException());\r
85     }\r
86     else if (t instanceof Failure && ((Failure) t).getCause()!=null) {\r
87       return getRootCause(((Failure) t).getCause());\r
88     }\r
89     else return t;\r
90   }\r
91 \r
92   private class XMLReaderHandler extends DefaultHandler {\r
93     private Stack includeFileStack;\r
94     private SAXParserFactory parserFactory;\r
95     private SectionsManager manager;\r
96     private List usedFiles;\r
97     private InputSource inputSource;\r
98 \r
99     public XMLReaderHandler(SAXParserFactory aParserFactory, SectionHandler aRootHandler, List aUsedFiles) {\r
100       super();\r
101 \r
102       includeFileStack=new Stack();\r
103       parserFactory=aParserFactory;\r
104       includeFileStack = new Stack();\r
105       manager = new SectionsManager();\r
106       usedFiles = aUsedFiles;\r
107       manager.pushHandler(aRootHandler);\r
108    }\r
109 \r
110     public String getLocatorDescription(Locator aLocator) {\r
111       return aLocator.getPublicId()+" ("+aLocator.getLineNumber()+")";\r
112     }\r
113 \r
114     public void setDocumentLocator(Locator aLocator) {\r
115       locator=aLocator;\r
116     }\r
117 \r
118     private void includeFile(String aFileName) throws XMLReaderExc, XMLReaderFailure, SAXParseException, SAXException {\r
119       File file;\r
120       SAXParser parser;\r
121 \r
122       try {\r
123         if (!includeFileStack.empty())\r
124           file = new File(new File((String) includeFileStack.peek()).getParent(), aFileName);\r
125         else\r
126           file = new File(aFileName);\r
127 \r
128         System.err.println("about to include "+file.getCanonicalPath());\r
129 \r
130         if (includeFileStack.contains(file.getCanonicalPath())) {\r
131           throw new XMLReaderExc("recursive inclusion of file "+file.getCanonicalPath());\r
132         }\r
133 \r
134         usedFiles.add(file);\r
135 \r
136         parser=parserFactory.newSAXParser();\r
137 \r
138         inputSource = new InputSource(new FileInputStream(file));\r
139         inputSource.setPublicId(file.getCanonicalPath());\r
140 \r
141         includeFileStack.push(file.getCanonicalPath());\r
142         filename = file.getCanonicalPath();\r
143         try {\r
144           parser.parse(inputSource, this);\r
145         }\r
146         finally {\r
147           includeFileStack.pop();\r
148         }\r
149         if (!includeFileStack.empty())\r
150           filename = (String) includeFileStack.peek();\r
151       }\r
152       catch (ParserConfigurationException e) {\r
153         throw new XMLReaderExc("Internal exception while including \""+aFileName+"\": "+e.getMessage());\r
154       }\r
155       catch (SAXParseException e) {\r
156         throw e;\r
157       }\r
158       catch (XMLReaderFailure e) {\r
159         throw e;\r
160       }\r
161       catch (FileNotFoundException e) {\r
162         throw new XMLReaderExc("Include file \""+aFileName+"\" not found: "+e.getMessage());\r
163       }\r
164       catch (IOException e) {\r
165         throw new XMLReaderExc("unable to open include file \""+aFileName+"\": "+e.getMessage());\r
166       }\r
167     }\r
168 \r
169     public void startElement(String aUri, String aTag, String aQualifiedName, Attributes anAttributes) throws SAXException {\r
170       Map attributesMap;\r
171       int i;\r
172 \r
173       try {\r
174         if (aQualifiedName.equals("include")) {\r
175           String fileName=anAttributes.getValue("file");\r
176 \r
177           if (fileName==null) {\r
178             throw new XMLReaderExc("include has no file attribute");\r
179           }\r
180 \r
181           includeFile(fileName);\r
182         }\r
183         else {\r
184           attributesMap = new HashMap();\r
185           for (i=0; i<anAttributes.getLength(); i++)\r
186             attributesMap.put(anAttributes.getQName(i), anAttributes.getValue(i));\r
187 \r
188           SectionHandler handler = manager.currentHandler().startElement(aQualifiedName, attributesMap);\r
189 \r
190           manager.pushHandler( handler );\r
191         }\r
192       }\r
193       catch (XMLReaderExc e) {\r
194         throw new SAXParseException(e.getMessage(), null, e);\r
195       }\r
196       catch (Exception e) {\r
197         throw new SAXException(e);\r
198       }\r
199     }\r
200 \r
201     public void endElement(String aUri, String aTag, String aQualifiedName) throws SAXException {\r
202       try\r
203       {\r
204         if (!aQualifiedName.equals("include")) {\r
205           SectionHandler handler = manager.popHandler();\r
206 \r
207           handler.finishSection();\r
208 \r
209           if (!manager.isEmpty()) {\r
210             manager.currentHandler().endElement(handler);\r
211           }\r
212         }\r
213       }\r
214       catch (XMLReaderExc e) {\r
215         throw new SAXParseException(e.getMessage(), null, e);\r
216       }\r
217       catch (Exception e) {\r
218         throw new SAXException(e);\r
219       }\r
220     }\r
221 \r
222     public void characters(char[] aBuffer, int aStart, int anEnd) throws SAXParseException {\r
223       String text = new String(aBuffer, aStart, anEnd).trim();\r
224       if ( text.length() > 0) {\r
225         throw new SAXParseException("Text not allowed", null, new XMLReaderExc("Text not allowed"));\r
226       }\r
227     }\r
228   }\r
229 \r
230   private class SectionsManager {\r
231     Stack handlerStack;\r
232 \r
233     public SectionsManager() {\r
234       handlerStack = new Stack();\r
235     }\r
236 \r
237     public void pushHandler(SectionHandler aSectionHandler) {\r
238       handlerStack.push(aSectionHandler);\r
239     }\r
240 \r
241     public SectionHandler popHandler() {\r
242       return (SectionHandler) handlerStack.pop();\r
243     }\r
244 \r
245     public SectionHandler currentHandler() {\r
246       return (SectionHandler) handlerStack.peek();\r
247     }\r
248 \r
249     public boolean isEmpty() {\r
250       return handlerStack.isEmpty();\r
251     }\r
252   }\r
253 \r
254   public static interface SectionHandler {\r
255     public abstract SectionHandler startElement(String aTag, Map anAttributes) throws XMLReaderExc;\r
256 \r
257     public abstract void endElement(SectionHandler aHandler) throws XMLReaderExc;\r
258 \r
259     public void finishSection() throws XMLReaderExc;\r
260   }\r
261 \r
262   public static abstract class AbstractSectionHandler implements SectionHandler {\r
263     public SectionHandler startElement(String aTag, Map anAttributes) throws XMLReaderExc {\r
264       return null;\r
265     };\r
266 \r
267     public void endElement(SectionHandler aHandler) throws XMLReaderExc {\r
268     };\r
269 \r
270     public void finishSection() throws XMLReaderExc {\r
271     }\r
272   }\r
273 \r
274   public static class XMLReaderExc extends Exc {\r
275     private boolean hasLocation;\r
276     private String filename;\r
277     private int lineNr;\r
278     private int columnNr;\r
279 \r
280     public XMLReaderExc(String aMessage) {\r
281       super(aMessage);\r
282       hasLocation = false;\r
283     }\r
284 \r
285     protected void setLocation(String aFilename, int aLineNr, int aColumnNr) {\r
286       filename = aFilename;\r
287       lineNr = aLineNr;\r
288       columnNr = aColumnNr;\r
289       hasLocation = true;\r
290     }\r
291 \r
292     public boolean getHasLocation() {\r
293       return hasLocation;\r
294     }\r
295 \r
296     public int getLineNr() {\r
297       return lineNr;\r
298     }\r
299 \r
300     public int getColumnNr() {\r
301       return columnNr;\r
302     }\r
303 \r
304     public String getFilename() {\r
305       return filename;\r
306     }\r
307   }\r
308 \r
309   public static class XMLReaderFailure extends Failure {\r
310     public XMLReaderFailure(String aMessage, Throwable aCause) {\r
311       super(aMessage, aCause);\r
312     }\r
313 \r
314     public XMLReaderFailure(Throwable aCause) {\r
315       super(aCause.getMessage(), aCause);\r
316     }\r
317   }\r
318 \r
319 }