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 */ 017package org.apache.commons.jxpath.xml; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.URL; 022import java.util.HashMap; 023 024import org.apache.commons.jxpath.Container; 025import org.apache.commons.jxpath.JXPathException; 026import org.apache.commons.jxpath.util.ClassLoaderUtil; 027 028/** 029 * An XML document container reads and parses XML only when it is 030 * accessed. JXPath traverses Containers transparently - 031 * you use the same paths to access objects in containers as you 032 * do to access those objects directly. You can create 033 * XMLDocumentContainers for various XML documents that may or 034 * may not be accessed by XPaths. If they are, they will be automatically 035 * read, parsed and traversed. If they are not - they won't be 036 * read at all. 037 * 038 * @author Dmitri Plotnikov 039 * @version $Revision: 916559 $ $Date: 2010-02-26 04:55:46 +0100 (Fr, 26 Feb 2010) $ 040 */ 041public class DocumentContainer extends XMLParser2 implements Container { 042 043 /** DOM constant */ 044 public static final String MODEL_DOM = "DOM"; 045 046 /** JDOM constant */ 047 public static final String MODEL_JDOM = "JDOM"; 048 049 private static final long serialVersionUID = -8713290334113427066L; 050 051 private static HashMap parserClasses = new HashMap(); 052 static { 053 parserClasses.put(MODEL_DOM, 054 "org.apache.commons.jxpath.xml.DOMParser"); 055 parserClasses.put(MODEL_JDOM, 056 "org.apache.commons.jxpath.xml.JDOMParser"); 057 } 058 059 private static HashMap parsers = new HashMap(); 060 061 private Object document; 062 private URL xmlURL; 063 private String model; 064 065 /** 066 * Add an XML parser. Parsers for the models "DOM" and "JDOM" are 067 * pre-registered. 068 * @param model model name 069 * @param parser parser 070 */ 071 public static void registerXMLParser(String model, XMLParser parser) { 072 parsers.put(model, parser); 073 } 074 075 /** 076 * Add a class of a custom XML parser. 077 * Parsers for the models "DOM" and "JDOM" are pre-registered. 078 * @param model model name 079 * @param parserClassName parser classname 080 */ 081 public static void registerXMLParser(String model, String parserClassName) { 082 parserClasses.put(model, parserClassName); 083 } 084 085 /** 086 * Use this constructor if the desired model is DOM. 087 * 088 * @param xmlURL is a URL for an XML file. 089 * Use getClass().getResource(resourceName) to load XML from a 090 * resource file. 091 */ 092 public DocumentContainer(URL xmlURL) { 093 this(xmlURL, MODEL_DOM); 094 } 095 096 /** 097 * Construct a new DocumentContainer. 098 * @param xmlURL is a URL for an XML file. Use getClass().getResource 099 * (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}