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 }