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    }