    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     * ReportGenerator.java
    029     * --------------------
    030     * (C)opyright 2002-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: ParserFrontend.java,v 1.8 2005/11/14 10:58:19 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 10-May-2002 : Initial version
    040     * 12-Dec-2002 : Fixed issues reported by Checkstyle (DG);
    041     * 29-Apr-2003 : Distilled from the JFreeReport project and moved into JCommon
    042     *
    043     */
    044
    045    package org.jfree.xml;
    046
    047    import java.io.BufferedInputStream;
    048    import java.io.IOException;
    049    import java.net.URL;
    050    import javax.xml.parsers.ParserConfigurationException;
    051    import javax.xml.parsers.SAXParser;
    052    import javax.xml.parsers.SAXParserFactory;
    053
    054    import org.jfree.util.Log;
    055    import org.xml.sax.EntityResolver;
    056    import org.xml.sax.InputSource;
    057    import org.xml.sax.SAXException;
    058    import org.xml.sax.XMLReader;
    059
    060    /**
    061     * The reportgenerator initializes the parser and provides an interface
    062     * the the default parser.
    063     *
    064     * To create a report from an URL, use
    065     * <code>
    066     * ReportGenerator.getInstance().parseReport (URL myURl, URL contentBase);
    067     * </code>
    068     *
    069     * @author Thomas Morgner
    070     */
    071    public class ParserFrontend {
    072
    073        /** The report handler. */
    074        private FrontendDefaultHandler defaulthandler;
    075
    076        /** The parser factory. */
    077        private SAXParserFactory factory;
    078
    079        /** The DTD. */
    080        private EntityResolver entityResolver;
    081
    082        /** A flag indicating whether to use a DTD to validate the xml input. */
    083        private boolean validateDTD;
    084
    085        /**
    086         * Creates a new report generator. The generator uses the singleton pattern by default,
    087         * so use generator.getInstance() to get the generator.
    088         *
    089         * @param parser the parser that is used to coordinate the parsing process.
    090         */
    091        protected ParserFrontend(final FrontendDefaultHandler parser) {
    092            if (parser == null) {
    093                throw new NullPointerException();
    094            }
    095            this.defaulthandler = parser;
    096        }
    097
    098        /**
    099         * Returns <code>true</code> if the report definition should be validated against the
    100         * DTD, and <code>false</code> otherwise.
    101         *
    102         * @return A boolean.
    103         */
    104        public boolean isValidateDTD() {
    105            return this.validateDTD;
    106        }
    107
    108        /**
    109         * Sets a flag that controls whether or not the report definition is validated
    110         * against the DTD.
    111         *
    112         * @param validateDTD  the flag.
    113         */
    114        public void setValidateDTD(final boolean validateDTD) {
    115            this.validateDTD = validateDTD;
    116        }
    117
    118        /**
    119         * Returns the entity resolver.
    120         *
    121         * @return The entity resolver.
    122         */
    123        public EntityResolver getEntityResolver() {
    124            return this.entityResolver;
    125        }
    126
    127        /**
    128         * Sets the entity resolver.
    129         *
    130         * @param entityResolver  the entity resolver.
    131         */
    132        public void setEntityResolver(final EntityResolver entityResolver) {
    133            this.entityResolver = entityResolver;
    134        }
    135
    136        /**
    137         * Returns a SAX parser.
    138         *
    139         * @return a SAXParser.
    140         *
    141         * @throws ParserConfigurationException if there is a problem configuring the parser.
    142         * @throws SAXException if there is a problem with the parser initialisation
    143         */
    144        protected SAXParser getParser() throws ParserConfigurationException, SAXException {
    145            if (this.factory == null) {
    146                this.factory = SAXParserFactory.newInstance();
    147                if (isValidateDTD()) {
    148                    try {
    149                        // dont touch the validating feature, if not needed ..
    150                        this.factory.setValidating(true);
    151                    }
    152                    catch (Exception ex) {
    153                        // the parser does not like the idea of validating ...
    154                        Log.debug("The parser will not validate the xml document.", ex);
    155                    }
    156                }
    157            }
    158            return this.factory.newSAXParser();
    159        }
    160
    161        /**
    162         * Sets the default handler used for parsing reports. This handler is used to
    163         * initiate parsing.
    164         *
    165         * @param handler  the handler.
    166         */
    167        public void setDefaultHandler(final FrontendDefaultHandler handler) {
    168            if (handler == null) {
    169                throw new NullPointerException();
    170            }
    171            this.defaulthandler = handler;
    172        }
    173
    174        /**
    175         * Returns the ElementDefinitionHandler used for parsing reports.
    176         *
    177         * @return the report handler.
    178         */
    179        public FrontendDefaultHandler getDefaultHandler() {
    180            return this.defaulthandler;
    181        }
    182
    183        /**
    184         * Creates a new instance of the currently set default handler and sets the contentbase
    185         * for the handler to <code>contentBase</code>.
    186         *
    187         * @param contentBase  the content base.
    188         *
    189         * @return the report handler.
    190         */
    191        protected FrontendDefaultHandler createDefaultHandler(final URL contentBase) {
    192            final FrontendDefaultHandler handler = getDefaultHandler().newInstance();
    193            if (contentBase != null) {
    194                handler.setConfigProperty(Parser.CONTENTBASE_KEY, contentBase.toExternalForm());
    195            }
    196            return handler;
    197        }
    198
    199        /**
    200         * Parses an XML report template file.
    201         *
    202         * @param input  the input source.
    203         * @param contentBase  the content base.
    204         *
    205         * @return the report.
    206         *
    207         * @throws ElementDefinitionException if an error occurred.
    208         */
    209        protected Object parse(final InputSource input, final URL contentBase)
    210            throws ElementDefinitionException {
    211            try {
    212                final SAXParser parser = getParser();
    213                final XMLReader reader = parser.getXMLReader();
    214
    215                try {
    216                    reader.setFeature("http://xml.org/sax/features/validation", isValidateDTD());
    217                }
    218                catch (SAXException se) {
    219                    Log.debug("The XMLReader will not validate the xml document.", se);
    220                }
    221                final FrontendDefaultHandler handler = createDefaultHandler(contentBase);
    222                configureReader(reader, handler);
    223                try {
    224                    reader.setContentHandler(handler);
    225                    reader.setDTDHandler(handler);
    226                    if (getEntityResolver() != null) {
    227                        reader.setEntityResolver(getEntityResolver());
    228                    }
    229                    reader.setErrorHandler(handler);
    230                    reader.parse(input);
    231                    return handler.getResult();
    232                }
    233                catch (IOException e) {
    234                    throw new ElementDefinitionException(e);
    235                }
    236            }
    237            catch (ParserConfigurationException e) {
    238                throw new ElementDefinitionException(e);
    239            }
    240            catch (SAXException e) {
    241                throw new ElementDefinitionException(e);
    242            }
    243        }
    244
    245        /**
    246         * Configures the xml reader. Use this to set features or properties
    247         * before the documents get parsed.
    248         *
    249         * @param handler the parser implementation that will handle the SAX-Callbacks.
    250         * @param reader the xml reader that should be configured.
    251         */
    252        protected void configureReader(final XMLReader reader, final FrontendDefaultHandler handler) {
    253            try {
    254                reader.setProperty
    255                    ("http://xml.org/sax/properties/lexical-handler", handler.getCommentHandler());
    256            }
    257            catch (SAXException se) {
    258                Log.debug("Comments are not supported by this SAX implementation.");
    259            }
    260        }
    261
    262        /**
    263         * Parses an XML file which is loaded using the given URL. All
    264         * needed relative file- and resourcespecification are loaded
    265         * using the URL <code>contentBase</code> as base.
    266         * <p>
    267         * After the report is generated, the ReportDefinition-source and the contentbase are
    268         * stored as string in the reportproperties.
    269         *
    270         * @param file  the URL for the report template file.
    271         * @param contentBase  the URL for the report template content base.
    272         *
    273         * @return the parsed report.
    274         *
    275         * @throws IOException if an I/O error occurs.
    276         * @throws ElementDefinitionException if there is a problem parsing the report template.
    277         */
    278        public Object parse(final URL file, final URL contentBase)
    279            throws ElementDefinitionException, IOException {
    280            if (file == null) {
    281                throw new NullPointerException("File may not be null");
    282            }
    283
    284            final BufferedInputStream bin = new BufferedInputStream(file.openStream());
    285            final InputSource in = new InputSource(bin);
    286            in.setSystemId(file.toString());
    287            final Object result = parse(in, contentBase);
    288            bin.close();
    289            return result;
    290        }
    291
    292    }