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); } }