001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.validation.jaxp;
018:
019: import java.io.IOException;
020: import java.util.HashSet;
021: import java.util.Set;
022:
023: import javax.xml.transform.sax.SAXSource;
024: import javax.xml.validation.SchemaFactory;
025:
026: import org.apache.avalon.framework.configuration.Configurable;
027: import org.apache.avalon.framework.configuration.Configuration;
028: import org.apache.avalon.framework.configuration.ConfigurationException;
029: import org.apache.avalon.framework.thread.ThreadSafe;
030: import org.apache.cocoon.components.validation.Schema;
031: import org.apache.cocoon.components.validation.Validator;
032: import org.apache.cocoon.components.validation.impl.AbstractSchemaParser;
033: import org.apache.cocoon.components.validation.impl.DraconianErrorHandler;
034: import org.apache.excalibur.source.Source;
035: import org.xml.sax.SAXException;
036:
037: /**
038: * <p>An implementation of the {@link SchemaParser} interface wrapping JAXP
039: * {@link SchemaFactory} instances.</p>
040: *
041: */
042: public class JaxpSchemaParser extends AbstractSchemaParser implements
043: Configurable, ThreadSafe {
044:
045: /** <p>The class name of the {@link SchemaFactory} to use.</p> */
046: private String className = null;
047: /** <p>The list of grammars supported by this instance.</p> */
048: private String[] grammars = null;
049:
050: /**
051: * <p>Create a new {@link JaxpSchemaParser} instance.</p>
052: */
053: public JaxpSchemaParser() {
054: super ();
055: }
056:
057: /**
058: * <p>Configure this instance.</p>
059: *
060: * <p>The {@link JaxpSchemaParser} requires at least one configuration element:
061: * <code><factory-class><i>class name</i></factory-class></code>.
062: * This specifies the JAXP {@link SchemaFactory} class to be used by this
063: * instance.</p>
064: *
065: * <p>Grammars will be automatically detected if the {@link SchemaFactory}
066: * supports one of the {@link Validator.GRAMMAR_RELAX_NG RELAX-NG} grammar,
067: * {@link Validator.GRAMMAR_XML_SCHEMA XML-Schema} grammar, or the
068: * {@link Validator.GRAMMAR_XML_DTD XML-DTD} grammar.</p>
069: *
070: * <p>If the factory is known to support different grammars, the default
071: * detection can be overridden specifying in the configuration something similar
072: * to the following:</p>
073: *
074: * <pre>
075: * <grammars>
076: * <grammar>... a first grammar identifier ...</grammar>
077: * <grammar>... another grammar identifier ...</grammar>
078: * </grammars>
079: * </pre>
080: */
081: public void configure(Configuration conf)
082: throws ConfigurationException {
083: this .className = conf.getChild("factory-class").getValue();
084: final SchemaFactory fact;
085: try {
086: fact = (SchemaFactory) Class.forName(this .className)
087: .newInstance();
088: } catch (Exception exception) {
089: String message = "Unable to instantiate factory "
090: + this .className;
091: throw new ConfigurationException(message, conf, exception);
092: }
093:
094: /* Detect languages or use the supplied ones */
095: Configuration languages[] = conf.getChild("grammars")
096: .getChildren("grammar");
097: Set grammars = new HashSet();
098: if (languages.length > 0) {
099:
100: /* If the configuration specified (formally) a list of grammars use it */
101: for (int x = 0; x < languages.length; x++) {
102: String language = languages[x].getValue();
103: if (fact.isSchemaLanguageSupported(language)) {
104: grammars.add(language);
105: continue;
106: }
107: /* If the configured language is not supported throw an exception */
108: String message = "JAXP SchemaFactory \""
109: + this .className + "\" "
110: + "does not support configured grammar "
111: + language;
112: throw new ConfigurationException(message, languages[x]);
113: }
114: } else {
115:
116: /* Attempt to detect the languages directly using the JAXP factory */
117: if (fact
118: .isSchemaLanguageSupported(Validator.GRAMMAR_XML_SCHEMA)) {
119: grammars.add(Validator.GRAMMAR_XML_SCHEMA);
120: }
121: if (fact
122: .isSchemaLanguageSupported(Validator.GRAMMAR_RELAX_NG)) {
123: grammars.add(Validator.GRAMMAR_RELAX_NG);
124: }
125: if (fact
126: .isSchemaLanguageSupported(Validator.GRAMMAR_XML_DTD)) {
127: grammars.add(Validator.GRAMMAR_XML_DTD);
128: }
129: }
130:
131: /* Store our grammars */
132: this .grammars = (String[]) grammars.toArray(new String[grammars
133: .size()]);
134: }
135:
136: /**
137: * <p>Parse the specified {@link Source} and return a new {@link Schema}.</p>
138: *
139: * <p>The returned {@link Schema} must be able to validate multiple documents
140: * via multiple invocations of {@link Schema#createValidator(ErrorHandler)}.</p>
141: *
142: * @param source the {@link Source} associated with the {@link Schema} to return.
143: * @return a <b>non-null</b> {@link Schema} instance.
144: * @throws SAXException if a grammar error occurred parsing the schema.
145: * @throws IOException if an I/O error occurred parsing the schema.
146: * @throws IllegalArgumentException if the specified grammar type is not one
147: * of the grammar types returned by the
148: * {@link #getSupportedGrammars()} method.
149: */
150: public Schema parseSchema(Source source, String grammar)
151: throws SAXException, IOException {
152: final SchemaFactory factory;
153: try {
154: factory = (SchemaFactory) Class.forName(this .className)
155: .newInstance();
156: } catch (Exception exception) {
157: String message = "Unable to instantiate factory "
158: + this .className;
159: throw new SAXException(message, exception);
160: }
161:
162: JaxpResolver r = new JaxpResolver(this .sourceResolver,
163: this .entityResolver);
164: SAXSource s = new SAXSource(r.resolveSource(source));
165: factory.setErrorHandler(DraconianErrorHandler.INSTANCE);
166: factory.setResourceResolver(r);
167:
168: return new JaxpSchema(factory.newSchema(s), r.close());
169: }
170:
171: public String[] getSupportedGrammars() {
172: return this.grammars;
173: }
174: }
|