    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}