aboutsummaryrefslogtreecommitdiff
path: root/src/com/sun/org/apache/xerces/internal/impl/xs/SubstitutionGroupHandler.java
blob: bd95b17be42dc4fab0d9de432f3623a97bff99cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/*
 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
 */
/*
 * Copyright 2001-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
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sun.org.apache.xerces.internal.impl.xs;

import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xs.XSConstants;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;
import com.sun.org.apache.xerces.internal.xs.XSSimpleTypeDefinition;
import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

/**
 * To store and validate information about substitutionGroup
 *
 * @xerces.internal
 *
 * @author Sandy Gao, IBM
 *
 * @version $Id: SubstitutionGroupHandler.java,v 1.6 2010-11-01 04:39:55 joehw Exp $
 */
public class SubstitutionGroupHandler {

    private static final XSElementDecl[] EMPTY_GROUP = new XSElementDecl[0];

    // grammar resolver
    XSGrammarBucket fGrammarBucket;

    /**
     * Default constructor
     */
    public SubstitutionGroupHandler(XSGrammarBucket grammarBucket) {
        fGrammarBucket = grammarBucket;
    }

    // 3.9.4 Element Sequence Locally Valid (Particle) 2.3.3
    // check whether one element decl matches an element with the given qname
    public XSElementDecl getMatchingElemDecl(QName element, XSElementDecl exemplar) {
        if (element.localpart == exemplar.fName &&
            element.uri == exemplar.fTargetNamespace) {
            return exemplar;
        }

        // if the exemplar is not a global element decl, then it's not possible
        // to be substituted by another element.
        if (exemplar.fScope != XSConstants.SCOPE_GLOBAL)
            return null;

        // if the decl blocks substitution, return false
        if ((exemplar.fBlock & XSConstants.DERIVATION_SUBSTITUTION) != 0)
            return null;

        // get grammar of the element
        SchemaGrammar sGrammar = fGrammarBucket.getGrammar(element.uri);
        if (sGrammar == null)
            return null;

        // get the decl for the element
        XSElementDecl eDecl = sGrammar.getGlobalElementDecl(element.localpart);
        if (eDecl == null)
            return null;

        // and check by using substitutionGroup information
        if (substitutionGroupOK(eDecl, exemplar, exemplar.fBlock))
            return eDecl;

        return null;
    }

