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;
027:
028: import java.util.HashSet;
029: import java.util.List;
030: import java.util.Set;
031:
032: import javax.activation.MimeType;
033: import javax.xml.namespace.QName;
034:
035: import com.sun.tools.internal.xjc.model.CClassInfo;
036: import com.sun.tools.internal.xjc.model.CCustomizations;
037: import com.sun.tools.internal.xjc.model.CElementInfo;
038: import com.sun.tools.internal.xjc.model.CElementPropertyInfo;
039: import static com.sun.tools.internal.xjc.model.CElementPropertyInfo.CollectionMode.*;
040: import com.sun.tools.internal.xjc.model.CNonElement;
041: import com.sun.tools.internal.xjc.model.CReferencePropertyInfo;
042: import com.sun.tools.internal.xjc.model.CTypeRef;
043: import com.sun.tools.internal.xjc.model.Model;
044: import com.sun.tools.internal.xjc.model.Multiplicity;
045: import com.sun.tools.internal.xjc.model.TypeUse;
046: import com.sun.tools.internal.xjc.model.nav.NType;
047: import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
048: import com.sun.tools.internal.xjc.reader.xmlschema.ClassSelector;
049: import com.sun.tools.internal.xjc.reader.xmlschema.SimpleTypeBuilder;
050: import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIGlobalBinding;
051: import com.sun.xml.internal.bind.v2.model.core.Element;
052: import com.sun.xml.internal.bind.v2.model.core.ID;
053: import com.sun.xml.internal.xsom.XSComponent;
054: import com.sun.xml.internal.xsom.XSElementDecl;
055: import com.sun.xml.internal.xsom.XSType;
056: import com.sun.xml.internal.xsom.XmlString;
057:
058: import org.xml.sax.Locator;
059:
060: /**
061: * Set of {@link Ref}.
062: *
063: * @author Kohsuke Kawaguchi
064: */
065: public final class RawTypeSet {
066:
067: public final Set<Ref> refs;
068:
069: /**
070: * True if this type set can form references to types.
071: */
072: public final Mode canBeTypeRefs;
073:
074: /**
075: * The occurence of the whole references.
076: */
077: public final Multiplicity mul;
078:
079: // computed inside canBeTypeRefs()
080: private CElementPropertyInfo.CollectionMode collectionMode;
081:
082: /**
083: * Should be called from one of the raw type set builders.
084: */
085: public RawTypeSet(Set<Ref> refs, Multiplicity m) {
086: this .refs = refs;
087: mul = m;
088: canBeTypeRefs = canBeTypeRefs();
089: }
090:
091: public CElementPropertyInfo.CollectionMode getCollectionMode() {
092: return collectionMode;
093: }
094:
095: public boolean isRequired() {
096: return mul.min > 0;
097: }
098:
099: /**
100: * Represents the possible binding option for this {@link RawTypeSet}.
101: */
102: public enum Mode {
103: /**
104: * This {@link RawTypeSet} can be either an reference property or
105: * an element property, and XJC recommends element property.
106: */
107: SHOULD_BE_TYPEREF(0),
108: /**
109: * This {@link RawTypeSet} can be either an reference property or
110: * an element property, and XJC recommends reference property.
111: */
112: CAN_BE_TYPEREF(1),
113: /**
114: * This {@link RawTypeSet} can be only bound to a reference property.
115: */
116: MUST_BE_REFERENCE(2);
117:
118: private final int rank;
119:
120: Mode(int rank) {
121: this .rank = rank;
122: }
123:
124: Mode or(Mode that) {
125: switch (Math.max(this .rank, that.rank)) {
126: case 0:
127: return SHOULD_BE_TYPEREF;
128: case 1:
129: return CAN_BE_TYPEREF;
130: case 2:
131: return MUST_BE_REFERENCE;
132: }
133: throw new AssertionError();
134: }
135: }
136:
137: /**
138: * Returns true if {@link #refs} can form refs of types.
139: *
140: * If there are multiple {@link Ref}s with the same type,
141: * we cannot make them into type refs. Or if any of the {@link Ref}
142: * says they cannot be in type refs, we cannot do that either.
143: *
144: * TODO: just checking if the refs are the same is not suffice.
145: * If two refs derive from each other, they cannot form a list of refs
146: * (because of a possible ambiguity).
147: */
148: private Mode canBeTypeRefs() {
149: Set<NType> types = new HashSet<NType>();
150:
151: collectionMode = mul.isAtMostOnce() ? NOT_REPEATED
152: : REPEATED_ELEMENT;
153:
154: // the way we compute this is that we start from the most optimistic value,
155: // and then gradually degrade as we find something problematic.
156: Mode mode = Mode.SHOULD_BE_TYPEREF;
157:
158: for (Ref r : refs) {
159: mode = mode.or(r.canBeType(this ));
160: if (mode == Mode.MUST_BE_REFERENCE)
161: return mode; // no need to continue the processing
162:
163: if (!types.add(r.toTypeRef(null).getTarget().getType()))
164: return Mode.MUST_BE_REFERENCE; // collision
165: if (r.isListOfValues()) {
166: if (refs.size() > 1 || !mul.isAtMostOnce())
167: return Mode.MUST_BE_REFERENCE; // restriction on @XmlList
168: collectionMode = REPEATED_VALUE;
169: }
170: }
171: return mode;
172: }
173:
174: public void addTo(CElementPropertyInfo prop) {
175: assert canBeTypeRefs != Mode.MUST_BE_REFERENCE;
176: if (mul.isZero())
177: return; // the property can't have any value
178:
179: List<CTypeRef> dst = prop.getTypes();
180: for (Ref t : refs)
181: dst.add(t.toTypeRef(prop));
182: }
183:
184: public void addTo(CReferencePropertyInfo prop) {
185: if (mul.isZero())
186: return; // the property can't have any value
187: for (Ref t : refs)
188: t.toElementRef(prop);
189: }
190:
191: public ID id() {
192: for (Ref t : refs) {
193: ID id = t.id();
194: if (id != ID.NONE)
195: return id;
196: }
197: return ID.NONE;
198: }
199:
200: public MimeType getExpectedMimeType() {
201: for (Ref t : refs) {
202: MimeType mt = t.getExpectedMimeType();
203: if (mt != null)
204: return mt;
205: }
206: return null;
207: }
208:
209: /**
210: * A reference to something.
211: *
212: * <p>
213: * A {@link Ref} can be either turned into {@link CTypeRef} to form
214: * an element property, or {@link Element} to form a reference property.
215: */
216: public static abstract class Ref {
217: /**
218: * @param ep
219: * the property to which the returned {@link CTypeRef} will be
220: * added to.
221: */
222: protected abstract CTypeRef toTypeRef(CElementPropertyInfo ep);
223:
224: protected abstract void toElementRef(CReferencePropertyInfo prop);
225:
226: /**
227: * Can this {@link Ref} be a type ref?
228: * @return false to veto.
229: * @param parent
230: */
231: protected abstract Mode canBeType(RawTypeSet parent);
232:
233: protected abstract boolean isListOfValues();
234:
235: /**
236: * When this {@link RawTypeSet} binds to a {@link CElementPropertyInfo},
237: * this method is used to determine if the property is ID or not.
238: */
239: protected abstract ID id();
240:
241: /**
242: * When this {@link RawTypeSet} binds to a {@link CElementPropertyInfo},
243: * this method is used to determine if the property has an associated expected MIME type or not.
244: */
245: protected MimeType getExpectedMimeType() {
246: return null;
247: }
248: }
249:
250: /**
251: * References to a type. Could be global or local.
252: */
253: public static final class XmlTypeRef extends Ref {
254: public final QName elementName;
255: public final TypeUse target;
256: public final Locator locator;
257: public final XSComponent source;
258: public final CCustomizations custs;
259: public final boolean nillable;
260: public final XmlString defaultValue;
261:
262: public XmlTypeRef(QName elementName, TypeUse target,
263: boolean nillable, XmlString defaultValue,
264: XSComponent source, CCustomizations custs, Locator loc) {
265: assert elementName != null;
266: assert target != null;
267:
268: this .elementName = elementName;
269: this .target = target;
270: this .source = source;
271: this .custs = custs;
272: this .nillable = nillable;
273: this .defaultValue = defaultValue;
274: this .locator = loc;
275: }
276:
277: public XmlTypeRef(QName elementName, XSType target,
278: boolean nillable, XmlString defaultValue) {
279: this (elementName, Ring.get(ClassSelector.class).bindToType(
280: target), nillable, defaultValue, target, Ring.get(
281: BGMBuilder.class).getBindInfo(target)
282: .toCustomizationList(), target.getLocator());
283: }
284:
285: public XmlTypeRef(XSElementDecl decl) {
286: this (new QName(decl.getTargetNamespace(), decl.getName()),
287: bindToType(decl), decl.isNillable(), decl
288: .getDefaultValue(), decl, Ring.get(
289: BGMBuilder.class).getBindInfo(decl)
290: .toCustomizationList(), decl.getLocator());
291: }
292:
293: protected CTypeRef toTypeRef(CElementPropertyInfo ep) {
294: if (ep != null && target.getAdapterUse() != null)
295: ep.setAdapter(target.getAdapterUse());
296: return new CTypeRef((CNonElement) target.getInfo(),
297: elementName, nillable, defaultValue);
298: }
299:
300: /**
301: * The whole type set can be later bound to a reference property,
302: * in which case we need to generate additional code to wrap this
303: * type reference into an element class.
304: *
305: * This method generates such an element class and returns it.
306: */
307: protected void toElementRef(CReferencePropertyInfo prop) {
308: CClassInfo scope = Ring.get(ClassSelector.class)
309: .getCurrentBean();
310: Model model = Ring.get(Model.class);
311:
312: if (target instanceof CClassInfo
313: && Ring.get(BIGlobalBinding.class).isSimpleMode()) {
314: CClassInfo bean = new CClassInfo(model, scope, model
315: .getNameConverter().toClassName(
316: elementName.getLocalPart()), locator,
317: null, elementName, source, custs);
318: bean.setBaseClass((CClassInfo) target);
319: prop.getElements().add(bean);
320: } else {
321: CElementInfo e = new CElementInfo(model, elementName,
322: scope, target, defaultValue, source, custs,
323: locator);
324: prop.getElements().add(e);
325: }
326: }
327:
328: protected Mode canBeType(RawTypeSet parent) {
329: // if we have an adapter or IDness, which requires special
330: // annotation, and there's more than one element,
331: // we have no place to put the special annotation, so we need JAXBElement.
332: if (parent.refs.size() > 1 || !parent.mul.isAtMostOnce()) {
333: if (target.getAdapterUse() != null
334: || target.idUse() != ID.NONE)
335: return Mode.MUST_BE_REFERENCE;
336: }
337:
338: // nillable and optional at the same time. needs an element wrapper to distinguish those
339: // two states. But this is not a hard requirement.
340: if (nillable && parent.mul.isOptional())
341: return Mode.CAN_BE_TYPEREF;
342:
343: return Mode.SHOULD_BE_TYPEREF;
344: }
345:
346: protected boolean isListOfValues() {
347: return target.isCollection();
348: }
349:
350: protected ID id() {
351: return target.idUse();
352: }
353:
354: protected MimeType getExpectedMimeType() {
355: return target.getExpectedMimeType();
356: }
357: }
358:
359: private static TypeUse bindToType(XSElementDecl decl) {
360: SimpleTypeBuilder stb = Ring.get(SimpleTypeBuilder.class);
361: stb.refererStack.push(decl);
362: TypeUse r = Ring.get(ClassSelector.class).bindToType(
363: decl.getType());
364: stb.refererStack.pop();
365: return r;
366: }
367: }
|