001    /* ========================================================================
002     * JCommon : a free general purpose class library for the Java(tm) platform
003     * ========================================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jcommon/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it
010     * under the terms of the GNU Lesser General Public License as published by
011     * the Free Software Foundation; either version 2.1 of the License, or
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022     * USA.
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025     * in the United States and other countries.]
026     *
027     * -----------
028     * Parser.java
029     * -----------
030     * (C)opyright 2003-2005, by Thomas Morgner and Contributors.
031     *
032     * Original Author:  Thomas Morgner (taquera@sherito.org);
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     * $Id: Parser.java,v 1.9 2008/09/10 09:20:49 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 09-Jan-2003 : Initial version.
040     * 29-Apr-2003 : Distilled from the JFreeReport project and moved into JCommon
041     * 14-Jul-2003 : More help with the error location given by catching all exceptions.
042     *
043     */
044    
045    package org.jfree.xml;
046    
047    import java.util.HashMap;
048    import java.util.Stack;
049    
050    import org.xml.sax.Attributes;
051    import org.xml.sax.SAXException;
052    
053    /**
054     * The Parser handles the SAXEvents and forwards the event call to the currently
055     * active ElementDefinitionHandler. Contains methods to manage and
056     * configure the parsing process.
057     * <p>
058     * An initial report definition handler must be set before the parser can be used.
059     *
060     * @author Thomas Morgner
061     */
062    public abstract class Parser extends FrontendDefaultHandler {
063    
064        /** A key for the content base. */
065        public static final String CONTENTBASE_KEY = "content-base";
066    
067        /** A stack for the active factories. */
068        private Stack activeFactories;
069    
070        /** The initial factory. */
071        private ElementDefinitionHandler initialFactory;
072    
073        /** Storage for temporary objects and factories used during the parsing process. */
074        private HashMap parserHelperObjects;
075    
076        /**
077         * Creates a new parser.
078         */
079        public Parser() {
080            this.activeFactories = new Stack();
081            this.parserHelperObjects = new HashMap();
082        }
083    
084        /**
085         * Returns the currently collected comments.
086         * @return the comments.
087         */
088        public String[] getComments() {
089            return getCommentHandler().getComments();
090        }
091    
092        /**
093         * Pushes a handler onto the stack.
094         *
095         * @param factory  the handler.
096         */
097        public void pushFactory(final ElementDefinitionHandler factory) {
098            this.activeFactories.push(factory);
099        }
100    
101        /**
102         * Reads a handler off the stack without removing it.
103         *
104         * @return The handler.
105         */
106        public ElementDefinitionHandler peekFactory() {
107            return (ElementDefinitionHandler) this.activeFactories.peek();
108        }
109    
110        /**
111         * Pops a handler from the stack.
112         *
113         * @return The handler.
114         */
115        public ElementDefinitionHandler popFactory() {
116            this.activeFactories.pop();
117            return peekFactory();
118        }
119    
120        /**
121         * Receive notification of the end of the document.
122         *
123         * <p>By default, do nothing.  Application writers may override this
124         * method in a subclass to take specific actions at the end
125         * of a document (such as finalising a tree or closing an output
126         * file).</p>
127         *
128         * @exception SAXException Any SAX exception, possibly wrapping another exception.
129         *
130         * @see org.xml.sax.ContentHandler#endDocument
131         */
132        public void endDocument() throws SAXException {
133            // ignored
134        }
135    
136        /**
137         * Receive notification of the beginning of the document.
138         *
139         * <p>By default, do nothing.  Application writers may override this
140         * method in a subclass to take specific actions at the beginning
141         * of a document (such as allocating the root node of a tree or
142         * creating an output file).</p>
143         *
144         * @exception SAXException Any SAX exception, possibly wrapping another exception.
145         * @see org.xml.sax.ContentHandler#startDocument
146         */
147        public void startDocument() throws SAXException {
148            this.activeFactories.clear();
149            pushFactory(getInitialFactory());
150        }
151    
152        /**
153         * Receive notification of character data inside an element.
154         *
155         * <p>By default, do nothing.  Application writers may override this
156         * method to take specific actions for each chunk of character data
157         * (such as adding the data to a node or buffer, or printing it to
158         * a file).</p>
159         *
160         * @param ch  the characters.
161         * @param start  the start position in the character array.
162         * @param length  the number of characters to use from the character array.
163         *
164         * @exception SAXException Any SAX exception, possibly wrapping another exception.
165         * @see org.xml.sax.ContentHandler#characters
166         */
167        public void characters(final char[] ch, final int start, final int length)
168            throws SAXException {
169            try {
170                peekFactory().characters(ch, start, length);
171            }
172            catch (ParseException pe) {
173                throw pe;
174            }
175            catch (Exception e) {
176                throw new ParseException(e, getLocator());
177            }
178        }
179    
180        /**
181         * Receive notification of the end of an element.
182         *
183         * <p>By default, do nothing.  Application writers may override this
184         * method in a subclass to take specific actions at the end of
185         * each element (such as finalising a tree node or writing
186         * output to a file).</p>
187         *
188         * @param uri  the URI.
189         * @param localName  the element type name.
190         * @param qName  the name.
191         *
192         * @exception SAXException Any SAX exception, possibly
193         *            wrapping another exception.
194         * @see org.xml.sax.ContentHandler#endElement
195         */
196        public void endElement(final String uri, final String localName, final String qName)
197            throws SAXException {
198            try {
199                peekFactory().endElement(qName);
200            }
201            catch (ParseException pe) {
202                throw pe;
203            }
204            catch (Exception e) {
205                throw new ParseException(e, getLocator());
206            }
207            finally {
208                getCommentHandler().clearComments();
209            }
210        }
211    
212    
213        /**
214         * Receive notification of the start of an element.
215         *
216         * <p>By default, do nothing.  Application writers may override this
217         * method in a subclass to take specific actions at the start of
218         * each element (such as allocating a new tree node or writing
219         * output to a file).</p>
220         *
221         * @param uri  the URI.
222         * @param localName  the element type name.
223         * @param qName  the name.
224         * @param attributes  the specified or defaulted attributes.
225         *
226         * @exception SAXException Any SAX exception, possibly
227         *            wrapping another exception.
228         * @see org.xml.sax.ContentHandler#startElement
229         */
230        public void startElement(final String uri, final String localName,
231                                 final String qName, final Attributes attributes)
232            throws SAXException {
233            try {
234                peekFactory().startElement(qName, attributes);
235            }
236            catch (ParseException pe) {
237                throw pe;
238            }
239            catch (Exception e) {
240                throw new ParseException(e, getLocator());
241            }
242            finally {
243                getCommentHandler().clearComments();
244            }
245        }
246    
247        /**
248         * Sets the initial handler.
249         *
250         * @param factory  the initial handler.
251         */
252        public void setInitialFactory(final ElementDefinitionHandler factory) {
253            this.initialFactory = factory;
254        }
255    
256        /**
257         * Returns the initial handler.
258         *
259         * @return The initial handler.
260         */
261        public ElementDefinitionHandler getInitialFactory() {
262            return this.initialFactory;
263        }
264    
265        /**
266         * Sets a helper object.
267         *
268         * @param key  the key.
269         * @param value  the value.
270         */
271        public void setHelperObject(final String key, final Object value) {
272            if (value == null) {
273                this.parserHelperObjects.remove(key);
274            }
275            else {
276                this.parserHelperObjects.put(key, value);
277            }
278        }
279    
280        /**
281         * Returns a helper object.
282         *
283         * @param key  the key.
284         *
285         * @return The object.
286         */
287        public Object getHelperObject(final String key) {
288            return this.parserHelperObjects.get(key);
289        }
290    
291        /**
292         * Returns a new instance of the parser.
293         *
294         * @return a new instance of the parser.
295         */
296        public abstract Parser getInstance();
297    
298        /**
299         * Returns a new instance of {@link FrontendDefaultHandler}.
300         *
301         * @return A new instance.
302         */
303        public final FrontendDefaultHandler newInstance() {
304            return getInstance();
305        }
306    
307        /**
308         * Returns the parsed result object after the parsing is complete. Calling
309         * this function during the parsing is undefined and may result in an
310         * IllegalStateException.
311         *
312         * @return the parsed result.
313         */
314        public abstract Object getResult();
315    }