Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: jbreport/core/DefaultXMLParseHandler.java


1   /*
2    * $Id: DefaultXMLParseHandler.java,v 1.1 2000/08/31 13:53:17 grantfin Exp $
3    *
4    * jbReport - A reporting library for Java
5    * Copyright (C) 2000 Grant Finnemore <grantfin@users.sourceforge.net>
6    *
7    * This library is free software; you can redistribute it and/or
8    * modify it under the terms of the GNU Lesser General Public
9    * License as published by the Free Software Foundation; either
10   * version 2 of the License, or (at your option) any later version.
11   *
12   * This library is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this library; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   */
21  package jbreport.core;
22  
23  import java.util.ResourceBundle;
24  import org.xml.sax.Attributes;
25  import org.xml.sax.InputSource;
26  import org.xml.sax.XMLReader;
27  import org.xml.sax.SAXException;
28  import org.xml.sax.SAXParseException;
29  import org.xml.sax.helpers.DefaultHandler;
30  import org.xml.sax.helpers.XMLReaderFactory;
31  
32  import jbreport.ReportElement;
33  import jbreport.ReportElementFactory;
34  import jbreport.ReportException;
35  import jbreport.ReportFacade;
36  import jbreport.util.Stack;
37  
38  /**
39   * This is the default xml parser handling class. It is registered with the 
40   * actual sax parser, and will receive the appropriate callbacks.
41   *
42   * <p> It should not know about concrete implementations of reporting elements,
43   * it should just make the appropriate calls to the ReportElementFactory 
44   * instance.
45   *
46   * <p> The sequence of events when traversing the parse tree is the following.
47   * <ol>
48   *   <li> On startElement, see if the top node in the stack knows how to deal
49   *        with its own children, 
50   *     <ul> 
51   *       <li> [it does] then pass the element type, attributes and cdata to the
52   *             top stack node while still in this node.
53   *       <li> [it does not] then try to create the appropriate class using
54   *            the ReportElementFactory instance.
55   *     </ul>
56   *   <li> Initialise the element with the type and attributes
57   *   <li> Collect any cdata before the endElement method
58   *   <li> On endElement, pass the cdata to the element, call end() on the 
59   *        element, endChild() on the parent, and pop the element off the stack.
60   * </ol>
61   * 
62   * @author Grant Finnemore
63   * @version $Revision: 1.1 $
64   */
65  public 
66  class DefaultXMLParseHandler extends DefaultHandler implements XMLHandler {
67  
68     /** The bundle of resources that provide default settings to the parser,
69      * and also determine which parser will be used.
70      */
71     private static ResourceBundle bundle = 
72        ResourceBundle.getBundle("jbreport.core.parse");
73  
74     /** The current XML parser. Should we recognize it, an attempt will be made
75      * to re-use rather than re-create it.
76      */
77     private static XMLReader reader;
78  
79     /** The stack that is used to determine the current state of parsing. It
80      * should be noted here that every xml element should have a mapping, or 
81      * that its parent should implement the appropriate handling interface.
82      */
83     private Stack stack = new Stack();
84  
85     /** The factory instance that will create the concrete element instances. */
86     private ReportElementFactory factory;
87  
88     /** The current cdata that exists for this element */
89     private StringBuffer cdata;
90  
91     //
92     // Constructors
93     //
94  
95     private DefaultXMLParseHandler() throws ReportException {
96        factory = ReportFacade.fetchElementFactory();
97     }
98  
99     //
100    // Factory methods
101    //
102 
103    /**
104     * Parse the xml which is read in through the given InputSource instance.
105     */
106    public static void parseXML(InputSource input) throws ReportException {
107       // Ensure that a reader is available for use
108       initializeReader();
109       // Create a parse handler for use of the parser
110       DefaultHandler handler = new DefaultXMLParseHandler();
111       reader.setContentHandler(handler);
112       reader.setErrorHandler(handler);
113       // Do the parsing
114       try {
115          reader.parse(input);
116       }
117       catch(SAXParseException spe) {
118          spe.printStackTrace(System.err);
119          throw new ReportException(spe);
120       } 
121       catch(SAXException se) {
122          if (se.getException() != null) {
123             se.getException().printStackTrace(System.err);
124          }
125          else {
126             se.printStackTrace(System.err);
127          }
128          throw new ReportException(se);
129       } 
130       catch(Exception e) {
131          throw new ReportException(e);
132       }
133    }
134 
135    /**
136     * Parse the xml which is contained by the given systemId.
137     */
138    public static void parseXML(String systemId) throws ReportException {
139       parseXML(new InputSource(systemId));
140    }
141 
142    //
143    // Methods overloaded from HandlerBase
144    //
145 
146    public void startElement(String uri, String localName, 
147                             String rawName, Attributes attributes) {
148 //        System.err.println("> " + localName);
149       boolean done = false;
150       cdata = new StringBuffer(); // Re-initialize on every new element
151       if(!stackEmpty()) {
152          ReportElement top = peekElement();
153          if(top instanceof XMLParsingElement 
154             && ((XMLParsingElement)top).xmlCanParse()) {
155             ((XMLParsingElement)top).xmlParse(this, localName, attributes);
156             done = true;
157          }
158       }
159       if(!done) {
160          try {
161             ReportElement elem = factory.createElement(localName);
162             // I don't want to do this, but I am more against putting the 
163             // XMLHandler interface into the base package. This has the effect
164             // that we are probably not able to handle programatic failures 
165             // to insert the group element, as there we don't necessarily have
166             // access to the stack, and, they might not even be using a stack.
167             if(elem instanceof AbstractReportElement) {
168                ((AbstractReportElement)elem).xmlHandler = this;
169             }
170             elem.xmlInitialize(localName, attributes);
171             pushElement(elem);
172          }
173          catch(ReportException e) {
174             handleErrorDuringParse(e, "startElement(" + uri + "," + localName
175                                    + "," + rawName + "," + attributes + ")");
176          }
177       }
178    }
179 
180    public void endElement(String uri, String localName, String rawName) {
181 //        System.err.println(">> " + localName);
182       try {
183          if(!stackEmpty()) {
184             ReportElement top = peekElement();
185             if(localName.equals(top.getType())) {
186                // Remove the element
187                top.xmlEnd(cdata.toString().trim());
188                // The stack might have changed in xmlEnd!
189                top = popElement();
190                if(!stackEmpty()) {
191                   ReportElement parent = peekElement();
192                   parent.xmlEndChild(top);
193                }
194             }
195             else if(top instanceof XMLParsingElement 
196                     && ((XMLParsingElement)top).xmlCanParse()) {
197                // Send it the appropriate data
198                XMLParsingElement elem = (XMLParsingElement)top;
199                elem.xmlCData(this, cdata.toString().trim());
200 //                 elem.xmlEnd(this);
201             }
202          }
203       }
204       catch(ReportException e) {
205          handleErrorDuringParse(e, "endElement(" + uri + "," + localName
206                                 + "," + rawName + ")");
207       }
208    }
209 
210    public void characters(char ch[], int start, int length) {
211       for(int i = start; i < start + length; i++) {
212          if(ch[i] != '\n' && ch[i] != '\r') {
213             cdata.append(ch[i]);
214          }
215       }
216 //        System.err.println("->" + cdata + "<-");
217    }
218 
219    //
220    // Implementation of the XMLHandler interface
221    //
222 
223    public void pushElement(ReportElement elem) {
224       stack.push(elem);
225    }
226 
227    public ReportElement peekElement() {
228       return (ReportElement)stack.peek();
229    }
230 
231    public ReportElement peekElement(int pos) {
232       return (ReportElement)stack.peek(pos);
233    }
234 
235    public ReportElement popElement() {
236       return (ReportElement)stack.pop();
237    }
238 
239    public boolean stackEmpty() {
240       return stack.empty();
241    }
242 
243    //
244    // Implementation methods
245    //
246 
247    /**
248     * This will check if a reader already exists, if not, it will create one.
249     * Should the reader already exist, then check to see if we recognize it. If
250     * so then try to reset it, otherwise just create a new one.
251     */
252    private static void initializeReader() throws ReportException {
253       if(reader == null) {
254          reader = createNewReader();
255       }
256       else {
257          boolean reset = false;
258          try {
259             // Ugh!!!
260             if(reader instanceof org.apache.xerces.framework.XMLParser) {
261                ((org.apache.xerces.framework.XMLParser)reader).reset();
262                reset = true;
263             }
264          }
265          catch(Exception e) {}
266          catch(NoClassDefFoundError e) {}
267          if(!reset) {
268             reader = createNewReader();
269          }
270       }
271    }
272 
273    /**
274     * This will create a new reader. It should not modify the reader attribute
275     * of the class.
276     */
277    private static XMLReader createNewReader() throws ReportException {
278       try {
279          String className = bundle.getString("parser.class");
280          XMLReader result = null;
281          if(className != null) {
282             result = XMLReaderFactory.createXMLReader(className);
283          }
284          else {
285             result = XMLReaderFactory.createXMLReader();
286          }
287          return result;
288       }
289       catch(SAXException e) {
290          throw new ReportException(e);
291       }
292    }
293 
294    /**
295     * This method will handle all report errors that occur during parsing in a 
296     * consistent fashion.
297     */
298    private void handleErrorDuringParse(ReportException e, String msg) {
299       e.printStackTrace(System.err);
300       throw new RuntimeException(e.toString() + "::" + msg);
301    }
302 
303 }