1 /* 2 * Copyright (C) 2008 The Android Open Source Project 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 17 package org.apache.harmony.xml; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.io.Reader; 22 import libcore.io.IoUtils; 23 import org.xml.sax.ContentHandler; 24 import org.xml.sax.DTDHandler; 25 import org.xml.sax.EntityResolver; 26 import org.xml.sax.ErrorHandler; 27 import org.xml.sax.InputSource; 28 import org.xml.sax.SAXException; 29 import org.xml.sax.SAXNotRecognizedException; 30 import org.xml.sax.SAXNotSupportedException; 31 import org.xml.sax.XMLReader; 32 import org.xml.sax.ext.LexicalHandler; 33 34 /** 35 * SAX wrapper around Expat. Interns strings. Does not support validation. 36 * Does not support {@link DTDHandler}. 37 */ 38 public class ExpatReader implements XMLReader { 39 /* 40 * ExpatParser accesses these fields directly during parsing. The user 41 * should be able to safely change them during parsing. 42 */ 43 /*package*/ ContentHandler contentHandler; 44 /*package*/ DTDHandler dtdHandler; 45 /*package*/ EntityResolver entityResolver; 46 /*package*/ ErrorHandler errorHandler; 47 /*package*/ LexicalHandler lexicalHandler; 48 49 private boolean processNamespaces = true; 50 private boolean processNamespacePrefixes = false; 51 52 private static final String LEXICAL_HANDLER_PROPERTY 53 = "http://xml.org/sax/properties/lexical-handler"; 54 55 private static class Feature { 56 private static final String BASE_URI = "http://xml.org/sax/features/"; 57 private static final String VALIDATION = BASE_URI + "validation"; 58 private static final String NAMESPACES = BASE_URI + "namespaces"; 59 private static final String NAMESPACE_PREFIXES = BASE_URI + "namespace-prefixes"; 60 private static final String STRING_INTERNING = BASE_URI + "string-interning"; 61 private static final String EXTERNAL_GENERAL_ENTITIES 62 = BASE_URI + "external-general-entities"; 63 private static final String EXTERNAL_PARAMETER_ENTITIES 64 = BASE_URI + "external-parameter-entities"; 65 } 66 getFeature(String name)67 public boolean getFeature(String name) 68 throws SAXNotRecognizedException, SAXNotSupportedException { 69 if (name == null) { 70 throw new NullPointerException("name == null"); 71 } 72 73 if (name.equals(Feature.VALIDATION) 74 || name.equals(Feature.EXTERNAL_GENERAL_ENTITIES) 75 || name.equals(Feature.EXTERNAL_PARAMETER_ENTITIES)) { 76 return false; 77 } 78 79 if (name.equals(Feature.NAMESPACES)) { 80 return processNamespaces; 81 } 82 83 if (name.equals(Feature.NAMESPACE_PREFIXES)) { 84 return processNamespacePrefixes; 85 } 86 87 if (name.equals(Feature.STRING_INTERNING)) { 88 return true; 89 } 90 91 throw new SAXNotRecognizedException(name); 92 } 93 setFeature(String name, boolean value)94 public void setFeature(String name, boolean value) 95 throws SAXNotRecognizedException, SAXNotSupportedException { 96 if (name == null) { 97 throw new NullPointerException("name == null"); 98 } 99 100 if (name.equals(Feature.VALIDATION) 101 || name.equals(Feature.EXTERNAL_GENERAL_ENTITIES) 102 || name.equals(Feature.EXTERNAL_PARAMETER_ENTITIES)) { 103 if (value) { 104 throw new SAXNotSupportedException("Cannot enable " + name); 105 } else { 106 // Default. 107 return; 108 } 109 } 110 111 if (name.equals(Feature.NAMESPACES)) { 112 processNamespaces = value; 113 return; 114 } 115 116 if (name.equals(Feature.NAMESPACE_PREFIXES)) { 117 processNamespacePrefixes = value; 118 return; 119 } 120 121 if (name.equals(Feature.STRING_INTERNING)) { 122 if (value) { 123 // Default. 124 return; 125 } else { 126 throw new SAXNotSupportedException("Cannot disable " + name); 127 } 128 } 129 130 throw new SAXNotRecognizedException(name); 131 } 132 getProperty(String name)133 public Object getProperty(String name) 134 throws SAXNotRecognizedException, SAXNotSupportedException { 135 if (name == null) { 136 throw new NullPointerException("name == null"); 137 } 138 139 if (name.equals(LEXICAL_HANDLER_PROPERTY)) { 140 return lexicalHandler; 141 } 142 143 throw new SAXNotRecognizedException(name); 144 } 145 setProperty(String name, Object value)146 public void setProperty(String name, Object value) 147 throws SAXNotRecognizedException, SAXNotSupportedException { 148 if (name == null) { 149 throw new NullPointerException("name == null"); 150 } 151 152 if (name.equals(LEXICAL_HANDLER_PROPERTY)) { 153 // The object must implement LexicalHandler 154 if (value instanceof LexicalHandler || value == null) { 155 this.lexicalHandler = (LexicalHandler) value; 156 return; 157 } 158 throw new SAXNotSupportedException("value doesn't implement " + 159 "org.xml.sax.ext.LexicalHandler"); 160 } 161 162 throw new SAXNotRecognizedException(name); 163 } 164 setEntityResolver(EntityResolver resolver)165 public void setEntityResolver(EntityResolver resolver) { 166 this.entityResolver = resolver; 167 } 168 getEntityResolver()169 public EntityResolver getEntityResolver() { 170 return entityResolver; 171 } 172 setDTDHandler(DTDHandler dtdHandler)173 public void setDTDHandler(DTDHandler dtdHandler) { 174 this.dtdHandler = dtdHandler; 175 } 176 getDTDHandler()177 public DTDHandler getDTDHandler() { 178 return dtdHandler; 179 } 180 setContentHandler(ContentHandler handler)181 public void setContentHandler(ContentHandler handler) { 182 this.contentHandler = handler; 183 } 184 getContentHandler()185 public ContentHandler getContentHandler() { 186 return this.contentHandler; 187 } 188 setErrorHandler(ErrorHandler handler)189 public void setErrorHandler(ErrorHandler handler) { 190 this.errorHandler = handler; 191 } 192 getErrorHandler()193 public ErrorHandler getErrorHandler() { 194 return errorHandler; 195 } 196 197 /** 198 * Returns the current lexical handler. 199 * 200 * @return the current lexical handler, or null if none has been registered 201 * @see #setLexicalHandler 202 */ getLexicalHandler()203 public LexicalHandler getLexicalHandler() { 204 return lexicalHandler; 205 } 206 207 /** 208 * Registers a lexical event handler. Supports neither 209 * {@link LexicalHandler#startEntity(String)} nor 210 * {@link LexicalHandler#endEntity(String)}. 211 * 212 * <p>If the application does not register a lexical handler, all 213 * lexical events reported by the SAX parser will be silently 214 * ignored.</p> 215 * 216 * <p>Applications may register a new or different handler in the 217 * middle of a parse, and the SAX parser must begin using the new 218 * handler immediately.</p> 219 * 220 * @param lexicalHandler listens for lexical events 221 * @see #getLexicalHandler() 222 */ setLexicalHandler(LexicalHandler lexicalHandler)223 public void setLexicalHandler(LexicalHandler lexicalHandler) { 224 this.lexicalHandler = lexicalHandler; 225 } 226 227 /** 228 * Returns true if this SAX parser processes namespaces. 229 * 230 * @see #setNamespaceProcessingEnabled(boolean) 231 */ isNamespaceProcessingEnabled()232 public boolean isNamespaceProcessingEnabled() { 233 return processNamespaces; 234 } 235 236 /** 237 * Enables or disables namespace processing. Set to true by default. If you 238 * enable namespace processing, the parser will invoke 239 * {@link ContentHandler#startPrefixMapping(String, String)} and 240 * {@link ContentHandler#endPrefixMapping(String)}, and it will filter 241 * out namespace declarations from element attributes. 242 * 243 * @see #isNamespaceProcessingEnabled() 244 */ setNamespaceProcessingEnabled(boolean processNamespaces)245 public void setNamespaceProcessingEnabled(boolean processNamespaces) { 246 this.processNamespaces = processNamespaces; 247 } 248 parse(InputSource input)249 public void parse(InputSource input) throws IOException, SAXException { 250 if (processNamespacePrefixes && processNamespaces) { 251 /* 252 * Expat has XML_SetReturnNSTriplet, but that still doesn't 253 * include xmlns attributes like this feature requires. We may 254 * have to implement namespace processing ourselves if we want 255 * this (not too difficult). We obviously "support" namespace 256 * prefixes if namespaces are disabled. 257 */ 258 throw new SAXNotSupportedException("The 'namespace-prefix' " + 259 "feature is not supported while the 'namespaces' " + 260 "feature is enabled."); 261 } 262 263 // Try the character stream. 264 Reader reader = input.getCharacterStream(); 265 if (reader != null) { 266 try { 267 parse(reader, input.getPublicId(), input.getSystemId()); 268 } finally { 269 IoUtils.closeQuietly(reader); 270 } 271 return; 272 } 273 274 // Try the byte stream. 275 InputStream in = input.getByteStream(); 276 String encoding = input.getEncoding(); 277 if (in != null) { 278 try { 279 parse(in, encoding, input.getPublicId(), input.getSystemId()); 280 } finally { 281 IoUtils.closeQuietly(in); 282 } 283 return; 284 } 285 286 String systemId = input.getSystemId(); 287 if (systemId == null) { 288 throw new SAXException("No input specified."); 289 } 290 291 // Try the system id. 292 in = ExpatParser.openUrl(systemId); 293 try { 294 parse(in, encoding, input.getPublicId(), systemId); 295 } finally { 296 IoUtils.closeQuietly(in); 297 } 298 } 299 parse(Reader in, String publicId, String systemId)300 private void parse(Reader in, String publicId, String systemId) 301 throws IOException, SAXException { 302 ExpatParser parser = new ExpatParser( 303 ExpatParser.CHARACTER_ENCODING, 304 this, 305 processNamespaces, 306 publicId, 307 systemId 308 ); 309 parser.parseDocument(in); 310 } 311 parse(InputStream in, String charsetName, String publicId, String systemId)312 private void parse(InputStream in, String charsetName, String publicId, String systemId) 313 throws IOException, SAXException { 314 ExpatParser parser = 315 new ExpatParser(charsetName, this, processNamespaces, publicId, systemId); 316 parser.parseDocument(in); 317 } 318 parse(String systemId)319 public void parse(String systemId) throws IOException, SAXException { 320 parse(new InputSource(systemId)); 321 } 322 } 323