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