001    /* $Id: XercesParser.java 992082 2010-09-02 19:50:06Z simonetripodi $
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    
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    import org.xml.sax.SAXException;
032    import org.xml.sax.SAXNotRecognizedException;
033    import org.xml.sax.SAXNotSupportedException;
034    
035    /**
036     * Create a <code>SAXParser</code> based on the underlying Xerces version.
037     * Currently, Xerces 2.3 and up doesn't implement schema validation the same way
038     * 2.1 was. In other to support schema validation in a portable way between 
039     * parser, some features/properties need to be set.
040     *
041     * @since 1.6
042     * @deprecated Create an XMLParser instance yourself, configure validation
043     *             appropriately, and pass it as a parameter to the
044     *             {@link org.apache.commons.digester.Digester} constructor, or use
045     *             {@link org.apache.commons.digester.Digester#setXMLSchema(javax.xml.validation.Schema)} for validation.
046     */
047    
048    @Deprecated
049    public class XercesParser{
050    
051        /**
052         * The Log to which all SAX event related logging calls will be made.
053         */
054        protected static Log log =
055            LogFactory.getLog("org.apache.commons.digester.Digester.sax");
056    
057    
058        /**
059         * The JAXP 1.2 property required to set up the schema location.
060         */
061        private static final String JAXP_SCHEMA_SOURCE =
062            "http://java.sun.com/xml/jaxp/properties/schemaSource";
063    
064    
065        /**
066         * The JAXP 1.2 property to set up the schemaLanguage used.
067         */
068        protected static String JAXP_SCHEMA_LANGUAGE =
069            "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
070    
071    
072        /**
073         * Xerces dynamic validation property
074         */
075        protected static String XERCES_DYNAMIC = 
076            "http://apache.org/xml/features/validation/dynamic";
077    
078    
079        /**
080         * Xerces schema validation property
081         */
082        protected static String XERCES_SCHEMA =
083            "http://apache.org/xml/features/validation/schema";
084    
085    
086        /**
087         * A <code>float</code> representing the underlying Xerces version
088         */
089        protected static float version;
090    
091    
092        /**
093         * The current Xerces version.
094         */
095        protected static String versionNumber = null;
096    
097    
098        /**
099         * Return the current Xerces version.
100         * @return the current Xerces version.
101         */
102        private static String getXercesVersion() {
103            // If for some reason we can't get the version, set it to 1.0.
104            String versionNumber = "1.0";
105            try{
106                // Use reflection to avoid a build dependency with Xerces.
107                Class<?> versionClass = 
108                                Class.forName("org.apache.xerces.impl.Version");
109                // Will return Xerces-J 2.x.0
110                Method method = 
111                    versionClass.getMethod("getVersion", (Class[])null); 
112                String version = (String)method.invoke(null, (Object[])null);
113                versionNumber = version.substring( "Xerces-J".length() , 
114                                                   version.lastIndexOf(".") ); 
115            } catch (Exception ex){
116                // Do nothing.
117            }
118            return versionNumber;
119        }
120    
121    
122        /**
123         * Create a <code>SAXParser</code> based on the underlying
124         * <code>Xerces</code> version.
125         * @param properties parser specific properties/features
126         * @return an XML Schema/DTD enabled <code>SAXParser</code>
127         */
128        public static SAXParser newSAXParser(Properties properties) 
129                throws ParserConfigurationException, 
130                       SAXException,
131                       SAXNotSupportedException {
132    
133            SAXParserFactory factory =  
134                            (SAXParserFactory)properties.get("SAXParserFactory");
135    
136            if (versionNumber == null){
137                versionNumber = getXercesVersion();
138                version = new Float( versionNumber ).floatValue();
139            }
140    
141            // Note: 2.2 is completely broken (with XML Schema). 
142            if (version > 2.1) {
143    
144                configureXerces(factory);
145                return factory.newSAXParser();
146            } else {
147                SAXParser parser = factory.newSAXParser();
148                configureOldXerces(parser,properties);
149                return parser;
150            }
151        }
152    
153    
154        /**
155         * Configure schema validation as recommended by the JAXP 1.2 spec.
156         * The <code>properties</code> object may contains information about
157         * the schema local and language. 
158         * @param properties parser optional info
159         */
160        private static void configureOldXerces(SAXParser parser, 
161                                               Properties properties) 
162                throws ParserConfigurationException, 
163                       SAXNotSupportedException {
164    
165            String schemaLocation = (String)properties.get("schemaLocation");
166            String schemaLanguage = (String)properties.get("schemaLanguage");
167    
168            try{
169                if (schemaLocation != null) {
170                    parser.setProperty(JAXP_SCHEMA_LANGUAGE, schemaLanguage);
171                    parser.setProperty(JAXP_SCHEMA_SOURCE, schemaLocation);
172                }
173            } catch (SAXNotRecognizedException e){
174                log.info(parser.getClass().getName() + ": " 
175                                            + e.getMessage() + " not supported."); 
176            }
177    
178        }
179    
180    
181        /**
182         * Configure schema validation as recommended by the Xerces spec. 
183         * Both DTD and Schema validation will be enabled simultaneously.
184         * <p>
185         * NOTE: This method is broken. It is supposed to set up validation
186         * against the schema specified in property "schemaLocation", but
187         * it doesn't.
188         *
189         * @param factory SAXParserFactory to be configured
190         */
191        private static void configureXerces(SAXParserFactory factory)
192                throws ParserConfigurationException, 
193                       SAXNotRecognizedException, 
194                       SAXNotSupportedException {
195    
196            factory.setFeature(XERCES_DYNAMIC, true);
197            factory.setFeature(XERCES_SCHEMA, true);
198    
199        }
200    }