src/share/jaxws_classes/com/sun/tools/internal/xjc/reader/xmlschema/ClassSelector.java

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
parent 0
373ffda63c9a
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

     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  */
    26 package com.sun.tools.internal.xjc.reader.xmlschema;
    28 import java.io.StringWriter;
    29 import java.util.HashMap;
    30 import java.util.HashSet;
    31 import java.util.Map;
    32 import java.util.Set;
    33 import java.util.Stack;
    35 import com.sun.codemodel.internal.JCodeModel;
    36 import com.sun.codemodel.internal.JJavaName;
    37 import com.sun.codemodel.internal.JPackage;
    38 import com.sun.codemodel.internal.util.JavadocEscapeWriter;
    39 import com.sun.istack.internal.NotNull;
    40 import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo;
    41 import com.sun.tools.internal.xjc.model.CClassInfo;
    42 import com.sun.tools.internal.xjc.model.CClassInfoParent;
    43 import com.sun.tools.internal.xjc.model.CElement;
    44 import com.sun.tools.internal.xjc.model.CElementInfo;
    45 import com.sun.tools.internal.xjc.model.CTypeInfo;
    46 import com.sun.tools.internal.xjc.model.TypeUse;
    47 import com.sun.tools.internal.xjc.model.CClass;
    48 import com.sun.tools.internal.xjc.model.CNonElement;
    49 import com.sun.tools.internal.xjc.reader.Ring;
    50 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIProperty;
    51 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BISchemaBinding;
    52 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.LocalScoping;
    53 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
    54 import com.sun.xml.internal.xsom.XSComplexType;
    55 import com.sun.xml.internal.xsom.XSComponent;
    56 import com.sun.xml.internal.xsom.XSDeclaration;
    57 import com.sun.xml.internal.xsom.XSElementDecl;
    58 import com.sun.xml.internal.xsom.XSSchema;
    59 import com.sun.xml.internal.xsom.XSSchemaSet;
    60 import com.sun.xml.internal.xsom.XSSimpleType;
    61 import com.sun.xml.internal.xsom.XSType;
    62 import com.sun.xml.internal.xsom.impl.util.SchemaWriter;
    63 import com.sun.xml.internal.xsom.util.ComponentNameFunction;
    65 import org.xml.sax.Locator;
    67 /**
    68  * Manages association between {@link XSComponent}s and generated
    69  * {@link CTypeInfo}s.
    70  *
    71  * <p>
    72  * This class determines which component is mapped to (or is not mapped to)
    73  * what types.
    74  *
    75  * @author
    76  *     Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
    77  */
    78 public final class ClassSelector extends BindingComponent {
    79     /** Center of owner classes. */
    80     private final BGMBuilder builder = Ring.get(BGMBuilder.class);
    83     /**
    84      * Map from XSComponents to {@link Binding}s. Keeps track of all
    85      * content interfaces that are already built or being built.
    86      */
    87     private final Map<XSComponent,Binding> bindMap = new HashMap<XSComponent,Binding>();
    89     /**
    90      * UGLY HACK.
    91      * <p>
    92      * To avoid cyclic dependency between binding elements and types,
    93      * we need additional markers that tell which elements are definitely not bound
    94      * to a class.
    95      * <p>
    96      * the cyclic dependency is as follows:
    97      * elements need to bind its types first, because otherwise it can't
    98      * determine T of JAXBElement<T>.
    99      * OTOH, types need to know whether its parent is bound to a class to decide
   100      * which class name to use.
   101      */
   102     /*package*/ final Map<XSComponent,CElementInfo> boundElements = new HashMap<XSComponent,CElementInfo>();
   104     /**
   105      * A list of {@link Binding}s object that needs to be built.
   106      */
   107     private final Stack<Binding> bindQueue = new Stack<Binding>();
   109     /**
   110      * {@link CClassInfo}s that are already {@link Binding#build() built}.
   111      */
   112     private final Set<CClassInfo> built = new HashSet<CClassInfo>();
   114     /**
   115      * Object that determines components that are mapped
   116      * to classes.
   117      */
   118     private final ClassBinder classBinder;
   120     /**
   121      * {@link CClassInfoParent}s that determines where a new class
   122      * should be created.
   123      */
   124     private final Stack<CClassInfoParent> classScopes = new Stack<CClassInfoParent>();
   126     /**
   127      * The component that is being bound to {@link #currentBean}.
   128      */
   129     private XSComponent currentRoot;
   130     /**
   131      * The bean representation we are binding right now.
   132      */
   133     private CClassInfo currentBean;
   136     private final class Binding {
   137         private final XSComponent sc;
   138         private final CTypeInfo bean;
   140         public Binding(XSComponent sc, CTypeInfo bean) {
   141             this.sc = sc;
   142             this.bean = bean;
   143         }
   145         void build() {
   146             if(!(this.bean instanceof CClassInfo))
   147                 return; // no need to "build"
   149             CClassInfo bean = (CClassInfo)this.bean;
   151             if(!built.add(bean))
   152                 return; // already built
   154             for( String reservedClassName : reservedClassNames ) {
   155                 if( bean.getName().equals(reservedClassName) ) {
   156                     getErrorReporter().error( sc.getLocator(),
   157                         Messages.ERR_RESERVED_CLASS_NAME, reservedClassName );
   158                     break;
   159                 }
   160             }
   162             // if this schema component is an element declaration
   163             // and it satisfies a set of conditions specified in the spec,
   164             // this class will receive a constructor.
   165             if(needValueConstructor(sc)) {
   166                 // TODO: fragile. There is no guarantee that the property name
   167                 // is in fact "value".
   168                 bean.addConstructor("value");
   169             }
   171             if(bean.javadoc==null)
   172                 addSchemaFragmentJavadoc(bean,sc);
   174             // build the body
   175             if(builder.getGlobalBinding().getFlattenClasses()==LocalScoping.NESTED)
   176                 pushClassScope(bean);
   177             else
   178                 pushClassScope(bean.parent());
   179             XSComponent oldRoot = currentRoot;
   180             CClassInfo oldBean = currentBean;
   181             currentRoot = sc;
   182             currentBean = bean;
   183             sc.visit(Ring.get(BindRed.class));
   184             currentBean = oldBean;
   185             currentRoot = oldRoot;
   186             popClassScope();
   188             // acknowledge property customization on this schema component,
   189             // since it is OK to have a customization at the point of declaration
   190             // even when no one is using it.
   191             BIProperty prop = builder.getBindInfo(sc).get(BIProperty.class);
   192             if(prop!=null)  prop.markAsAcknowledged();
   193         }
   194     }
   197     // should be instanciated only from BGMBuilder.
   198     public ClassSelector() {
   199         classBinder = new Abstractifier(new DefaultClassBinder());
   200         Ring.add(ClassBinder.class,classBinder);
   202         classScopes.push(null);  // so that the getClassFactory method returns null
   204         XSComplexType anyType = Ring.get(XSSchemaSet.class).getComplexType(WellKnownNamespace.XML_SCHEMA,"anyType");
   205         bindMap.put(anyType,new Binding(anyType,CBuiltinLeafInfo.ANYTYPE));
   206     }
   208     /** Gets the current class scope. */
   209     public final CClassInfoParent getClassScope() {
   210         assert !classScopes.isEmpty();
   211         return classScopes.peek();
   212     }
   214     public final void pushClassScope( CClassInfoParent clsFctry ) {
   215         assert clsFctry!=null;
   216         classScopes.push(clsFctry);
   217     }
   219     public final void popClassScope() {
   220         classScopes.pop();
   221     }
   223     public XSComponent getCurrentRoot() {
   224         return currentRoot;
   225     }
   227     public CClassInfo getCurrentBean() {
   228         return currentBean;
   229     }
   231     /**
   232      * Checks if the given component is bound to a class.
   233      */
   234     public final CElement isBound( XSElementDecl x, XSComponent referer ) {
   235         CElementInfo r = boundElements.get(x);
   236         if(r!=null)
   237             return r;
   238         return bindToType(x,referer);
   239     }
   241     /**
   242      * Checks if the given component is being mapped to a type.
   243      * If so, build that type and return that object.
   244      * If it is not being mapped to a type item, return null.
   245      */
   246     public CTypeInfo bindToType( XSComponent sc, XSComponent referer ) {
   247         return _bindToClass(sc,referer,false);
   248     }
   250     //
   251     // some schema components are guaranteed to map to a particular CTypeInfo.
   252     // the following versions capture those constraints in the signature
   253     // and making the bindToType invocation more type safe.
   254     //
   256     public CElement bindToType( XSElementDecl e, XSComponent referer ) {
   257         return (CElement)_bindToClass(e,referer,false);
   258     }
   260     public CClass bindToType( XSComplexType t, XSComponent referer, boolean cannotBeDelayed ) {
   261         // this assumption that a complex type always binds to a ClassInfo
   262         // does not hold for xs:anyType --- our current approach of handling
   263         // this idiosynchracy is to make sure that xs:anyType doesn't use
   264         // this codepath.
   265         return (CClass)_bindToClass(t,referer,cannotBeDelayed);
   266     }
   268     public TypeUse bindToType( XSType t, XSComponent referer ) {
   269         if(t instanceof XSSimpleType) {
   270             return Ring.get(SimpleTypeBuilder.class).build((XSSimpleType)t);
   271         } else
   272             return (CNonElement)_bindToClass(t,referer,false);
   273     }
   275     /**
   276      * The real meat of the "bindToType" code.
   277      *
   278      * @param cannotBeDelayed
   279      *      if the binding of the body of the class cannot be defered
   280      *      and needs to be done immediately. If the flag is false,
   281      *      the binding of the body will be done later, to avoid
   282      *      cyclic binding problem.
   283      * @param referer
   284      *      The component that refers to <tt>sc</tt>. This can be null,
   285      *      if figuring out the referer is too hard, in which case
   286      *      the error message might be less user friendly.
   287      */
   288     // TODO: consider getting rid of "cannotBeDelayed"
   289     CTypeInfo _bindToClass( @NotNull XSComponent sc, XSComponent referer, boolean cannotBeDelayed ) {
   290         // check if this class is already built.
   291         if(!bindMap.containsKey(sc)) {
   292             // craete a bind task
   294             // if this is a global declaration, make sure they will be generated
   295             // under a package.
   296             boolean isGlobal = false;
   297             if( sc instanceof XSDeclaration ) {
   298                 isGlobal = ((XSDeclaration)sc).isGlobal();
   299                 if( isGlobal )
   300                     pushClassScope( new CClassInfoParent.Package(
   301                         getPackage(((XSDeclaration)sc).getTargetNamespace())) );
   302             }
   304             // otherwise check if this component should become a class.
   305             CElement bean = sc.apply(classBinder);
   307             if( isGlobal )
   308                 popClassScope();
   310             if(bean==null)
   311                 return null;
   313             // can this namespace generate a class?
   314             if (bean instanceof CClassInfo) {
   315                 XSSchema os = sc.getOwnerSchema();
   316                 BISchemaBinding sb = builder.getBindInfo(os).get(BISchemaBinding.class);
   317                 if(sb!=null && !sb.map) {
   318                     // nope
   319                     getErrorReporter().error(sc.getLocator(),
   320                         Messages.ERR_REFERENCE_TO_NONEXPORTED_CLASS, sc.apply( new ComponentNameFunction() ) );
   321                     getErrorReporter().error(sb.getLocation(),
   322                         Messages.ERR_REFERENCE_TO_NONEXPORTED_CLASS_MAP_FALSE, os.getTargetNamespace() );
   323                     if(referer!=null)
   324                         getErrorReporter().error(referer.getLocator(),
   325                             Messages.ERR_REFERENCE_TO_NONEXPORTED_CLASS_REFERER, referer.apply( new ComponentNameFunction() ) );
   326                 }
   327             }
   330             queueBuild( sc, bean );
   331         }
   333         Binding bind = bindMap.get(sc);
   334         if( cannotBeDelayed )
   335             bind.build();
   337         return bind.bean;
   338     }
   340     /**
   341      * Runs all the pending build tasks.
   342      */
   343     public void executeTasks() {
   344         while( bindQueue.size()!=0 )
   345             bindQueue.pop().build();
   346     }
   355     /**
   356      * Determines if the given component needs to have a value
   357      * constructor (a constructor that takes a parmater.) on ObjectFactory.
   358      */
   359     private boolean needValueConstructor( XSComponent sc ) {
   360         if(!(sc instanceof XSElementDecl))  return false;
   362         XSElementDecl decl = (XSElementDecl)sc;
   363         if(!decl.getType().isSimpleType())  return false;
   365         return true;
   366     }
   368     private static final String[] reservedClassNames = new String[]{"ObjectFactory"};
   370     public void queueBuild( XSComponent sc, CElement bean ) {
   371         // it is an error if the same component is built twice,
   372         // or the association is modified.
   373         Binding b = new Binding(sc,bean);
   374         bindQueue.push(b);
   375         Binding old = bindMap.put(sc, b);
   376         assert old==null || old.bean==bean;
   377     }
   380     /**
   381      * Copies a schema fragment into the javadoc of the generated class.
   382      */
   383     private void addSchemaFragmentJavadoc( CClassInfo bean, XSComponent sc ) {
   385         // first, pick it up from <documentation> if any.
   386         String doc = builder.getBindInfo(sc).getDocumentation();
   387         if(doc!=null)
   388             append(bean, doc);
   390         // then the description of where this component came from
   391         Locator loc = sc.getLocator();
   392         String fileName = null;
   393         if(loc!=null) {
   394             fileName = loc.getPublicId();
   395             if(fileName==null)
   396                 fileName = loc.getSystemId();
   397         }
   398         if(fileName==null)  fileName="";
   400         String lineNumber=Messages.format( Messages.JAVADOC_LINE_UNKNOWN);
   401         if(loc!=null && loc.getLineNumber()!=-1)
   402             lineNumber = String.valueOf(loc.getLineNumber());
   404         String componentName = sc.apply( new ComponentNameFunction() );
   405         String jdoc = Messages.format( Messages.JAVADOC_HEADING, componentName, fileName, lineNumber );
   406         append(bean,jdoc);
   408         // then schema fragment
   409         StringWriter out = new StringWriter();
   410         out.write("<pre>\n");
   411         SchemaWriter sw = new SchemaWriter(new JavadocEscapeWriter(out));
   412         sc.visit(sw);
   413         out.write("</pre>");
   414         append(bean,out.toString());
   415     }
   417     private void append(CClassInfo bean, String doc) {
   418         if(bean.javadoc==null)
   419             bean.javadoc = doc+'\n';
   420         else
   421             bean.javadoc += '\n'+doc+'\n';
   422     }
   425     /**
   426      * Set of package names that are tested (set of <code>String</code>s.)
   427      *
   428      * This set is used to avoid duplicating "incorrect package name"
   429      * errors.
   430      */
   431     private static Set<String> checkedPackageNames = new HashSet<String>();
   433     /**
   434      * Gets the Java package to which classes from
   435      * this namespace should go.
   436      *
   437      * <p>
   438      * Usually, the getOuterClass method should be used
   439      * to determine where to put a class.
   440      */
   441     public JPackage getPackage(String targetNamespace) {
   442         XSSchema s = Ring.get(XSSchemaSet.class).getSchema(targetNamespace);
   444         BISchemaBinding sb =
   445             builder.getBindInfo(s).get(BISchemaBinding.class);
   446         if(sb!=null)    sb.markAsAcknowledged();
   448         String name = null;
   450         // "-p" takes precedence over everything else
   451         if( builder.defaultPackage1 != null )
   452             name = builder.defaultPackage1;
   454         // use the <jaxb:package> customization
   455         if( name == null && sb!=null && sb.getPackageName()!=null )
   456             name = sb.getPackageName();
   458         // the JAX-RPC option goes below the <jaxb:package>
   459         if( name == null && builder.defaultPackage2 != null )
   460             name = builder.defaultPackage2;
   462         // generate the package name from the targetNamespace
   463         if( name == null )
   464             name = builder.getNameConverter().toPackageName( targetNamespace );
   466         // hardcode a package name because the code doesn't compile
   467         // if it generated into the default java package
   468         if( name == null )
   469             name = "generated"; // the last resort
   472         // check if the package name is a valid name.
   473         if( checkedPackageNames.add(name) ) {
   474             // this is the first time we hear about this package name.
   475             if( !JJavaName.isJavaPackageName(name) )
   476                 // TODO: s.getLocator() is not very helpful.
   477                 // ideally, we'd like to use the locator where this package name
   478                 // comes from.
   479                 getErrorReporter().error(s.getLocator(),
   480                     Messages.ERR_INCORRECT_PACKAGE_NAME, targetNamespace, name );
   481         }
   483         return Ring.get(JCodeModel.class)._package(name);
   484     }
   485 }

mercurial