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