    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jxpath.xml;
    018
    019    import java.io.IOException;
    020    import java.io.InputStream;
    021    import java.net.URL;
    022    import java.util.HashMap;
    023
    024    import org.apache.commons.jxpath.Container;
    025    import org.apache.commons.jxpath.JXPathException;
    026
    027    /**
    028     * An XML document container reads and parses XML only when it is
    029     * accessed.  JXPath traverses Containers transparently -
    030     * you use the same paths to access objects in containers as you
    031     * do to access those objects directly.  You can create
    032     * XMLDocumentContainers for various XML documents that may or
    033     * may not be accessed by XPaths.  If they are, they will be automatically
    034     * read, parsed and traversed. If they are not - they won't be
    035     * read at all.
    036     *
    037     * @author Dmitri Plotnikov
    038     * @version $Revision: 668329 $ $Date: 2008-06-16 16:59:48 -0500 (Mon, 16 Jun 2008) $
    039     */
    040    public class DocumentContainer extends XMLParser2 implements Container {
    041
    042        /** DOM constant */
    043        public static final String MODEL_DOM = "DOM";
    044
    045        /** JDOM constant */
    046        public static final String MODEL_JDOM = "JDOM";
    047
    048        private static final long serialVersionUID = -8713290334113427066L;
    049
    050        private static HashMap parserClasses = new HashMap();
    051        static {
    052            parserClasses.put(MODEL_DOM,
    053                              "org.apache.commons.jxpath.xml.DOMParser");
    054            parserClasses.put(MODEL_JDOM,
    055                              "org.apache.commons.jxpath.xml.JDOMParser");
    056        }
    057
    058        private static HashMap parsers = new HashMap();
    059
    060        private Object document;
    061        private URL xmlURL;
    062        private String model;
    063
    064        /**
    065         * Add an XML parser.  Parsers for the models "DOM" and "JDOM" are
    066         * pre-registered.
    067         * @param model model name
    068         * @param parser parser
    069         */
    070        public static void registerXMLParser(String model, XMLParser parser) {
    071            parsers.put(model, parser);
    072        }
    073
    074        /**
    075         * Add a class of a custom XML parser.
    076         * Parsers for the models "DOM" and "JDOM" are pre-registered.
    077         * @param model model name
    078         * @param parserClassName parser classname
    079         */
    080        public static void registerXMLParser(String model, String parserClassName) {
    081            parserClasses.put(model, parserClassName);
    082        }
    083
    084        /**
    085         * Use this constructor if the desired model is DOM.
    086         *
    087         * @param xmlURL is a URL for an XML file.
    088         * Use getClass().getResource(resourceName) to load XML from a
    089         * resource file.
    090         */
    091        public DocumentContainer(URL xmlURL) {
    092            this(xmlURL, MODEL_DOM);
    093        }
    094
    095        /**
    096         * Construct a new DocumentContainer.
    097         * @param xmlURL is a URL for an XML file. Use getClass().getResource
    098         *               (resourceName) to load XML from a resource file.
    099         *
    100         * @param model is one of the MODEL_* constants defined in this class. It
    101         *              determines which parser should be used to load the XML.
    102         */
    103        public DocumentContainer(URL xmlURL, String model) {
    104            this.xmlURL = xmlURL;
    105            if (xmlURL == null) {
    106                throw new JXPathException("XML URL is null");
    107            }
    108            this.model = model;
    109        }
    110
    111        /**
    112         * Reads XML, caches it internally and returns the Document.
    113         * @return Object
    114         */
    115        public Object getValue() {
    116            if (document == null) {
    117                try {
    118                    InputStream stream = null;
    119                    try {
    120                        if (xmlURL != null) {
    121                            stream = xmlURL.openStream();
    122                        }
    123                        document = parseXML(stream);
    124                    }
    125                    finally {
    126                        if (stream != null) {
    127                            stream.close();
    128                        }
    129                    }
    130                }
    131                catch (IOException ex) {
    132                    throw new JXPathException(
    133                        "Cannot read XML from: " + xmlURL.toString(),
    134                        ex);
    135                }
    136            }
    137            return document;
    138        }
    139
    140        /**
    141         * Parses XML using the parser for the specified model.
    142         * @param stream InputStream
    143         * @return Object
    144         */
    145        public Object parseXML(InputStream stream) {
    146            XMLParser parser = getParser(model);
    147            if (parser instanceof XMLParser2) {
    148                XMLParser2 parser2 = (XMLParser2) parser;
    149                parser2.setValidating(isValidating());
    150                parser2.setNamespaceAware(isNamespaceAware());
    151                parser2.setIgnoringElementContentWhitespace(
    152                        isIgnoringElementContentWhitespace());
    153                parser2.setExpandEntityReferences(isExpandEntityReferences());
    154                parser2.setIgnoringComments(isIgnoringComments());
    155                parser2.setCoalescing(isCoalescing());
    156            }
    157            return parser.parseXML(stream);
    158        }
    159
    160        /**
    161         * Throws an UnsupportedOperationException.
    162         * @param value value (not) to set
    163         */
    164        public void setValue(Object value) {
    165            throw new UnsupportedOperationException();
    166        }
    167
    168        /**
    169         * Maps a model type to a parser.
    170         * @param model input model type
    171         * @return XMLParser
    172         */
    173        private static XMLParser getParser(String model) {
    174            XMLParser parser = (XMLParser) parsers.get(model);
    175            if (parser == null) {
    176                String className = (String) parserClasses.get(model);
    177                if (className == null) {
    178                    throw new JXPathException("Unsupported XML model: " + model);
    179                }
    180                try {
    181                    Class clazz = Class.forName(className);
    182                    parser = (XMLParser) clazz.newInstance();
    183                }
    184                catch (Exception ex) {
    185                    throw new JXPathException(
    186                        "Cannot allocate XMLParser: " + className, ex);
    187                }
    188                parsers.put(model, parser);
    189            }
    190            return parser;
    191        }
    192    }