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 
  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.model.Aspect;
  67 import static com.sun.tools.internal.xjc.model.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 
 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 }