/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.xml; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import libcore.io.IoUtils; import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.XMLReader; import org.xml.sax.ext.LexicalHandler; /** * SAX wrapper around Expat. Interns strings. Does not support validation. * Does not support {@link DTDHandler}. */ public class ExpatReader implements XMLReader { /* * ExpatParser accesses these fields directly during parsing. The user * should be able to safely change them during parsing. */ /*package*/ ContentHandler contentHandler; /*package*/ DTDHandler dtdHandler; /*package*/ EntityResolver entityResolver; /*package*/ ErrorHandler errorHandler; /*package*/ LexicalHandler lexicalHandler; private boolean processNamespaces = true; private boolean processNamespacePrefixes = false; private static final String LEXICAL_HANDLER_PROPERTY = "http://xml.org/sax/properties/lexical-handler"; private static class Feature { private static final String BASE_URI = "http://xml.org/sax/features/"; private static final String VALIDATION = BASE_URI + "validation"; private static final String NAMESPACES = BASE_URI + "namespaces"; private static final String NAMESPACE_PREFIXES = BASE_URI + "namespace-prefixes"; private static final String STRING_INTERNING = BASE_URI + "string-interning"; private static final String EXTERNAL_GENERAL_ENTITIES = BASE_URI + "external-general-entities"; private static final String EXTERNAL_PARAMETER_ENTITIES = BASE_URI + "external-parameter-entities"; } public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { throw new NullPointerException("name == null"); } if (name.equals(Feature.VALIDATION) || name.equals(Feature.EXTERNAL_GENERAL_ENTITIES) || name.equals(Feature.EXTERNAL_PARAMETER_ENTITIES)) { return false; } if (name.equals(Feature.NAMESPACES)) { return processNamespaces; } if (name.equals(Feature.NAMESPACE_PREFIXES)) { return processNamespacePrefixes; } if (name.equals(Feature.STRING_INTERNING)) { return true; } throw new SAXNotRecognizedException(name); } public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { throw new NullPointerException("name == null"); } if (name.equals(Feature.VALIDATION) || name.equals(Feature.EXTERNAL_GENERAL_ENTITIES) || name.equals(Feature.EXTERNAL_PARAMETER_ENTITIES)) { if (value) { throw new SAXNotSupportedException("Cannot enable " + name); } else { // Default. return; } } if (name.equals(Feature.NAMESPACES)) { processNamespaces = value; return; } if (name.equals(Feature.NAMESPACE_PREFIXES)) { processNamespacePrefixes = value; return; } if (name.equals(Feature.STRING_INTERNING)) { if (value) { // Default. return; } else { throw new SAXNotSupportedException("Cannot disable " + name); } } throw new SAXNotRecognizedException(name); } public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { throw new NullPointerException("name == null"); } if (name.equals(LEXICAL_HANDLER_PROPERTY)) { return lexicalHandler; } throw new SAXNotRecognizedException(name); } public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { throw new NullPointerException("name == null"); } if (name.equals(LEXICAL_HANDLER_PROPERTY)) { // The object must implement LexicalHandler if (value instanceof LexicalHandler || value == null) { this.lexicalHandler = (LexicalHandler) value; return; } throw new SAXNotSupportedException("value doesn't implement " + "org.xml.sax.ext.LexicalHandler"); } throw new SAXNotRecognizedException(name); } public void setEntityResolver(EntityResolver resolver) { this.entityResolver = resolver; } public EntityResolver getEntityResolver() { return entityResolver; } public void setDTDHandler(DTDHandler dtdHandler) { this.dtdHandler = dtdHandler; } public DTDHandler getDTDHandler() { return dtdHandler; } public void setContentHandler(ContentHandler handler) { this.contentHandler = handler; } public ContentHandler getContentHandler() { return this.contentHandler; } public void setErrorHandler(ErrorHandler handler) { this.errorHandler = handler; } public ErrorHandler getErrorHandler() { return errorHandler; } /** * Returns the current lexical handler. * * @return the current lexical handler, or null if none has been registered * @see #setLexicalHandler */ public LexicalHandler getLexicalHandler() { return lexicalHandler; } /** * Registers a lexical event handler. Supports neither * {@link LexicalHandler#startEntity(String)} nor * {@link LexicalHandler#endEntity(String)}. * * <p>If the application does not register a lexical handler, all * lexical events reported by the SAX parser will be silently * ignored.</p> * * <p>Applications may register a new or different handler in the * middle of a parse, and the SAX parser must begin using the new * handler immediately.</p> * * @param lexicalHandler listens for lexical events * @see #getLexicalHandler() */ public void setLexicalHandler(LexicalHandler lexicalHandler) { this.lexicalHandler = lexicalHandler; } /** * Returns true if this SAX parser processes namespaces. * * @see #setNamespaceProcessingEnabled(boolean) */ public boolean isNamespaceProcessingEnabled() { return processNamespaces; } /** * Enables or disables namespace processing. Set to true by default. If you * enable namespace processing, the parser will invoke * {@link ContentHandler#startPrefixMapping(String, String)} and * {@link ContentHandler#endPrefixMapping(String)}, and it will filter * out namespace declarations from element attributes. * * @see #isNamespaceProcessingEnabled() */ public void setNamespaceProcessingEnabled(boolean processNamespaces) { this.processNamespaces = processNamespaces; } public void parse(InputSource input) throws IOException, SAXException { if (processNamespacePrefixes && processNamespaces) { /* * Expat has XML_SetReturnNSTriplet, but that still doesn't * include xmlns attributes like this feature requires. We may * have to implement namespace processing ourselves if we want * this (not too difficult). We obviously "support" namespace * prefixes if namespaces are disabled. */ throw new SAXNotSupportedException("The 'namespace-prefix' " + "feature is not supported while the 'namespaces' " + "feature is enabled."); } // Try the character stream. Reader reader = input.getCharacterStream(); if (reader != null) { try { parse(reader, input.getPublicId(), input.getSystemId()); } finally { IoUtils.closeQuietly(reader); } return; } // Try the byte stream. InputStream in = input.getByteStream(); String encoding = input.getEncoding(); if (in != null) { try { parse(in, encoding, input.getPublicId(), input.getSystemId()); } finally { IoUtils.closeQuietly(in); } return; } String systemId = input.getSystemId(); if (systemId == null) { throw new SAXException("No input specified."); } // Try the system id. in = ExpatParser.openUrl(systemId); try { parse(in, encoding, input.getPublicId(), systemId); } finally { IoUtils.closeQuietly(in); } } private void parse(Reader in, String publicId, String systemId) throws IOException, SAXException { ExpatParser parser = new ExpatParser( ExpatParser.CHARACTER_ENCODING, this, processNamespaces, publicId, systemId ); parser.parseDocument(in); } private void parse(InputStream in, String encoding, String publicId, String systemId) throws IOException, SAXException { ExpatParser parser = new ExpatParser( encoding, this, processNamespaces, publicId, systemId ); parser.parseDocument(in); } public void parse(String systemId) throws IOException, SAXException { parse(new InputSource(systemId)); } }