view src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogReader.java @ 761:e4bc32cbffad

8130051: Cleanup usage of reflection in jaxp Summary: replaced usage of reflection with direct access where possible, removed obsolete code where possible. Reviewed-by: joehw
author dfuchs
date Tue, 30 Jun 2015 12:04:27 +0200
parents 93a5ed174422
children
line wrap: on
line source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.sun.org.apache.xml.internal.resolver.readers;

import com.sun.org.apache.xml.internal.resolver.Catalog;
import com.sun.org.apache.xml.internal.resolver.CatalogException;
import com.sun.org.apache.xml.internal.resolver.CatalogManager;
import com.sun.org.apache.xml.internal.resolver.helpers.Debug;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.Hashtable;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.AttributeList;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DocumentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;
import sun.reflect.misc.ReflectUtil;

/**
 * A SAX-based CatalogReader.
 *
 * <p>This class is used to read XML Catalogs using the SAX. This reader
 * has an advantage over the DOM-based reader in that it functions on
 * the stream of SAX events. It has the disadvantage
 * that it cannot look around in the tree.</p>
 *
 * <p>Since the choice of CatalogReaders (in the InputStream case) can only
 * be made on the basis of MIME type, the following problem occurs: only
 * one CatalogReader can exist for all XML mime types. In order to get
 * around this problem, the SAXCatalogReader relies on a set of external
 * CatalogParsers to actually build the catalog.</p>
 *
 * <p>The selection of CatalogParsers is made on the basis of the QName
 * of the root element of the document.</p>
 *
 * @see Catalog
 * @see CatalogReader
 * @see SAXCatalogReader
 * @see TextCatalogReader
 * @see DOMCatalogParser
 *
 * @author Norman Walsh
 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
 *
 */
public class SAXCatalogReader implements CatalogReader, ContentHandler, DocumentHandler {
    /** The SAX Parser Factory */
    protected SAXParserFactory parserFactory = null;

    /** The SAX Parser Class */
    protected String parserClass = null;

    /**
     * Mapping table from QNames to CatalogParser classes.
     *
     * <p>Each key in this hash table has the form "elementname"
     * or "{namespaceuri}elementname". The former is used if the
     * namespace URI is null.</p>
     */
    protected Hashtable namespaceMap = new Hashtable();

    /** The parser in use for the current catalog. */
    private SAXCatalogParser saxParser = null;

    /** Set if something goes horribly wrong. It allows the class to
     * ignore the rest of the events that are received.
     */
    private boolean abandonHope = false;

    /** The Catalog that we're working for. */
    private Catalog catalog;

    /** Set the XML SAX Parser Factory.
     */
    public void setParserFactory(SAXParserFactory parserFactory) {
        this.parserFactory = parserFactory;
    }

    /** Set the XML SAX Parser Class
     */
    public void setParserClass(String parserClass) {
        this.parserClass = parserClass;
    }

    /** Get the parser factory currently in use. */
    public SAXParserFactory getParserFactory() {
        return parserFactory;
    }

    /** Get the parser class currently in use. */
    public String getParserClass() {
        return parserClass;
    }

    /** The debug class to use for this reader.
     *
     * This is a bit of a hack. Anyway, whenever we read for a catalog,
     * we extract the debug object
     * from the catalog's manager so that we can use it to print messages.
     *
     * In production, we don't really expect any messages so it doesn't
     * really matter. But it's still a bit of a hack.
     */
    protected Debug debug = CatalogManager.getStaticManager().debug;

    /** The constructor */
    public SAXCatalogReader() {
        parserFactory = null;
        parserClass = null;
    }

    /** The constructor */
    public SAXCatalogReader(SAXParserFactory parserFactory) {
        this.parserFactory = parserFactory;
    }

    /** The constructor */
    public SAXCatalogReader(String parserClass) {
        this.parserClass = parserClass;
    }

    /**
     * Set the SAXCatalogParser class for the given namespace/root
     * element type.
     */
    public void setCatalogParser(String namespaceURI,
            String rootElement,
            String parserClass) {
        namespaceURI = namespaceURI != null ? namespaceURI.trim() : "";
        namespaceMap.put("{"+namespaceURI+"}"+rootElement, parserClass);
    }

    /**
     * Get the SAXCatalogParser class for the given namespace/root
     * element type.
     */
    public String getCatalogParser(String namespaceURI,
            String rootElement) {
        namespaceURI = namespaceURI != null ? namespaceURI.trim() : "";
        return (String) namespaceMap.get("{"+namespaceURI+"}"+rootElement);
    }

