com.sun.tools.xjc.reader.TypeUtil Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.tools.xjc.reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JType;
import com.sun.tools.xjc.ErrorReceiver;
import org.xml.sax.Locator;
import org.xml.sax.SAXParseException;
/**
* Type-related utility methods.
*
* @author
* Kohsuke KAWAGUCHI
*/
public class TypeUtil {
/**
* Computes the common base type of two types.
*
* @param types
* set of {@link JType} objects.
*/
public static JType getCommonBaseType( JCodeModel codeModel, Collection types ) {
return getCommonBaseType( codeModel, types.toArray(new JType[types.size()]) );
}
/**
* Computes the common base type of types.
*
* TODO: this is a very interesting problem. Since one type has possibly
* multiple base types, it's not an easy problem.
* The current implementation is very naive.
*
* To make the result deterministic across differente JVMs, we have to
* use a Set whose ordering is deterministic.
*/
public static JType getCommonBaseType(JCodeModel codeModel, JType... t) {
// first, eliminate duplicates.
Set uniqueTypes = new TreeSet(typeComparator);
for (JType type : t)
uniqueTypes.add(type);
// if this yields only one type. return now.
// this is the only case where we can return a primitive type
// from this method
if (uniqueTypes.size() == 1)
return uniqueTypes.iterator().next();
// assertion failed. nullType can be used only under a very special circumstance
assert !uniqueTypes.isEmpty();
// the null type doesn't need to be taken into account.
uniqueTypes.remove(codeModel.NULL);
// box all the types and compute the intersection of all types
Set s = null;
for (JType type : uniqueTypes) {
JClass cls = type.boxify();
if (s == null)
s = getAssignableTypes(cls);
else
s.retainAll(getAssignableTypes(cls));
}
// any JClass can be casted to Object, so make sure it's always there
s.add( codeModel.ref(Object.class));
// refine 's' by removing "lower" types.
// for example, if we have both java.lang.Object and
// java.io.InputStream, then we don't want to use java.lang.Object.
JClass[] raw = s.toArray(new JClass[s.size()]);
s.clear();
for (int i = 0; i < raw.length; i++) { // for each raw[i]
int j;
for (j = 0; j < raw.length; j++) { // see if raw[j] "includes" raw[i]
if (i == j)
continue;
if (raw[i].isAssignableFrom(raw[j]))
break; // raw[j] is derived from raw[i], hence j includes i.
}
if (j == raw.length)
// no other type inclueds raw[i]. remember this value.
s.add(raw[i]);
}
assert !s.isEmpty(); // since at least java.lang.Object has to be there
// we now pick the candidate for the return type
JClass result = pickOne(s);
// finally, sometimes this method is used to compute the base type of types like
// JAXBElement, JAXBElement, and JAXBElement.
// for those inputs, at this point result=JAXBElement.
//
// here, we'll try to figure out the parameterization
// so that we can return JAXBElement instead of just "JAXBElement".
if(result.isParameterized())
return result;
// for each uniqueType we store the list of base type parameterization
List> parameters = new ArrayList>(uniqueTypes.size());
int paramLen = -1;
for (JType type : uniqueTypes) {
JClass cls = type.boxify();
JClass bp = cls.getBaseClass(result);
// if there's no parameterization in the base type,
// we won't do any better than . Thus no point in trying to figure out the parameterization.
// just return the base type.
if(bp.equals(result))
return result;
assert bp.isParameterized();
List tp = bp.getTypeParameters();
parameters.add(tp);
assert paramLen==-1 || paramLen==tp.size();
// since 'bp' always is a parameterized version of 'result', it should always
// have the same number of parameters.
paramLen = tp.size();
}
List paramResult = new ArrayList();
List argList = new ArrayList(parameters.size());
// for each type parameter compute the common base type
for( int i=0; i list : parameters)
argList.add(list.get(i));
// compute the lower bound.
JClass bound = (JClass)getCommonBaseType(codeModel,argList);
boolean allSame = true;
for (JClass a : argList)
allSame &= a.equals(bound);
if(!allSame)
bound = bound.wildcard();
paramResult.add(bound);
}
return result.narrow(paramResult);
}
private static JClass pickOne(Set s) {
// we may have more than one candidates at this point.
// any user-defined generated types should have
// precedence over system-defined existing types.
//
// so try to return such a type if any.
for (JClass c : s)
if (c instanceof JDefinedClass)
return c;
// we can do more if we like. for example,
// we can avoid types in the RI runtime.
// but for now, just return the first one.
return s.iterator().next();
}
private static Set getAssignableTypes( JClass t ) {
Set r = new TreeSet(typeComparator);
getAssignableTypes(t,r);
return r;
}
/**
* Returns the set of all classes/interfaces that a given type
* implements/extends, including itself.
*
* For example, if you pass java.io.FilterInputStream, then the returned
* set will contain java.lang.Object, java.lang.InputStream, and
* java.lang.FilterInputStream.
*/
private static void getAssignableTypes( JClass t, Set s ) {
if(!s.add(t))
return;
// add its raw type
s.add(t.erasure());
// if this type is added for the first time,
// recursively process the super class.
JClass _super = t._extends();
if(_super!=null)
getAssignableTypes(_super,s);
// recursively process all implemented interfaces
Iterator itr = t._implements();
while(itr.hasNext())
getAssignableTypes(itr.next(),s);
}
/**
* Obtains a {@link JType} object for the string representation
* of a type.
*/
public static JType getType( JCodeModel codeModel,
String typeName, ErrorReceiver errorHandler, Locator errorSource ) {
try {
return codeModel.parseType(typeName);
} catch( ClassNotFoundException ee ) {
// make it a warning
errorHandler.warning( new SAXParseException(
Messages.ERR_CLASS_NOT_FOUND.format(typeName)
,errorSource));
// recover by assuming that it's a class that derives from Object
return codeModel.directClass(typeName);
}
}
/**
* Compares {@link JType} objects by their names.
*/
private static final Comparator typeComparator = new Comparator() {
public int compare(JType t1, JType t2) {
return t1.fullName().compareTo(t2.fullName());
}
};
}