001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.internal.xjc.reader.xmlschema.ct;
027:
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.Map;
031:
032: import com.sun.tools.internal.xjc.model.CClassInfo;
033: import com.sun.tools.internal.xjc.reader.xmlschema.WildcardNameClassBuilder;
034: import com.sun.xml.internal.xsom.XSAttributeUse;
035: import com.sun.xml.internal.xsom.XSComplexType;
036: import com.sun.xml.internal.xsom.XSContentType;
037: import com.sun.xml.internal.xsom.XSDeclaration;
038: import com.sun.xml.internal.xsom.XSElementDecl;
039: import com.sun.xml.internal.xsom.XSModelGroup;
040: import com.sun.xml.internal.xsom.XSModelGroupDecl;
041: import com.sun.xml.internal.xsom.XSParticle;
042: import com.sun.xml.internal.xsom.XSType;
043: import com.sun.xml.internal.xsom.XSWildcard;
044: import com.sun.xml.internal.xsom.visitor.XSTermFunction;
045:
046: import com.sun.xml.internal.rngom.nc.ChoiceNameClass;
047: import com.sun.xml.internal.rngom.nc.NameClass;
048: import com.sun.xml.internal.rngom.nc.SimpleNameClass;
049:
050: /**
051: * Binds a complex type derived from another complex type by extension.
052: *
053: * @author
054: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
055: */
056: final class ExtendedComplexTypeBuilder extends CTBuilder {
057:
058: /**
059: * Map from {@link XSComplexType} to {@link NameClass}[2] that
060: * represents the names used in its child elements [0] and
061: * attributes [1].
062: */
063: private final Map<XSComplexType, NameClass[]> characteristicNameClasses = new HashMap<XSComplexType, NameClass[]>();
064:
065: public boolean isApplicable(XSComplexType ct) {
066: XSType baseType = ct.getBaseType();
067: return baseType != schemas.getAnyType()
068: && baseType.isComplexType()
069: && ct.getDerivationMethod() == XSType.EXTENSION;
070: }
071:
072: public void build(XSComplexType ct) {
073: XSComplexType baseType = ct.getBaseType().asComplexType();
074:
075: // build the base class
076: CClassInfo baseClass = selector.bindToType(baseType, true);
077: assert baseClass != null; // global complex type must map to a class
078:
079: selector.getCurrentBean().setBaseClass(baseClass);
080:
081: // derivation by extension.
082: ComplexTypeBindingMode baseTypeFlag = builder
083: .getBindingMode(baseType);
084:
085: XSContentType explicitContent = ct.getExplicitContent();
086:
087: if (!checkIfExtensionSafe(baseType, ct)) {
088: // error. We can't handle any further extension
089: errorReceiver.error(ct.getLocator(),
090: Messages.ERR_NO_FURTHER_EXTENSION.format(baseType
091: .getName(), ct.getName()));
092: return;
093: }
094:
095: // explicit content is always either empty or a particle.
096: if (explicitContent != null
097: && explicitContent.asParticle() != null) {
098:
099: if (baseTypeFlag == ComplexTypeBindingMode.NORMAL) {
100: // if we have additional explicit content, process them.
101:
102: builder
103: .recordBindingMode(
104: ct,
105: bgmBuilder.getParticleBinder()
106: .checkFallback(
107: explicitContent
108: .asParticle()) ? ComplexTypeBindingMode.FALLBACK_REST
109: : ComplexTypeBindingMode.NORMAL);
110:
111: bgmBuilder.getParticleBinder().build(
112: explicitContent.asParticle());
113:
114: } else {
115: // the base class has already done the fallback.
116: // don't add anything new
117: builder.recordBindingMode(ct, baseTypeFlag);
118: }
119: } else {
120: // if it's empty, no additional processing is necessary
121: builder.recordBindingMode(ct, baseTypeFlag);
122: }
123:
124: // adds attributes and we are through.
125: green.attContainer(ct);
126: }
127:
128: /**
129: * Checks if this new extension is safe.
130: *
131: * UGLY.
132: * <p>
133: * If you have ctA extending ctB and ctB restricting ctC, our
134: * Java classes will look like CtAImpl extending CtBImpl
135: * extending CtCImpl.
136: *
137: * <p>
138: * Since a derived class unmarshaller uses the base class unmarshaller,
139: * this could potentially result in incorrect unmarshalling.
140: * We used to just reject such a case, but then we found that
141: * there are schemas that are using it.
142: *
143: * <p>
144: * One generalized observation that we reached is that if the extension
145: * is only adding new elements/attributes which has never been used
146: * in any of its base class (IOW, if none of the particle / attribute use /
147: * attribute wildcard can match the name of newly added elements/attributes)
148: * then it is safe to add them.
149: *
150: * <p>
151: * This function checks if the derivation chain to this type is
152: * not using restriction, and if it is, then checks if it is safe
153: * according to the above condition.
154: *
155: * @return false
156: * If this complex type needs to be rejected.
157: */
158: private boolean checkIfExtensionSafe(XSComplexType baseType,
159: XSComplexType this Type) {
160: XSComplexType lastType = getLastRestrictedType(baseType);
161:
162: if (lastType == null)
163: return true; // no restriction in derivation chain
164:
165: NameClass anc = NameClass.NULL;
166: // build name class for attributes in new complex type
167: Iterator itr = this Type.iterateDeclaredAttributeUses();
168: while (itr.hasNext())
169: anc = new ChoiceNameClass(anc,
170: getNameClass(((XSAttributeUse) itr.next())
171: .getDecl()));
172: // TODO: attribute wildcard
173:
174: NameClass enc = getNameClass(this Type.getExplicitContent());
175:
176: // check against every base type ... except the root anyType
177: while (lastType != lastType.getBaseType()) {
178: if (checkCollision(anc, enc, lastType))
179: return false;
180:
181: if (lastType.getBaseType().isSimpleType())
182: // if the base type is a simple type, there won't be
183: // any further name collision.
184: return true;
185:
186: lastType = lastType.getBaseType().asComplexType();
187: }
188:
189: return true; // OK
190: }
191:
192: /**
193: * Checks if the particles/attributes defined in the type parameter
194: * collides with the name classes of anc/enc.
195: *
196: * @return true if there's a collision.
197: */
198: private boolean checkCollision(NameClass anc, NameClass enc,
199: XSComplexType type) {
200: NameClass[] chnc = characteristicNameClasses.get(type);
201: if (chnc == null) {
202: chnc = new NameClass[2];
203: chnc[0] = getNameClass(type.getContentType());
204:
205: // build attribute name classes
206: NameClass nc = NameClass.NULL;
207: Iterator itr = type.iterateAttributeUses();
208: while (itr.hasNext())
209: anc = new ChoiceNameClass(anc,
210: getNameClass(((XSAttributeUse) itr.next())
211: .getDecl()));
212: XSWildcard wc = type.getAttributeWildcard();
213: if (wc != null)
214: nc = new ChoiceNameClass(nc, WildcardNameClassBuilder
215: .build(wc));
216: chnc[1] = nc;
217:
218: characteristicNameClasses.put(type, chnc);
219: }
220:
221: return chnc[0].hasOverlapWith(enc)
222: || chnc[1].hasOverlapWith(anc);
223: }
224:
225: /**
226: * Gets a {@link NameClass} that represents all the terms in the given content type.
227: * If t is not a particle, just return an empty name class.
228: */
229: private NameClass getNameClass(XSContentType t) {
230: if (t == null)
231: return NameClass.NULL;
232: XSParticle p = t.asParticle();
233: if (p == null)
234: return NameClass.NULL;
235: else
236: return p.getTerm().apply(contentModelNameClassBuilder);
237: }
238:
239: /**
240: * Gets a {@link SimpleNameClass} from the name of a {@link XSDeclaration}.
241: */
242: private NameClass getNameClass(XSDeclaration decl) {
243: return new SimpleNameClass(decl.getTargetNamespace(), decl
244: .getName());
245: }
246:
247: /**
248: * Computes a name class that represents everything in a given content model.
249: */
250: private final XSTermFunction<NameClass> contentModelNameClassBuilder = new XSTermFunction<NameClass>() {
251: public NameClass wildcard(XSWildcard wc) {
252: return WildcardNameClassBuilder.build(wc);
253: }
254:
255: public NameClass modelGroupDecl(XSModelGroupDecl decl) {
256: return modelGroup(decl.getModelGroup());
257: }
258:
259: public NameClass modelGroup(XSModelGroup group) {
260: NameClass nc = NameClass.NULL;
261: for (int i = 0; i < group.getSize(); i++)
262: nc = new ChoiceNameClass(nc, group.getChild(i)
263: .getTerm().apply(this ));
264: return nc;
265: }
266:
267: public NameClass elementDecl(XSElementDecl decl) {
268: return getNameClass(decl);
269: }
270: };
271:
272: /**
273: * Looks for the derivation chain t_1 > t_2 > ... > t
274: * and find t_i such that t_i derives by restriction but
275: * for every j>i, t_j derives by extension.
276: *
277: * @return null
278: * If there's no such t_i or if t_i is any type.
279: */
280: private XSComplexType getLastRestrictedType(XSComplexType t) {
281: if (t.getBaseType() == schemas.getAnyType())
282: return null; // we don't count the restriction from anyType
283: if (t.getDerivationMethod() == XSType.RESTRICTION)
284: return t;
285:
286: XSComplexType baseType = t.getBaseType().asComplexType();
287: if (baseType != null)
288: return getLastRestrictedType(baseType);
289: else
290: return null;
291: }
292: }
|