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; 27 28 import static com.sun.tools.internal.xjc.outline.Aspect.EXPOSED; 29 30 import java.io.Serializable; 31 import java.net.URL; 32 import java.util.Collection; 33 import java.util.HashMap; 34 import java.util.LinkedHashMap; 35 import java.util.HashSet; 36 import java.util.Iterator; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.TreeSet; 40 41 import javax.xml.bind.JAXBContext; 42 import javax.xml.bind.JAXBException; 43 import javax.xml.bind.annotation.XmlAttachmentRef; 44 import javax.xml.bind.annotation.XmlID; 45 import javax.xml.bind.annotation.XmlIDREF; 46 import javax.xml.namespace.QName; 47 48 import com.sun.codemodel.internal.ClassType; 49 import com.sun.codemodel.internal.JAnnotatable; 50 import com.sun.codemodel.internal.JClass; 51 import com.sun.codemodel.internal.JClassAlreadyExistsException; 52 import com.sun.codemodel.internal.JClassContainer; 53 import com.sun.codemodel.internal.JCodeModel; 54 import com.sun.codemodel.internal.JDefinedClass; 55 import com.sun.codemodel.internal.JEnumConstant; 56 import com.sun.codemodel.internal.JExpr; 57 import com.sun.codemodel.internal.JExpression; 58 import com.sun.codemodel.internal.JFieldVar; 59 import com.sun.codemodel.internal.JForEach; 60 import com.sun.codemodel.internal.JInvocation; 61 import com.sun.codemodel.internal.JJavaName; 62 import com.sun.codemodel.internal.JMethod; 63 import com.sun.codemodel.internal.JMod; 64 import com.sun.codemodel.internal.JPackage; 65 import com.sun.codemodel.internal.JType; 66 import com.sun.codemodel.internal.JVar; 67 import com.sun.codemodel.internal.fmt.JStaticJavaFile; 68 import com.sun.tools.internal.xjc.AbortException; 69 import com.sun.tools.internal.xjc.ErrorReceiver; 70 import com.sun.tools.internal.xjc.api.SpecVersion; 71 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlAnyAttributeWriter; 72 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumValueWriter; 73 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlEnumWriter; 74 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlJavaTypeAdapterWriter; 75 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlMimeTypeWriter; 76 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlRootElementWriter; 77 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSeeAlsoWriter; 78 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlTypeWriter; 79 import com.sun.tools.internal.xjc.generator.bean.field.FieldRenderer; 80 import com.sun.tools.internal.xjc.model.CAdapter; 81 import com.sun.tools.internal.xjc.model.CAttributePropertyInfo; 82 import com.sun.tools.internal.xjc.model.CClassInfo; 83 import com.sun.tools.internal.xjc.model.CClassInfoParent; 84 import com.sun.tools.internal.xjc.model.CElementInfo; 85 import com.sun.tools.internal.xjc.model.CEnumConstant; 86 import com.sun.tools.internal.xjc.model.CEnumLeafInfo; 87 import com.sun.tools.internal.xjc.model.CPropertyInfo; 88 import com.sun.tools.internal.xjc.model.CTypeRef; 89 import com.sun.tools.internal.xjc.model.Model; 90 import com.sun.tools.internal.xjc.model.CClassRef; 91 import com.sun.tools.internal.xjc.outline.Aspect; 92 import com.sun.tools.internal.xjc.outline.ClassOutline; 93 import com.sun.tools.internal.xjc.outline.EnumConstantOutline; 94 import com.sun.tools.internal.xjc.outline.EnumOutline; 95 import com.sun.tools.internal.xjc.outline.FieldOutline; 96 import com.sun.tools.internal.xjc.outline.Outline; 97 import com.sun.tools.internal.xjc.outline.PackageOutline; 98 import com.sun.tools.internal.xjc.util.CodeModelClassFactory; 99 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter; 100 import com.sun.xml.internal.xsom.XmlString; 101 import com.sun.istack.internal.NotNull; 102 import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; 103 104 /** 105 * Generates fields and accessors. 106 */ 107 public final class BeanGenerator implements Outline { 108 109 /** Simplifies class/interface creation and collision detection. */ 110 private final CodeModelClassFactory codeModelClassFactory; 111 private final ErrorReceiver errorReceiver; 112 /** all {@link PackageOutline}s keyed by their {@link PackageOutline#_package}. */ 113 private final Map<JPackage, PackageOutlineImpl> packageContexts = new LinkedHashMap<JPackage, PackageOutlineImpl>(); 114 /** all {@link ClassOutline}s keyed by their {@link ClassOutline#target}. */ 115 private final Map<CClassInfo, ClassOutlineImpl> classes = new LinkedHashMap<CClassInfo, ClassOutlineImpl>(); 116 /** all {@link EnumOutline}s keyed by their {@link EnumOutline#target}. */ 117 private final Map<CEnumLeafInfo, EnumOutline> enums = new LinkedHashMap<CEnumLeafInfo, EnumOutline>(); 118 /** 119 * Generated runtime classes. 120 */ 121 private final Map<Class, JClass> generatedRuntime = new LinkedHashMap<Class, JClass>(); 122 /** the model object which we are processing. */ 123 private final Model model; 124 private final JCodeModel codeModel; 125 /** 126 * for each property, the information about the generated field. 127 */ 128 private final Map<CPropertyInfo, FieldOutline> fields = new LinkedHashMap<CPropertyInfo, FieldOutline>(); 129 /** 130 * elements that generate classes to the generated classes. 131 */ 132 /*package*/ final Map<CElementInfo, ElementOutlineImpl> elements = new LinkedHashMap<CElementInfo, ElementOutlineImpl>(); 133 134 /** 135 * Generates beans into code model according to the BGM, 136 * and produces the reflection model. 137 * 138 * @param _errorReceiver 139 * This object will receive all the errors discovered 140 * during the back-end stage. 141 * 142 * @return 143 * returns a {@link Outline} which will in turn 144 * be used to further generate marshaller/unmarshaller, 145 * or null if the processing fails (errors should have been 146 * reported to the error recevier.) 147 */ 148 public static Outline generate(Model model, ErrorReceiver _errorReceiver) { 149 150 try { 151 return new BeanGenerator(model, _errorReceiver); 152 } catch (AbortException e) { 153 return null; 154 } 155 } 156 157 private BeanGenerator(Model _model, ErrorReceiver _errorReceiver) { 158 159 this.model = _model; 160 this.codeModel = model.codeModel; 161 this.errorReceiver = _errorReceiver; 162 this.codeModelClassFactory = new CodeModelClassFactory(errorReceiver); 163 164 // build enum classes 165 for (CEnumLeafInfo p : model.enums().values()) { 166 enums.put(p, generateEnumDef(p)); 167 } 168 169 JPackage[] packages = getUsedPackages(EXPOSED); 170 171 // generates per-package code and remember the results as contexts. 172 for (JPackage pkg : packages) { 173 getPackageContext(pkg); 174 } 175 176 // create the class definitions for all the beans first. 177 // this should also fill in PackageContext#getClasses 178 for (CClassInfo bean : model.beans().values()) { 179 getClazz(bean); 180 } 181 182 // compute the package-level setting 183 for (PackageOutlineImpl p : packageContexts.values()) { 184 p.calcDefaultValues(); 185 } 186 187 JClass OBJECT = codeModel.ref(Object.class); 188 189 // inheritance relationship needs to be set before we generate fields, or otherwise 190 // we'll fail to compute the correct type signature (namely the common base type computation) 191 for (ClassOutlineImpl cc : getClasses()) { 192 193 // setup inheritance between implementation hierarchy. 194 CClassInfo superClass = cc.target.getBaseClass(); 195 if (superClass != null) { 196 // use the specified super class 197 model.strategy._extends(cc, getClazz(superClass)); 198 } else { 199 CClassRef refSuperClass = cc.target.getRefBaseClass(); 200 if (refSuperClass != null) { 201 cc.implClass._extends(refSuperClass.toType(this, EXPOSED)); 202 } else { 203 // use the default one, if any 204 if (model.rootClass != null && cc.implClass._extends().equals(OBJECT)) { 205 cc.implClass._extends(model.rootClass); 206 } 207 if (model.rootInterface != null) { 208 cc.ref._implements(model.rootInterface); 209 } 210 } 211 } 212 213 // if serialization support is turned on, generate 214 // [RESULT] 215 // class ... implements Serializable { 216 // private static final long serialVersionUID = <id>; 217 // .... 218 // } 219 if (model.serializable) { 220 cc.implClass._implements(Serializable.class); 221 if (model.serialVersionUID != null) { 222 cc.implClass.field( 223 JMod.PRIVATE | JMod.STATIC | JMod.FINAL, 224 codeModel.LONG, 225 "serialVersionUID", 226 JExpr.lit(model.serialVersionUID)); 227 } 228 } 229 230 CClassInfoParent base = cc.target.parent(); 231 if ((base != null) && (base instanceof CClassInfo)) { 232 String pkg = base.getOwnerPackage().name(); 233 String shortName = base.fullName().substring(base.fullName().indexOf(pkg)+pkg.length()+1); 234 if (cc.target.shortName.equals(shortName)) { 235 getErrorReceiver().error(cc.target.getLocator(), Messages.ERR_KEYNAME_COLLISION.format(shortName)); 236 } 237 } 238 239 } 240 241 // fill in implementation classes 242 for (ClassOutlineImpl co : getClasses()) { 243 generateClassBody(co); 244 } 245 246 for (EnumOutline eo : enums.values()) { 247 generateEnumBody(eo); 248 } 249 250 // create factories for the impl-less elements 251 for (CElementInfo ei : model.getAllElements()) { 252 getPackageContext(ei._package()).objectFactoryGenerator().populate(ei); 253 } 254 255 if (model.options.debugMode) { 256 generateClassList(); 257 } 258 } 259 260 /** 261 * Generates a class that knows how to create an instance of JAXBContext 262 * 263 * <p> 264 * This is used in the debug mode so that a new properly configured 265 * {@link JAXBContext} object can be used. 266 */ 267 @SuppressWarnings("CallToThreadDumpStack") 268 private void generateClassList() { 269 try { 270 JDefinedClass jc = codeModel.rootPackage()._class("JAXBDebug"); 271 JMethod m = jc.method(JMod.PUBLIC | JMod.STATIC, JAXBContext.class, "createContext"); 272 JVar $classLoader = m.param(ClassLoader.class, "classLoader"); 273 m._throws(JAXBException.class); 274 JInvocation inv = codeModel.ref(JAXBContext.class).staticInvoke("newInstance"); 275 m.body()._return(inv); 276 277 switch (model.strategy) { 278 case INTF_AND_IMPL: { 279 StringBuilder buf = new StringBuilder(); 280 for (PackageOutlineImpl po : packageContexts.values()) { 281 if (buf.length() > 0) { 282 buf.append(':'); 283 } 284 buf.append(po._package().name()); 285 } 286 inv.arg(buf.toString()).arg($classLoader); 287 break; 288 } 289 case BEAN_ONLY: 290 for (ClassOutlineImpl cc : getClasses()) { 291 inv.arg(cc.implRef.dotclass()); 292 } 293 for (PackageOutlineImpl po : packageContexts.values()) { 294 inv.arg(po.objectFactory().dotclass()); 295 } 296 break; 297 default: 298 throw new IllegalStateException(); 299 } 300 } catch (JClassAlreadyExistsException e) { 301 e.printStackTrace(); 302 // after all, we are in the debug mode. a little sloppiness is OK. 303 // this error is not fatal. just continue. 304 } 305 } 306 307 public Model getModel() { 308 return model; 309 } 310 311 public JCodeModel getCodeModel() { 312 return codeModel; 313 } 314 315 public JClassContainer getContainer(CClassInfoParent parent, Aspect aspect) { 316 CClassInfoParent.Visitor<JClassContainer> v; 317 switch (aspect) { 318 case EXPOSED: 319 v = exposedContainerBuilder; 320 break; 321 case IMPLEMENTATION: 322 v = implContainerBuilder; 323 break; 324 default: 325 assert false; 326 throw new IllegalStateException(); 327 } 328 return parent.accept(v); 329 } 330 331 public final JType resolve(CTypeRef ref, Aspect a) { 332 return ref.getTarget().getType().toType(this, a); 333 } 334 private final CClassInfoParent.Visitor<JClassContainer> exposedContainerBuilder = 335 new CClassInfoParent.Visitor<JClassContainer>() { 336 337 public JClassContainer onBean(CClassInfo bean) { 338 return getClazz(bean).ref; 339 } 340 341 public JClassContainer onElement(CElementInfo element) { 342 // hmm... 343 return getElement(element).implClass; 344 } 345 346 public JClassContainer onPackage(JPackage pkg) { 347 return model.strategy.getPackage(pkg, EXPOSED); 348 } 349 }; 350 private final CClassInfoParent.Visitor<JClassContainer> implContainerBuilder = 351 new CClassInfoParent.Visitor<JClassContainer>() { 352 353 public JClassContainer onBean(CClassInfo bean) { 354 return getClazz(bean).implClass; 355 } 356 357 public JClassContainer onElement(CElementInfo element) { 358 return getElement(element).implClass; 359 } 360 361 public JClassContainer onPackage(JPackage pkg) { 362 return model.strategy.getPackage(pkg, Aspect.IMPLEMENTATION); 363 } 364 }; 365 366 /** 367 * Returns all <i>used</i> JPackages. 368 * 369 * A JPackage is considered as "used" if a ClassItem or 370 * a InterfaceItem resides in that package. 371 * 372 * This value is dynamically calculated every time because 373 * one can freely remove ClassItem/InterfaceItem. 374 * 375 * @return 376 * Given the same input, the order of packages in the array 377 * is always the same regardless of the environment. 378 */ 379 public final JPackage[] getUsedPackages(Aspect aspect) { 380 Set<JPackage> s = new TreeSet<JPackage>(); 381 382 for (CClassInfo bean : model.beans().values()) { 383 JClassContainer cont = getContainer(bean.parent(), aspect); 384 if (cont.isPackage()) { 385 s.add((JPackage) cont); 386 } 387 } 388 389 for (CElementInfo e : model.getElementMappings(null).values()) { 390 // at the first glance you might think we should be iterating all elements, 391 // not just global ones, but if you think about it, local ones live inside 392 // another class, so those packages are already enumerated when we were 393 // walking over CClassInfos. 394 s.add(e._package()); 395 } 396 397 return s.toArray(new JPackage[s.size()]); 398 } 399 400 public ErrorReceiver getErrorReceiver() { 401 return errorReceiver; 402 } 403 404 public CodeModelClassFactory getClassFactory() { 405 return codeModelClassFactory; 406 } 407 408 public PackageOutlineImpl getPackageContext(JPackage p) { 409 PackageOutlineImpl r = packageContexts.get(p); 410 if (r == null) { 411 r = new PackageOutlineImpl(this, model, p); 412 packageContexts.put(p, r); 413 } 414 return r; 415 } 416 417 /** 418 * Generates the minimum {@link JDefinedClass} skeleton 419 * without filling in its body. 420 */ 421 private ClassOutlineImpl generateClassDef(CClassInfo bean) { 422 ImplStructureStrategy.Result r = model.strategy.createClasses(this, bean); 423 JClass implRef; 424 425 if (bean.getUserSpecifiedImplClass() != null) { 426 // create a place holder for a user-specified class. 427 JDefinedClass usr; 428 try { 429 usr = codeModel._class(bean.getUserSpecifiedImplClass()); 430 // but hide that file so that it won't be generated. 431 usr.hide(); 432 } catch (JClassAlreadyExistsException e) { 433 // it's OK for this to collide. 434 usr = e.getExistingClass(); 435 } 436 usr._extends(r.implementation); 437 implRef = usr; 438 } else { 439 implRef = r.implementation; 440 } 441 442 return new ClassOutlineImpl(this, bean, r.exposed, r.implementation, implRef); 443 } 444 445 public Collection<ClassOutlineImpl> getClasses() { 446 // make sure that classes are fully populated 447 assert model.beans().size() == classes.size(); 448 return classes.values(); 449 } 450 451 public ClassOutlineImpl getClazz(CClassInfo bean) { 452 ClassOutlineImpl r = classes.get(bean); 453 if (r == null) { 454 classes.put(bean, r = generateClassDef(bean)); 455 } 456 return r; 457 } 458 459 public ElementOutlineImpl getElement(CElementInfo ei) { 460 ElementOutlineImpl def = elements.get(ei); 461 if (def == null && ei.hasClass()) { 462 // create one. in the constructor it adds itself to the elements. 463 def = new ElementOutlineImpl(this, ei); 464 } 465 return def; 466 } 467 468 public EnumOutline getEnum(CEnumLeafInfo eli) { 469 return enums.get(eli); 470 } 471 472 public Collection<EnumOutline> getEnums() { 473 return enums.values(); 474 } 475 476 public Iterable<? extends PackageOutline> getAllPackageContexts() { 477 return packageContexts.values(); 478 } 479 480 public FieldOutline getField(CPropertyInfo prop) { 481 return fields.get(prop); 482 } 483 484 /** 485 * Generates the body of a class. 486 * 487 */ 488 private void generateClassBody(ClassOutlineImpl cc) { 489 CClassInfo target = cc.target; 490 491 // used to simplify the generated annotations 492 String mostUsedNamespaceURI = cc._package().getMostUsedNamespaceURI(); 493 494 // [RESULT] 495 // @XmlType(name="foo", targetNamespace="bar://baz") 496 XmlTypeWriter xtw = cc.implClass.annotate2(XmlTypeWriter.class); 497 writeTypeName(cc.target.getTypeName(), xtw, mostUsedNamespaceURI); 498 499 if (model.options.target.isLaterThan(SpecVersion.V2_1)) { 500 // @XmlSeeAlso 501 Iterator<CClassInfo> subclasses = cc.target.listSubclasses(); 502 if (subclasses.hasNext()) { 503 XmlSeeAlsoWriter saw = cc.implClass.annotate2(XmlSeeAlsoWriter.class); 504 while (subclasses.hasNext()) { 505 CClassInfo s = subclasses.next(); 506 saw.value(getClazz(s).implRef); 507 } 508 } 509 } 510 511 if (target.isElement()) { 512 String namespaceURI = target.getElementName().getNamespaceURI(); 513 String localPart = target.getElementName().getLocalPart(); 514 515 // [RESULT] 516 // @XmlRootElement(name="foo", targetNamespace="bar://baz") 517 XmlRootElementWriter xrew = cc.implClass.annotate2(XmlRootElementWriter.class); 518 xrew.name(localPart); 519 if (!namespaceURI.equals(mostUsedNamespaceURI)) // only generate if necessary 520 { 521 xrew.namespace(namespaceURI); 522 } 523 } 524 525 if (target.isOrdered()) { 526 for (CPropertyInfo p : target.getProperties()) { 527 if (!(p instanceof CAttributePropertyInfo)) { 528 if (!((p instanceof CReferencePropertyInfo) 529 && ((CReferencePropertyInfo) p).isDummy())) { 530 xtw.propOrder(p.getName(false)); 531 } 532 } 533 } 534 } else { 535 // produce empty array 536 xtw.getAnnotationUse().paramArray("propOrder"); 537 } 538 539 for (CPropertyInfo prop : target.getProperties()) { 540 generateFieldDecl(cc, prop); 541 } 542 543 if (target.declaresAttributeWildcard()) { 544 generateAttributeWildcard(cc); 545 } 546 547 // generate some class level javadoc 548 cc.ref.javadoc().append(target.javadoc); 549 550 cc._package().objectFactoryGenerator().populate(cc); 551 } 552 553 private void writeTypeName(QName typeName, XmlTypeWriter xtw, String mostUsedNamespaceURI) { 554 if (typeName == null) { 555 xtw.name(""); 556 } else { 557 xtw.name(typeName.getLocalPart()); 558 final String typeNameURI = typeName.getNamespaceURI(); 559 if (!typeNameURI.equals(mostUsedNamespaceURI)) // only generate if necessary 560 { 561 xtw.namespace(typeNameURI); 562 } 563 } 564 } 565 566 /** 567 * Generates an attribute wildcard property on a class. 568 */ 569 private void generateAttributeWildcard(ClassOutlineImpl cc) { 570 String FIELD_NAME = "otherAttributes"; 571 String METHOD_SEED = model.getNameConverter().toClassName(FIELD_NAME); 572 573 JClass mapType = codeModel.ref(Map.class).narrow(QName.class, String.class); 574 JClass mapImpl = codeModel.ref(HashMap.class).narrow(QName.class, String.class); 575 576 // [RESULT] 577 // Map<QName,String> m = new HashMap<QName,String>(); 578 JFieldVar $ref = cc.implClass.field(JMod.PRIVATE, 579 mapType, FIELD_NAME, JExpr._new(mapImpl)); 580 $ref.annotate2(XmlAnyAttributeWriter.class); 581 582 MethodWriter writer = cc.createMethodWriter(); 583 584 JMethod $get = writer.declareMethod(mapType, "get" + METHOD_SEED); 585 $get.javadoc().append( 586 "Gets a map that contains attributes that aren't bound to any typed property on this class.\n\n" 587 + "<p>\n" 588 + "the map is keyed by the name of the attribute and \n" 589 + "the value is the string value of the attribute.\n" 590 + "\n" 591 + "the map returned by this method is live, and you can add new attribute\n" 592 + "by updating the map directly. Because of this design, there's no setter.\n"); 593 $get.javadoc().addReturn().append("always non-null"); 594 595 $get.body()._return($ref); 596 } 597 598 /** 599 * Generates the minimum {@link JDefinedClass} skeleton 600 * without filling in its body. 601 */ 602 private EnumOutline generateEnumDef(CEnumLeafInfo e) { 603 JDefinedClass type; 604 605 type = getClassFactory().createClass( 606 getContainer(e.parent, EXPOSED), e.shortName, e.getLocator(), ClassType.ENUM); 607 type.javadoc().append(e.javadoc); 608 609 return new EnumOutline(e, type) { 610 611 @Override 612 public 613 @NotNull 614 Outline parent() { 615 return BeanGenerator.this; 616 } 617 }; 618 } 619 620 private void generateEnumBody(EnumOutline eo) { 621 JDefinedClass type = eo.clazz; 622 CEnumLeafInfo e = eo.target; 623 624 XmlTypeWriter xtw = type.annotate2(XmlTypeWriter.class); 625 writeTypeName(e.getTypeName(), xtw, 626 eo._package().getMostUsedNamespaceURI()); 627 628 JCodeModel cModel = model.codeModel; 629 630 // since constant values are never null, no point in using the boxed types. 631 JType baseExposedType = e.base.toType(this, EXPOSED).unboxify(); 632 JType baseImplType = e.base.toType(this, Aspect.IMPLEMENTATION).unboxify(); 633 634 635 XmlEnumWriter xew = type.annotate2(XmlEnumWriter.class); 636 xew.value(baseExposedType); 637 638 639 boolean needsValue = e.needsValueField(); 640 641 // for each member <m>, 642 // [RESULT] 643 // <EnumName>(<deserializer of m>(<value>)); 644 645 Set<String> enumFieldNames = new HashSet<String>(); // record generated field names to detect collision 646 647 for (CEnumConstant mem : e.members) { 648 String constName = mem.getName(); 649 650 if (!JJavaName.isJavaIdentifier(constName)) { 651 // didn't produce a name. 652 getErrorReceiver().error(e.getLocator(), 653 Messages.ERR_UNUSABLE_NAME.format(mem.getLexicalValue(), constName)); 654 } 655 656 if (!enumFieldNames.add(constName)) { 657 getErrorReceiver().error(e.getLocator(), Messages.ERR_NAME_COLLISION.format(constName)); 658 } 659 660 // [RESULT] 661 // <Const>(...) 662 // ASSUMPTION: datatype is outline-independent 663 JEnumConstant constRef = type.enumConstant(constName); 664 if (needsValue) { 665 constRef.arg(e.base.createConstant(this, new XmlString(mem.getLexicalValue()))); 666 } 667 668 if (!mem.getLexicalValue().equals(constName)) { 669 constRef.annotate2(XmlEnumValueWriter.class).value(mem.getLexicalValue()); 670 } 671 672 // set javadoc 673 if (mem.javadoc != null) { 674 constRef.javadoc().append(mem.javadoc); 675 } 676 677 eo.constants.add(new EnumConstantOutline(mem, constRef) { 678 }); 679 } 680 681 682 if (needsValue) { 683 // [RESULT] 684 // final <valueType> value; 685 JFieldVar $value = type.field(JMod.PRIVATE | JMod.FINAL, baseExposedType, "value"); 686 687 // [RESULT] 688 // public <valuetype> value() { return value; } 689 type.method(JMod.PUBLIC, baseExposedType, "value").body()._return($value); 690 691 // [RESULT] 692 // <constructor>(<valueType> v) { 693 // this.value=v; 694 // } 695 { 696 JMethod m = type.constructor(0); 697 m.body().assign($value, m.param(baseImplType, "v")); 698 } 699 700 // [RESULT] 701 // public static <Const> fromValue(<valueType> v) { 702 // for( <Const> c : <Const>.values() ) { 703 // if(c.value == v) // or equals 704 // return c; 705 // } 706 // throw new IllegalArgumentException(...); 707 // } 708 { 709 JMethod m = type.method(JMod.PUBLIC | JMod.STATIC, type, "fromValue"); 710 JVar $v = m.param(baseExposedType, "v"); 711 JForEach fe = m.body().forEach(type, "c", type.staticInvoke("values")); 712 JExpression eq; 713 if (baseExposedType.isPrimitive()) { 714 eq = fe.var().ref($value).eq($v); 715 } else { 716 eq = fe.var().ref($value).invoke("equals").arg($v); 717 } 718 719 fe.body()._if(eq)._then()._return(fe.var()); 720 721 JInvocation ex = JExpr._new(cModel.ref(IllegalArgumentException.class)); 722 723 JExpression strForm; 724 if (baseExposedType.isPrimitive()) { 725 strForm = cModel.ref(String.class).staticInvoke("valueOf").arg($v); 726 } else if (baseExposedType == cModel.ref(String.class)) { 727 strForm = $v; 728 } else { 729 strForm = $v.invoke("toString"); 730 } 731 m.body()._throw(ex.arg(strForm)); 732 } 733 } else { 734 // [RESULT] 735 // public String value() { return name(); } 736 type.method(JMod.PUBLIC, String.class, "value").body()._return(JExpr.invoke("name")); 737 738 // [RESULT] 739 // public <Const> fromValue(String v) { return valueOf(v); } 740 JMethod m = type.method(JMod.PUBLIC | JMod.STATIC, type, "fromValue"); 741 m.body()._return(JExpr.invoke("valueOf").arg(m.param(String.class, "v"))); 742 } 743 } 744 745 /** 746 * Determines the FieldRenderer used for the given FieldUse, 747 * then generates the field declaration and accessor methods. 748 * 749 * The <code>fields</code> map will be updated with the newly 750 * created FieldRenderer. 751 */ 752 private FieldOutline generateFieldDecl(ClassOutlineImpl cc, CPropertyInfo prop) { 753 FieldRenderer fr = prop.realization; 754 if (fr == null) // none is specified. use the default factory 755 { 756 fr = model.options.getFieldRendererFactory().getDefault(); 757 } 758 759 FieldOutline field = fr.generate(cc, prop); 760 fields.put(prop, field); 761 762 return field; 763 } 764 765 /** 766 * Generates {@link XmlJavaTypeAdapter} from {@link PropertyInfo} if necessary. 767 * Also generates other per-property annotations 768 * (such as {@link XmlID}, {@link XmlIDREF}, and {@link XmlMimeType} if necessary. 769 */ 770 public final void generateAdapterIfNecessary(CPropertyInfo prop, JAnnotatable field) { 771 CAdapter adapter = prop.getAdapter(); 772 if (adapter != null) { 773 if (adapter.getAdapterIfKnown() == SwaRefAdapter.class) { 774 field.annotate(XmlAttachmentRef.class); 775 } else { 776 // [RESULT] 777 // @XmlJavaTypeAdapter( Foo.class ) 778 XmlJavaTypeAdapterWriter xjtw = field.annotate2(XmlJavaTypeAdapterWriter.class); 779 xjtw.value(adapter.adapterType.toType(this, EXPOSED)); 780 } 781 } 782 783 switch (prop.id()) { 784 case ID: 785 field.annotate(XmlID.class); 786 break; 787 case IDREF: 788 field.annotate(XmlIDREF.class); 789 break; 790 } 791 792 if (prop.getExpectedMimeType() != null) { 793 field.annotate2(XmlMimeTypeWriter.class).value(prop.getExpectedMimeType().toString()); 794 } 795 } 796 797 public final JClass addRuntime(Class clazz) { 798 JClass g = generatedRuntime.get(clazz); 799 if (g == null) { 800 // put code into a separate package to avoid name conflicts. 801 JPackage implPkg = getUsedPackages(Aspect.IMPLEMENTATION)[0].subPackage("runtime"); 802 g = generateStaticClass(clazz, implPkg); 803 generatedRuntime.put(clazz, g); 804 } 805 return g; 806 } 807 808 public JClass generateStaticClass(Class src, JPackage out) { 809 String shortName = getShortName(src.getName()); 810 811 // some people didn't like our jars to contain files with .java extension, 812 // so when we build jars, we'' use ".java_". But when we run from the workspace, 813 // we want the original source code to be used, so we check both here. 814 // see bug 6211503. 815 URL res = src.getResource(shortName + ".java"); 816 if (res == null) { 817 res = src.getResource(shortName + ".java_"); 818 } 819 if (res == null) { 820 throw new InternalError("Unable to load source code of " + src.getName() + " as a resource"); 821 } 822 823 JStaticJavaFile sjf = new JStaticJavaFile(out, shortName, res, null); 824 out.addResourceFile(sjf); 825 return sjf.getJClass(); 826 } 827 828 private String getShortName(String name) { 829 return name.substring(name.lastIndexOf('.') + 1); 830 } 831 }