| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2000-2003 The Apache Software Foundation. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xerces" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999, International |
| * Business Machines, Inc., http://www.apache.org. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| package org.apache.xerces.impl.xs; |
| |
| import org.apache.xerces.xni.parser.XMLConfigurationException; |
| import org.apache.xerces.xni.parser.XMLErrorHandler; |
| import org.apache.xerces.xni.parser.XMLEntityResolver; |
| import org.apache.xerces.xni.parser.XMLInputSource; |
| import org.apache.xerces.xni.XNIException; |
| import org.apache.xerces.xni.grammars.XMLGrammarLoader; |
| import org.apache.xerces.xni.grammars.XMLGrammarDescription; |
| import org.apache.xerces.xni.grammars.XMLGrammarPool; |
| import org.apache.xerces.xni.grammars.Grammar; |
| import org.xml.sax.InputSource; |
| |
| import org.apache.xerces.impl.XMLErrorReporter; |
| import org.apache.xerces.impl.dv.InvalidDatatypeValueException; |
| import org.apache.xerces.impl.xs.models.CMBuilder; |
| import org.apache.xerces.impl.xs.traversers.XSDHandler; |
| import org.apache.xerces.impl.Constants; |
| import org.apache.xerces.impl.XMLEntityManager; |
| import org.apache.xerces.util.SymbolTable; |
| import org.apache.xerces.util.XMLSymbols; |
| import org.apache.xerces.util.DefaultErrorHandler; |
| |
| import java.util.Locale; |
| import java.util.Hashtable; |
| import java.util.StringTokenizer; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.io.InputStream; |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.util.Vector; |
| import org.apache.xerces.impl.xs.models.CMNodeFactory; |
| |
| /** |
| * This class implements XMLGrammarLoader. It is designed to interact |
| * either with a proxy for a user application which wants to preparse schemas, |
| * or with our own Schema validator. It is hoped that none of these "external" |
| * classes will therefore need to communicate directly |
| * with XSDHandler in future. |
| * <p>This class only knows how to make XSDHandler do its thing. |
| * The caller must ensure that all its properties (schemaLocation, JAXPSchemaSource |
| * etc.) have been properly set. |
| * |
| * @author Neil Graham, IBM |
| * @version $Id$ |
| */ |
| |
| public class XMLSchemaLoader implements XMLGrammarLoader { |
| |
| // Feature identifiers: |
| |
| /** Feature identifier: schema full checking*/ |
| protected static final String SCHEMA_FULL_CHECKING = |
| Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_FULL_CHECKING; |
| |
| /** Feature identifier: continue after fatal error. */ |
| protected static final String CONTINUE_AFTER_FATAL_ERROR = |
| Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE; |
| |
| /** Feature identifier: allow java encodings to be recognized when parsing schema docs. */ |
| protected static final String ALLOW_JAVA_ENCODINGS = |
| Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE; |
| |
| /** Feature identifier: standard uri conformant feature. */ |
| protected static final String STANDARD_URI_CONFORMANT_FEATURE = |
| Constants.XERCES_FEATURE_PREFIX + Constants.STANDARD_URI_CONFORMANT_FEATURE; |
| |
| // recognized features: |
| private static final String[] RECOGNIZED_FEATURES = { |
| SCHEMA_FULL_CHECKING, |
| CONTINUE_AFTER_FATAL_ERROR, |
| ALLOW_JAVA_ENCODINGS, |
| STANDARD_URI_CONFORMANT_FEATURE |
| }; |
| |
| // property identifiers |
| |
| /** Property identifier: symbol table. */ |
| public static final String SYMBOL_TABLE = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; |
| |
| /** Property identifier: error reporter. */ |
| public static final String ERROR_REPORTER = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY; |
| |
| /** Property identifier: error handler. */ |
| protected static final String ERROR_HANDLER = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY; |
| |
| /** Property identifier: entity resolver. */ |
| public static final String ENTITY_RESOLVER = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; |
| |
| /** Property identifier: grammar pool. */ |
| public static final String XMLGRAMMAR_POOL = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY; |
| |
| /** Property identifier: schema location. */ |
| protected static final String SCHEMA_LOCATION = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_LOCATION; |
| |
| /** Property identifier: no namespace schema location. */ |
| protected static final String SCHEMA_NONS_LOCATION = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_NONS_LOCATION; |
| |
| /** Property identifier: JAXP schema source. */ |
| protected static final String JAXP_SCHEMA_SOURCE = |
| Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_SOURCE; |
| |
| // recognized properties |
| private static final String [] RECOGNIZED_PROPERTIES = { |
| SYMBOL_TABLE, |
| ERROR_REPORTER, |
| ERROR_HANDLER, |
| ENTITY_RESOLVER, |
| XMLGRAMMAR_POOL, |
| SCHEMA_LOCATION, |
| SCHEMA_NONS_LOCATION, |
| JAXP_SCHEMA_SOURCE, |
| }; |
| |
| // Data |
| |
| // is Schema Full Checking enabled |
| private boolean fIsCheckedFully = false; |
| |
| // is allow-java-encodings enabled? |
| private boolean fAllowJavaEncodings = false; |
| |
| // enforcing strict uri? |
| private boolean fStrictURI = false; |
| |
| private SymbolTable fSymbolTable = null; |
| private XMLErrorReporter fErrorReporter = new XMLErrorReporter (); |
| private XMLEntityResolver fEntityResolver = null; |
| private XMLGrammarPool fGrammarPool = null; |
| private String fExternalSchemas = null; |
| private String fExternalNoNSSchema = null; |
| private Object fJAXPSource = null; |
| private Hashtable fJAXPCache; |
| private Locale fLocale = Locale.getDefault(); |
| |
| private XSDHandler fSchemaHandler; |
| // the grammar bucket |
| private XSGrammarBucket fGrammarBucket; |
| private XSDeclarationPool fDeclPool = null; |
| private SubstitutionGroupHandler fSubGroupHandler; |
| private CMBuilder fCMBuilder; |
| // boolean that tells whether we've tested the JAXP property. |
| private boolean fJAXPProcessed = false; |
| |
| |
| // containers |
| private XSDDescription fXSDDescription = new XSDDescription(); |
| |
| // default constructor. Create objects we absolutely need: |
| public XMLSchemaLoader() { |
| this( new SymbolTable(), null, new XMLEntityManager(), null, null, null); |
| } |
| |
| public XMLSchemaLoader(SymbolTable symbolTable) { |
| this( symbolTable, null, new XMLEntityManager(), null, null, null); |
| } |
| |
| XMLSchemaLoader(XMLErrorReporter errorReporter, |
| XSGrammarBucket grammarBucket, |
| SubstitutionGroupHandler sHandler, CMBuilder builder) { |
| this(null, errorReporter, null, grammarBucket, sHandler, builder); |
| } |
| |
| XMLSchemaLoader(SymbolTable symbolTable, |
| XMLErrorReporter errorReporter, |
| XMLEntityResolver entityResolver, |
| XSGrammarBucket grammarBucket, |
| SubstitutionGroupHandler sHandler, |
| CMBuilder builder) { |
| fSymbolTable = symbolTable; |
| if(errorReporter == null) { |
| errorReporter = new XMLErrorReporter (); |
| errorReporter.setProperty(ERROR_HANDLER, new DefaultErrorHandler()); |
| } |
| fErrorReporter = errorReporter; |
| fEntityResolver = entityResolver; |
| if(grammarBucket == null ) { |
| grammarBucket = new XSGrammarBucket(); |
| } |
| fGrammarBucket = grammarBucket; |
| if(sHandler == null) { |
| sHandler = new SubstitutionGroupHandler(fGrammarBucket); |
| } |
| fSubGroupHandler = sHandler; |
| //get an instance of the CMNodeFactory */ |
| CMNodeFactory nodeFactory = new CMNodeFactory() ; |
| //REVISIT: shouldn't the SecurityManager be allowed to set, if an application tries to load standalone schema - nb. |
| if(builder == null) { |
| builder = new CMBuilder(nodeFactory); |
| } |
| fCMBuilder = builder; |
| fSchemaHandler = new XSDHandler(fGrammarBucket); |
| fDeclPool = new XSDeclarationPool(); |
| fJAXPCache = new Hashtable(); |
| } |
| |
| /** |
| * Returns a list of feature identifiers that are recognized by |
| * this XMLGrammarLoader. This method may return null if no features |
| * are recognized. |
| */ |
| public String[] getRecognizedFeatures() { |
| return (String[])(RECOGNIZED_FEATURES.clone()); |
| } // getRecognizedFeatures(): String[] |
| |
| /** |
| * Returns the state of a feature. |
| * |
| * @param featureId The feature identifier. |
| * |
| * @throws XMLConfigurationException Thrown on configuration error. |
| */ |
| public boolean getFeature(String featureId) |
| throws XMLConfigurationException { |
| if(featureId.equals(SCHEMA_FULL_CHECKING)) { |
| return fIsCheckedFully; |
| } else if(featureId.equals(CONTINUE_AFTER_FATAL_ERROR)) { |
| return fErrorReporter.getFeature(CONTINUE_AFTER_FATAL_ERROR); |
| } |
| throw new XMLConfigurationException(XMLConfigurationException.NOT_RECOGNIZED, featureId); |
| } // getFeature (String): boolean |
| |
| /** |
| * Sets the state of a feature. |
| * |
| * @param featureId The feature identifier. |
| * @param state The state of the feature. |
| * |
| * @throws XMLConfigurationException Thrown when a feature is not |
| * recognized or cannot be set. |
| */ |
| public void setFeature(String featureId, |
| boolean state) throws XMLConfigurationException { |
| if(featureId.equals(SCHEMA_FULL_CHECKING)) { |
| fIsCheckedFully = state; |
| } else if(featureId.equals(CONTINUE_AFTER_FATAL_ERROR)) { |
| fErrorReporter.setFeature(CONTINUE_AFTER_FATAL_ERROR, state); |
| } else if(featureId.equals(ALLOW_JAVA_ENCODINGS)) { |
| fAllowJavaEncodings = state; |
| } else if(featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) { |
| fStrictURI = state; |
| } else { |
| throw new XMLConfigurationException(XMLConfigurationException.NOT_RECOGNIZED, featureId); |
| } |
| } // setFeature(String, boolean) |
| |
| /** |
| * Returns a list of property identifiers that are recognized by |
| * this XMLGrammarLoader. This method may return null if no properties |
| * are recognized. |
| */ |
| public String[] getRecognizedProperties() { |
| return (String[])(RECOGNIZED_PROPERTIES.clone()); |
| } // getRecognizedProperties(): String[] |
| |
| /** |
| * Returns the state of a property. |
| * |
| * @param propertyId The property identifier. |
| * |
| * @throws XMLConfigurationException Thrown on configuration error. |
| */ |
| public Object getProperty(String propertyId) |
| throws XMLConfigurationException { |
| if(propertyId.equals( SYMBOL_TABLE)) { |
| return fSymbolTable; |
| } else if(propertyId.equals( ERROR_REPORTER)) { |
| return fErrorReporter; |
| } else if(propertyId.equals( ERROR_HANDLER)) { |
| return fErrorReporter.getErrorHandler(); |
| } else if(propertyId.equals( ENTITY_RESOLVER)) { |
| return fEntityResolver; |
| } else if(propertyId.equals( XMLGRAMMAR_POOL)) { |
| return fGrammarPool; |
| } else if(propertyId.equals( SCHEMA_LOCATION)) { |
| return fExternalSchemas; |
| } else if(propertyId.equals( SCHEMA_NONS_LOCATION) ){ |
| return fExternalNoNSSchema; |
| } else if(propertyId.equals( JAXP_SCHEMA_SOURCE)) { |
| return fJAXPSource; |
| } |
| throw new XMLConfigurationException(XMLConfigurationException.NOT_RECOGNIZED, propertyId); |
| } // getProperty(String): Object |
| |
| /** |
| * Sets the state of a property. |
| * |
| * @param propertyId The property identifier. |
| * @param state The state of the property. |
| * |
| * @throws XMLConfigurationException Thrown when a property is not |
| * recognized or cannot be set. |
| */ |
| public void setProperty(String propertyId, |
| Object state) throws XMLConfigurationException, ClassCastException { |
| if(propertyId.equals( SYMBOL_TABLE)) { |
| fSymbolTable = (SymbolTable)state; |
| } else if(propertyId.equals( ERROR_REPORTER)) { |
| fErrorReporter = (XMLErrorReporter)state; |
| } else if(propertyId.equals( ERROR_HANDLER)) { |
| fErrorReporter.setProperty(propertyId, state); |
| } else if(propertyId.equals( ENTITY_RESOLVER)) { |
| fEntityResolver = (XMLEntityResolver)state; |
| } else if(propertyId.equals( XMLGRAMMAR_POOL)) { |
| fGrammarPool = (XMLGrammarPool)state; |
| } else if(propertyId.equals( SCHEMA_LOCATION)) { |
| fExternalSchemas = (String)state; |
| } else if(propertyId.equals( SCHEMA_NONS_LOCATION)) { |
| fExternalNoNSSchema = (String)state; |
| } else if(propertyId.equals( JAXP_SCHEMA_SOURCE)) { |
| fJAXPSource = state; |
| fJAXPProcessed = false; |
| } else |
| throw new XMLConfigurationException(XMLConfigurationException.NOT_RECOGNIZED, propertyId); |
| } // setProperty(String, Object) |
| |
| /** |
| * Set the locale to use for messages. |
| * |
| * @param locale The locale object to use for localization of messages. |
| * |
| * @exception XNIException Thrown if the parser does not support the |
| * specified locale. |
| */ |
| public void setLocale(Locale locale) { |
| fLocale = locale; |
| } // setLocale(Locale) |
| |
| /** Return the Locale the XMLGrammarLoader is using. */ |
| public Locale getLocale() { |
| return fLocale; |
| } // getLocale(): Locale |
| |
| /** |
| * Sets the error handler. |
| * |
| * @param errorHandler The error handler. |
| */ |
| public void setErrorHandler(XMLErrorHandler errorHandler) { |
| fErrorReporter.setProperty(ERROR_HANDLER, errorHandler); |
| } // setErrorHandler(XMLErrorHandler) |
| |
| /** Returns the registered error handler. */ |
| public XMLErrorHandler getErrorHandler() { |
| return fErrorReporter.getErrorHandler(); |
| } // getErrorHandler(): XMLErrorHandler |
| |
| /** |
| * Sets the entity resolver. |
| * |
| * @param entityResolver The new entity resolver. |
| */ |
| public void setEntityResolver(XMLEntityResolver entityResolver) { |
| fEntityResolver = entityResolver; |
| } // setEntityResolver(XMLEntityResolver) |
| |
| /** Returns the registered entity resolver. */ |
| public XMLEntityResolver getEntityResolver() { |
| return fEntityResolver; |
| } // getEntityResolver(): XMLEntityResolver |
| |
| // reset all objects that "belong" to this one. |
| public void reset () { |
| fGrammarBucket.reset(); |
| //we should retreive the initial grammar set given by the application |
| //to the parser and put it in local grammar bucket. |
| |
| // make sure error reporter knows about schemas... |
| if(fErrorReporter.getMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN) == null) { |
| fErrorReporter.putMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN, new XSMessageFormatter()); |
| } |
| |
| if(fGrammarPool != null) { |
| |
| Grammar [] initialGrammars = fGrammarPool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_SCHEMA); |
| for (int i = 0; i < initialGrammars.length; i++) { |
| // put this grammar into the bucket, along with grammars |
| // imported by it (directly or indirectly) |
| if (!fGrammarBucket.putGrammar((SchemaGrammar)(initialGrammars[i]), true)) { |
| // REVISIT: a conflict between new grammar(s) and grammars |
| // in the bucket. What to do? A warning? An exception? |
| fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN, |
| "GrammarConflict", null, |
| XMLErrorReporter.SEVERITY_WARNING); |
| } |
| } |
| fCMBuilder.setDeclPool(null); |
| } else { |
| fDeclPool.reset(); |
| fCMBuilder.setDeclPool(fDeclPool); |
| } |
| |
| fSchemaHandler.reset(fErrorReporter, fEntityResolver, |
| fSymbolTable, fGrammarPool, fAllowJavaEncodings, fStrictURI); |
| if(fGrammarPool == null) { |
| fDeclPool.reset(); |
| fSchemaHandler.setDeclPool(fDeclPool); |
| } else { |
| fSchemaHandler.setDeclPool(null); |
| } |
| fSubGroupHandler.reset(); |
| fJAXPProcessed = false; |
| } // reset() |
| |
| /** |
| * Returns a Grammar object by parsing the contents of the |
| * entity pointed to by source. |
| * |
| * @param source the location of the entity which forms |
| * the starting point of the grammar to be constructed. |
| * @throws IOException When a problem is encountered reading the entity |
| * XNIException When a condition arises (such as a FatalError) that requires parsing |
| * of the entity be terminated. |
| */ |
| public Grammar loadGrammar(XMLInputSource source) |
| throws IOException, XNIException { |
| reset(); |
| XSDDescription desc = new XSDDescription(); |
| desc.fContextType = XSDDescription.CONTEXT_PREPARSE; |
| desc.setBaseSystemId(source.getBaseSystemId()); |
| desc.setLiteralSystemId( source.getSystemId()); |
| // none of the other fields make sense for preparsing |
| Hashtable locationPairs = new Hashtable(); |
| // Process external schema location properties. |
| // We don't call tokenizeSchemaLocationStr here, because we also want |
| // to check whether the values are valid URI. |
| processExternalHints(fExternalSchemas, fExternalNoNSSchema, |
| locationPairs, fErrorReporter); |
| SchemaGrammar grammar = loadSchema(desc, source, locationPairs); |
| if(grammar != null && fGrammarPool != null) { |
| fGrammarPool.cacheGrammars(XMLGrammarDescription.XML_SCHEMA, fGrammarBucket.getGrammars()); |
| } |
| return grammar; |
| } // loadGrammar(XMLInputSource): Grammar |
| |
| SchemaGrammar loadSchema(XSDDescription desc, |
| XMLInputSource source, |
| Hashtable locationPairs) throws IOException, XNIException { |
| |
| // this should only be done once per invocation of this object; |
| // unless application alters JAXPSource in the mean time. |
| if(!fJAXPProcessed) { |
| processJAXPSchemaSource(locationPairs); |
| } |
| SchemaGrammar grammar = fSchemaHandler.parseSchema(source, desc, locationPairs); |
| // is full-checking enabled? If so, if we're preparsing we'll |
| // need to let XSConstraints have a go at the new grammar. |
| if(fIsCheckedFully) { |
| XSConstraints.fullSchemaChecking(fGrammarBucket, fSubGroupHandler, fCMBuilder, fErrorReporter); |
| } |
| return grammar; |
| } // loadSchema(XSDDescription, XMLInputSource): SchemaGrammar |
| |
| // this makes use of the schema location property values. |
| // we store the namespace/location pairs in a hashtable (use "" as the |
| // namespace of absent namespace). when resolving an entity, we first try |
| // to find in the hashtable whether there is a value for that namespace, |
| // if so, pass that location value to the user-defined entity resolver. |
| public static XMLInputSource resolveDocument(XSDDescription desc, Hashtable locationPairs, |
| XMLEntityResolver entityResolver) throws IOException { |
| String loc = null; |
| // we consider the schema location properties for import |
| if (desc.getContextType() == XSDDescription.CONTEXT_IMPORT || |
| desc.fromInstance()) { |
| // use empty string as the key for absent namespace |
| String namespace = desc.getTargetNamespace(); |
| String ns = namespace == null ? XMLSymbols.EMPTY_STRING : namespace; |
| // get the location hint for that namespace |
| LocationArray tempLA = (LocationArray)locationPairs.get(ns); |
| if(tempLA != null) |
| loc = tempLA.getFirstLocation(); |
| } |
| |
| // if it's not import, or if the target namespace is not set |
| // in the schema location properties, use location hint |
| if (loc == null) { |
| String[] hints = desc.getLocationHints(); |
| if (hints != null && hints.length > 0) |
| loc = hints[0]; |
| } |
| |
| String expandedLoc = XMLEntityManager.expandSystemId(loc, desc.getBaseSystemId(), false); |
| desc.setLiteralSystemId(loc); |
| desc.setExpandedSystemId(expandedLoc); |
| return entityResolver.resolveEntity(desc); |
| } |
| |
| // add external schema locations to the location pairs |
| public static void processExternalHints(String sl, String nsl, |
| Hashtable locations, |
| XMLErrorReporter er) { |
| if (sl != null) { |
| try { |
| // get the attribute decl for xsi:schemaLocation |
| // because external schema location property has the same syntax |
| // as xsi:schemaLocation |
| XSAttributeDecl attrDecl = SchemaGrammar.SG_XSI.getGlobalAttributeDecl(SchemaSymbols.XSI_SCHEMALOCATION); |
| // validation the string value to get the list of URI's |
| attrDecl.fType.validate(sl, null, null); |
| if (!tokenizeSchemaLocationStr(sl, locations)) { |
| // report warning (odd number of items) |
| er.reportError(XSMessageFormatter.SCHEMA_DOMAIN, |
| "SchemaLocation", |
| new Object[]{sl}, |
| XMLErrorReporter.SEVERITY_WARNING); |
| } |
| } |
| catch (InvalidDatatypeValueException ex) { |
| // report warning (not list of URI's) |
| er.reportError(XSMessageFormatter.SCHEMA_DOMAIN, |
| ex.getKey(), ex.getArgs(), |
| XMLErrorReporter.SEVERITY_WARNING); |
| } |
| } |
| |
| if (nsl != null) { |
| try { |
| // similarly for no ns schema location property |
| XSAttributeDecl attrDecl = SchemaGrammar.SG_XSI.getGlobalAttributeDecl(SchemaSymbols.XSI_NONAMESPACESCHEMALOCATION); |
| attrDecl.fType.validate(nsl, null, null); |
| LocationArray la = ((LocationArray)locations.get(XMLSymbols.EMPTY_STRING)); |
| if(la == null) { |
| la = new LocationArray(); |
| locations.put(XMLSymbols.EMPTY_STRING, la); |
| } |
| la.addLocation(nsl); |
| } |
| catch (InvalidDatatypeValueException ex) { |
| // report warning (not a URI) |
| er.reportError(XSMessageFormatter.SCHEMA_DOMAIN, |
| ex.getKey(), ex.getArgs(), |
| XMLErrorReporter.SEVERITY_WARNING); |
| } |
| } |
| } |
| // this method takes a SchemaLocation string. |
| // If an error is encountered, false is returned; |
| // otherwise, true is returned. In either case, locations |
| // is augmented to include as many tokens as possible. |
| // @param schemaStr The schemaLocation string to tokenize |
| // @param locations Hashtable mapping namespaces to LocationArray objects holding lists of locaitons |
| // @return true if no problems; false if string could not be tokenized |
| public static boolean tokenizeSchemaLocationStr(String schemaStr, Hashtable locations) { |
| if (schemaStr!= null) { |
| StringTokenizer t = new StringTokenizer(schemaStr, " \n\t\r"); |
| String namespace, location; |
| while (t.hasMoreTokens()) { |
| namespace = t.nextToken (); |
| if (!t.hasMoreTokens()) { |
| return false; // error! |
| } |
| location = t.nextToken(); |
| LocationArray la = ((LocationArray)locations.get(namespace)); |
| if(la == null) { |
| la = new LocationArray(); |
| locations.put(namespace, la); |
| } |
| la.addLocation(location); |
| } |
| } |
| return true; |
| } // tokenizeSchemaLocation(String, Hashtable): boolean |
| |
| /** |
| * Translate the various JAXP SchemaSource property types to XNI |
| * XMLInputSource. Valid types are: String, org.xml.sax.InputSource, |
| * InputStream, File, or Object[] of any of previous types. |
| * REVISIT: the JAXP 1.2 spec is less than clear as to whether this property |
| * should be available to imported schemas. I have assumed |
| * that it should. - NG |
| */ |
| private void processJAXPSchemaSource(Hashtable locationPairs) throws IOException { |
| fJAXPProcessed = true; |
| if (fJAXPSource == null) { |
| return; |
| } |
| |
| Class componentType = fJAXPSource.getClass().getComponentType(); |
| XMLInputSource xis = null; |
| String sid = null; |
| if (componentType == null) { |
| // Not an array |
| if(fJAXPSource instanceof InputStream || |
| fJAXPSource instanceof InputSource) { |
| SchemaGrammar g = (SchemaGrammar)fJAXPCache.get(fJAXPSource); |
| if(g != null) { |
| fGrammarBucket.putGrammar(g); |
| return; |
| } |
| } |
| fXSDDescription.reset(); |
| xis = xsdToXMLInputSource(fJAXPSource); |
| sid = xis.getSystemId(); |
| fXSDDescription.fContextType = XSDDescription.CONTEXT_PREPARSE; |
| if (sid != null) { |
| fXSDDescription.setLiteralSystemId(sid); |
| fXSDDescription.setExpandedSystemId(sid); |
| fXSDDescription.fLocationHints = new String[]{sid}; |
| } |
| SchemaGrammar g = loadSchema(fXSDDescription, xis, locationPairs); |
| if(fJAXPSource instanceof InputStream || |
| fJAXPSource instanceof InputSource) { |
| fJAXPCache.put(fJAXPSource, g); |
| } |
| fGrammarBucket.putGrammar(g); |
| return ; |
| } else if ( (componentType != Object.class) && |
| (componentType != String.class) && |
| (componentType != File.class) && |
| (componentType != InputStream.class) && |
| (componentType != InputSource.class) |
| ) { |
| // Not an Object[], String[], File[], InputStream[], InputSource[] |
| throw new XMLConfigurationException( |
| XMLConfigurationException.NOT_SUPPORTED, "\""+JAXP_SCHEMA_SOURCE+ |
| "\" property cannot have an array of type {"+componentType.getName()+ |
| "}. Possible types of the array supported are Object, String, File, "+ |
| "InputStream, InputSource."); |
| } |
| |
| // JAXP spec. allow []s of type String, File, InputStream, |
| // InputSource also, apart from [] of type Object. |
| Object[] objArr = (Object[]) fJAXPSource; |
| //make local vector for storing targetn namespaces of schemasources specified in object arrays. |
| Vector jaxpSchemaSourceNamespaces = new Vector() ; |
| for (int i = 0; i < objArr.length; i++) { |
| if(objArr[i] instanceof InputStream || |
| objArr[i] instanceof InputSource) { |
| SchemaGrammar g = (SchemaGrammar)fJAXPCache.get(objArr[i]); |
| if (g != null) { |
| fGrammarBucket.putGrammar(g); |
| continue; |
| } |
| } |
| fXSDDescription.reset(); |
| xis = xsdToXMLInputSource(objArr[i]); |
| sid = xis.getSystemId(); |
| fXSDDescription.fContextType = XSDDescription.CONTEXT_PREPARSE; |
| if (sid != null) { |
| fXSDDescription.setLiteralSystemId(sid); |
| fXSDDescription.setExpandedSystemId(sid); |
| fXSDDescription.fLocationHints = new String[]{sid}; |
| } |
| String targetNamespace = null ; |
| SchemaGrammar grammar = loadSchema(fXSDDescription, xis, locationPairs); |
| if(grammar != null){ |
| targetNamespace = grammar.getTargetNamespace() ; |
| if(jaxpSchemaSourceNamespaces.contains(targetNamespace)){ |
| //when an array of objects is passed it is illegal to have two schemas that share same namespace. |
| throw new java.lang.IllegalArgumentException( |
| " When using array of Objects as the value of SCHEMA_SOURCE property , " + |
| "no two Schemas should share the same targetNamespace. " ); |
| } |
| else{ |
| jaxpSchemaSourceNamespaces.add(targetNamespace) ; |
| } |
| if(objArr[i] instanceof InputStream || |
| objArr[i] instanceof InputSource) { |
| fJAXPCache.put(objArr[i], grammar); |
| } |
| fGrammarBucket.putGrammar(grammar); |
| } |
| else{ |
| //REVISIT: What should be the acutal behavior if grammar can't be loaded as specified in schema source? |
| } |
| } |
| }//processJAXPSchemaSource |
| |
| private XMLInputSource xsdToXMLInputSource( |
| Object val) |
| { |
| if (val instanceof String) { |
| // String value is treated as a URI that is passed through the |
| // EntityResolver |
| String loc = (String) val; |
| |
| if (fEntityResolver != null) { |
| |
| fXSDDescription.reset(); |
| fXSDDescription.setValues(null, loc, null, null); |
| XMLInputSource xis = null; |
| try { |
| xis = fEntityResolver.resolveEntity(fXSDDescription); |
| } catch (IOException ex) { |
| fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN, |
| "schema_reference.4", |
| new Object[] { loc }, XMLErrorReporter.SEVERITY_ERROR); |
| } |
| if (xis == null) { |
| // REVISIT: can this happen? |
| // Treat value as a URI and pass in as systemId |
| return new XMLInputSource(null, loc, null); |
| } |
| return xis; |
| } |
| } else if (val instanceof InputSource) { |
| return saxToXMLInputSource((InputSource) val); |
| } else if (val instanceof InputStream) { |
| return new XMLInputSource(null, null, null, |
| (InputStream) val, null); |
| } else if (val instanceof File) { |
| File file = (File) val; |
| InputStream is = null; |
| try { |
| is = new BufferedInputStream(new FileInputStream(file)); |
| } catch (FileNotFoundException ex) { |
| fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN, |
| "schema_reference.4", new Object[] { file.toString() }, |
| XMLErrorReporter.SEVERITY_ERROR); |
| } |
| return new XMLInputSource(null, null, null, is, null); |
| } |
| throw new XMLConfigurationException( |
| XMLConfigurationException.NOT_SUPPORTED, "\""+JAXP_SCHEMA_SOURCE+ |
| "\" property cannot have a value of type {"+val.getClass().getName()+ |
| "}. Possible types of the value supported are String, File, InputStream, "+ |
| "InputSource OR an array of these types."); |
| } |
| |
| |
| //Convert a SAX InputSource to an equivalent XNI XMLInputSource |
| |
| private static XMLInputSource saxToXMLInputSource(InputSource sis) { |
| String publicId = sis.getPublicId(); |
| String systemId = sis.getSystemId(); |
| |
| Reader charStream = sis.getCharacterStream(); |
| if (charStream != null) { |
| return new XMLInputSource(publicId, systemId, null, charStream, |
| null); |
| } |
| |
| InputStream byteStream = sis.getByteStream(); |
| if (byteStream != null) { |
| return new XMLInputSource(publicId, systemId, null, byteStream, |
| sis.getEncoding()); |
| } |
| |
| return new XMLInputSource(publicId, systemId, null); |
| } |
| |
| static class LocationArray{ |
| |
| int length ; |
| String [] locations = new String[2]; |
| |
| public void resize(int oldLength , int newLength){ |
| String [] temp = new String[newLength] ; |
| System.arraycopy(locations, 0, temp, 0, Math.min(oldLength, newLength)); |
| locations = temp ; |
| length = Math.min(oldLength, newLength); |
| } |
| |
| public void addLocation(String location){ |
| if(length >= locations.length ){ |
| resize(length, Math.max(1, length*2)); |
| } |
| locations[length++] = location; |
| }//setLocation() |
| |
| public String [] getLocationArray(){ |
| if(length < locations.length ){ |
| resize(locations.length, length); |
| } |
| return locations; |
| }//getLocationArray() |
| |
| public String getFirstLocation(){ |
| return length > 0 ? locations[0] : null; |
| } |
| |
| public int getLength(){ |
| return length ; |
| } |
| |
| } //locationArray |
| |
| } // XMLGrammarLoader |
| |