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
045package org.jfree.xml;
046
047import java.util.HashMap;
048import java.util.Stack;
049
050import org.xml.sax.Attributes;
051import 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 */
062public 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}