1 /*
   2  * Copyright (c) 1997, 2011, 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 
  26 package com.sun.tools.internal.xjc.generator.bean.field;
  27 
  28 import java.util.List;
  29 
  30 import com.sun.codemodel.internal.JBlock;
  31 import com.sun.codemodel.internal.JClass;
  32 import com.sun.codemodel.internal.JExpr;
  33 import com.sun.codemodel.internal.JExpression;
  34 import com.sun.codemodel.internal.JFieldRef;
  35 import com.sun.codemodel.internal.JFieldVar;
  36 import com.sun.codemodel.internal.JMethod;
  37 import com.sun.codemodel.internal.JMod;
  38 import com.sun.codemodel.internal.JOp;
  39 import com.sun.codemodel.internal.JPrimitiveType;
  40 import com.sun.codemodel.internal.JType;
  41 import com.sun.tools.internal.xjc.generator.bean.ClassOutlineImpl;
  42 import 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  */
  55 abstract 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 }