View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2001-2005 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * 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.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 com.sun.org.apache.xerces.internal.xni.QName;
28  import java.util.Hashtable;
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 hashtable 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     Hashtable fSubGroupsB = new Hashtable();
180     private static final OneSubGroup[] EMPTY_VECTOR = new OneSubGroup[0];
181     // The real substitution groups (after "block")
182     Hashtable fSubGroups = new Hashtable();
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         Object subGroup = fSubGroups.get(element);
225         if (subGroup != null)
226             return (XSElementDecl[])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