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