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