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