1/*
2 * Copyright (c) 1997, 2012, 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.List;
29
30import com.sun.codemodel.internal.JBlock;
31import com.sun.codemodel.internal.JClass;
32import com.sun.codemodel.internal.JExpr;
33import com.sun.codemodel.internal.JExpression;
34import com.sun.codemodel.internal.JFieldRef;
35import com.sun.codemodel.internal.JFieldVar;
36import com.sun.codemodel.internal.JMethod;
37import com.sun.codemodel.internal.JMod;
38import com.sun.codemodel.internal.JOp;
39import com.sun.codemodel.internal.JPrimitiveType;
40import com.sun.codemodel.internal.JType;
41import com.sun.tools.internal.xjc.generator.bean.ClassOutlineImpl;
42import com.sun.tools.internal.xjc.model.CPropertyInfo;
43
44/**
45 * Common code for property renderer that generates a List as
46 * its underlying data structure.
47 *
48 * <p>
49 * For performance reasons, the actual list object used to store
50 * data is lazily created.
51 *
52 * @author
53 *     Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
54 */
55abstract class AbstractListField extends AbstractField {
56    /** The field that stores the list. */
57    protected JFieldVar field;
58
59    /**
60     * a method that lazily initializes a List.
61     * Lazily created.
62     *
63     * [RESULT]
64     * List _getFoo() {
65     *   if(field==null)
66     *     field = create new list;
67     *   return field;
68     * }
69     */
70    private JMethod internalGetter;
71
72    /**
73     * If this collection property is a collection of a primitive type,
74     * this variable refers to that primitive type.
75     * Otherwise null.
76     */
77    protected final JPrimitiveType primitiveType;
78
79    protected final JClass listT = codeModel.ref(List.class).narrow(exposedType.boxify());
80
81    /**
82     * True to create a new instance of List eagerly in the constructor.
83     * False otherwise.
84     *
85     * <p>
86     * Setting it to true makes the generated code slower (as more list instances need to be
87     * allocated), but it works correctly if the user specifies the custom type of a list.
88     */
89    private final boolean eagerInstanciation;
90
91    /**
92     * Call {@link #generate()} method right after this.
93     */
94    protected AbstractListField(ClassOutlineImpl outline, CPropertyInfo prop, boolean eagerInstanciation) {
95        super(outline,prop);
96        this.eagerInstanciation = eagerInstanciation;
97
98        if( implType instanceof JPrimitiveType ) {
99            // primitive types don't have this tricky distinction
100            assert implType==exposedType;
101            primitiveType = (JPrimitiveType)implType;
102        } else
103            primitiveType = null;
104    }
105
106    protected final void generate() {
107
108        // for the collectionType customization to take effect, the field needs to be strongly typed,
109        // not just List<Foo>.
110        field = outline.implClass.field( JMod.PROTECTED, listT, prop.getName(false) );
111        if(eagerInstanciation)
112            field.init(newCoreList());
113
114        annotate(field);
115
116        // generate the rest of accessors
117        generateAccessors();
118    }
119
120    private void generateInternalGetter() {
121        internalGetter = outline.implClass.method(JMod.PROTECTED,listT,"_get"+prop.getName(true));
122        if(!eagerInstanciation) {
123            // if eagerly instanciated, the field can't be null
124            fixNullRef(internalGetter.body());
125        }
126        internalGetter.body()._return(field);
127    }
128
129    /**
130     * Generates statement(s) so that the successive {@link Accessor#ref(boolean)} with
131     * true will always return a non-null list.
132     *
133     * This is useful to avoid generating redundant internal getter.
134     */
135    protected final void fixNullRef(JBlock block) {
136        block._if(field.eq(JExpr._null()))._then()
137            .assign(field,newCoreList());
138    }
139
140    public JType getRawType() {
141        return codeModel.ref(List.class).narrow(exposedType.boxify());
142    }
143
144    private JExpression newCoreList() {
145        return JExpr._new(getCoreListType());
146    }
147
148    /**
149     * Concrete class that implements the List interface.
150     * Used as the actual data storage.
151     */
152    protected abstract JClass getCoreListType();
153
154
155    /** Generates accessor methods. */
156    protected abstract void generateAccessors();
157
158
159
160    /**
161     *
162     *
163     * @author
164     *     Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
165     */
166    protected abstract class Accessor extends AbstractField.Accessor {
167
168        /**
169         * Reference to the {@link AbstractListField#field}
170         * of the target object.
171         */
172        protected final JFieldRef field;
173
174        protected Accessor( JExpression $target ) {
175            super($target);
176            field = $target.ref(AbstractListField.this.field);
177        }
178
179
180        protected final JExpression unbox( JExpression exp ) {
181            if(primitiveType==null) return exp;
182            else                    return primitiveType.unwrap(exp);
183        }
184        protected final JExpression box( JExpression exp ) {
185            if(primitiveType==null) return exp;
186            else                    return primitiveType.wrap(exp);
187        }
188
189        /**
190         * Returns a reference to the List field that stores the data.
191         * <p>
192         * Using this method hides the fact that the list is lazily
193         * created.
194         *
195         * @param canBeNull
196         *      if true, the returned expression may be null (this is
197         *      when the list is still not constructed.) This could be
198         *      useful when the caller can deal with null more efficiently.
199         *      When the list is null, it should be treated as if the list
200         *      is empty.
201         *
202         *      if false, the returned expression will never be null.
203         *      This is the behavior users would see.
204         */
205        protected final JExpression ref(boolean canBeNull) {
206            if(canBeNull)
207                return field;
208            if(internalGetter==null)
209                generateInternalGetter();
210            return $target.invoke(internalGetter);
211        }
212
213        public JExpression count() {
214            return JOp.cond( field.eq(JExpr._null()), JExpr.lit(0), field.invoke("size") );
215        }
216
217        public void unsetValues( JBlock body ) {
218            body.assign(field,JExpr._null());
219        }
220        public JExpression hasSetValue() {
221            return field.ne(JExpr._null()).cand(field.invoke("isEmpty").not());
222        }
223    }
224
225}
226