    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.xml;
    17
    18  import java.io.InputStream;
    19  import java.net.URL;
    20  import java.util.HashMap;
    21
    22  import org.apache.commons.jxpath.Container;
    23  import org.apache.commons.jxpath.JXPathException;
    24
    25  /***
    26   * An XML document container reads and parses XML only when it is
    27   * accessed.  JXPath traverses Containers transparently -
    28   * you use the same paths to access objects in containers as you
    29   * do to access those objects directly.  You can create
    30   * XMLDocumentContainers for various XML documents that may or
    31   * may not be accessed by XPaths.  If they are, they will be automatically
    32   * read, parsed and traversed. If they are not - they won't be
    33   * read at all.
    34   *
    35   * @author Dmitri Plotnikov
    36   * @version $Revision: 1.9 $ $Date: 2004/02/29 14:17:37 $
    37   */
    38  public class DocumentContainer extends XMLParser2 implements Container {
    39
    40      public static final String MODEL_DOM = "DOM";
    41      public static final String MODEL_JDOM = "JDOM";
    42
    43      private Object document;
    44      private URL xmlURL;
    45      private String model;
    46
    47      private static HashMap parserClasses = new HashMap();
    48      static {
    49          parserClasses.put(MODEL_DOM,
    50                            "org.apache.commons.jxpath.xml.DOMParser");
    51          parserClasses.put(MODEL_JDOM,
    52                            "org.apache.commons.jxpath.xml.JDOMParser");
    53      }
    54
    55      private static HashMap parsers = new HashMap();
    56
    57      /***
    58       * Add an XML parser.  Parsers for the models "DOM" and "JDOM" are
    59       * pre-registered.
    60       */
    61      public static void registerXMLParser(String model, XMLParser parser) {
    62          parsers.put(model, parser);
    63      }
    64
    65      /***
    66       * Add a class of a custom XML parser.
    67       * Parsers for the models "DOM" and "JDOM" are pre-registered.
    68       */
    69      public static void registerXMLParser(String model, String parserClassName) {
    70          parserClasses.put(model, parserClassName);
    71      }
    72
    73      /***
    74       * Use this constructor if the desired model is DOM.
    75       *
    76       * @param URL is a URL for an XML file.
    77       * Use getClass().getResource(resourceName) to load XML from a
    78       * resource file.
    79       */
    80      public DocumentContainer(URL xmlURL) {
    81          this(xmlURL, MODEL_DOM);
    82      }
    83
    84      /***
    85       * @param  URL is a URL for an XML file. Use getClass().getResource
    86       * (resourceName) to load XML from a resource file.
    87       *
    88       * @param model is one of the MODEL_* constants defined in this class. It
    89       *   determines which parser should be used to load the XML.
    90       */
    91      public DocumentContainer(URL xmlURL, String model) {
    92          this.xmlURL = xmlURL;
    93          if (xmlURL == null) {
    94              throw new JXPathException("XML URL is null");
    95          }
    96          this.model = model;
    97      }
    98
    99      /***
    100      * Reads XML, caches it internally and returns the Document.
    101      */
    102     public Object getValue() {
    103         if (document == null) {
    104             try {
    105                 InputStream stream = null;
    106                 try {
    107                     if (xmlURL != null) {
    108                         stream = xmlURL.openStream();
    109                     }
    110                     document = parseXML(stream);
    111                 }
    112                 finally {
    113                     if (stream != null) {
    114                         stream.close();
    115                     }
    116                 }
    117             }
    118             catch (Exception ex) {
    119                 throw new JXPathException(
    120                     "Cannot read XML from: " + xmlURL.toString(),
    121                     ex);
    122             }
    123         }
    124         return document;
    125     }
    126
    127     /***
    128      * Parses XML using the parser for the specified model.
    129      */
    130     public Object parseXML(InputStream stream) {
    131         XMLParser parser = getParser(model);
    132         if (parser instanceof XMLParser2) {
    133             XMLParser2 parser2 = (XMLParser2) parser;
    134             parser2.setValidating(isValidating());
    135             parser2.setNamespaceAware(isNamespaceAware());
    136             parser2.setIgnoringElementContentWhitespace(
    137                     isIgnoringElementContentWhitespace());
    138             parser2.setExpandEntityReferences(isExpandEntityReferences());
    139             parser2.setIgnoringComments(isIgnoringComments());
    140             parser2.setCoalescing(isCoalescing());
    141         }
    142         return parser.parseXML(stream);
    143     }
    144
    145     /***
    146      * Throws an UnsupportedOperationException
    147      */
    148     public void setValue(Object value) {
    149         throw new UnsupportedOperationException();
    150     }
    151
    152     /***
    153      * Maps a model type to a parser.
    154      */
    155     private static final XMLParser getParser(String model) {
    156         XMLParser parser = (XMLParser) parsers.get(model);
    157         if (parser == null) {
    158             String className = (String) parserClasses.get(model);
    159             if (className == null) {
    160                 throw new JXPathException("Unsupported XML model: " + model);
    161             }
    162             try {
    163                 Class clazz = Class.forName(className);
    164                 parser = (XMLParser) clazz.newInstance();
    165             }
    166             catch (Exception ex) {
    167                 throw new JXPathException(
    168                     "Cannot allocate XMLParser: " + className);
    169             }
    170             parsers.put(model, parser);
    171         }
    172         return parser;
    173     }
    174 }