blob: defbee18645c1b71042471f0354c79c329d139fa [file] [log] [blame]
/*
* 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 org.apache.cocoon.components.validation.jing;
import java.io.IOException;
import java.util.Stack;
import org.apache.cocoon.components.validation.impl.ValidationResolver;
import org.apache.excalibur.source.SourceResolver;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import com.thaiopensource.xml.sax.XMLReaderCreator;
/**
* <p>A simple resolver used when parsing RELAX NG schemas through the use of
* <a href="http://www.thaiopensource.com/relaxng/jing.html">JING</a>.</p>
*
* <p>This is not thread safe and not recyclable. Once used, it <b>must</b> be
* garbage collected.</p>
*
*/
public class JingResolver extends ValidationResolver implements XMLReaderCreator {
/** <p>The current {@link Stack} of {@link InputSource}s being parsed. </p> */
private final Stack parsedSourceStack = new Stack();
/**
* <p>Create a new {@link JingResolver} instance.</p>
*/
public JingResolver(SourceResolver sourceResolver,
EntityResolver entityResolver) {
super(sourceResolver, entityResolver);
this.parsedSourceStack.push(null);
}
/**
* <p>Push a new {@link InputSource} in the stack used for relative URIs
* resolution.</p>
*/
public void pushInputSource(InputSource inputSource) {
this.parsedSourceStack.push(inputSource);
}
/**
* <p>Pop the last {@link InputSource} from the stack used for relative URIs
* resolution.</p>
*/
public InputSource popInputSource() {
if (this.parsedSourceStack.empty()) return null;
return (InputSource) this.parsedSourceStack.pop();
}
/**
* <p>Return the {@link PropertyMap} associated with this instance and usable
* by <a href="http://www.thaiopensource.com/relaxng/jing.html">JING</a>.</p>
*/
//public PropertyMap getProperties() {
// return this.validatorProperties;
//}
/* =========================================================================== */
/* SAX2 ENTITY RESOLVER INTERFACE IMPLEMENTATION */
/* =========================================================================== */
/**
* <p>Resolve an {@link InputSource} from a public ID and/or a system ID.</p>
*
* <p>This method can be called only while a schema is being parsed and will
* resolve URIs against a dynamic {@link Stack} of {@link InputSource}s.</p>
*
* <p>Since <a href="http://www.thaiopensource.com/relaxng/jing.html">JING</a>
* doesn't offer a complete URI resolution contract, a {@link Stack} is kept
* for all the sources parsed while reading a schema. Keeping in mind that the
* last {@link InputSource} pushed in the {@link Stack} can be considered to be
* the "base URI" for the current resolution, full relative resolution of system
* IDs can be achieved this way.<p>
*
* <p>Note that this method of resolving URIs by keeping a {@link Stack} of
* processed URIs is a <i>sort of a hack</i>, but it mimics the internal state
* of <a href="http://www.thaiopensource.com/relaxng/jing.html">JING</a> itself:
* if URI resolution fails, the {@link Stack} analysis is the first part to
* look at.</p>
*
* <p>Resolution will use the {@link EntityResolver} specified at construction
* to resolve public IDs, and then the {@link SourceResolver} again specified at
* construction to resolve the system IDs and to access the underlying byte
* streams of the entities to be parsed.</p>
*
* @param publicId the public ID of the entity to resolve.
* @param systemId the system ID of the entity to resolve.
* @return a <b>non-null</b> {@link InputSource} instance.
* @throws IOException if an I/O error occurred resolving the entity.
* @throws SAXException if an XML error occurred resolving the entity.
*/
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
InputSource source = (InputSource) this.parsedSourceStack.peek();
if (source == null) {
return super.resolveEntity(publicId, systemId);
} else {
return super.resolveEntity(source.getSystemId(), publicId, systemId);
}
}
/* =========================================================================== */
/* CALL JING TO ACCESS A CACHED OR FRESHLY PARSED SCHEMA INSTANCE */
/* =========================================================================== */
/**
* <p>Create an {@link XMLReader} instance that can be used by
* <a href="http://www.thaiopensource.com/relaxng/jing.html">JING</a> for
* parsing schemas.</p>
*
* <p>The returned {@link XMLReader} will keep track of populating/clearing the
* {@link Stack} of {@link InputSource}s kept for URI resolution as explained
* in the description of the {@link #resolveEntity(String, String)} method.</p>
*
* @see JingReader
* @return a <b>non-null</b> {@link XMLReader} instance.
* @throws SAXException if an error occurrent creating the {@link XMLReader}.
*/
public XMLReader createXMLReader()
throws SAXException {
return new JingReader(this);
}
}