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 }