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