    /**
     * Parse an XML Catalog file.
     *
     * @param catalog The catalog to which this catalog file belongs
     * @param fileUrl The URL or filename of the catalog file to process
     *
     * @throws MalformedURLException Improper fileUrl
     * @throws IOException Error reading catalog file
     */
    public void readCatalog(Catalog catalog, String fileUrl)
            throws MalformedURLException, IOException,
            CatalogException {

        URL url = null;

        try {
            url = new URL(fileUrl);
        } catch (MalformedURLException e) {
            url = new URL("file:///" + fileUrl);
        }

        debug = catalog.getCatalogManager().debug;

        try {
            URLConnection urlCon = url.openConnection();
            readCatalog(catalog, urlCon.getInputStream());
        } catch (FileNotFoundException e) {
            catalog.getCatalogManager().debug.message(1, "Failed to load catalog, file not found",
                    url.toString());
        }
    }

    /**
     * Parse an XML Catalog stream.
     *
     * @param catalog The catalog to which this catalog file belongs
     * @param is The input stream from which the catalog will be read
     *
     * @throws MalformedURLException Improper fileUrl
     * @throws IOException Error reading catalog file
     * @throws CatalogException A Catalog exception
     */
    public void readCatalog(Catalog catalog, InputStream is)
            throws IOException, CatalogException {

        // Create an instance of the parser
        if (parserFactory == null && parserClass == null) {
            debug.message(1, "Cannot read SAX catalog without a parser");
            throw new CatalogException(CatalogException.UNPARSEABLE);
        }

        debug = catalog.getCatalogManager().debug;
        EntityResolver bResolver = catalog.getCatalogManager().getBootstrapResolver();

        this.catalog = catalog;

        try {
            if (parserFactory != null) {
                SAXParser parser = parserFactory.newSAXParser();
                SAXParserHandler spHandler = new SAXParserHandler();
                spHandler.setContentHandler(this);
                if (bResolver != null) {
                    spHandler.setEntityResolver(bResolver);
                }
                parser.parse(new InputSource(is), spHandler);
            } else {
                Class<?> c =  ReflectUtil.forName(parserClass);
                if (!Parser.class.isAssignableFrom(c)) {
                    throw new ClassCastException(parserClass
                                + " cannot be cast to "
                                + Parser.class.getName());
                }
                Parser parser = (Parser) c.newInstance();
                parser.setDocumentHandler(this);
                if (bResolver != null) {
                    parser.setEntityResolver(bResolver);
                }
                parser.parse(new InputSource(is));
            }
        } catch (ClassNotFoundException cnfe) {
            throw new CatalogException(CatalogException.UNPARSEABLE);
        } catch (IllegalAccessException iae) {
            throw new CatalogException(CatalogException.UNPARSEABLE);
        } catch (InstantiationException ie) {
            throw new CatalogException(CatalogException.UNPARSEABLE);
        } catch (ParserConfigurationException pce) {
            throw new CatalogException(CatalogException.UNKNOWN_FORMAT);
        } catch (SAXException se) {
            Exception e = se.getException();
            // FIXME: there must be a better way
            UnknownHostException uhe = new UnknownHostException();
            FileNotFoundException fnfe = new FileNotFoundException();
            if (e != null) {
                if (e.getClass() == uhe.getClass()) {
                    throw new CatalogException(CatalogException.PARSE_FAILED,
                            e.toString());
                } else if (e.getClass() == fnfe.getClass()) {
                    throw new CatalogException(CatalogException.PARSE_FAILED,
                            e.toString());
                }
            }
            throw new CatalogException(se);
        }
    }

    // ----------------------------------------------------------------------
    // Implement the SAX ContentHandler interface

    /** The SAX <code>setDocumentLocator</code> method. Does nothing. */
    public void setDocumentLocator (Locator locator) {
        if (saxParser != null) {
            saxParser.setDocumentLocator(locator);
        }
    }

    /** The SAX <code>startDocument</code> method. Does nothing. */
    public void startDocument () throws SAXException {
        saxParser = null;
        abandonHope = false;
        return;
    }

    /** The SAX <code>endDocument</code> method. Does nothing. */
    public void endDocument ()throws SAXException {
        if (saxParser != null) {
            saxParser.endDocument();
        }
    }

