1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl.xs;
  22 
  23 import com.sun.org.apache.xerces.internal.xni.QName;
  24 import com.sun.org.apache.xerces.internal.xs.XSConstants;
  25 import com.sun.org.apache.xerces.internal.xs.XSElementDeclaration;
  26 import com.sun.org.apache.xerces.internal.xs.XSObjectList;
  27 import com.sun.org.apache.xerces.internal.xs.XSSimpleTypeDefinition;
  28 import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
  29 import java.util.ArrayList;
  30 import java.util.HashMap;
  31 import java.util.List;
  32 import java.util.Map;
  33 
  34 /**
  35  * To store and validate information about substitutionGroup
  36  *
  37  * @xerces.internal
  38  *
  39  * @author Sandy Gao, IBM
  40  *
  41  * @LastModified: Nov 2017
  42  */
  43 public class SubstitutionGroupHandler {
  44 
  45     private static final XSElementDecl[] EMPTY_GROUP = new XSElementDecl[0];
  46 
  47     // global element declaration resolver
  48     private final XSElementDeclHelper fXSElementDeclHelper;
  49 
  50     /**
  51      * Default constructor
  52      */
  53     public SubstitutionGroupHandler(XSElementDeclHelper elementDeclHelper) {
  54         fXSElementDeclHelper = elementDeclHelper;
  55     }
  56 
  57     // 3.9.4 Element Sequence Locally Valid (Particle) 2.3.3
  58     // check whether one element decl matches an element with the given qname
  59     public XSElementDecl getMatchingElemDecl(QName element, XSElementDecl exemplar) {
  60         if (element.localpart == exemplar.fName &&
  61             element.uri == exemplar.fTargetNamespace) {
  62             return exemplar;
  63         }
  64 
  65         // if the exemplar is not a global element decl, then it's not possible
  66         // to be substituted by another element.
  67         if (exemplar.fScope != XSConstants.SCOPE_GLOBAL) {
  68             return null;
  69         }
  70 
  71         // if the decl blocks substitution, return false
  72         if ((exemplar.fBlock & XSConstants.DERIVATION_SUBSTITUTION) != 0) {
  73             return null;
  74         }
  75 
  76         // get the decl for the element
  77         XSElementDecl eDecl = fXSElementDeclHelper.getGlobalElementDecl(element);
  78         if (eDecl == null) {
  79             return null;
  80         }
  81 
  82         // and check by using substitutionGroup information
  83         if (substitutionGroupOK(eDecl, exemplar, exemplar.fBlock)) {
  84             return eDecl;
  85         }
  86 
  87         return null;
  88     }
  89 
  90     // 3.3.6 Substitution Group OK (Transitive)
  91     // check whether element can substitute exemplar
  92     protected boolean substitutionGroupOK(XSElementDecl element, XSElementDecl exemplar, short blockingConstraint) {
  93         // For an element declaration (call it D) to be validly substitutable for another element declaration (call it C) subject to a blocking constraint (a subset of {substitution, extension, restriction}, the value of a {disallowed substitutions}) one of the following must be true:
  94         // 1. D and C are the same element declaration.
  95         if (element == exemplar) {
  96             return true;
  97         }
  98 
  99         // 2 All of the following must be true:
 100         // 2.1 The blocking constraint does not contain substitution.
 101         if ((blockingConstraint & XSConstants.DERIVATION_SUBSTITUTION) != 0) {
 102             return false;
 103         }
 104 
 105         // 2.2 There is a chain of {substitution group affiliation}s from D to C, that is, either D's {substitution group affiliation} is C, or D's {substitution group affiliation}'s {substitution group affiliation} is C, or . . .
 106         XSElementDecl subGroup = element.fSubGroup;
 107         while (subGroup != null && subGroup != exemplar) {
 108             subGroup = subGroup.fSubGroup;
 109         }
 110 
 111         if (subGroup == null) {
 112             return false;
 113         }
 114 
 115         // 2.3 The set of all {derivation method}s involved in the derivation of D's {type definition} from C's {type definition} does not intersect with the union of the blocking constraint, C's {prohibited substitutions} (if C is complex, otherwise the empty set) and the {prohibited substitutions} (respectively the empty set) of any intermediate {type definition}s in the derivation of D's {type definition} from C's {type definition}.
 116         // prepare the combination of {derivation method} and
 117         // {disallowed substitution}
 118         return typeDerivationOK(element.fType, exemplar.fType, blockingConstraint);
 119     }
 120 
 121     private boolean typeDerivationOK(XSTypeDefinition derived, XSTypeDefinition base, short blockingConstraint) {
 122 
 123         short devMethod = 0, blockConstraint = blockingConstraint;
 124 
 125         // "derived" should be derived from "base"
 126         // add derivation methods of derived types to devMethod;
 127         // add block of base types to blockConstraint.
 128         XSTypeDefinition type = derived;
 129         while (type != base && type != SchemaGrammar.fAnyType) {
 130             if (type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
 131                 devMethod |= ((XSComplexTypeDecl)type).fDerivedBy;
 132             }
 133             else {
 134                 devMethod |= XSConstants.DERIVATION_RESTRICTION;
 135             }
 136             type = type.getBaseType();
 137             // type == null means the current type is anySimpleType,
 138             // whose base type should be anyType
 139             if (type == null) {
 140                 type = SchemaGrammar.fAnyType;
 141             }
 142             if (type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
 143                 blockConstraint |= ((XSComplexTypeDecl)type).fBlock;
 144             }
 145         }
 146         if (type != base) {
 147             // If the base is a union, check if "derived" is allowed through any of the member types.
 148             if (base.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
 149                 XSSimpleTypeDefinition st = (XSSimpleTypeDefinition) base;
 150                 if (st.getVariety() ==  XSSimpleTypeDefinition.VARIETY_UNION) {
 151                     XSObjectList memberTypes = st.getMemberTypes();
 152                     final int length = memberTypes.getLength();
 153                     for (int i = 0; i < length; ++i) {
 154                         if (typeDerivationOK(derived, (XSTypeDefinition) memberTypes.item(i), blockingConstraint)) {
 155                             return true;
 156                         }
 157                     }
 158                 }
 159             }
 160             return false;
 161         }
 162         if ((devMethod & blockConstraint) != 0) {
 163             return false;
 164         }
 165         return true;
 166     }
 167 
 168     // check whether element is in exemplar's substitution group
 169     public boolean inSubstitutionGroup(XSElementDecl element, XSElementDecl exemplar) {
 170         // [Definition:]  Every element declaration (call this HEAD) in the {element declarations} of a schema defines a substitution group, a subset of those {element declarations}, as follows:
 171         // Define PSG, the potential substitution group for HEAD, as follows:
 172         // 1 The element declaration itself is in PSG;
 173         // 2 PSG is closed with respect to {substitution group affiliation}, that is, if any element declaration in the {element declarations} has a {substitution group affiliation} in PSG, then it is also in PSG itself.
 174         // HEAD's actual substitution group is then the set consisting of each member of PSG such that all of the following must be true:
 175         // 1 Its {abstract} is false.
 176         // 2 It is validly substitutable for HEAD subject to an empty blocking constraint, as defined in Substitution Group OK (Transitive) (3.3.6).
 177         return substitutionGroupOK(element, exemplar, exemplar.fBlock);
 178     }
 179 
 180     // to store substitution group information
 181     // the key to the map is an element decl, and the value is
 182     // - a Vector, which contains all elements that has this element as their
 183     //   substitution group affilication
 184     // - an array of OneSubGroup, which contains its substitution group before block.
 185     Map<XSElementDecl, Object> fSubGroupsB = new HashMap<>();
 186     private static final OneSubGroup[] EMPTY_VECTOR = new OneSubGroup[0];
 187     // The real substitution groups (after "block")
 188     Map<XSElementDecl, XSElementDecl[]> fSubGroups = new HashMap<>();
 189 
 190     /**
 191      * clear the internal registry of substitutionGroup information
 192      */
 193     public void reset() {
 194         fSubGroupsB.clear();
 195         fSubGroups.clear();
 196     }
 197 
 198     /**
 199      * add a list of substitution group information.
 200      */
 201     @SuppressWarnings("unchecked")
 202     public void addSubstitutionGroup(XSElementDecl[] elements) {
 203         XSElementDecl subHead, element;
 204         List<XSElementDecl> subGroup;
 205         // for all elements with substitution group affiliation
 206         for (int i = elements.length-1; i >= 0; i--) {
 207             element = elements[i];
 208             subHead = element.fSubGroup;
 209             // check whether this an entry for this element
 210             subGroup = (List<XSElementDecl>)fSubGroupsB.get(subHead);
 211             if (subGroup == null) {
 212                 // if not, create a new one
 213                 subGroup = new ArrayList<>();
 214                 fSubGroupsB.put(subHead, subGroup);
 215             }
 216             // add to the vactor
 217             subGroup.add(element);
 218         }
 219     }
 220 
 221     /**
 222      * get all elements that can substitute the given element,
 223      * according to the spec, we shouldn't consider the {block} constraints.
 224      *
 225      * from the spec, substitution group of a given element decl also contains
 226      * the element itself. but the array returned from this method doesn't
 227      * containt this element.
 228      */
 229     public XSElementDecl[] getSubstitutionGroup(XSElementDecl element) {
 230         // If we already have sub group for this element, just return it.
 231         XSElementDecl[] subGroup = fSubGroups.get(element);
 232         if (subGroup != null)
 233             return subGroup;
 234 
 235         if ((element.fBlock & XSConstants.DERIVATION_SUBSTITUTION) != 0) {
 236             fSubGroups.put(element, EMPTY_GROUP);
 237             return EMPTY_GROUP;
 238         }
 239 
 240         // Otherwise, get all potential sub group elements
 241         // (without considering "block" on this element
 242         OneSubGroup[] groupB = getSubGroupB(element, new OneSubGroup());
 243         int len = groupB.length, rlen = 0;
 244         XSElementDecl[] ret = new XSElementDecl[len];
 245         // For each of such elements, check whether the derivation methods
 246         // overlap with "block". If not, add it to the sub group
 247         for (int i = 0 ; i < len; i++) {
 248             if ((element.fBlock & groupB[i].dMethod) == 0)
 249                 ret[rlen++] = groupB[i].sub;
 250         }
 251         // Resize the array if necessary
 252         if (rlen < len) {
 253             XSElementDecl[] ret1 = new XSElementDecl[rlen];
 254             System.arraycopy(ret, 0, ret1, 0, rlen);
 255             ret = ret1;
 256         }
 257         // Store the subgroup
 258         fSubGroups.put(element, ret);
 259 
 260         return ret;
 261     }
 262 
 263     // Get potential sub group element (without considering "block")
 264     private OneSubGroup[] getSubGroupB(XSElementDecl element, OneSubGroup methods) {
 265         Object subGroup = fSubGroupsB.get(element);
 266 
 267         // substitution group for this one is empty
 268         if (subGroup == null) {
 269             fSubGroupsB.put(element, EMPTY_VECTOR);
 270             return EMPTY_VECTOR;
 271         }
 272 
 273         // we've already calculated the element, just return.
 274         if (subGroup instanceof OneSubGroup[])
 275             return (OneSubGroup[])subGroup;
 276 
 277         // we only have the *direct* substitutions
 278         @SuppressWarnings("unchecked")
 279         List<XSElementDecl> group = (ArrayList<XSElementDecl>)subGroup;
 280         List<OneSubGroup> newGroup = new ArrayList<>();
 281         OneSubGroup[] group1;
 282         // then for each of the direct substitutions, get its substitution
 283         // group, and combine the groups together.
 284         short dMethod, bMethod, dSubMethod, bSubMethod;
 285         for (int i = group.size()-1, j; i >= 0; i--) {
 286             // Check whether this element is blocked. If so, ignore it.
 287             XSElementDecl sub = group.get(i);
 288             if (!getDBMethods(sub.fType, element.fType, methods))
 289                 continue;
 290             // Remember derivation methods and blocks from the types
 291             dMethod = methods.dMethod;
 292             bMethod = methods.bMethod;
 293             // Add this one to potential group
 294             newGroup.add(new OneSubGroup(sub, methods.dMethod, methods.bMethod));
 295             // Get potential group for this element
 296             group1 = getSubGroupB(sub, methods);
 297             for (j = group1.length-1; j >= 0; j--) {
 298                 // For each of them, check whether it's blocked (by type)
 299                 dSubMethod = (short)(dMethod | group1[j].dMethod);
 300                 bSubMethod = (short)(bMethod | group1[j].bMethod);
 301                 // Ignore it if it's blocked
 302                 if ((dSubMethod & bSubMethod) != 0)
 303                     continue;
 304                 newGroup.add(new OneSubGroup(group1[j].sub, dSubMethod, bSubMethod));
 305             }
 306         }
 307         // Convert to an array
 308         OneSubGroup[] ret = new OneSubGroup[newGroup.size()];
 309         for (int i = newGroup.size()-1; i >= 0; i--) {
 310             ret[i] = newGroup.get(i);
 311         }
 312         // Store the potential sub group
 313         fSubGroupsB.put(element, ret);
 314 
 315         return ret;
 316     }
 317 
 318     private boolean getDBMethods(XSTypeDefinition typed, XSTypeDefinition typeb,
 319                                  OneSubGroup methods) {
 320         short dMethod = 0, bMethod = 0;
 321         while (typed != typeb && typed != SchemaGrammar.fAnyType) {
 322             if (typed.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE)
 323                 dMethod |= ((XSComplexTypeDecl)typed).fDerivedBy;
 324             else
 325                 dMethod |= XSConstants.DERIVATION_RESTRICTION;
 326             typed = typed.getBaseType();
 327             // type == null means the current type is anySimpleType,
 328             // whose base type should be anyType
 329             if (typed == null)
 330                 typed = SchemaGrammar.fAnyType;
 331             if (typed.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE)
 332                 bMethod |= ((XSComplexTypeDecl)typed).fBlock;
 333         }
 334         // No derivation relation, or blocked, return false
 335         if (typed != typeb || (dMethod & bMethod) != 0)
 336             return false;
 337 
 338         // Remember the derivation methods and blocks, return true.
 339         methods.dMethod = dMethod;
 340         methods.bMethod = bMethod;
 341         return true;
 342     }
 343 
 344     // Record the information about how one element substitute another one
 345     private static final class OneSubGroup {
 346         OneSubGroup() {}
 347         OneSubGroup(XSElementDecl sub, short dMethod, short bMethod) {
 348             this.sub = sub;
 349             this.dMethod = dMethod;
 350             this.bMethod = bMethod;
 351         }
 352         // The element that substitutes another one
 353         XSElementDecl sub;
 354         // The combination of all derivation methods from sub's type to
 355         // the head's type
 356         short dMethod;
 357         // The combination of {block} of the types in the derivation chain
 358         // excluding sub's type
 359         short bMethod;
 360     }
 361 } // class SubstitutionGroupHandler