package com.thaiopensource.relaxng;
import com.thaiopensource.validate.auto.AutoSchemaReader;
import com.thaiopensource.util.PropertyMapBuilder;
import com.thaiopensource.validate.Flag;
import com.thaiopensource.validate.IncorrectSchemaException;
import com.thaiopensource.validate.SchemaReader;
import com.thaiopensource.xml.sax.XMLReaderCreator;
import com.thaiopensource.validate.Schema;
import com.thaiopensource.validate.ValidateProperty;
import com.thaiopensource.validate.prop.rng.RngProperty;
import com.thaiopensource.validate.rng.CompactSchemaReader;
import org.relaxng.datatype.DatatypeLibraryFactory;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.IOException;
/**
* A factory for RELAX NG schemas. The factory creates <code>Schema</code> objects from their
* XML representation.
*
* A single <code>SchemaFactory</code> is <em>not</em> safe for concurrent
* access by multiple threads; it must be accessed by at most one thread at a time.
* Schemas can be created concurrently by using a distinct <code>SchemaFactory</code> for each
* thread. However, the <code>Schema</code> objects created <em>are</em> safe for concurrent
* access by multiple threads.
*
* @author <a href="mailto:jjc@jclark.com">James Clark</a>
*/
public class SchemaFactory {
private PropertyMapBuilder properties = new PropertyMapBuilder();
private boolean compactSyntax = false;
private SchemaReader autoSchemaLanguage = new AutoSchemaReader();
/**
* Constructs a schema factory.
*/
public SchemaFactory() {
}
/**
* Creates a schema by parsing an XML document. A non-null <code>XMLReaderCreator</code> must be specified
* with <code>setXMLReaderCreator</code> before calling <code>createSchema</code>. The <code>ErrorHandler</code>
* is allowed to be <code>null</code>. The <code>DatatypeLibraryFactory</code> is allowed to be <code>null</code>.
*
* <p>Normally, if a schema cannot be created, <code>createSchema</code> will throw
* a <code>IncorrectSchemaException</code>; however,
* before doing so, one or more errors will be reported using the <code>ErrorHandler</code> if it is non-null. If the
* <code>ErrorHandler</code> throws a <code>SAXException</code>, then <code>createSchema</code> will pass this
* through rather than throwing a <code>IncorrectSchemaException</code>. Similarly, if <code>XMLReader.parse</code>
* throws a <code>SAXException</code> or <code>IOException</code>, then <code>createSchema</code> will pass
* this through rather than throwing a <code>IncorrectSchemaException</code>. Thus, if an error handler
* is specified that reports errors to the user, there is no need to report any additional message to the
* user if <code>createSchema</code> throws <code>IncorrectSchemaException</code>.
*
* @param in the <code>InputSource</code> containing the XML document to be parsed;
* must not be <code>null</code>
* @return the <code>Schema</code> constructed from the XML document;
* never <code>null</code>.
*
* @throws IOException if an I/O error occurs
* @throws SAXException if there is an XML parsing error and the XMLReader or ErrorHandler
* throws a SAXException
* @throws com.thaiopensource.validate.IncorrectSchemaException if the XML document was not a correct RELAX NG schema
* @throws NullPointerException if the current XMLReaderCreator is <code>null</code>
*/
public Schema createSchema(InputSource in) throws IOException, SAXException, IncorrectSchemaException {
SchemaReader r = compactSyntax ? CompactSchemaReader.getInstance() : autoSchemaLanguage;
return r.createSchema(in, properties.toPropertyMap());
}
/**
* Specifies the XMLReaderCreator to be used for creating <code>XMLReader</code>s for parsing
* the XML document. Because of <code>include</code> and <code>externalRef</code> elements,
* parsing a single RELAX NG may require the creation of multiple more than one <code>XMLReader</code>.
* A non-null XMLReaderCreator must be specified before calling <code>createSchema</code>.
*
* @param xrc the <code>XMLReaderCreator</code> to be used for parsing the XML document containing
* the schema; may be <code>null</code>
* @see #getXMLReaderCreator
*/
public void setXMLReaderCreator(XMLReaderCreator xrc) {
properties.put(ValidateProperty.XML_READER_CREATOR, xrc);
}
/**
* Returns the current <code>XMLReaderCreator</code> as specified by <code>setXMLReaderCreator</code>.
* If <code>XMLReaderCreator</code> has never been called, then <code>getXMLReaderCreator</code>
* returns null.
*
* @return the <code>XMLReaderCreator</code> that will be used for parsing the XML document containing
* the schema; may be <code>null</code>
*
* @see #setXMLReaderCreator
*/
public XMLReaderCreator getXMLReaderCreator() {
return (XMLReaderCreator)properties.get(ValidateProperty.XML_READER_CREATOR);
}
/**
* Specifies the <code>ErrorHandler</code> to be used for reporting errors while creating the schema.
* This does not affect the error handler used for validation.
*
* @param eh the <code>ErrorHandler</code> to be used for reporting errors while creating the schema;
* may be <code>null</code>.
* @see #getErrorHandler
*/
public void setErrorHandler(ErrorHandler eh) {
properties.put(ValidateProperty.ERROR_HANDLER, eh);
}
/**
* Returns the <code>ErrorHandler</code> that will be used for reporting errors while creating the
* schema. If <code>setErrorHandler</code> has not been called for this <code>SchemaFactory</code>,
* then <code>getErrorHandler</code> returns <code>null</code>.
*
* @return the <code>ErrorHandler</code> to be used for reporting errors while creating the schema;
* may be <code>null</code>.
* @see #setErrorHandler
*/
public ErrorHandler getErrorHandler() {
return (ErrorHandler)properties.get(ValidateProperty.ERROR_HANDLER);
}
/**
* Specifies the <code>DatatypeLibraryFactory</code> to be used for handling datatypes in the schema.
* This also determines how datatypes are handled during validation. If <code>null</code> is
* specified then only the builtin datatypes will be supported.
*
* @param dlf the <code>DatatypeLibraryFactory</code> to be used for handling datatypes in the schema
* @see #getDatatypeLibraryFactory
*/
public void setDatatypeLibraryFactory(DatatypeLibraryFactory dlf) {
properties.put(RngProperty.DATATYPE_LIBRARY_FACTORY, dlf);
}
/**
* Returns the <code>DatatypeLibraryFactory</code> that will be used for handling datatypes in the
* schema. If <code>setDatatypeLibraryFactory</code> has not been called for this <code>SchemaFactory</code>,
* then <code>getDatatypeLibraryFactory</code> returns <code>null</code>.
*
* @return the <code>DatatypeLibraryFactory</code> to be used for handling datatypes in the schema;
* may be null.
* @see #setDatatypeLibraryFactory
*/
public DatatypeLibraryFactory getDatatypeLibraryFactory() {
return (DatatypeLibraryFactory)properties.get(RngProperty.DATATYPE_LIBRARY_FACTORY);
}
/**
* Specifies whether to perform checking of ID/IDREF/IDREFS attributes in accordance with
* RELAX NG DTD Compatibility.
*
* @param checkIdIdref <code>true</code> if ID/IDREF/IDREFS checking should be performed;
* <code>false</code> otherwise
*
* @see #getCheckIdIdref
* @see <a href="http://www.oasis-open.org/committees/relax-ng/compatibility.html#id">RELAX NG DTD Compatibility</a>
*/
public void setCheckIdIdref(boolean checkIdIdref) {
properties.put(RngProperty.CHECK_ID_IDREF, checkIdIdref ? Flag.PRESENT : null);
}
/**
* Indicates whether ID/IDREF/IDREFS attributes will be checked in accordance RELAX NG DTD
* Compatibility. If <code>setCheckIdIdref</code> has not been called for this <code>SchemaFactory</code>,
* then <code>getCheckIdref</code> will return <code>false</code>.
*
* @return <code>true</code> if ID/IDREF/IDREFS attributes will be checked;
* <code>false</code> otherwise.
*
* @see #setCheckIdIdref
* @see <a href="http://www.oasis-open.org/committees/relax-ng/compatibility.html#id">RELAX NG DTD Compatibility</a>
*/
public boolean getCheckIdIdref() {
return properties.contains(RngProperty.CHECK_ID_IDREF);
}
/**
* Specifies whether to use the compact syntax to parse the RELAX NG schema rather than the normal XML syntax.
*
* @param compactSyntax <code>true</code> if the compact syntax should be used; <code>false</code>
* if the XML syntax should be used
* @see #getCompactSyntax
*/
public void setCompactSyntax(boolean compactSyntax) {
this.compactSyntax = compactSyntax;
}
/**
* Indicates whether the compact syntax will be used to parse the RELAX NG schema rather than
* the normal XML syntax.
*
* @return <code>true</code> if the compact syntax will be used; <code>false</code> if the XML
* syntax will be used
*/
public boolean getCompactSyntax() {
return compactSyntax;
}
public void setFeasible(boolean feasible) {
properties.put(RngProperty.FEASIBLE, feasible ? Flag.PRESENT : null);
}
public boolean getFeasible() {
return properties.contains(RngProperty.FEASIBLE);
}
}