1/*
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.internal.xjc.reader.xmlschema.ct;
27
28import java.util.HashMap;
29import java.util.Iterator;
30import java.util.Map;
31
32import com.sun.tools.internal.xjc.reader.xmlschema.WildcardNameClassBuilder;
33import com.sun.xml.internal.xsom.XSAttributeUse;
34import com.sun.xml.internal.xsom.XSComplexType;
35import com.sun.xml.internal.xsom.XSContentType;
36import com.sun.xml.internal.xsom.XSDeclaration;
37import com.sun.xml.internal.xsom.XSElementDecl;
38import com.sun.xml.internal.xsom.XSModelGroup;
39import com.sun.xml.internal.xsom.XSModelGroupDecl;
40import com.sun.xml.internal.xsom.XSParticle;
41import com.sun.xml.internal.xsom.XSType;
42import com.sun.xml.internal.xsom.XSWildcard;
43import com.sun.xml.internal.xsom.visitor.XSTermFunction;
44import javax.xml.namespace.QName;
45
46import com.sun.xml.internal.rngom.nc.ChoiceNameClass;
47import com.sun.xml.internal.rngom.nc.NameClass;
48import com.sun.xml.internal.rngom.nc.SimpleNameClass;
49
50/**
51 * Binds a complex type derived from another complex type by extension.
52 *
53 * @author
54 *     Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
55 */
56abstract class AbstractExtendedComplexTypeBuilder extends CTBuilder {
57
58    /**
59     * Map from {@link XSComplexType} to {@link NameClass}[2] that
60     * represents the names used in its child elements [0] and
61     * attributes [1].
62     */
63    protected final Map<XSComplexType, NameClass[]> characteristicNameClasses = new HashMap<XSComplexType, NameClass[]>();
64
65    /**
66     * Computes a name class that represents everything in a given content model.
67     */
68    protected final XSTermFunction<NameClass> contentModelNameClassBuilder = new XSTermFunction<NameClass>() {
69        @Override
70        public NameClass wildcard(XSWildcard wc) {
71            return WildcardNameClassBuilder.build(wc);
72        }
73
74        @Override
75        public NameClass modelGroupDecl(XSModelGroupDecl decl) {
76            return modelGroup(decl.getModelGroup());
77        }
78
79        @Override
80        public NameClass modelGroup(XSModelGroup group) {
81            NameClass nc = NameClass.NULL;
82            for( int i=0; i<group.getSize(); i++ )
83                nc = new ChoiceNameClass(nc, group.getChild(i).getTerm().apply(this));
84            return nc;
85        }
86
87        public NameClass elementDecl(XSElementDecl decl) {
88            return getNameClass(decl);
89        }
90    };
91
92    /**
93     * Checks if the particles/attributes defined in the type parameter
94     * collides with the name classes of anc/enc.
95     *
96     * @return true if there's a collision.
97     */
98    protected boolean checkCollision(NameClass anc, NameClass enc, XSComplexType type) {
99        NameClass[] chnc = characteristicNameClasses.get(type);
100        if (chnc == null) {
101            chnc = new NameClass[2];
102            chnc[0] = getNameClass(type.getContentType());
103
104            // build attribute name classes
105            NameClass nc = NameClass.NULL;
106            Iterator itr = type.iterateAttributeUses();
107            while( itr.hasNext() )
108                anc = new ChoiceNameClass(anc, getNameClass(((XSAttributeUse) itr.next()).getDecl()));
109            XSWildcard wc = type.getAttributeWildcard();
110            if(wc!=null)
111                nc = new ChoiceNameClass(nc, WildcardNameClassBuilder.build(wc));
112            chnc[1] = nc;
113
114            characteristicNameClasses.put(type, chnc);
115        }
116
117        return chnc[0].hasOverlapWith(enc) || chnc[1].hasOverlapWith(anc);
118    }
119
120    /**
121     * Looks for the derivation chain t_1 > t_2 > ... > t
122     * and find t_i such that t_i derives by restriction but
123     * for every j>i, t_j derives by extension.
124     *
125     * @return null
126     *      If there's no such t_i or if t_i is any type.
127     */
128    protected XSComplexType getLastRestrictedType(XSComplexType t) {
129        if (t.getBaseType() == schemas.getAnyType()) {
130            return null;   // we don't count the restriction from anyType
131        }
132        if (t.getDerivationMethod() == XSType.RESTRICTION) {
133            return t;
134        }
135
136        XSComplexType baseType = t.getBaseType().asComplexType();
137        if (baseType != null) {
138            return getLastRestrictedType(baseType);
139        } else {
140            return null;
141        }
142    }
143
144    /**
145     * Checks if this new extension is safe.
146     *
147     * UGLY.
148     * <p>
149     * If you have ctA extending ctB and ctB restricting ctC, our
150     * Java classes will look like CtAImpl extending CtBImpl
151     * extending CtCImpl.
152     *
153     * <p>
154     * Since a derived class unmarshaller uses the base class unmarshaller,
155     * this could potentially result in incorrect unmarshalling.
156     * We used to just reject such a case, but then we found that
157     * there are schemas that are using it.
158     *
159     * <p>
160     * One generalized observation that we reached is that if the extension
161     * is only adding new elements/attributes which has never been used
162     * in any of its base class (IOW, if none of the particle / attribute use /
163     * attribute wildcard can match the name of newly added elements/attributes)
164     * then it is safe to add them.
165     *
166     * <p>
167     * This function checks if the derivation chain to this type is
168     * not using restriction, and if it is, then checks if it is safe
169     * according to the above condition.
170     *
171     * @return false
172     *      If this complex type needs to be rejected.
173     */
174    protected boolean checkIfExtensionSafe(XSComplexType baseType, XSComplexType thisType) {
175        XSComplexType lastType = getLastRestrictedType(baseType);
176
177        if (lastType == null) {
178            return true;    // no restriction in derivation chain
179        }
180        NameClass anc = NameClass.NULL;
181        // build name class for attributes in new complex type
182        Iterator itr = thisType.iterateDeclaredAttributeUses();
183        while (itr.hasNext()) {
184            anc = new ChoiceNameClass(anc, getNameClass(((XSAttributeUse) itr.next()).getDecl()));
185        }
186        // TODO: attribute wildcard
187
188        NameClass enc = getNameClass(thisType.getExplicitContent());
189
190        // check against every base type ... except the root anyType
191        while (lastType != lastType.getBaseType()) {
192            if (checkCollision(anc, enc, lastType)) {
193                return false;
194            }
195
196            if (lastType.getBaseType().isSimpleType()) // if the base type is a simple type, there won't be
197            // any further name collision.
198            {
199                return true;
200            }
201
202            lastType = lastType.getBaseType().asComplexType();
203        }
204
205        return true;    // OK
206    }
207
208    /**
209     * Gets a {@link NameClass} that represents all the terms in the given content type.
210     * If t is not a particle, just return an empty name class.
211     */
212    private NameClass getNameClass(XSContentType t) {
213        if(t==null) return NameClass.NULL;
214        XSParticle p = t.asParticle();
215        if(p==null) return NameClass.NULL;
216        else        return p.getTerm().apply(contentModelNameClassBuilder);
217    }
218
219    /**
220     * Gets a {@link SimpleNameClass} from the name of a {@link XSDeclaration}.
221     */
222    private NameClass getNameClass(XSDeclaration decl) {
223        return new SimpleNameClass(new QName(decl.getTargetNamespace(), decl.getName()));
224    }
225
226}
227