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.ArrayList; 29 import java.util.Collection; 30 import java.util.List; 31 32 import javax.xml.bind.annotation.W3CDomHandler; 33 import javax.xml.bind.annotation.XmlList; 34 import javax.xml.bind.annotation.XmlMixed; 35 import javax.xml.bind.annotation.XmlNsForm; 36 import javax.xml.bind.annotation.XmlValue; 37 import javax.xml.bind.annotation.XmlInlineBinaryData; 38 import javax.xml.namespace.QName; 39 40 import com.sun.codemodel.internal.JAnnotatable; 41 import com.sun.codemodel.internal.JClass; 42 import com.sun.codemodel.internal.JCodeModel; 43 import com.sun.codemodel.internal.JExpr; 44 import com.sun.codemodel.internal.JExpression; 45 import com.sun.codemodel.internal.JFieldVar; 46 import com.sun.codemodel.internal.JMod; 47 import com.sun.codemodel.internal.JType; 48 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAnyElementWriter; 49 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAttributeWriter; 50 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementRefWriter; 51 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementRefsWriter; 52 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementWriter; 53 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlElementsWriter; 54 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSchemaTypeWriter; 55 import com.sun.tools.internal.xjc.generator.bean.ClassOutlineImpl; 56 import com.sun.tools.internal.xjc.model.CAttributePropertyInfo; 57 import com.sun.tools.internal.xjc.model.CElement; 58 import com.sun.tools.internal.xjc.model.CElementInfo; 59 import com.sun.tools.internal.xjc.model.CElementPropertyInfo; 60 import com.sun.tools.internal.xjc.model.CPropertyInfo; 61 import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; 62 import com.sun.tools.internal.xjc.model.CTypeInfo; 63 import com.sun.tools.internal.xjc.model.CTypeRef; 64 import com.sun.tools.internal.xjc.model.CValuePropertyInfo; 65 import com.sun.tools.internal.xjc.model.nav.NClass; 66 import com.sun.tools.internal.xjc.outline.Aspect; 67 import static com.sun.tools.internal.xjc.outline.Aspect.IMPLEMENTATION; 68 import com.sun.tools.internal.xjc.outline.ClassOutline; 69 import com.sun.tools.internal.xjc.outline.FieldAccessor; 70 import com.sun.tools.internal.xjc.outline.FieldOutline; 71 import com.sun.tools.internal.xjc.reader.TypeUtil; 72 import com.sun.tools.internal.xjc.Options; 73 import com.sun.tools.internal.xjc.api.SpecVersion; 74 import com.sun.xml.internal.bind.api.impl.NameConverter; 75 import 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 */ 87 abstract 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 AbstractField( ClassOutlineImpl outline, CPropertyInfo prop )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 parent()114 public final ClassOutline parent() { 115 return outline; 116 } 117 getPropertyInfo()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 */ annotate( JAnnotatable field )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 annotateReference(JAnnotatable field)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 */ annotateElement(JAnnotatable field)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 */ writeXmlElementAnnotation( JAnnotatable field, CTypeRef ctype, JType jtype, boolean checkWrapper )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 */ getOptions()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 getXew(boolean checkWrapper, JAnnotatable field)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 */ annotateAttribute(JAnnotatable field)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 Accessor( JExpression $target )385 protected Accessor( JExpression $target ) { 386 this.$target = $target; 387 } 388 owner()389 public final FieldOutline owner() { 390 return AbstractField.this; 391 } 392 getPropertyInfo()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 */ generateField( JType type )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 */ castToImplType( JExpression exp )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 */ getType(final Aspect aspect)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 */ listPossibleTypes( CPropertyInfo prop )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 */ resolve(CTypeRef typeRef,Aspect a)487 private JType resolve(CTypeRef typeRef,Aspect a) { 488 return outline.parent().resolve(typeRef,a); 489 } 490 491 } 492