src/share/jaxws_classes/com/sun/xml/internal/bind/v2/model/impl/ModelBuilder.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 450
b0c2840e2513
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

     1 /*
     2  * Copyright (c) 1997, 2012, 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.xml.internal.bind.v2.model.impl;
    28 import java.lang.reflect.ParameterizedType;
    29 import java.lang.reflect.Type;
    30 import java.util.HashMap;
    31 import java.util.Map;
    32 import java.util.logging.Level;
    33 import java.util.logging.Logger;
    35 import javax.xml.bind.JAXBElement;
    36 import javax.xml.bind.annotation.XmlAttachmentRef;
    37 import javax.xml.bind.annotation.XmlRegistry;
    38 import javax.xml.bind.annotation.XmlSchema;
    39 import javax.xml.bind.annotation.XmlSeeAlso;
    40 import javax.xml.bind.annotation.XmlTransient;
    41 import javax.xml.namespace.QName;
    43 import com.sun.xml.internal.bind.util.Which;
    44 import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
    45 import com.sun.xml.internal.bind.v2.model.annotation.ClassLocatable;
    46 import com.sun.xml.internal.bind.v2.model.annotation.Locatable;
    47 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
    48 import com.sun.xml.internal.bind.v2.model.core.ErrorHandler;
    49 import com.sun.xml.internal.bind.v2.model.core.LeafInfo;
    50 import com.sun.xml.internal.bind.v2.model.core.NonElement;
    51 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
    52 import com.sun.xml.internal.bind.v2.model.core.PropertyKind;
    53 import com.sun.xml.internal.bind.v2.model.core.Ref;
    54 import com.sun.xml.internal.bind.v2.model.core.RegistryInfo;
    55 import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
    56 import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
    57 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
    58 import com.sun.xml.internal.bind.v2.model.runtime.RuntimePropertyInfo;
    59 import com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException;
    60 import com.sun.xml.internal.bind.WhiteSpaceProcessor;
    62 /**
    63  * Builds a {@link TypeInfoSet} (a set of JAXB properties)
    64  * by using {@link ElementInfoImpl} and {@link ClassInfoImpl}.
    65  * from annotated Java classes.
    66  *
    67  * <p>
    68  * This class uses {@link Navigator} and {@link AnnotationReader} to
    69  * work with arbitrary annotation source and arbitrary Java model.
    70  * For this purpose this class is parameterized.
    71  *
    72  * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
    73  */
    74 public class ModelBuilder<T,C,F,M> implements ModelBuilderI<T,C,F,M> {
    75     private static final Logger logger;
    77     /**
    78      * {@link TypeInfo}s that are built will go into this set.
    79      */
    80     final TypeInfoSetImpl<T,C,F,M> typeInfoSet;
    82     public final AnnotationReader<T,C,F,M> reader;
    84     public final Navigator<T,C,F,M> nav;
    86     /**
    87      * Used to detect collisions among global type names.
    88      */
    89     private final Map<QName,TypeInfo> typeNames = new HashMap<QName,TypeInfo>();
    91     /**
    92      * JAXB doesn't want to use namespaces unless we are told to, but WS-I BP
    93      * conformace requires JAX-RPC to always use a non-empty namespace URI.
    94      * (see http://www.ws-i.org/Profiles/BasicProfile-1.0-2004-04-16.html#WSDLTYPES R2105)
    95      *
    96      * <p>
    97      * To work around this issue, we allow the use of the empty namespaces to be
    98      * replaced by a particular designated namespace URI.
    99      *
   100      * <p>
   101      * This field keeps the value of that replacing namespace URI.
   102      * When there's no replacement, this field is set to "".
   103      */
   104     public final String defaultNsUri;
   107     /**
   108      * Packages whose registries are already added.
   109      */
   110     /*package*/ final Map<String,RegistryInfoImpl<T,C,F,M>> registries
   111             = new HashMap<String,RegistryInfoImpl<T,C,F,M>>();
   113     private final Map<C,C> subclassReplacements;
   115     /**
   116      * @see #setErrorHandler
   117      */
   118     private ErrorHandler errorHandler;
   119     private boolean hadError;
   121     /**
   122      * Set to true if the model includes {@link XmlAttachmentRef}. JAX-WS
   123      * needs to know this information.
   124      */
   125     public boolean hasSwaRef;
   127     private final ErrorHandler proxyErrorHandler = new ErrorHandler() {
   128         public void error(IllegalAnnotationException e) {
   129             reportError(e);
   130         }
   131     };
   133     public ModelBuilder(
   134             AnnotationReader<T, C, F, M> reader,
   135             Navigator<T, C, F, M> navigator,
   136             Map<C, C> subclassReplacements,
   137             String defaultNamespaceRemap
   138     ) {
   140         this.reader = reader;
   141         this.nav = navigator;
   142         this.subclassReplacements = subclassReplacements;
   143         if(defaultNamespaceRemap==null)
   144             defaultNamespaceRemap = "";
   145         this.defaultNsUri = defaultNamespaceRemap;
   146         reader.setErrorHandler(proxyErrorHandler);
   147         typeInfoSet = createTypeInfoSet();
   148     }
   150     /**
   151      * Makes sure that we are running with 2.1 JAXB API,
   152      * and report an error if not.
   153      */
   154     static {
   155         try {
   156             XmlSchema s = null;
   157             s.location();
   158         } catch (NullPointerException e) {
   159             // as epxected
   160         } catch (NoSuchMethodError e) {
   161             // this is not a 2.1 API. Where is it being loaded from?
   162             Messages res;
   163             if (SecureLoader.getClassClassLoader(XmlSchema.class) == null) {
   164                 res = Messages.INCOMPATIBLE_API_VERSION_MUSTANG;
   165             } else {
   166                 res = Messages.INCOMPATIBLE_API_VERSION;
   167             }
   169             throw new LinkageError( res.format(
   170                 Which.which(XmlSchema.class),
   171                 Which.which(ModelBuilder.class)
   172             ));
   173         }
   174     }
   176     /**
   177      * Makes sure that we don't have conflicting 1.0 runtime,
   178      * and report an error if we do.
   179      */
   180     static {
   181         try {
   182             WhiteSpaceProcessor.isWhiteSpace("xyz");
   183         } catch (NoSuchMethodError e) {
   184             // we seem to be getting 1.0 runtime
   185             throw new LinkageError( Messages.RUNNING_WITH_1_0_RUNTIME.format(
   186                 Which.which(WhiteSpaceProcessor.class),
   187                 Which.which(ModelBuilder.class)
   188             ));
   189         }
   190     }
   192     /**
   193      * Logger init
   194      */
   195     static {
   196         logger = Logger.getLogger(ModelBuilder.class.getName());
   197     }
   199     protected TypeInfoSetImpl<T,C,F,M> createTypeInfoSet() {
   200         return new TypeInfoSetImpl<T,C,F,M>(nav,reader,BuiltinLeafInfoImpl.createLeaves(nav));
   201     }
   203     /**
   204      * Builds a JAXB {@link ClassInfo} model from a given class declaration
   205      * and adds that to this model owner.
   206      *
   207      * <p>
   208      * Return type is either {@link ClassInfo} or {@link LeafInfo} (for types like
   209      * {@link String} or {@link Enum}-derived ones)
   210      */
   211     public NonElement<T,C> getClassInfo( C clazz, Locatable upstream ) {
   212         return getClassInfo(clazz,false,upstream);
   213     }
   215     /**
   216      * For limited cases where the caller needs to search for a super class.
   217      * This is necessary because we don't want {@link #subclassReplacements}
   218      * to kick in for the super class search, which will cause infinite recursion.
   219      */
   220     public NonElement<T,C> getClassInfo( C clazz, boolean searchForSuperClass, Locatable upstream ) {
   221         assert clazz!=null;
   222         NonElement<T,C> r = typeInfoSet.getClassInfo(clazz);
   223         if(r!=null)
   224             return r;
   226         if(nav.isEnum(clazz)) {
   227             EnumLeafInfoImpl<T,C,F,M> li = createEnumLeafInfo(clazz,upstream);
   228             typeInfoSet.add(li);
   229             r = li;
   230             addTypeName(r);
   231         } else {
   232             boolean isReplaced = subclassReplacements.containsKey(clazz);
   233             if(isReplaced && !searchForSuperClass) {
   234                 // handle it as if the replacement was specified
   235                 r = getClassInfo(subclassReplacements.get(clazz),upstream);
   236             } else
   237             if(reader.hasClassAnnotation(clazz,XmlTransient.class) || isReplaced) {
   238                 // handle it as if the base class was specified
   239                 r = getClassInfo( nav.getSuperClass(clazz), searchForSuperClass,
   240                         new ClassLocatable<C>(upstream,clazz,nav) );
   241             } else {
   242                 ClassInfoImpl<T,C,F,M> ci = createClassInfo(clazz,upstream);
   243                 typeInfoSet.add(ci);
   245                 // compute the closure by eagerly expanding references
   246                 for( PropertyInfo<T,C> p : ci.getProperties() ) {
   247                     if(p.kind()== PropertyKind.REFERENCE) {
   248                         // make sure that we have a registry for this package
   249                         addToRegistry(clazz, (Locatable) p);
   250                         Class[] prmzdClasses = getParametrizedTypes(p);
   251                         if (prmzdClasses != null) {
   252                             for (Class prmzdClass : prmzdClasses) {
   253                                 if (prmzdClass != clazz) {
   254                                     addToRegistry((C) prmzdClass, (Locatable) p);
   255                                 }
   256                             }
   257                         }
   258                     }
   260                     for( TypeInfo<T,C> t : p.ref() )
   261                         ; // just compute a reference should be suffice
   262                 }
   263                 ci.getBaseClass(); // same as above.
   265                 r = ci;
   266                 addTypeName(r);
   267             }
   268         }
   271         // more reference closure expansion. @XmlSeeAlso
   272         XmlSeeAlso sa = reader.getClassAnnotation(XmlSeeAlso.class, clazz, upstream);
   273         if(sa!=null) {
   274             for( T t : reader.getClassArrayValue(sa,"value") ) {
   275                 getTypeInfo(t,(Locatable)sa);
   276             }
   277         }
   280         return r;
   281     }
   283     /**
   284      * Adding package's ObjectFactory methods to registry
   285      * @param clazz which package will be used
   286      * @param p location
   287      */
   288     private void addToRegistry(C clazz, Locatable p) {
   289         String pkg = nav.getPackageName(clazz);
   290         if (!registries.containsKey(pkg)) {
   291             // insert the package's object factory
   292             C c = nav.loadObjectFactory(clazz, pkg);
   293             if (c != null)
   294                 addRegistry(c, p);
   295         }
   296     }
   298     /**
   299      * Getting parametrized classes of {@code JAXBElement<...>} property
   300      * @param p property which parametrized types we will try to get
   301      * @return null - if it's not JAXBElement property, or it's not parametrized, and array of parametrized classes in other case
   302      */
   303     private Class[] getParametrizedTypes(PropertyInfo p) {
   304         try {
   305             Type pType = ((RuntimePropertyInfo) p).getIndividualType();
   306             if (pType instanceof ParameterizedType) {
   307                 ParameterizedType prmzdType = (ParameterizedType) pType;
   308                 if (prmzdType.getRawType() == JAXBElement.class) {
   309                     Type[] actualTypes = prmzdType.getActualTypeArguments();
   310                     Class[] result = new Class[actualTypes.length];
   311                     for (int i = 0; i < actualTypes.length; i++) {
   312                         result[i] = (Class) actualTypes[i];
   313                     }
   314                     return result;
   315                 }
   316             }
   317         } catch (Exception e) {
   318             logger.log(Level.FINE, "Error in ModelBuilder.getParametrizedTypes. " + e.getMessage());
   319         }
   320         return null;
   321     }
   323     /**
   324      * Checks the uniqueness of the type name.
   325      */
   326     private void addTypeName(NonElement<T, C> r) {
   327         QName t = r.getTypeName();
   328         if(t==null)     return;
   330         TypeInfo old = typeNames.put(t,r);
   331         if(old!=null) {
   332             // collision
   333             reportError(new IllegalAnnotationException(
   334                     Messages.CONFLICTING_XML_TYPE_MAPPING.format(r.getTypeName()),
   335                     old, r ));
   336         }
   337     }
   339     /**
   340      * Have the builder recognize the type (if it hasn't done so yet),
   341      * and returns a {@link NonElement} that represents it.
   342      *
   343      * @return
   344      *      always non-null.
   345      */
   346     public NonElement<T,C> getTypeInfo(T t,Locatable upstream) {
   347         NonElement<T,C> r = typeInfoSet.getTypeInfo(t);
   348         if(r!=null)     return r;
   350         if(nav.isArray(t)) { // no need for checking byte[], because above typeInfoset.getTypeInfo() would return non-null
   351             ArrayInfoImpl<T,C,F,M> ai =
   352                 createArrayInfo(upstream, t);
   353             addTypeName(ai);
   354             typeInfoSet.add(ai);
   355             return ai;
   356         }
   358         C c = nav.asDecl(t);
   359         assert c!=null : t.toString()+" must be a leaf, but we failed to recognize it.";
   360         return getClassInfo(c,upstream);
   361     }
   363     /**
   364      * This method is used to add a root reference to a model.
   365      */
   366     public NonElement<T,C> getTypeInfo(Ref<T,C> ref) {
   367         // TODO: handle XmlValueList
   368         assert !ref.valueList;
   369         C c = nav.asDecl(ref.type);
   370         if(c!=null && reader.getClassAnnotation(XmlRegistry.class,c,null/*TODO: is this right?*/)!=null) {
   371             if(!registries.containsKey(nav.getPackageName(c)))
   372                 addRegistry(c,null);
   373             return null;    // TODO: is this correct?
   374         } else
   375             return getTypeInfo(ref.type,null);
   376     }
   379     protected EnumLeafInfoImpl<T,C,F,M> createEnumLeafInfo(C clazz,Locatable upstream) {
   380         return new EnumLeafInfoImpl<T,C,F,M>(this,upstream,clazz,nav.use(clazz));
   381     }
   383     protected ClassInfoImpl<T,C,F,M> createClassInfo(C clazz, Locatable upstream ) {
   384         return new ClassInfoImpl<T,C,F,M>(this,upstream,clazz);
   385     }
   387     protected ElementInfoImpl<T,C,F,M> createElementInfo(
   388         RegistryInfoImpl<T,C,F,M> registryInfo, M m) throws IllegalAnnotationException {
   389         return new ElementInfoImpl<T,C,F,M>(this,registryInfo,m);
   390     }
   392     protected ArrayInfoImpl<T,C,F,M> createArrayInfo(Locatable upstream, T arrayType) {
   393         return new ArrayInfoImpl<T, C, F, M>(this,upstream,arrayType);
   394     }
   397     /**
   398      * Visits a class with {@link XmlRegistry} and records all the element mappings
   399      * in it.
   400      */
   401     public RegistryInfo<T,C> addRegistry(C registryClass, Locatable upstream ) {
   402         return new RegistryInfoImpl<T,C,F,M>(this,upstream,registryClass);
   403     }
   405     /**
   406      * Gets a {@link RegistryInfo} for the given package.
   407      *
   408      * @return
   409      *      null if no registry exists for the package.
   410      *      unlike other getXXX methods on this class,
   411      *      this method is side-effect free.
   412      */
   413     public RegistryInfo<T,C> getRegistry(String packageName) {
   414         return registries.get(packageName);
   415     }
   417     private boolean linked;
   419     /**
   420      * Called after all the classes are added to the type set
   421      * to "link" them together.
   422      *
   423      * <p>
   424      * Don't expose implementation classes in the signature.
   425      *
   426      * @return
   427      *      fully built {@link TypeInfoSet} that represents the model,
   428      *      or null if there was an error.
   429      */
   430     public TypeInfoSet<T,C,F,M> link() {
   432         assert !linked;
   433         linked = true;
   435         for( ElementInfoImpl ei : typeInfoSet.getAllElements() )
   436             ei.link();
   438         for( ClassInfoImpl ci : typeInfoSet.beans().values() )
   439             ci.link();
   441         for( EnumLeafInfoImpl li : typeInfoSet.enums().values() )
   442             li.link();
   444         if(hadError)
   445             return null;
   446         else
   447             return typeInfoSet;
   448     }
   450 //
   451 //
   452 // error handling
   453 //
   454 //
   456     /**
   457      * Sets the error handler that receives errors discovered during the model building.
   458      *
   459      * @param errorHandler
   460      *      can be null.
   461      */
   462     public void setErrorHandler(ErrorHandler errorHandler) {
   463         this.errorHandler = errorHandler;
   464     }
   466     public final void reportError(IllegalAnnotationException e) {
   467         hadError = true;
   468         if(errorHandler!=null)
   469             errorHandler.error(e);
   470     }
   472     public boolean isReplaced(C sc) {
   473         return subclassReplacements.containsKey(sc);
   474     }
   476     @Override
   477     public Navigator<T, C, F, M> getNavigator() {
   478         return nav;
   479     }
   481     @Override
   482     public AnnotationReader<T, C, F, M> getReader() {
   483         return reader;
   484     }
   485 }

mercurial