    // 3.3.6 Substitution Group OK (Transitive)
    // check whether element can substitute exemplar
    protected boolean substitutionGroupOK(XSElementDecl element, XSElementDecl exemplar, short blockingConstraint) {
        // 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:
        // 1. D and C are the same element declaration.
        if (element == exemplar)
            return true;

        // 2 All of the following must be true:
        // 2.1 The blocking constraint does not contain substitution.
        if ((blockingConstraint & XSConstants.DERIVATION_SUBSTITUTION) != 0)
            return false;

        // 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 . . .
        XSElementDecl subGroup = element.fSubGroup;
        while (subGroup != null && subGroup != exemplar) {
            subGroup = subGroup.fSubGroup;
        }

        if (subGroup == null)
            return false;

        // 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}.
        // prepare the combination of {derivation method} and
        // {disallowed substitution}
        return typeDerivationOK(element.fType, exemplar.fType, blockingConstraint);
    }
    private boolean typeDerivationOK(XSTypeDefinition derived, XSTypeDefinition base, short blockingConstraint) {

        short devMethod = 0, blockConstraint = blockingConstraint;

        // "derived" should be derived from "base"
        // add derivation methods of derived types to devMethod;
        // add block of base types to blockConstraint.
        XSTypeDefinition type = derived;
        while (type != base && type != SchemaGrammar.fAnyType) {
            if (type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
                devMethod |= ((XSComplexTypeDecl)type).fDerivedBy;
            }
            else {
                devMethod |= XSConstants.DERIVATION_RESTRICTION;
            }
            type = type.getBaseType();
            // type == null means the current type is anySimpleType,
            // whose base type should be anyType
            if (type == null) {
                type = SchemaGrammar.fAnyType;
            }
            if (type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
                blockConstraint |= ((XSComplexTypeDecl)type).fBlock;
            }
        }
        if (type != base) {
            // If the base is a union, check if "derived" is allowed through any of the member types.
            if (base.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
                XSSimpleTypeDefinition st = (XSSimpleTypeDefinition) base;
                if (st.getVariety() ==  XSSimpleTypeDefinition.VARIETY_UNION) {
                    XSObjectList memberTypes = st.getMemberTypes();
                    final int length = memberTypes.getLength();
                    for (int i = 0; i < length; ++i) {
                        if (typeDerivationOK(derived, (XSTypeDefinition) memberTypes.item(i), blockingConstraint)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        if ((devMethod & blockConstraint) != 0) {
            return false;
        }
        return true;
    }

    // check whether element is in exemplar's substitution group
    public boolean inSubstitutionGroup(XSElementDecl element, XSElementDecl exemplar) {
        // [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:
        // Define PSG, the potential substitution group for HEAD, as follows:
        // 1 The element declaration itself is in PSG;
        // 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.
        // HEAD's actual substitution group is then the set consisting of each member of PSG such that all of the following must be true:
        // 1 Its {abstract} is false.
        // 2 It is validly substitutable for HEAD subject to an empty blocking constraint, as defined in Substitution Group OK (Transitive) (3.3.6).
        return substitutionGroupOK(element, exemplar, exemplar.fBlock);
    }

    // to store substitution group information
    // the key to the map is an element decl, and the value is
    // - a Vector, which contains all elements that has this element as their
    //   substitution group affilication
    // - an array of OneSubGroup, which contains its substitution group before block.
    Map<XSElementDecl, Object> fSubGroupsB = new HashMap<>();
    private static final OneSubGroup[] EMPTY_VECTOR = new OneSubGroup[0];
    // The real substitution groups (after "block")
    Map<XSElementDecl, XSElementDecl[]> fSubGroups = new HashMap<>();

    /**
     * clear the internal registry of substitutionGroup information
     */
    public void reset() {
        fSubGroupsB.clear();
        fSubGroups.clear();
    }

    /**
     * add a list of substitution group information.
     */
    public void addSubstitutionGroup(XSElementDecl[] elements) {
        XSElementDecl subHead, element;
        Vector subGroup;
        // for all elements with substitution group affiliation
        for (int i = elements.length-1; i >= 0; i--) {
            element = elements[i];
            subHead = element.fSubGroup;
            // check whether this an entry for this element
            subGroup = (Vector)fSubGroupsB.get(subHead);
            if (subGroup == null) {
                // if not, create a new one
                subGroup = new Vector();
                fSubGroupsB.put(subHead, subGroup);
            }
            // add to the vactor
            subGroup.addElement(element);
        }
    }

    /**
     * get all elements that can substitute the given element,
     * according to the spec, we shouldn't consider the {block} constraints.
     *
     * from the spec, substitution group of a given element decl also contains
     * the element itself. but the array returned from this method doesn't
     * containt this element.
     */
    public XSElementDecl[] getSubstitutionGroup(XSElementDecl element) {
        // If we already have sub group for this element, just return it.
        XSElementDecl[] subGroup = fSubGroups.get(element);
        if (subGroup != null)
            return subGroup;

        if ((element.fBlock & XSConstants.DERIVATION_SUBSTITUTION) != 0) {
            fSubGroups.put(element, EMPTY_GROUP);
            return EMPTY_GROUP;
        }

        // Otherwise, get all potential sub group elements
        // (without considering "block" on this element
        OneSubGroup[] groupB = getSubGroupB(element, new OneSubGroup());
        int len = groupB.length, rlen = 0;
        XSElementDecl[] ret = new XSElementDecl[len];
        // For each of such elements, check whether the derivation methods
        // overlap with "block". If not, add it to the sub group
        for (int i = 0 ; i < len; i++) {
            if ((element.fBlock & groupB[i].dMethod) == 0)
                ret[rlen++] = groupB[i].sub;
        }
        // Resize the array if necessary
        if (rlen < len) {
            XSElementDecl[] ret1 = new XSElementDecl[rlen];
            System.arraycopy(ret, 0, ret1, 0, rlen);
            ret = ret1;
        }
        // Store the subgroup
        fSubGroups.put(element, ret);

        return ret;
    }

    // Get potential sub group element (without considering "block")
    private OneSubGroup[] getSubGroupB(XSElementDecl element, OneSubGroup methods) {
        Object subGroup = fSubGroupsB.get(element);

        // substitution group for this one is empty
        if (subGroup == null) {
            fSubGroupsB.put(element, EMPTY_VECTOR);
            return EMPTY_VECTOR;
        }

        // we've already calculated the element, just return.
        if (subGroup instanceof OneSubGroup[])
            return (OneSubGroup[])subGroup;

        // we only have the *direct* substitutions
        Vector group = (Vector)subGroup, newGroup = new Vector();
        OneSubGroup[] group1;
        // then for each of the direct substitutions, get its substitution
        // group, and combine the groups together.
        short dMethod, bMethod, dSubMethod, bSubMethod;
        for (int i = group.size()-1, j; i >= 0; i--) {
            // Check whether this element is blocked. If so, ignore it.
            XSElementDecl sub = (XSElementDecl)group.elementAt(i);
            if (!getDBMethods(sub.fType, element.fType, methods))
                continue;
            // Remember derivation methods and blocks from the types
            dMethod = methods.dMethod;
            bMethod = methods.bMethod;
            // Add this one to potential group
            newGroup.addElement(new OneSubGroup(sub, methods.dMethod, methods.bMethod));
            // Get potential group for this element
            group1 = getSubGroupB(sub, methods);
            for (j = group1.length-1; j >= 0; j--) {
                // For each of them, check whether it's blocked (by type)
                dSubMethod = (short)(dMethod | group1[j].dMethod);
                bSubMethod = (short)(bMethod | group1[j].bMethod);
                // Ignore it if it's blocked
                if ((dSubMethod & bSubMethod) != 0)
                    continue;
                newGroup.addElement(new OneSubGroup(group1[j].sub, dSubMethod, bSubMethod));
            }
        }
        // Convert to an array
        OneSubGroup[] ret = new OneSubGroup[newGroup.size()];
        for (int i = newGroup.size()-1; i >= 0; i--) {
            ret[i] = (OneSubGroup)newGroup.elementAt(i);
        }
        // Store the potential sub group
        fSubGroupsB.put(element, ret);

        return ret;
    }

    private boolean getDBMethods(XSTypeDefinition typed, XSTypeDefinition typeb,
                                 OneSubGroup methods) {
        short dMethod = 0, bMethod = 0;
        while (typed != typeb && typed != SchemaGrammar.fAnyType) {
            if (typed.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE)
                dMethod |= ((XSComplexTypeDecl)typed).fDerivedBy;
            else
                dMethod |= XSConstants.DERIVATION_RESTRICTION;
            typed = typed.getBaseType();
            // type == null means the current type is anySimpleType,
            // whose base type should be anyType
            if (typed == null)
                typed = SchemaGrammar.fAnyType;
            if (typed.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE)
                bMethod |= ((XSComplexTypeDecl)typed).fBlock;
        }
        // No derivation relation, or blocked, return false
        if (typed != typeb || (dMethod & bMethod) != 0)
            return false;

        // Remember the derivation methods and blocks, return true.
        methods.dMethod = dMethod;
        methods.bMethod = bMethod;
        return true;
    }

    // Record the information about how one element substitute another one
    private static final class OneSubGroup {
        OneSubGroup() {}
        OneSubGroup(XSElementDecl sub, short dMethod, short bMethod) {
            this.sub = sub;
            this.dMethod = dMethod;
            this.bMethod = bMethod;
        }
        // The element that substitutes another one
        XSElementDecl sub;
        // The combination of all derivation methods from sub's type to
        // the head's type
        short dMethod;
        // The combination of {block} of the types in the derivation chain
        // excluding sub's type
        short bMethod;
    }
} // class SubstitutionGroupHandler