jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java
changeset 27111 7a491d709b83
parent 25868 686eef1e7a79
child 33349 975138b77cff
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java	Wed Jul 05 20:04:04 2017 +0200
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java	Mon Oct 13 14:09:03 2014 -0700
@@ -1,13 +1,13 @@
 /*
- * reserved comment block
- * DO NOT REMOVE OR ALTER!
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  */
 /*
- * Copyright 2000-2005 The Apache Software Foundation.
- *
- * Licensed 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
+ * 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
  *
@@ -20,26 +20,12 @@
 
 package com.sun.org.apache.xerces.internal.impl.xs;
 
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.Hashtable;
-import java.util.Locale;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
 import com.sun.org.apache.xerces.internal.dom.DOMErrorImpl;
 import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter;
 import com.sun.org.apache.xerces.internal.dom.DOMStringListImpl;
 import com.sun.org.apache.xerces.internal.impl.Constants;
 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
-import com.sun.org.apache.xerces.internal.impl.dv.DVFactoryException;
 import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
 import com.sun.org.apache.xerces.internal.impl.dv.SchemaDVFactory;
 import com.sun.org.apache.xerces.internal.impl.dv.xs.SchemaDVFactoryImpl;
@@ -49,13 +35,16 @@
 import com.sun.org.apache.xerces.internal.util.DOMEntityResolverWrapper;
 import com.sun.org.apache.xerces.internal.util.DOMErrorHandlerWrapper;
 import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler;
+import com.sun.org.apache.xerces.internal.util.MessageFormatter;
 import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings;
 import com.sun.org.apache.xerces.internal.util.Status;
 import com.sun.org.apache.xerces.internal.util.SymbolTable;
+import com.sun.org.apache.xerces.internal.util.URI.MalformedURIException;
 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
 import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
 import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
+import com.sun.org.apache.xerces.internal.xni.QName;
 import com.sun.org.apache.xerces.internal.xni.XNIException;
 import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
@@ -72,14 +61,26 @@
 import com.sun.org.apache.xerces.internal.xs.StringList;
 import com.sun.org.apache.xerces.internal.xs.XSLoader;
 import com.sun.org.apache.xerces.internal.xs.XSModel;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.WeakHashMap;
 import javax.xml.XMLConstants;
 import org.w3c.dom.DOMConfiguration;
 import org.w3c.dom.DOMError;
 import org.w3c.dom.DOMErrorHandler;
+import org.w3c.dom.DOMException;
 import org.w3c.dom.DOMStringList;
-import org.w3c.dom.DOMException;
 import org.w3c.dom.ls.LSInput;
 import org.w3c.dom.ls.LSResourceResolver;
 import org.xml.sax.InputSource;
@@ -101,7 +102,7 @@
  * @author Neil Graham, IBM
  */
 
-public class XMLSchemaLoader implements XMLGrammarLoader, XMLComponent,
+public class XMLSchemaLoader implements XMLGrammarLoader, XMLComponent, XSElementDeclHelper,
 // XML Component API
 XSLoader, DOMConfiguration {
 
@@ -249,8 +250,7 @@
     // Data
 
     // features and properties
-    private ParserConfigurationSettings fLoaderConfig = new ParserConfigurationSettings();
-    private SymbolTable fSymbolTable = null;
+    private final ParserConfigurationSettings fLoaderConfig = new ParserConfigurationSettings();
     private XMLErrorReporter fErrorReporter = new XMLErrorReporter ();
     private XMLEntityManager fEntityManager = null;
     private XMLEntityResolver fUserEntityResolver = null;
@@ -276,7 +276,7 @@
     private XSDDescription fXSDDescription = new XSDDescription();
     private String faccessExternalSchema = Constants.EXTERNAL_ACCESS_DEFAULT;
 
-    private Map fJAXPCache;
+    private WeakHashMap fJAXPCache;
     private Locale fLocale = Locale.getDefault();
 
     // XSLoader attributes
@@ -350,8 +350,8 @@
             grammarBucket = new XSGrammarBucket();
         }
         fGrammarBucket = grammarBucket;
-        if(sHandler == null) {
-            sHandler = new SubstitutionGroupHandler(fGrammarBucket);
+        if (sHandler == null) {
+            sHandler = new SubstitutionGroupHandler(this);
         }
         fSubGroupHandler = sHandler;
 
@@ -360,10 +360,7 @@
         }
         fCMBuilder = builder;
         fSchemaHandler = new XSDHandler(fGrammarBucket);
