1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.internal.xjc.model;
  27 
  28 import com.sun.codemodel.internal.JClass;
  29 import com.sun.codemodel.internal.JCodeModel;
  30 import com.sun.codemodel.internal.JPackage;
  31 import com.sun.tools.internal.xjc.ErrorReceiver;
  32 import com.sun.tools.internal.xjc.Options;
  33 import com.sun.tools.internal.xjc.Plugin;
  34 import com.sun.tools.internal.xjc.api.ClassNameAllocator;
  35 import com.sun.tools.internal.xjc.generator.bean.BeanGenerator;
  36 import com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy;
  37 import com.sun.tools.internal.xjc.model.nav.NClass;
  38 import com.sun.tools.internal.xjc.model.nav.NType;
  39 import com.sun.tools.internal.xjc.model.nav.NavigatorImpl;
  40 import com.sun.tools.internal.xjc.outline.Outline;
  41 import com.sun.tools.internal.xjc.reader.xmlschema.Messages;
  42 import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
  43 import com.sun.xml.internal.bind.api.impl.NameConverter;
  44 import com.sun.xml.internal.bind.v2.model.core.Ref;
  45 import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
  46 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
  47 import com.sun.xml.internal.bind.v2.util.FlattenIterator;
  48 import com.sun.xml.internal.xsom.XSComponent;
  49 import com.sun.xml.internal.xsom.XSSchemaSet;
  50 import org.xml.sax.Locator;
  51 import org.xml.sax.SAXException;
  52 import org.xml.sax.helpers.LocatorImpl;
  53 
  54 import javax.xml.bind.annotation.XmlAttribute;
  55 import javax.xml.bind.annotation.XmlNsForm;
  56 import javax.xml.bind.annotation.XmlTransient;
  57 import javax.xml.namespace.QName;
  58 import javax.xml.transform.Result;
  59 import java.util.Collections;
  60 import java.util.HashMap;
  61 import java.util.HashSet;
  62 import java.util.Iterator;
  63 import java.util.LinkedHashMap;
  64 import java.util.Map;
  65 import java.util.Set;
  66 
  67 /**
  68  * Root of the object model that represents the code that needs to be generated.
  69  *
  70  * <p>
  71  * A {@link Model} is a schema language neutral representation of the
  72  * result of a schema parsing. The back-end then works against this model
  73  * to turn this into a series of Java source code.
  74  *
  75  * @author Kohsuke Kawaguchi
  76  */
  77 public final class Model implements TypeInfoSet<NType,NClass,Void,Void>, CCustomizable {
  78 
  79     /**
  80      * Generated beans.
  81      */
  82     private final Map<NClass,CClassInfo> beans = new LinkedHashMap<NClass,CClassInfo>();
  83 
  84     /**
  85      * Generated enums.
  86      */
  87     private final Map<NClass,CEnumLeafInfo> enums = new LinkedHashMap<NClass,CEnumLeafInfo>();
  88 
  89     /**
  90      * The element mappings.
  91      */
  92     private final Map<NClass/*scope*/,Map<QName,CElementInfo>> elementMappings =
  93             new LinkedHashMap<NClass, Map<QName, CElementInfo>>();
  94 
  95     private final Iterable<? extends CElementInfo> allElements =
  96         new Iterable<CElementInfo>() {
  97             public Iterator<CElementInfo> iterator() {
  98                 return new FlattenIterator<CElementInfo>(elementMappings.values());
  99             }
 100         };
 101 
 102     /**
 103      * {@link TypeUse}s for all named types.
 104      * <p>
 105      * I really don't want to promote the notion of a 'type' in any place except in the XML Schema code,
 106      * but this needs to be exposed for JAX-RPC. A reference to a named XML type will be converted into
 107      * a reference to a Java type with annotations.
 108      */
 109     private final Map<QName,TypeUse> typeUses = new LinkedHashMap<QName, TypeUse>();
 110 
 111     /**
 112      * {@link NameConverter} to be used.
 113      */
 114     private NameConverter nameConverter;
 115 
 116     /**
 117      * Single linked list that connects all {@link CCustomizations} that belong to this model.
 118      *
 119      * @see CCustomizations#next
 120      */
 121     /*package*/ CCustomizations customizations;
 122 
 123     /**
 124      * This field controls the generation of package level annotations for s2j
 125      */
 126     private boolean packageLevelAnnotations = true;
 127 
 128     /**
 129      * If this model was built from XML Schema, this field
 130      * stores the root object of the parse schema model.
 131      * Otherwise null.
 132      *
 133      * @since 2.1.1
 134      */
 135     public final XSSchemaSet schemaComponent;
 136 
 137     private CCustomizations globalCustomizations = new CCustomizations();
 138 
 139     /**
 140      * @param nc
 141      *      Usually this should be set in the constructor, but we do allow this parameter
 142      *      to be initially null, and then set later.
 143      * @param schemaComponent
 144      *      The source schema model, if this is built from XSD.
 145      */
 146     public Model( Options opts, JCodeModel cm, NameConverter nc, ClassNameAllocator allocator, XSSchemaSet schemaComponent ) {
 147         this.options = opts;
 148         this.codeModel = cm;
 149         this.nameConverter = nc;
 150         this.defaultSymbolSpace = new SymbolSpace(codeModel);
 151         defaultSymbolSpace.setType(codeModel.ref(Object.class));
 152 
 153         elementMappings.put(null, new LinkedHashMap<QName, CElementInfo>());
 154 
 155         if(opts.automaticNameConflictResolution)
 156             allocator = new AutoClassNameAllocator(allocator);
 157         this.allocator = new ClassNameAllocatorWrapper(allocator);
 158         this.schemaComponent = schemaComponent;
 159         this.globalCustomizations.setParent(this, this);
 160     }
 161 
 162     public void setNameConverter(NameConverter nameConverter) {
 163         assert this.nameConverter==null;
 164         assert nameConverter!=null;
 165         this.nameConverter = nameConverter;
 166     }
 167 
 168     /**
 169      * Gets the name converter that shall be used to parse XML names into Java names.
 170      */
 171     public final NameConverter getNameConverter() {
 172         return nameConverter;
 173     }
 174 
 175     public boolean isPackageLevelAnnotations() {
 176         return packageLevelAnnotations;
 177     }
 178 
 179     public void setPackageLevelAnnotations(boolean packageLevelAnnotations) {
 180         this.packageLevelAnnotations = packageLevelAnnotations;
 181     }
 182 
 183     /**
 184      * This model uses this code model exclusively.
 185      */
 186     @XmlTransient
 187     public final JCodeModel codeModel;
 188 
 189     /**
 190      * Command-line options used for building this model.
 191      */
 192     public final Options options;
 193 
 194     /**
 195      * True to generate serializable classes.
 196      */
 197     @XmlAttribute
 198     public boolean serializable;
 199 
 200     /**
 201      * serial version UID to be generated.
 202      *
 203      * null if not to generate serialVersionUID field.
 204      */
 205     @XmlAttribute
 206     public Long serialVersionUID;
 207 
 208     /**
 209      * If non-null, all the generated classes should eventually derive from this class.
 210      */
 211     @XmlTransient
 212     public JClass rootClass;
 213 
 214     /**
 215      * If non-null, all the generated interfaces should eventually derive from this interface.
 216      */
 217     @XmlTransient
 218     public JClass rootInterface;
 219 
 220     /**
 221      * Specifies the code generation strategy.
 222      * Must not be null.
 223      */
 224     public ImplStructureStrategy strategy = ImplStructureStrategy.BEAN_ONLY;
 225 
 226     /**
 227      * This allocator has the final say on deciding the class name.
 228      * Must not be null.
 229      *
 230      * <p>
 231      * Model classes are responsible for using the allocator.
 232      * This allocator interaction should be transparent to the user/builder
 233      * of the model.
 234      */
 235     /*package*/ final ClassNameAllocatorWrapper allocator;
 236 
 237     /**
 238      * Default ID/IDREF symbol space. Any ID/IDREF without explicit
 239      * reference to a symbol space is assumed to use this default
 240      * symbol space.
 241      */
 242     @XmlTransient
 243     public final SymbolSpace defaultSymbolSpace;
 244 
 245     /** All the defined {@link SymbolSpace}s keyed by their name. */
 246     private final Map<String,SymbolSpace> symbolSpaces = new HashMap<String,SymbolSpace>();
 247 
 248     public SymbolSpace getSymbolSpace( String name ) {
 249         SymbolSpace ss = symbolSpaces.get(name);
 250         if(ss==null)
 251             symbolSpaces.put(name,ss=new SymbolSpace(codeModel));
 252         return ss;
 253     }
 254 
 255     /**
 256      * Fully-generate the source code into the given model.
 257      *
 258      * @return
 259      *      null if there was any errors. Otherwise it returns a valid
 260      *      {@link Outline} object, which captures how the model objects
 261      *      are mapped to the generated source code.
 262      *      <p>
 263      *      Add-ons can use those information to further augment the generated
 264      *      source code.
 265      */
 266     public Outline generateCode(Options opt,ErrorReceiver receiver) {
 267         ErrorReceiverFilter ehf = new ErrorReceiverFilter(receiver);
 268 
 269         // run extensions // moved to BGMBuilder._build() - issue with hyperjaxb3
 270 //        for( Plugin ma : opt.activePlugins )
 271 //            ma.postProcessModel(this,ehf);
 272 
 273         Outline o = BeanGenerator.generate(this, ehf);
 274 
 275         try {// run extensions
 276             for( Plugin ma : opt.activePlugins )
 277                 ma.run(o,opt,ehf);
 278         } catch (SAXException e) {
 279             // fatal error. error should have been reported
 280             return null;
 281         }
 282 
 283         // check for unused plug-in customizations.
 284         // these can be only checked after the plug-ins run, so it's here.
 285         // the JAXB bindings are checked by XMLSchema's builder.
 286         Set<CCustomizations> check = new HashSet<CCustomizations>();
 287         for( CCustomizations c=customizations; c!=null; c=c.next ) {
 288             if(!check.add(c)) {
 289                 throw new AssertionError(); // detect a loop
 290             }
 291             for (CPluginCustomization p : c) {
 292                 if(!p.isAcknowledged()) {
 293                     ehf.error(
 294                         p.locator,
 295                         Messages.format(
 296                             Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION,
 297                             p.element.getNodeName()
 298                         ));
 299                     ehf.error(
 300                         c.getOwner().getLocator(),
 301                         Messages.format(
 302                             Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION_LOCATION));
 303                 }
 304             }
 305         }
 306 
 307         if(ehf.hadError())
 308             o = null;
 309         return o;
 310     }
 311 
 312     /**
 313      * Represents the "top-level binding".
 314      *
 315      * <p>
 316      * This is used to support the use of a schema inside WSDL.
 317      * For XML Schema, the top-level binding is a map from
 318      * global element declarations to its representation class.
 319      *
 320      * <p>
 321      * For other schema languages, it should follow the appendices in
 322      * WSDL (but in practice no one would use WSDL with a schema language
 323      * other than XML Schema, so it doesn't really matter.)
 324      *
 325      * <p>
 326      * This needs to be filled by the front-end.
 327      */
 328     public final Map<QName,CClassInfo> createTopLevelBindings() {
 329         Map<QName,CClassInfo> r = new HashMap<QName,CClassInfo>();
 330         for( CClassInfo b : beans().values() ) {
 331             if(b.isElement())
 332                 r.put(b.getElementName(),b);
 333         }
 334         return r;
 335     }
 336 
 337     public Navigator<NType,NClass,Void,Void> getNavigator() {
 338         return NavigatorImpl.theInstance;
 339     }
 340 
 341     public CNonElement getTypeInfo(NType type) {
 342         CBuiltinLeafInfo leaf = CBuiltinLeafInfo.LEAVES.get(type);
 343         if(leaf!=null)      return leaf;
 344 
 345         return getClassInfo(getNavigator().asDecl(type));
 346     }
 347 
 348     public CBuiltinLeafInfo getAnyTypeInfo() {
 349         return CBuiltinLeafInfo.ANYTYPE;
 350     }
 351 
 352     public CNonElement getTypeInfo(Ref<NType,NClass> ref) {
 353         // TODO: handle XmlValueList
 354         assert !ref.valueList;
 355         return getTypeInfo(ref.type);
 356     }
 357 
 358     public Map<NClass,CClassInfo> beans() {
 359         return beans;
 360     }
 361 
 362     public Map<NClass,CEnumLeafInfo> enums() {
 363         return enums;
 364     }
 365 
 366     public Map<QName,TypeUse> typeUses() {
 367         return typeUses;
 368     }
 369 
 370     /**
 371      * No array mapping generation for XJC.
 372      */
 373     public Map<NType, ? extends CArrayInfo> arrays() {
 374         return Collections.emptyMap();
 375     }
 376 
 377     public Map<NType, ? extends CBuiltinLeafInfo> builtins() {
 378         return CBuiltinLeafInfo.LEAVES;
 379     }
 380 
 381     public CClassInfo getClassInfo(NClass t) {
 382         return beans.get(t);
 383     }
 384 
 385     public CElementInfo getElementInfo(NClass scope,QName name) {
 386         Map<QName,CElementInfo> m = elementMappings.get(scope);
 387         if(m!=null) {
 388             CElementInfo r = m.get(name);
 389             if(r!=null)     return r;
 390         }
 391         return elementMappings.get(null).get(name);
 392     }
 393 
 394     public Map<QName,CElementInfo> getElementMappings(NClass scope) {
 395         return elementMappings.get(scope);
 396     }
 397 
 398     public Iterable<? extends CElementInfo> getAllElements() {
 399         return allElements;
 400     }
 401 
 402     /**
 403      * @deprecated
 404      *      Always return null. Perhaps you are interested in {@link #schemaComponent}?
 405      */
 406     public XSComponent getSchemaComponent() {
 407         return null;
 408     }
 409 
 410     /**
 411      * @deprecated
 412      *      No line number available for the "root" component.
 413      */
 414     public Locator getLocator() {
 415         LocatorImpl r = new LocatorImpl();
 416         r.setLineNumber(-1);
 417         r.setColumnNumber(-1);
 418         return r;
 419     }
 420 
 421     /**
 422      * Gets the global customizations.
 423      */
 424     public CCustomizations getCustomizations() {
 425         return globalCustomizations;
 426     }
 427 
 428     /**
 429      * Not implemented in the compile-time model.
 430      */
 431     public Map<String, String> getXmlNs(String namespaceUri) {
 432         return Collections.emptyMap();
 433     }
 434 
 435     public Map<String, String> getSchemaLocations() {
 436         return Collections.emptyMap();
 437     }
 438 
 439     public XmlNsForm getElementFormDefault(String nsUri) {
 440         throw new UnsupportedOperationException();
 441     }
 442 
 443     public XmlNsForm getAttributeFormDefault(String nsUri) {
 444         throw new UnsupportedOperationException();
 445     }
 446 
 447     public void dump(Result out) {
 448         // TODO
 449         throw new UnsupportedOperationException();
 450     }
 451 
 452     /*package*/ void add( CEnumLeafInfo e ) {
 453         enums.put( e.getClazz(), e );
 454     }
 455 
 456     /*package*/ void add( CClassInfo ci ) {
 457         beans.put( ci.getClazz(), ci );
 458     }
 459 
 460     /*package*/ void add( CElementInfo ei ) {
 461         NClass clazz = null;
 462         if(ei.getScope()!=null)
 463             clazz = ei.getScope().getClazz();
 464 
 465         Map<QName,CElementInfo> m = elementMappings.get(clazz);
 466         if(m==null)
 467             elementMappings.put(clazz, m = new LinkedHashMap<QName, CElementInfo>());
 468         m.put(ei.getElementName(),ei);
 469     }
 470 
 471 
 472     private final Map<JPackage,CClassInfoParent.Package> cache = new HashMap<JPackage,CClassInfoParent.Package>();
 473 
 474     public CClassInfoParent.Package getPackage(JPackage pkg) {
 475         CClassInfoParent.Package r = cache.get(pkg);
 476         if(r==null)
 477             cache.put(pkg,r=new CClassInfoParent.Package(pkg));
 478         return r;
 479     }
 480 
 481     /*package*/ static final Locator EMPTY_LOCATOR;
 482 
 483     static {
 484         LocatorImpl l = new LocatorImpl();
 485         l.setColumnNumber(-1);
 486         l.setLineNumber(-1);
 487         EMPTY_LOCATOR = l;
 488     }
 489 }