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 }