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 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 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: }