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.model;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collection;
  30 import java.util.HashSet;
  31 import java.util.Iterator;
  32 import java.util.List;
  33 import java.util.Set;
  34 
  35 import javax.xml.bind.annotation.XmlElement;
  36 import javax.xml.bind.annotation.XmlID;
  37 import javax.xml.bind.annotation.XmlIDREF;
  38 import javax.xml.bind.annotation.XmlRootElement;
  39 import javax.xml.namespace.QName;
  40 
  41 import com.sun.codemodel.internal.JClass;
  42 import com.sun.codemodel.internal.JCodeModel;
  43 import com.sun.codemodel.internal.JPackage;
  44 import com.sun.istack.internal.Nullable;
  45 import com.sun.tools.internal.xjc.Language;
  46 import com.sun.tools.internal.xjc.model.nav.NClass;
  47 import com.sun.tools.internal.xjc.model.nav.NType;
  48 import com.sun.tools.internal.xjc.outline.Outline;
  49 import com.sun.tools.internal.xjc.reader.Ring;
  50 import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
  51 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIFactoryMethod;
  52 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
  53 import com.sun.xml.internal.bind.v2.model.core.Element;
  54 import com.sun.xml.internal.xsom.XSComponent;
  55 
  56 import org.xml.sax.Locator;
  57 
  58 /**
  59  * Mutable {@link ClassInfo} representation.
  60  *
  61  * <p>
  62  * Schema parsers build these objects.
  63  *
  64  * @author Kohsuke Kawaguchi
  65  */
  66 public final class CClassInfo extends AbstractCElement implements ClassInfo<NType,NClass>, CClassInfoParent, CClass, NClass {
  67 
  68     @XmlIDREF
  69     private CClass baseClass;
  70 
  71     /**
  72      * List of all subclasses, together with {@link #nextSibling}.
  73      *
  74      * If this class has no sub-class, this field is null. Otherwise,
  75      * this field points to a sub-class of this class. From there you can enumerate
  76      * all the sub-classes by using {@link #nextSibling}.
  77      */
  78     private CClassInfo firstSubclass;
  79 
  80     /**
  81      * @see #firstSubclass
  82      */
  83     private CClassInfo nextSibling = null;
  84 
  85     /**
  86      * @see #getTypeName()
  87      */
  88     private final QName typeName;
  89 
  90     /**
  91      * Custom {@link #getSqueezedName() squeezed name}, if any.
  92      */
  93     private /*almost final*/ @Nullable String squeezedName;
  94 
  95     /**
  96      * If this class also gets {@link XmlRootElement}, the class name.
  97      */
  98     private final @Nullable QName elementName;
  99 
 100     private boolean isOrdered = true;
 101 
 102     private final List<CPropertyInfo> properties = new ArrayList<CPropertyInfo>();
 103 
 104     /**
 105      * TODO: revisit this design.
 106      * we should at least do a basic encapsulation to avoid careless
 107      * mistakes. Maybe we should even differ the javadoc generation
 108      * by queueing runners.
 109      */
 110     public String javadoc;
 111 
 112     @XmlIDREF
 113     private final CClassInfoParent parent;
 114 
 115     /**
 116      * short name.
 117      */
 118     public final String shortName;
 119 
 120     /**
 121      * Optional user-specified implementation override class.
 122      */
 123     private @Nullable String implClass;
 124 
 125     /**
 126      * The {@link Model} object to which this bean belongs.
 127      */
 128     public final Model model;
 129 
 130     /**
 131      * @see #hasAttributeWildcard()
 132      */
 133     private boolean hasAttributeWildcard;
 134 
 135 
 136     public CClassInfo(Model model,JPackage pkg, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) {
 137         this(model,model.getPackage(pkg),shortName,location,typeName,elementName,source,customizations);
 138     }
 139 
 140     public CClassInfo(Model model,CClassInfoParent p, String shortName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) {
 141         super(model,source,location,customizations);
 142         this.model = model;
 143         this.parent = p;
 144         this.shortName = model.allocator.assignClassName(parent,shortName);
 145         this.typeName = typeName;
 146         this.elementName = elementName;
 147 
 148         Language schemaLanguage = model.options.getSchemaLanguage();
 149         if ((schemaLanguage != null) &&
 150             (schemaLanguage.equals(Language.XMLSCHEMA) || schemaLanguage.equals(Language.WSDL))) {
 151             BIFactoryMethod factoryMethod = Ring.get(BGMBuilder.class).getBindInfo(source).get(BIFactoryMethod.class);
 152             if(factoryMethod!=null) {
 153                 factoryMethod.markAsAcknowledged();
 154                 this.squeezedName = factoryMethod.name;
 155             }
 156         }
 157 
 158         model.add(this);
 159     }
 160 
 161     public CClassInfo(Model model,JCodeModel cm, String fullName, Locator location, QName typeName, QName elementName, XSComponent source, CCustomizations customizations) {
 162         super(model,source,location,customizations);
 163         this.model = model;
 164         int idx = fullName.indexOf('.');
 165         if(idx<0) {
 166             this.parent = model.getPackage(cm.rootPackage());
 167             this.shortName = model.allocator.assignClassName(parent,fullName);
 168         } else {
 169             this.parent = model.getPackage(cm._package(fullName.substring(0,idx)));
 170             this.shortName = model.allocator.assignClassName(parent,fullName.substring(idx+1));
 171         }
 172         this.typeName = typeName;
 173         this.elementName = elementName;
 174 
 175         model.add(this);
 176     }
 177 
 178     public boolean hasAttributeWildcard() {
 179         return hasAttributeWildcard;
 180     }
 181 
 182     public void hasAttributeWildcard(boolean hasAttributeWildcard) {
 183         this.hasAttributeWildcard = hasAttributeWildcard;
 184     }
 185 
 186     public boolean hasSubClasses() {
 187         return firstSubclass!=null;
 188     }
 189 
 190     /**
 191      * Returns true if a new attribute wildcard property needs to be
 192      * declared on this class.
 193      */
 194     public boolean declaresAttributeWildcard() {
 195         return hasAttributeWildcard && !inheritsAttributeWildcard();
 196     }
 197 
 198     /**
 199      * Returns true if this class inherits a wildcard attribute property
 200      * from its ancestor classes.
 201      */
 202     public boolean inheritsAttributeWildcard() {
 203         if (getRefBaseClass() != null) {
 204             CClassRef cref = (CClassRef)baseClass;
 205             if (cref.getSchemaComponent().getForeignAttributes().size() > 0) {
 206                 return true;
 207             }
 208         } else {
 209             for( CClassInfo c=getBaseClass(); c!=null; c=c.getBaseClass() ) {
 210                 if(c.hasAttributeWildcard)
 211                     return true;
 212             }
 213         }
 214         return false;
 215     }
 216 
 217 
 218     public NClass getClazz() {
 219         return this;
 220     }
 221 
 222     public CClassInfo getScope() {
 223         return null;
 224     }
 225 
 226     @XmlID
 227     public String getName() {
 228         return fullName();
 229     }
 230 
 231     /**
 232      * Returns the "squeezed name" of this bean token.
 233      * <p>
 234      * The squeezed name of a bean is the concatenation of
 235      * the names of its outer classes and itself.
 236      * <p>
 237      * Thus if the bean is "org.acme.foo.Bean", then the squeezed name is "Bean",
 238      * if the bean is "org.acme.foo.Outer1.Outer2.Bean", then "Outer1Outer2Bean".
 239      * <p>
 240      * This is used by the code generator
 241      */
 242     @XmlElement
 243     public String getSqueezedName() {
 244         if (squeezedName != null)  return squeezedName;
 245         return calcSqueezedName.onBean(this);
 246     }
 247 
 248     private static final CClassInfoParent.Visitor<String> calcSqueezedName = new Visitor<String>() {
 249         public String onBean(CClassInfo bean) {
 250             return bean.parent.accept(this)+bean.shortName;
 251         }
 252 
 253         public String onElement(CElementInfo element) {
 254             return element.parent.accept(this)+element.shortName();
 255         }
 256 
 257         public String onPackage(JPackage pkg) {
 258             return "";
 259         }
 260     };
 261 
 262     /**
 263      * Returns a mutable list.
 264      */
 265     public List<CPropertyInfo> getProperties() {
 266         return properties;
 267     }
 268 
 269     public boolean hasValueProperty() {
 270         throw new UnsupportedOperationException();
 271     }
 272 
 273     /**
 274      * Gets a propery by name.
 275      */
 276     public CPropertyInfo getProperty(String name) {
 277         // TODO: does this method need to be fast?
 278         for( CPropertyInfo p : properties )
 279             if(p.getName(false).equals(name))
 280                 return p;
 281         return null;
 282     }
 283 
 284     public boolean hasProperties() {
 285         return !getProperties().isEmpty();
 286     }
 287 
 288     public boolean isElement() {
 289         return elementName!=null;
 290     }
 291 
 292     /**
 293      * Guaranteed to return this.
 294      */
 295     @Deprecated
 296     public CNonElement getInfo() {
 297         return this;
 298     }
 299 
 300     public Element<NType,NClass> asElement() {
 301         if(isElement())
 302             return this;
 303         else
 304             return null;
 305     }
 306 
 307     public boolean isOrdered() {
 308         return isOrdered;
 309     }
 310 
 311     /**
 312      * @deprecated
 313      *      if you are calling this method directly, you must be doing something wrong.
 314      */
 315     public boolean isFinal() {
 316         return false;
 317     }
 318 
 319     public void setOrdered(boolean value) {
 320         isOrdered = value;
 321     }
 322 
 323     public QName getElementName() {
 324         return elementName;
 325     }
 326 
 327     public QName getTypeName() {
 328         return typeName;
 329     }
 330 
 331     public boolean isSimpleType() {
 332         throw new UnsupportedOperationException();
 333     }
 334 
 335     /**
 336      * Returns the FQCN of this bean.
 337      */
 338     public String fullName() {
 339         String r = parent.fullName();
 340         if(r.length()==0)   return shortName;
 341         else                return r+'.'+shortName;
 342     }
 343 
 344     public CClassInfoParent parent() {
 345         return parent;
 346     }
 347 
 348     public void setUserSpecifiedImplClass(String implClass) {
 349         assert this.implClass==null;
 350         assert implClass!=null;
 351         this.implClass = implClass;
 352     }
 353 
 354     public String getUserSpecifiedImplClass() {
 355         return implClass;
 356     }
 357 
 358 
 359     /**
 360      * Adds a new property.
 361      */
 362     public void addProperty(CPropertyInfo prop) {
 363         if(prop.ref().isEmpty())
 364             // this property isn't contributing anything
 365             // this happens when you try to map an empty sequence to a property
 366             return;
 367         prop.setParent(this);
 368         properties.add(prop);
 369     }
 370 
 371     /**
 372      * This method accepts both {@link CClassInfo} (which means the base class
 373      * is also generated), or {@link CClassRef} (which means the base class is
 374      * already generated and simply referenced.)
 375      *
 376      * The latter is treated somewhat special --- from the rest of the model
 377      * this external base class is invisible. This modeling might need more
 378      * thoughts to get right.
 379      */
 380     public void setBaseClass(CClass base) {
 381         assert baseClass==null;
 382         assert base!=null;
 383         baseClass = base;
 384 
 385         assert nextSibling==null;
 386         if (base instanceof CClassInfo) {
 387             CClassInfo realBase = (CClassInfo) base;
 388             this.nextSibling = realBase.firstSubclass;
 389             realBase.firstSubclass = this;
 390         }
 391     }
 392 
 393     /**
 394      * This inherited version returns null if this class extends from {@link CClassRef}.
 395      *
 396      * @see #getRefBaseClass()
 397      */
 398     public CClassInfo getBaseClass() {
 399         if (baseClass instanceof CClassInfo) {
 400             return (CClassInfo) baseClass;
 401         } else {
 402             return null;
 403         }
 404     }
 405 
 406     public CClassRef getRefBaseClass() {
 407         if (baseClass instanceof CClassRef) {
 408             return (CClassRef) baseClass;
 409         } else {
 410             return null;
 411         }
 412     }
 413 
 414     /**
 415      * Enumerates all the sub-classes of this class.
 416      */
 417     public Iterator<CClassInfo> listSubclasses() {
 418         return new Iterator<CClassInfo>() {
 419             CClassInfo cur = firstSubclass;
 420             public boolean hasNext() {
 421                 return cur!=null;
 422             }
 423 
 424             public CClassInfo next() {
 425                 CClassInfo r = cur;
 426                 cur = cur.nextSibling;
 427                 return r;
 428             }
 429 
 430             public void remove() {
 431                 throw new UnsupportedOperationException();
 432             }
 433         };
 434     }
 435 
 436     public CClassInfo getSubstitutionHead() {
 437         CClassInfo c=getBaseClass();
 438         while(c!=null && !c.isElement())
 439             c=c.getBaseClass();
 440         return c;
 441     }
 442 
 443 
 444     /**
 445      * Interfaces to be implemented.
 446      * Lazily constructed.
 447      */
 448     private Set<JClass> _implements = null;
 449 
 450     public void _implements(JClass c) {
 451         if(_implements==null)
 452             _implements = new HashSet<JClass>();
 453         _implements.add(c);
 454     }
 455 
 456 
 457     /** Constructor declarations. array of {@link Constructor}s. */
 458     private final List<Constructor> constructors = new ArrayList<Constructor>(1);
 459 
 460     /** Creates a new constructor declaration and adds it. */
 461     public void addConstructor( String... fieldNames ) {
 462         constructors.add(new Constructor(fieldNames));
 463     }
 464 
 465     /** list all constructor declarations. */
 466     public Collection<? extends Constructor> getConstructors() {
 467         return constructors;
 468     }
 469 
 470     public final <T> T accept(Visitor<T> visitor) {
 471         return visitor.onBean(this);
 472     }
 473 
 474     public JPackage getOwnerPackage() {
 475         return parent.getOwnerPackage();
 476     }
 477 
 478     public final NClass getType() {
 479         return this;
 480     }
 481 
 482     public final JClass toType(Outline o, Aspect aspect) {
 483         switch(aspect) {
 484         case IMPLEMENTATION:
 485             return o.getClazz(this).implRef;
 486         case EXPOSED:
 487             return o.getClazz(this).ref;
 488         default:
 489             throw new IllegalStateException();
 490         }
 491     }
 492 
 493     public boolean isBoxedType() {
 494         return false;
 495     }
 496 
 497     public String toString() {
 498         return fullName();
 499     }
 500 }