-        if (fDeclPool != null) {
-            fDeclPool.reset();
-        }
-        fJAXPCache = new HashMap();
+        fJAXPCache = new WeakHashMap();
 
         fSettingsChanged = true;
     }
@@ -527,8 +524,8 @@
      * Returns a Grammar object by parsing the contents of the
      * entities pointed to by sources.
      *
-     * @param source[]  the locations of the entity which forms
-     *                      the staring point of the grammars to be constructed
+     * @param source the locations of the entity which forms
+     * the staring point of the grammars to be constructed
      * @throws IOException  when a problem is encounted reading the entity
      * @throws XNIException when a condition arises (such as a FatalError) that requires parsing
      *                          of the entity be terminated
@@ -618,7 +615,8 @@
         return grammar;
     } // loadSchema(XSDDescription, XMLInputSource):  SchemaGrammar
 
-    /** This method tries to resolve location of the given schema.
+    /**
+     * This method tries to resolve location of the given schema.
      * The loader stores the namespace/location pairs in a hashtable (use "" as the
      * namespace of absent namespace). When resolving an entity, loader first tries
      * to find in the hashtable whether there is a value for that namespace,
@@ -627,7 +625,7 @@
      * @param desc
      * @param locationPairs
      * @param entityResolver
-     * @return
+     * @return the XMLInputSource
      * @throws IOException
      */
     public static XMLInputSource resolveDocument(XSDDescription desc, Map locationPairs,
@@ -671,7 +669,7 @@
                 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)) {
+                if (!tokenizeSchemaLocationStr(sl, locations, null)) {
                     // report warning (odd number of items)
                     er.reportError(XSMessageFormatter.SCHEMA_DOMAIN,
                             "SchemaLocation",
@@ -714,7 +712,7 @@
     // @param schemaStr     The schemaLocation string to tokenize
     // @param locations     HashMap 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, Map locations) {
+    public static boolean tokenizeSchemaLocationStr(String schemaStr, Map locations, String base) {
         if (schemaStr!= null) {
             StringTokenizer t = new StringTokenizer(schemaStr, " \n\t\r");
             String namespace, location;
@@ -729,6 +727,12 @@
                     la = new LocationArray();
                     locations.put(namespace, la);
                 }
+                if (base != null) {
+                    try {
+                        location = XMLEntityManager.expandSystemId(location, base, false);
+                    } catch (MalformedURIException e) {
+                    }
+                }
                 la.addLocation(location);
             }
         }
@@ -756,10 +760,10 @@
         String sid = null;
         if (componentType == null) {
             // Not an array
-            if(fJAXPSource instanceof InputStream ||
+            if (fJAXPSource instanceof InputStream ||
                     fJAXPSource instanceof InputSource) {
                 SchemaGrammar g = (SchemaGrammar)fJAXPCache.get(fJAXPSource);
-                if(g != null) {
+                if (g != null) {
                     fGrammarBucket.putGrammar(g);
                     return;
                 }
@@ -776,38 +780,40 @@
             }
             SchemaGrammar g = loadSchema(fXSDDescription, xis, locationPairs);
             // it is possible that we won't be able to resolve JAXP schema-source location
-            if (g != null){
-                if(fJAXPSource instanceof InputStream ||
+            if (g != null) {
+                if (fJAXPSource instanceof InputStream ||
                         fJAXPSource instanceof InputSource) {
                     fJAXPCache.put(fJAXPSource, g);
-                    if(fIsCheckedFully) {
+                    if (fIsCheckedFully) {
                         XSConstraints.fullSchemaChecking(fGrammarBucket, fSubGroupHandler, fCMBuilder, fErrorReporter);
                     }
                 }
                 fGrammarBucket.putGrammar(g);
             }
-            return ;
-        } else if ( (componentType != Object.class) &&
+            return;
+        }
+        else if ( (componentType != Object.class) &&
                 (componentType != String.class) &&
-                (componentType != File.class) &&
-                (componentType != InputStream.class) &&
-                (componentType != InputSource.class)
+                !File.class.isAssignableFrom(componentType) &&
+                !InputStream.class.isAssignableFrom(componentType) &&
+                !InputSource.class.isAssignableFrom(componentType) &&
+                !componentType.isInterface()
         ) {
             // Not an Object[], String[], File[], InputStream[], InputSource[]
+            MessageFormatter mf = fErrorReporter.getMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN);
             throw new XMLConfigurationException(
-                    Status.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.");
+                    Status.NOT_SUPPORTED,
+                    mf.formatMessage(fErrorReporter.getLocale(), "jaxp12-schema-source-type.2",
+                    new Object [] {componentType.getName()}));
         }
 
         // 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() ;
+        // make local array for storing target namespaces of schemasources specified in object arrays.
+        ArrayList jaxpSchemaSourceNamespaces = new ArrayList();
         for (int i = 0; i < objArr.length; i++) {
-            if(objArr[i] instanceof InputStream ||
+            if (objArr[i] instanceof InputStream ||
                     objArr[i] instanceof InputSource) {
                 SchemaGrammar g = (SchemaGrammar)fJAXPCache.get(objArr[i]);
                 if (g != null) {
@@ -829,18 +835,18 @@
             // load schema
             SchemaGrammar grammar = fSchemaHandler.parseSchema(xis,fXSDDescription, locationPairs);
 
-            if(fIsCheckedFully) {
+            if (fIsCheckedFully) {
                 XSConstraints.fullSchemaChecking(fGrammarBucket, fSubGroupHandler, fCMBuilder, fErrorReporter);
             }
-            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. " );
+            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.
+                    MessageFormatter mf = fErrorReporter.getMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN);
+                    throw new java.lang.IllegalArgumentException(mf.formatMessage(fErrorReporter.getLocale(),
+                            "jaxp12-schema-source-ns", null));
                 }
-                else{
+                else {
                     jaxpSchemaSourceNamespaces.add(targetNamespace) ;
                 }
                 if(objArr[i] instanceof InputStream ||
@@ -849,15 +855,13 @@
                 }
                 fGrammarBucket.putGrammar(grammar);
             }
-            else{
+            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)
-    {
+    private XMLInputSource xsdToXMLInputSource(Object val) {
         if (val instanceof String) {
             // String value is treated as a URI that is passed through the
             // EntityResolver
@@ -867,7 +871,8 @@
             XMLInputSource xis = null;
             try {
                 xis = fEntityManager.resolveEntity(fXSDDescription);
-            } catch (IOException ex) {
+            }
+            catch (IOException ex) {
                 fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN,
                         "schema_reference.4",
                         new Object[] { loc }, XMLErrorReporter.SEVERITY_ERROR);
@@ -878,12 +883,15 @@
                 return new XMLInputSource(null, loc, null);
             }
             return xis;
-        } else if (val instanceof InputSource) {
+        }
+        else if (val instanceof InputSource) {
             return saxToXMLInputSource((InputSource) val);
-        } else if (val instanceof InputStream) {
+        }
+        else if (val instanceof InputStream) {
             return new XMLInputSource(null, null, null,
                     (InputStream) val, null);
-        } else if (val instanceof File) {
+        }
+        else if (val instanceof File) {
             File file = (File) val;
             InputStream is = null;
             try {
@@ -893,13 +901,13 @@
                         "schema_reference.4", new Object[] { file.toString() },
                         XMLErrorReporter.SEVERITY_ERROR);
             }
-            return new XMLInputSource(null, null, null, is, null);
+            return new XMLInputSource(null, file.toURI().toString(), null, is, null);
         }
+        MessageFormatter mf = fErrorReporter.getMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN);
         throw new XMLConfigurationException(
-                Status.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.");
+                Status.NOT_SUPPORTED,
+                mf.formatMessage(fErrorReporter.getLocale(), "jaxp12-schema-source-type.1",
+                new Object [] {val != null ? val.getClass().getName() : "null"}));
     }
 
 
@@ -999,13 +1007,22 @@
 
         fSubGroupHandler.reset();
 
-        boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true);
+        boolean parser_settings = true;
+        // If the component manager is the loader config don't bother querying it since it doesn't
+        // recognize the PARSER_SETTINGS feature. Prevents an XMLConfigurationException from being
+        // thrown.
+        if (componentManager != fLoaderConfig) {
+            parser_settings = componentManager.getFeature(PARSER_SETTINGS, true);
+        }
 
         if (!parser_settings || !fSettingsChanged){
             // need to reprocess JAXP schema sources
             fJAXPProcessed = false;
             // reinitialize grammar bucket
             initGrammarBucket();
+            if (fDeclPool != null) {
+                fDeclPool.reset();
+            }
             return;
         }
 
@@ -1028,26 +1045,6 @@
             fSchemaHandler.setDVFactory(dvFactory);
         }
 
-        boolean psvi = componentManager.getFeature(AUGMENT_PSVI, false);
-
-        if (!psvi) {
-            if (fDeclPool != null) {
-                fDeclPool.reset();
-            }
-            else {
-                fDeclPool = new XSDeclarationPool();
-            }
-            fCMBuilder.setDeclPool(fDeclPool);
-            fSchemaHandler.setDeclPool(fDeclPool);
-            if (dvFactory instanceof SchemaDVFactoryImpl) {
-                fDeclPool.setDVFactory((SchemaDVFactoryImpl)dvFactory);
-                ((SchemaDVFactoryImpl)dvFactory).setDeclPool(fDeclPool);
-            }
-        } else {
-            fCMBuilder.setDeclPool(null);
-            fSchemaHandler.setDeclPool(null);
-        }
-
         // get schema location properties
         try {
             fExternalSchemas = (String) componentManager.getProperty(SCHEMA_LOCATION);
@@ -1064,6 +1061,36 @@
         // clear grammars, and put the one for schema namespace there
         fGrammarPool = (XMLGrammarPool) componentManager.getProperty(XMLGRAMMAR_POOL, null);
         initGrammarBucket();
+
+        boolean psvi = componentManager.getFeature(AUGMENT_PSVI, false);
+
+        // Only use the decl pool when there is no chance that the schema
+        // components will be exposed or cached.
+        // TODO: when someone calls loadGrammar(XMLInputSource), the schema is
+        // always exposed even without the use of a grammar pool.
+        // Disabling the "decl pool" feature for now until we understand when
+        // it can be safely used.
+        if (!psvi && fGrammarPool == null && false) {
+            if (fDeclPool != null) {
+                fDeclPool.reset();
+            }
+            else {
+                fDeclPool = new XSDeclarationPool();
+            }
+            fCMBuilder.setDeclPool(fDeclPool);
+            fSchemaHandler.setDeclPool(fDeclPool);
+            if (dvFactory instanceof SchemaDVFactoryImpl) {
+                fDeclPool.setDVFactory((SchemaDVFactoryImpl)dvFactory);
+                ((SchemaDVFactoryImpl)dvFactory).setDeclPool(fDeclPool);
+            }
+        } else {
+            fCMBuilder.setDeclPool(null);
+            fSchemaHandler.setDeclPool(null);
+            if (dvFactory instanceof SchemaDVFactoryImpl) {
+                ((SchemaDVFactoryImpl)dvFactory).setDeclPool(null);
+            }
+        }
+
         // get continue-after-fatal-error feature
         try {
             boolean fatalError = componentManager.getFeature(CONTINUE_AFTER_FATAL_ERROR, false);
@@ -1083,7 +1110,8 @@
     private void initGrammarBucket(){
         if(fGrammarPool != null) {
             Grammar [] initialGrammars = fGrammarPool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_SCHEMA);
-            for (int i = 0; i < initialGrammars.length; i++) {
+            final int length = (initialGrammars != null) ? initialGrammars.length : 0;
+            for (int i = 0; i < length; ++i) {
                 // put this grammar into the bucket, along with grammars
                 // imported by it (directly or indirectly)
                 if (!fGrammarBucket.putGrammar((SchemaGrammar)(initialGrammars[i]), true)) {
@@ -1119,7 +1147,7 @@
     }
 
     /* (non-Javadoc)
-     * @see com.sun.org.apache.xerces.internal.xs.XSLoader#loadInputList(com.sun.org.apache.xerces.internal.xs.DOMInputList)
+     * @see com.sun.org.apache.xerces.internal.xs.XSLoader#loadInputList(com.sun.org.apache.xerces.internal.xs.LSInputList)
      */
     public XSModel loadInputList(LSInputList is) {
         int length = is.getLength();
@@ -1250,7 +1278,7 @@
      */
     public DOMStringList getParameterNames() {
         if (fRecognizedParameters == null){
-            Vector v = new Vector();
+            ArrayList v = new ArrayList();
             v.add(Constants.DOM_VALIDATE);
             v.add(Constants.DOM_ERROR_HANDLER);
             v.add(Constants.DOM_RESOURCE_RESOLVER);
@@ -1391,4 +1419,13 @@
         return xis;
     }
 
+    // Implements XSElementDeclHelper interface
+    public XSElementDecl getGlobalElementDecl(QName element) {
+        SchemaGrammar sGrammar = fGrammarBucket.getGrammar(element.uri);
+        if (sGrammar != null) {
+            return sGrammar.getGlobalElementDecl(element.localpart);
+        }
+        return null;
+    }
+
 } // XMLGrammarLoader