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