Home | History | Annotate | Download | only in xml
      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 
     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 
     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 
    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 
    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 
    165     public void setEntityResolver(EntityResolver resolver) {
    166         this.entityResolver = resolver;
    167     }
    168 
    169     public EntityResolver getEntityResolver() {
    170         return entityResolver;
    171     }
    172 
    173     public void setDTDHandler(DTDHandler dtdHandler) {
    174         this.dtdHandler = dtdHandler;
    175     }
    176 
    177     public DTDHandler getDTDHandler() {
    178         return dtdHandler;
    179     }
    180 
    181     public void setContentHandler(ContentHandler handler) {
    182         this.contentHandler = handler;
    183     }
    184 
    185     public ContentHandler getContentHandler() {
    186         return this.contentHandler;
    187     }
    188 
    189     public void setErrorHandler(ErrorHandler handler) {
    190         this.errorHandler = handler;
    191     }
    192 
    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      */
    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      */
    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      */
    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      */
    245     public void setNamespaceProcessingEnabled(boolean processNamespaces) {
    246         this.processNamespaces = processNamespaces;
    247     }
    248 
    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 
    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 
    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 
    319     public void parse(String systemId) throws IOException, SAXException {
    320         parse(new InputSource(systemId));
    321     }
    322 }
    323