    /**
     * The SAX <code>startElement</code> method.
     *
     * <p>The catalog parser is selected based on the namespace of the
     * first element encountered in the catalog.</p>
     */
    public void startElement (String name,
            AttributeList atts)
                    throws SAXException {

        if (abandonHope) {
            return;
        }

        if (saxParser == null) {
            String prefix = "";
            if (name.indexOf(':') > 0) {
                prefix = name.substring(0, name.indexOf(':'));
            }

            String localName = name;
            if (localName.indexOf(':') > 0) {
                localName = localName.substring(localName.indexOf(':')+1);
            }

            String namespaceURI = null;
            if (prefix.length() == 0) {
                namespaceURI = atts.getValue("xmlns");
            } else {
                namespaceURI = atts.getValue("xmlns:" + prefix);
            }

            String saxParserClass = getCatalogParser(namespaceURI,
                    localName);

            if (saxParserClass == null) {
                abandonHope = true;
                if (namespaceURI == null) {
                    debug.message(2, "No Catalog parser for " + name);
                } else {
                    debug.message(2, "No Catalog parser for "
                            + "{" + namespaceURI + "}"
                            + name);
                }
                return;
            }

            try {
                saxParser = (SAXCatalogParser)
                        ReflectUtil.forName(saxParserClass).newInstance();

                saxParser.setCatalog(catalog);
                saxParser.startDocument();
                saxParser.startElement(name, atts);
            } catch (ClassNotFoundException cnfe) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, cnfe.toString());
            } catch (InstantiationException ie) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, ie.toString());
            } catch (IllegalAccessException iae) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, iae.toString());
            } catch (ClassCastException cce ) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, cce.toString());
            }
        } else {
            saxParser.startElement(name, atts);
        }
    }

    /**
     * The SAX2 <code>startElement</code> method.
     *
     * <p>The catalog parser is selected based on the namespace of the
     * first element encountered in the catalog.</p>
     */
    public void startElement (String namespaceURI,
            String localName,
            String qName,
            Attributes atts)
                    throws SAXException {

        if (abandonHope) {
            return;
        }

        if (saxParser == null) {
            String saxParserClass = getCatalogParser(namespaceURI,
                    localName);

            if (saxParserClass == null) {
                abandonHope = true;
                if (namespaceURI == null) {
                    debug.message(2, "No Catalog parser for " + localName);
                } else {
                    debug.message(2, "No Catalog parser for "
                            + "{" + namespaceURI + "}"
                            + localName);
                }
                return;
            }

            try {
                saxParser = (SAXCatalogParser)
                        ReflectUtil.forName(saxParserClass).newInstance();

                saxParser.setCatalog(catalog);
                saxParser.startDocument();
                saxParser.startElement(namespaceURI, localName, qName, atts);
            } catch (ClassNotFoundException cnfe) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, cnfe.toString());
            } catch (InstantiationException ie) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, ie.toString());
            } catch (IllegalAccessException iae) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, iae.toString());
            } catch (ClassCastException cce ) {
                saxParser = null;
                abandonHope = true;
                debug.message(2, cce.toString());
            }
        } else {
            saxParser.startElement(namespaceURI, localName, qName, atts);
        }
    }

    /** The SAX <code>endElement</code> method. Does nothing. */
    public void endElement (String name) throws SAXException {
        if (saxParser != null) {
            saxParser.endElement(name);
        }
    }

    /** The SAX2 <code>endElement</code> method. Does nothing. */
    public void endElement (String namespaceURI,
            String localName,
            String qName) throws SAXException {
        if (saxParser != null) {
            saxParser.endElement(namespaceURI, localName, qName);
        }
    }

    /** The SAX <code>characters</code> method. Does nothing. */
    public void characters (char ch[], int start, int length)
            throws SAXException {
        if (saxParser != null) {
            saxParser.characters(ch, start, length);
        }
    }

    /** The SAX <code>ignorableWhitespace</code> method. Does nothing. */
    public void ignorableWhitespace (char ch[], int start, int length)
            throws SAXException {
        if (saxParser != null) {
            saxParser.ignorableWhitespace(ch, start, length);
        }
    }

    /** The SAX <code>processingInstruction</code> method. Does nothing. */
    public void processingInstruction (String target, String data)
            throws SAXException {
        if (saxParser != null) {
            saxParser.processingInstruction(target, data);
        }
    }

    /** The SAX <code>startPrefixMapping</code> method. Does nothing. */
    public void startPrefixMapping (String prefix, String uri)
            throws SAXException {
        if (saxParser != null) {
            saxParser.startPrefixMapping (prefix, uri);
        }
    }

    /** The SAX <code>endPrefixMapping</code> method. Does nothing. */
    public void endPrefixMapping (String prefix)
            throws SAXException {
        if (saxParser != null) {
            saxParser.endPrefixMapping (prefix);
        }
    }

    /** The SAX <code>skippedentity</code> method. Does nothing. */
    public void skippedEntity (String name)
            throws SAXException {
        if (saxParser != null) {
            saxParser.skippedEntity(name);
        }
    }
}