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