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