aoqi@0: /*
aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0:
aoqi@0: package com.sun.tools.internal.xjc.reader;
aoqi@0:
aoqi@0: import java.util.ArrayList;
aoqi@0: import java.util.Collection;
aoqi@0: import java.util.Comparator;
aoqi@0: import java.util.Iterator;
aoqi@0: import java.util.List;
aoqi@0: import java.util.Set;
aoqi@0: import java.util.TreeSet;
aoqi@0:
aoqi@0: import com.sun.codemodel.internal.JClass;
aoqi@0: import com.sun.codemodel.internal.JCodeModel;
aoqi@0: import com.sun.codemodel.internal.JDefinedClass;
aoqi@0: import com.sun.codemodel.internal.JType;
aoqi@0: import com.sun.tools.internal.xjc.ErrorReceiver;
aoqi@0:
aoqi@0: import org.xml.sax.Locator;
aoqi@0: import org.xml.sax.SAXParseException;
aoqi@0:
aoqi@0: /**
aoqi@0: * Type-related utility methods.
aoqi@0: *
aoqi@0: * @author
aoqi@0: * Kohsuke KAWAGUCHI
aoqi@0: */
aoqi@0: public class TypeUtil {
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Computes the common base type of two types.
aoqi@0: *
aoqi@0: * @param types
aoqi@0: * set of {@link JType} objects.
aoqi@0: */
aoqi@0: public static JType getCommonBaseType( JCodeModel codeModel, Collection extends JType> types ) {
aoqi@0: return getCommonBaseType( codeModel, types.toArray(new JType[types.size()]) );
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Computes the common base type of types.
aoqi@0: *
aoqi@0: * TODO: this is a very interesting problem. Since one type has possibly
aoqi@0: * multiple base types, it's not an easy problem.
aoqi@0: * The current implementation is very naive.
aoqi@0: *
aoqi@0: * To make the result deterministic across differente JVMs, we have to
aoqi@0: * use a Set whose ordering is deterministic.
aoqi@0: */
aoqi@0: public static JType getCommonBaseType(JCodeModel codeModel, JType... t) {
aoqi@0: // first, eliminate duplicates.
aoqi@0: Set uniqueTypes = new TreeSet(typeComparator);
aoqi@0: for (JType type : t)
aoqi@0: uniqueTypes.add(type);
aoqi@0:
aoqi@0: // if this yields only one type. return now.
aoqi@0: // this is the only case where we can return a primitive type
aoqi@0: // from this method
aoqi@0: if (uniqueTypes.size() == 1)
aoqi@0: return uniqueTypes.iterator().next();
aoqi@0:
aoqi@0: // assertion failed. nullType can be used only under a very special circumstance
aoqi@0: assert !uniqueTypes.isEmpty();
aoqi@0:
aoqi@0: // the null type doesn't need to be taken into account.
aoqi@0: uniqueTypes.remove(codeModel.NULL);
aoqi@0:
aoqi@0: // box all the types and compute the intersection of all types
aoqi@0: Set s = null;
aoqi@0:
aoqi@0: for (JType type : uniqueTypes) {
aoqi@0: JClass cls = type.boxify();
aoqi@0:
aoqi@0: if (s == null)
aoqi@0: s = getAssignableTypes(cls);
aoqi@0: else
aoqi@0: s.retainAll(getAssignableTypes(cls));
aoqi@0: }
aoqi@0:
aoqi@0: // any JClass can be casted to Object, so make sure it's always there
aoqi@0: s.add( codeModel.ref(Object.class));
aoqi@0:
aoqi@0: // refine 's' by removing "lower" types.
aoqi@0: // for example, if we have both java.lang.Object and
aoqi@0: // java.io.InputStream, then we don't want to use java.lang.Object.
aoqi@0:
aoqi@0: JClass[] raw = s.toArray(new JClass[s.size()]);
aoqi@0: s.clear();
aoqi@0:
aoqi@0: for (int i = 0; i < raw.length; i++) { // for each raw[i]
aoqi@0: int j;
aoqi@0: for (j = 0; j < raw.length; j++) { // see if raw[j] "includes" raw[i]
aoqi@0: if (i == j)
aoqi@0: continue;
aoqi@0:
aoqi@0: if (raw[i].isAssignableFrom(raw[j]))
aoqi@0: break; // raw[j] is derived from raw[i], hence j includes i.
aoqi@0: }
aoqi@0:
aoqi@0: if (j == raw.length)
aoqi@0: // no other type inclueds raw[i]. remember this value.
aoqi@0: s.add(raw[i]);
aoqi@0: }
aoqi@0:
aoqi@0: assert !s.isEmpty(); // since at least java.lang.Object has to be there
aoqi@0:
aoqi@0: // we now pick the candidate for the return type
aoqi@0: JClass result = pickOne(s);
aoqi@0:
aoqi@0: // finally, sometimes this method is used to compute the base type of types like
aoqi@0: // JAXBElement, JAXBElement, and JAXBElement.
aoqi@0: // for those inputs, at this point result=JAXBElement.
aoqi@0: //
aoqi@0: // here, we'll try to figure out the parameterization
aoqi@0: // so that we can return JAXBElement extends D> instead of just "JAXBElement".
aoqi@0: if(result.isParameterized())
aoqi@0: return result;
aoqi@0:
aoqi@0: // for each uniqueType we store the list of base type parameterization
aoqi@0: List> parameters = new ArrayList>(uniqueTypes.size());
aoqi@0: int paramLen = -1;
aoqi@0:
aoqi@0: for (JType type : uniqueTypes) {
aoqi@0: JClass cls = type.boxify();
aoqi@0: JClass bp = cls.getBaseClass(result);
aoqi@0: // if there's no parameterization in the base type,
aoqi@0: // we won't do any better than >. Thus no point in trying to figure out the parameterization.
aoqi@0: // just return the base type.
aoqi@0: if(bp.equals(result))
aoqi@0: return result;
aoqi@0:
aoqi@0: assert bp.isParameterized();
aoqi@0: List tp = bp.getTypeParameters();
aoqi@0: parameters.add(tp);
aoqi@0:
aoqi@0: assert paramLen==-1 || paramLen==tp.size();
aoqi@0: // since 'bp' always is a parameterized version of 'result', it should always
aoqi@0: // have the same number of parameters.
aoqi@0: paramLen = tp.size();
aoqi@0: }
aoqi@0:
aoqi@0: List paramResult = new ArrayList();
aoqi@0: List argList = new ArrayList(parameters.size());
aoqi@0: // for each type parameter compute the common base type
aoqi@0: for( int i=0; i list : parameters)
aoqi@0: argList.add(list.get(i));
aoqi@0:
aoqi@0: // compute the lower bound.
aoqi@0: JClass bound = (JClass)getCommonBaseType(codeModel,argList);
aoqi@0: boolean allSame = true;
aoqi@0: for (JClass a : argList)
aoqi@0: allSame &= a.equals(bound);
aoqi@0: if(!allSame)
aoqi@0: bound = bound.wildcard();
aoqi@0:
aoqi@0: paramResult.add(bound);
aoqi@0: }
aoqi@0:
aoqi@0: return result.narrow(paramResult);
aoqi@0: }
aoqi@0:
aoqi@0: private static JClass pickOne(Set s) {
aoqi@0: // we may have more than one candidates at this point.
aoqi@0: // any user-defined generated types should have
aoqi@0: // precedence over system-defined existing types.
aoqi@0: //
aoqi@0: // so try to return such a type if any.
aoqi@0: for (JClass c : s)
aoqi@0: if (c instanceof JDefinedClass)
aoqi@0: return c;
aoqi@0:
aoqi@0: // we can do more if we like. for example,
aoqi@0: // we can avoid types in the RI runtime.
aoqi@0: // but for now, just return the first one.
aoqi@0: return s.iterator().next();
aoqi@0: }
aoqi@0:
aoqi@0: private static Set getAssignableTypes( JClass t ) {
aoqi@0: Set r = new TreeSet(typeComparator);
aoqi@0: getAssignableTypes(t,r);
aoqi@0: return r;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Returns the set of all classes/interfaces that a given type
aoqi@0: * implements/extends, including itself.
aoqi@0: *
aoqi@0: * For example, if you pass java.io.FilterInputStream, then the returned
aoqi@0: * set will contain java.lang.Object, java.lang.InputStream, and
aoqi@0: * java.lang.FilterInputStream.
aoqi@0: */
aoqi@0: private static void getAssignableTypes( JClass t, Set s ) {
aoqi@0: if(!s.add(t))
aoqi@0: return;
aoqi@0:
aoqi@0: // add its raw type
aoqi@0: s.add(t.erasure());
aoqi@0:
aoqi@0: // if this type is added for the first time,
aoqi@0: // recursively process the super class.
aoqi@0: JClass _super = t._extends();
aoqi@0: if(_super!=null)
aoqi@0: getAssignableTypes(_super,s);
aoqi@0:
aoqi@0: // recursively process all implemented interfaces
aoqi@0: Iterator itr = t._implements();
aoqi@0: while(itr.hasNext())
aoqi@0: getAssignableTypes(itr.next(),s);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Obtains a {@link JType} object for the string representation
aoqi@0: * of a type.
aoqi@0: */
aoqi@0: public static JType getType( JCodeModel codeModel,
aoqi@0: String typeName, ErrorReceiver errorHandler, Locator errorSource ) {
aoqi@0:
aoqi@0: try {
aoqi@0: return codeModel.parseType(typeName);
aoqi@0: } catch( ClassNotFoundException ee ) {
aoqi@0:
aoqi@0: // make it a warning
aoqi@0: errorHandler.warning( new SAXParseException(
aoqi@0: Messages.ERR_CLASS_NOT_FOUND.format(typeName)
aoqi@0: ,errorSource));
aoqi@0:
aoqi@0: // recover by assuming that it's a class that derives from Object
aoqi@0: return codeModel.directClass(typeName);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Compares {@link JType} objects by their names.
aoqi@0: */
aoqi@0: private static final Comparator typeComparator = new Comparator() {
aoqi@0: public int compare(JType t1, JType t2) {
aoqi@0: return t1.fullName().compareTo(t2.fullName());
aoqi@0: }
aoqi@0: };
aoqi@0: }