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