    001    /* $Id: XercesParser.java 729116 2008-12-23 21:06:34Z rahul $
    002     *
    003     * Licensed to the Apache Software Foundation (ASF) under one or more
    004     * contributor license agreements.  See the NOTICE file distributed with
    005     * this work for additional information regarding copyright ownership.
    006     * The ASF licenses this file to You under the Apache License, Version 2.0
    007     * (the "License"); you may not use this file except in compliance with
    008     * the License.  You may obtain a copy of the License at
    009     *
    010     *      http://www.apache.org/licenses/LICENSE-2.0
    011     *
    012     * Unless required by applicable law or agreed to in writing, software
    013     * distributed under the License is distributed on an "AS IS" BASIS,
    014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    015     * See the License for the specific language governing permissions and
    016     * limitations under the License.
    017     */
    018
    019
    020    package org.apache.commons.digester.parser;
    021
    022    import java.lang.reflect.Method;
    023    import java.util.Properties;
    024
    025    import javax.xml.parsers.ParserConfigurationException;
    026    import javax.xml.parsers.SAXParser;
    027    import javax.xml.parsers.SAXParserFactory;
    028    import javax.xml.validation.Schema;
    029
    030    import org.apache.commons.digester.Digester;
    031    import org.apache.commons.logging.Log;
    032    import org.apache.commons.logging.LogFactory;
    033    import org.xml.sax.SAXException;
    034    import org.xml.sax.SAXNotRecognizedException;
    035    import org.xml.sax.SAXNotSupportedException;
    036
    037    /**
    038     * Create a <code>SAXParser</code> based on the underlying Xerces version.
    039     * Currently, Xerces 2.3 and up doesn't implement schema validation the same way
    040     * 2.1 was. In other to support schema validation in a portable way between
    041     * parser, some features/properties need to be set.
    042     *
    043     * @since 1.6
    044     * @deprecated Create an XMLParser instance yourself, configure validation
    045     *             appropriately, and pass it as a parameter to the
    046     *             {@link Digester} constructor, or use
    047     *             {@link Digester#setXMLSchema(Schema)} for validation.
    048     */
    049
    050    public class XercesParser{
    051
    052        /**
    053         * The Log to which all SAX event related logging calls will be made.
    054         */
    055        protected static Log log =
    056            LogFactory.getLog("org.apache.commons.digester.Digester.sax");
    057
    058
    059        /**
    060         * The JAXP 1.2 property required to set up the schema location.
    061         */
    062        private static final String JAXP_SCHEMA_SOURCE =
    063            "http://java.sun.com/xml/jaxp/properties/schemaSource";
    064
    065
    066        /**
    067         * The JAXP 1.2 property to set up the schemaLanguage used.
    068         */
    069        protected static String JAXP_SCHEMA_LANGUAGE =
    070            "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
    071
    072
    073        /**
    074         * Xerces dynamic validation property
    075         */
    076        protected static String XERCES_DYNAMIC =
    077            "http://apache.org/xml/features/validation/dynamic";
    078
    079
    080        /**
    081         * Xerces schema validation property
    082         */
    083        protected static String XERCES_SCHEMA =
    084            "http://apache.org/xml/features/validation/schema";
    085
    086
    087        /**
    088         * A <code>float</code> representing the underlying Xerces version
    089         */
    090        protected static float version;
    091
    092
    093        /**
    094         * The current Xerces version.
    095         */
    096        protected static String versionNumber = null;
    097
    098
    099        /**
    100         * Return the current Xerces version.
    101         * @return the current Xerces version.
    102         */
    103        private static String getXercesVersion() {
    104            // If for some reason we can't get the version, set it to 1.0.
    105            String versionNumber = "1.0";
    106            try{
    107                // Use reflection to avoid a build dependency with Xerces.
    108                Class<?> versionClass =
    109                                Class.forName("org.apache.xerces.impl.Version");
    110                // Will return Xerces-J 2.x.0
    111                Method method =
    112                    versionClass.getMethod("getVersion", (Class[])null);
    113                String version = (String)method.invoke(null, (Object[])null);
    114                versionNumber = version.substring( "Xerces-J".length() ,
    115                                                   version.lastIndexOf(".") );
    116            } catch (Exception ex){
    117                // Do nothing.
    118            }
    119            return versionNumber;
    120        }
    121
    122
    123        /**
    124         * Create a <code>SAXParser</code> based on the underlying
    125         * <code>Xerces</code> version.
    126         * @param properties parser specific properties/features
    127         * @return an XML Schema/DTD enabled <code>SAXParser</code>
    128         */
    129        public static SAXParser newSAXParser(Properties properties)
    130                throws ParserConfigurationException,
    131                       SAXException,
    132                       SAXNotSupportedException {
    133
    134            SAXParserFactory factory =
    135                            (SAXParserFactory)properties.get("SAXParserFactory");
    136
    137            if (versionNumber == null){
    138                versionNumber = getXercesVersion();
    139                version = new Float( versionNumber ).floatValue();
    140            }
    141
    142            // Note: 2.2 is completely broken (with XML Schema).
    143            if (version > 2.1) {
    144
    145                configureXerces(factory);
    146                return factory.newSAXParser();
    147            } else {
    148                SAXParser parser = factory.newSAXParser();
    149                configureOldXerces(parser,properties);
    150                return parser;
    151            }
    152        }
    153
    154
    155        /**
    156         * Configure schema validation as recommended by the JAXP 1.2 spec.
    157         * The <code>properties</code> object may contains information about
    158         * the schema local and language.
    159         * @param properties parser optional info
    160         */
    161        private static void configureOldXerces(SAXParser parser,
    162                                               Properties properties)
    163                throws ParserConfigurationException,
    164                       SAXNotSupportedException {
    165
    166            String schemaLocation = (String)properties.get("schemaLocation");
    167            String schemaLanguage = (String)properties.get("schemaLanguage");
    168
    169            try{
    170                if (schemaLocation != null) {
    171                    parser.setProperty(JAXP_SCHEMA_LANGUAGE, schemaLanguage);
    172                    parser.setProperty(JAXP_SCHEMA_SOURCE, schemaLocation);
    173                }
    174            } catch (SAXNotRecognizedException e){
    175                log.info(parser.getClass().getName() + ": "
    176                                            + e.getMessage() + " not supported.");
    177            }
    178
    179        }
    180
    181
    182        /**
    183         * Configure schema validation as recommended by the Xerces spec.
    184         * Both DTD and Schema validation will be enabled simultaneously.
    185         * <p>
    186         * NOTE: This method is broken. It is supposed to set up validation
    187         * against the schema specified in property "schemaLocation", but
    188         * it doesn't.
    189         *
    190         * @param factory SAXParserFactory to be configured
    191         */
    192        private static void configureXerces(SAXParserFactory factory)
    193                throws ParserConfigurationException,
    194                       SAXNotRecognizedException,
    195                       SAXNotSupportedException {
    196
    197            factory.setFeature(XERCES_DYNAMIC, true);
    198            factory.setFeature(XERCES_SCHEMA, true);
    199
    200        }
    201    }