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.generator.bean.field;
037:
038: import java.util.ArrayList;
039: import java.util.Collection;
040: import java.util.List;
041:
042: import javax.xml.bind.annotation.W3CDomHandler;
043: import javax.xml.bind.annotation.XmlList;
044: import javax.xml.bind.annotation.XmlMixed;
045: import javax.xml.bind.annotation.XmlNsForm;
046: import javax.xml.bind.annotation.XmlValue;
047: import javax.xml.bind.annotation.XmlInlineBinaryData;
048: import javax.xml.namespace.QName;
049:
050: import com.sun.codemodel.JAnnotatable;
051: import com.sun.codemodel.JClass;
052: import com.sun.codemodel.JCodeModel;
053: import com.sun.codemodel.JExpr;
054: import com.sun.codemodel.JExpression;
055: import com.sun.codemodel.JFieldVar;
056: import com.sun.codemodel.JMod;
057: import com.sun.codemodel.JType;
058: import com.sun.tools.xjc.generator.annotation.spec.XmlAnyElementWriter;
059: import com.sun.tools.xjc.generator.annotation.spec.XmlAttributeWriter;
060: import com.sun.tools.xjc.generator.annotation.spec.XmlElementRefWriter;
061: import com.sun.tools.xjc.generator.annotation.spec.XmlElementRefsWriter;
062: import com.sun.tools.xjc.generator.annotation.spec.XmlElementWriter;
063: import com.sun.tools.xjc.generator.annotation.spec.XmlElementsWriter;
064: import com.sun.tools.xjc.generator.annotation.spec.XmlSchemaTypeWriter;
065: import com.sun.tools.xjc.generator.bean.ClassOutlineImpl;
066: import com.sun.tools.xjc.model.CAttributePropertyInfo;
067: import com.sun.tools.xjc.model.CElement;
068: import com.sun.tools.xjc.model.CElementInfo;
069: import com.sun.tools.xjc.model.CElementPropertyInfo;
070: import com.sun.tools.xjc.model.CPropertyInfo;
071: import com.sun.tools.xjc.model.CReferencePropertyInfo;
072: import com.sun.tools.xjc.model.CTypeInfo;
073: import com.sun.tools.xjc.model.CTypeRef;
074: import com.sun.tools.xjc.model.CValuePropertyInfo;
075: import com.sun.tools.xjc.model.nav.NClass;
076: import com.sun.tools.xjc.outline.Aspect;
077: import static com.sun.tools.xjc.outline.Aspect.IMPLEMENTATION;
078: import com.sun.tools.xjc.outline.ClassOutline;
079: import com.sun.tools.xjc.outline.FieldAccessor;
080: import com.sun.tools.xjc.outline.FieldOutline;
081: import com.sun.tools.xjc.reader.TypeUtil;
082: import com.sun.xml.bind.v2.TODO;
083:
084: /**
085: * Useful base class for implementing {@link FieldOutline}.
086: *
087: * <p>
088: * This class just provides a few utility methods and keep some
089: * important variables so that they can be readily accessed any time.
090: *
091: * @author
092: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
093: */
094: abstract class AbstractField implements FieldOutline {
095:
096: protected final ClassOutlineImpl outline;
097:
098: protected final CPropertyInfo prop;
099:
100: protected final JCodeModel codeModel;
101:
102: /**
103: * The type of this field, which can hold all the possible types.
104: */
105: protected final JType implType;
106:
107: /**
108: * The publicly visible type of this field.
109: * If we are generating value classes implType==exposedType.
110: */
111: protected final JType exposedType;
112:
113: protected AbstractField(ClassOutlineImpl outline, CPropertyInfo prop) {
114: this .outline = outline;
115: this .prop = prop;
116: this .codeModel = outline.parent().getCodeModel();
117: this .implType = getType(IMPLEMENTATION);
118: this .exposedType = getType(Aspect.EXPOSED);
119: }
120:
121: public final ClassOutline parent() {
122: return outline;
123: }
124:
125: public final CPropertyInfo getPropertyInfo() {
126: return prop;
127: }
128:
129: /**
130: * Annotate the field according to the recipes given as {@link CPropertyInfo}.
131: */
132: protected void annotate(JAnnotatable field) {
133:
134: assert (field != null);
135:
136: /*
137: TODO: consider moving this logic to somewhere else
138: so that it can be better shared, for how a field gets
139: annotated doesn't really depend on how we generate accessors.
140:
141: so perhaps we should separate those two.
142: */
143:
144: // TODO: consider a visitor
145: if (prop instanceof CAttributePropertyInfo) {
146: annotateAttribute(field);
147: } else if (prop instanceof CElementPropertyInfo) {
148: annotateElement(field);
149: } else if (prop instanceof CValuePropertyInfo) {
150: field.annotate(XmlValue.class);
151: } else if (prop instanceof CReferencePropertyInfo) {
152: annotateReference(field);
153: }
154:
155: outline.parent().generateAdapterIfNecessary(prop, field);
156:
157: QName st = prop.getSchemaType();
158: if (st != null)
159: field.annotate2(XmlSchemaTypeWriter.class).name(
160: st.getLocalPart()).namespace(st.getNamespaceURI());
161:
162: if (prop.inlineBinaryData())
163: field.annotate(XmlInlineBinaryData.class);
164: }
165:
166: private void annotateReference(JAnnotatable field) {
167: CReferencePropertyInfo rp = (CReferencePropertyInfo) prop;
168:
169: TODO.prototype();
170: // this is just a quick hack to get the basic test working
171:
172: Collection<CElement> elements = rp.getElements();
173:
174: XmlElementRefWriter refw;
175: if (elements.size() == 1) {
176: refw = field.annotate2(XmlElementRefWriter.class);
177: CElement e = elements.iterator().next();
178: refw.name(e.getElementName().getLocalPart()).namespace(
179: e.getElementName().getNamespaceURI()).type(
180: e.getType()
181: .toType(outline.parent(), IMPLEMENTATION));
182: } else if (elements.size() > 1) {
183: XmlElementRefsWriter refsw = field
184: .annotate2(XmlElementRefsWriter.class);
185: for (CElement e : elements) {
186: refw = refsw.value();
187: refw.name(e.getElementName().getLocalPart()).namespace(
188: e.getElementName().getNamespaceURI()).type(
189: e.getType().toType(outline.parent(),
190: IMPLEMENTATION));
191: }
192: }
193:
194: if (rp.isMixed())
195: field.annotate(XmlMixed.class);
196:
197: NClass dh = rp.getDOMHandler();
198: if (dh != null) {
199: XmlAnyElementWriter xaew = field
200: .annotate2(XmlAnyElementWriter.class);
201: xaew.lax(rp.getWildcard().allowTypedObject);
202:
203: final JClass value = dh.toType(outline.parent(),
204: IMPLEMENTATION);
205: if (!value.equals(codeModel.ref(W3CDomHandler.class))) {
206: xaew.value(value);
207: }
208: }
209:
210: }
211:
212: /**
213: * Annotate the element property 'field'
214: */
215: private void annotateElement(JAnnotatable field) {
216: CElementPropertyInfo ep = (CElementPropertyInfo) prop;
217: List<CTypeRef> types = ep.getTypes();
218:
219: if (ep.isValueList()) {
220: field.annotate(XmlList.class);
221: }
222:
223: assert ep.getXmlName() == null;
224: // if( eName!=null ) { // wrapper
225: // XmlElementWrapperWriter xcw = field.annotate2(XmlElementWrapperWriter.class);
226: // xcw.name(eName.getLocalPart())
227: // .namespace(eName.getNamespaceURI());
228: // }
229:
230: if (types.size() == 1) {
231: CTypeRef t = types.get(0);
232: writeXmlElementAnnotation(field, t, resolve(t,
233: IMPLEMENTATION), false);
234: } else {
235: for (CTypeRef t : types) {
236: // generate @XmlElements
237: writeXmlElementAnnotation(field, t, resolve(t,
238: IMPLEMENTATION), true);
239: }
240: xesw = null;
241: }
242: }
243:
244: /**
245: * Generate the simplest XmlElement annotation possible taking all semantic optimizations
246: * into account. This method is essentially equivalent to:
247: *
248: * xew.name(ctype.getTagName().getLocalPart())
249: * .namespace(ctype.getTagName().getNamespaceURI())
250: * .type(jtype)
251: * .defaultValue(ctype.getDefaultValue());
252: *
253: * @param field
254: * @param ctype
255: * @param jtype
256: * @param checkWrapper true if the method might need to generate XmlElements
257: */
258: private void writeXmlElementAnnotation(JAnnotatable field,
259: CTypeRef ctype, JType jtype, boolean checkWrapper) {
260:
261: // lazily create - we don't know if we need to generate anything yet
262: XmlElementWriter xew = null;
263:
264: // these values are used to determine how to optimize the generated annotation
265: XmlNsForm formDefault = parent()._package()
266: .getElementFormDefault();
267: String propName = prop.getName(false);
268:
269: String enclosingTypeNS;
270:
271: if (parent().target.getTypeName() == null)
272: enclosingTypeNS = parent()._package()
273: .getMostUsedNamespaceURI();
274: else
275: enclosingTypeNS = parent().target.getTypeName()
276: .getNamespaceURI();
277:
278: // generate the name property?
279: String generatedName = ctype.getTagName().getLocalPart();
280: if (!generatedName.equals(propName)) {
281: if (xew == null)
282: xew = getXew(checkWrapper, field);
283: xew.name(generatedName);
284: }
285:
286: // generate the namespace property?
287: String generatedNS = ctype.getTagName().getNamespaceURI();
288: if (((formDefault == XmlNsForm.QUALIFIED) && !generatedNS
289: .equals(enclosingTypeNS))
290: || ((formDefault == XmlNsForm.UNQUALIFIED) && !generatedNS
291: .equals(""))) {
292: if (xew == null)
293: xew = getXew(checkWrapper, field);
294: xew.namespace(generatedNS);
295: }
296:
297: // generate the required() property?
298: CElementPropertyInfo ep = (CElementPropertyInfo) prop;
299: if (ep.isRequired() && exposedType.isReference()) {
300: if (xew == null)
301: xew = getXew(checkWrapper, field);
302: xew.required(true);
303: }
304:
305: // generate the type property?
306:
307: // I'm not too sure if this is the right place to handle this, but
308: // if the schema definition is requiring this element, we should point to a primitive type,
309: // not wrapper type (to correctly carry forward the required semantics.)
310: // if it's a collection, we can't use a primitive, however.
311: if (ep.isRequired() && !prop.isCollection())
312: jtype = jtype.unboxify();
313:
314: // when generating code for 1.4, the runtime can't infer that ArrayList<Foo> derives
315: // from Collection<Foo> (because List isn't parameterized), so always expclitly
316: // generate @XmlElement(type=...)
317: if (!jtype.equals(exposedType)
318: || (parent().parent().getModel().options.runtime14 && prop
319: .isCollection())) {
320: if (xew == null)
321: xew = getXew(checkWrapper, field);
322: xew.type(jtype);
323: }
324:
325: // generate defaultValue property?
326: final String defaultValue = ctype.getDefaultValue();
327: if (defaultValue != null) {
328: if (xew == null)
329: xew = getXew(checkWrapper, field);
330: xew.defaultValue(defaultValue);
331: }
332:
333: // generate the nillable property?
334: if (ctype.isNillable()) {
335: if (xew == null)
336: xew = getXew(checkWrapper, field);
337: xew.nillable(true);
338: }
339: }
340:
341: // ugly hack to lazily create
342: private XmlElementsWriter xesw = null;
343:
344: private XmlElementWriter getXew(boolean checkWrapper,
345: JAnnotatable field) {
346: XmlElementWriter xew;
347: if (checkWrapper) {
348: if (xesw == null) {
349: xesw = field.annotate2(XmlElementsWriter.class);
350: }
351: xew = xesw.value();
352: } else {
353: xew = field.annotate2(XmlElementWriter.class);
354: }
355: return xew;
356: }
357:
358: /**
359: * Annotate the attribute property 'field'
360: */
361: private void annotateAttribute(JAnnotatable field) {
362: CAttributePropertyInfo ap = (CAttributePropertyInfo) prop;
363: QName attName = ap.getXmlName();
364:
365: // [RESULT]
366: // @XmlAttribute(name="foo", required=true, namespace="bar://baz")
367: XmlAttributeWriter xaw = field
368: .annotate2(XmlAttributeWriter.class);
369:
370: final String generatedName = attName.getLocalPart();
371: final String generatedNS = attName.getNamespaceURI();
372:
373: // generate name property?
374: if (!generatedName.equals(ap.getName(false))) {
375: xaw.name(generatedName);
376: }
377:
378: // generate namespace property?
379: if (!generatedNS.equals("")) { // assume attributeFormDefault == unqualified
380: xaw.namespace(generatedNS);
381: }
382:
383: // generate required property?
384: if (ap.isRequired()) {
385: xaw.required(true);
386: }
387: }
388:
389: /**
390: * Useful base class for implementing {@link FieldAccessor}.
391: */
392: protected abstract class Accessor implements FieldAccessor {
393:
394: /**
395: * Evaluates to the target object this accessor should access.
396: */
397: protected final JExpression $target;
398:
399: protected Accessor(JExpression $target) {
400: this .$target = $target;
401: }
402:
403: public final FieldOutline owner() {
404: return AbstractField.this ;
405: }
406:
407: public final CPropertyInfo getPropertyInfo() {
408: return prop;
409: }
410: }
411:
412: //
413: //
414: // utility methods
415: //
416: //
417:
418: /**
419: * Generates the field declaration.
420: */
421: protected final JFieldVar generateField(JType type) {
422: return outline.implClass.field(JMod.PROTECTED, type, prop
423: .getName(false));
424: }
425:
426: /**
427: * Case from {@link #exposedType} to {@link #implType} if necessary.
428: */
429: protected final JExpression castToImplType(JExpression exp) {
430: if (implType == exposedType)
431: return exp;
432: else
433: return JExpr.cast(implType, exp);
434: }
435:
436: /**
437: * Compute the type of a {@link CPropertyInfo}
438: * @param aspect
439: */
440: protected JType getType(final Aspect aspect) {
441: if (prop.getAdapter() != null)
442: return prop.getAdapter().customType.toType(
443: outline.parent(), aspect);
444:
445: final class TypeList extends ArrayList<JType> {
446: void add(CTypeInfo t) {
447: add(t.getType().toType(outline.parent(), aspect));
448: if (t instanceof CElementInfo) {
449: // UGLY. element substitution is implemented in a way that
450: // the derived elements are not assignable to base elements.
451: // so when we compute the signature, we have to take derived types
452: // into account
453: add(((CElementInfo) t).getSubstitutionMembers());
454: }
455: }
456:
457: void add(Collection<? extends CTypeInfo> col) {
458: for (CTypeInfo typeInfo : col)
459: add(typeInfo);
460: }
461: }
462: TypeList r = new TypeList();
463: r.add(prop.ref());
464:
465: JType t;
466: if (prop.baseType != null)
467: t = prop.baseType;
468: else
469: t = TypeUtil.getCommonBaseType(codeModel, r);
470:
471: // if item type is unboxable, convert t=Integer -> t=int
472: // the in-memory data structure can't have primitives directly,
473: // but this guarantees that items cannot legal hold null,
474: // which helps us improve the boundary signature between our
475: // data structure and user code
476: if (prop.isUnboxable())
477: t = t.unboxify();
478: return t;
479: }
480:
481: /**
482: * Returns contents to be added to javadoc.
483: */
484: protected final List<Object> listPossibleTypes(CPropertyInfo prop) {
485: List<Object> r = new ArrayList<Object>();
486: for (CTypeInfo tt : prop.ref()) {
487: JType t = tt.getType().toType(outline.parent(),
488: Aspect.EXPOSED);
489: if (t.isPrimitive() || t.isArray())
490: r.add(t.fullName());
491: else {
492: r.add(t);
493: r.add("\n");
494: }
495: }
496:
497: return r;
498: }
499:
500: /**
501: * return the Java type for the given type reference in the model.
502: */
503: private JType resolve(CTypeRef typeRef, Aspect a) {
504: return outline.parent().resolve(typeRef, a);
505: }
506:
507: }
|