/**
* Copyright (C) 2006-2017 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.reflect.factory;
import spoon.reflect.code.CtNewClass;
import spoon.reflect.declaration.CtAnnotation;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtConstructor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtFormalTypeDeclarer;
import spoon.reflect.declaration.CtInterface;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.reference.CtArrayTypeReference;
import spoon.reflect.reference.CtIntersectionTypeReference;
import spoon.reflect.reference.CtTypeParameterReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtAbstractVisitor;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.DefaultCoreFactory;
import spoon.support.SpoonClassNotFoundException;
import spoon.support.StandardEnvironment;
import spoon.support.visitor.ClassTypingContext;
import spoon.support.visitor.GenericTypeAdapter;
import spoon.support.visitor.MethodTypingContext;
import spoon.support.visitor.java.JavaReflectionTreeBuilder;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static spoon.testing.utils.ModelUtils.createFactory;
/**
* The {@link CtType} sub-factory.
*/
public class TypeFactory extends SubFactory {
private static final Set<String> NULL_PACKAGE_CLASSES = Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList("void", "boolean", "byte", "short", "char", "int", "float", "long",
"double",
// TODO (leventov) it is questionable to me that nulltype should also be here
CtTypeReference.NULL_TYPE_NAME)));
public final CtTypeReference<?> NULL_TYPE = createReference(CtTypeReference.NULL_TYPE_NAME);
public final CtTypeReference<Void> VOID = createReference(Void.class);
public final CtTypeReference<String> STRING = createReference(String.class);
public final CtTypeReference<Boolean> BOOLEAN = createReference(Boolean.class);
public final CtTypeReference<Byte> BYTE = createReference(Byte.class);
public final CtTypeReference<Character> CHARACTER = createReference(Character.class);
public final CtTypeReference<Integer> INTEGER = createReference(Integer.class);
public final CtTypeReference<Long> LONG = createReference(Long.class);
public final CtTypeReference<Float> FLOAT = createReference(Float.class);
public final CtTypeReference<Double> DOUBLE = createReference(Double.class);
public final CtTypeReference<Void> VOID_PRIMITIVE = createReference(void.class);
public final CtTypeReference<Boolean> BOOLEAN_PRIMITIVE = createReference(boolean.class);
public final CtTypeReference<Byte> BYTE_PRIMITIVE = createReference(byte.class);
public final CtTypeReference<Character> CHARACTER_PRIMITIVE = createReference(char.class);
public final CtTypeReference<Integer> INTEGER_PRIMITIVE = createReference(int.class);
public final CtTypeReference<Long> LONG_PRIMITIVE = createReference(long.class);
public final CtTypeReference<Float> FLOAT_PRIMITIVE = createReference(float.class);
public final CtTypeReference<Double> DOUBLE_PRIMITIVE = createReference(double.class);
public final CtTypeReference<Short> SHORT = createReference(Short.class);
public final CtTypeReference<Short> SHORT_PRIMITIVE = createReference(short.class);
public final CtTypeReference<Date> DATE = createReference(Date.class);
public final CtTypeReference<Object> OBJECT = createReference(Object.class);
private final Map<Class<?>, CtType<?>> shadowCache = new HashMap<>();
/**
* Returns a reference on the null type (type of null).
*/
public CtTypeReference<?> nullType() {
return NULL_TYPE.clone();
}
/**
* Returns a reference on the void type.
*/
public CtTypeReference<Void> voidType() {
return VOID.clone();
}
/**
* Returns a reference on the void primitive type.
*/
public CtTypeReference<Void> voidPrimitiveType() {
return VOID_PRIMITIVE.clone();
}
/**
* Returns a reference on the string type.
*/
public CtTypeReference<String> stringType() {
return STRING.clone();
}
/**
* Returns a reference on the boolean type.
*/
public CtTypeReference<Boolean> booleanType() {
return BOOLEAN.clone();
}
/**
* Returns a reference on the boolean primitive type.
*/
public CtTypeReference<Boolean> booleanPrimitiveType() {
return BOOLEAN_PRIMITIVE.clone();
}
/**
* Returns a reference on the byte type.
*/
public CtTypeReference<Byte> byteType() {
return BYTE.clone();
}
/**
* Returns a reference on the byte primitive type.
*/
public CtTypeReference<Byte> bytePrimitiveType() {
return BYTE_PRIMITIVE.clone();
}
/**
* Returns a reference on the character type.
*/
public CtTypeReference<Character> characterType() {
return CHARACTER.clone();
}
/**
* Returns a reference on the character primitive type.
*/
public CtTypeReference<Character> characterPrimitiveType() {
return CHARACTER_PRIMITIVE.clone();
}
/**
* Returns a reference on the integer type.
*/
public CtTypeReference<Integer> integerType() {
return INTEGER.clone();
}
/**
* Returns a reference on the integer primitive type.
*/
public CtTypeReference<Integer> integerPrimitiveType() {
return INTEGER_PRIMITIVE.clone();
}
/**
* Returns a reference on the long type.
*/
public CtTypeReference<Long> longType() {
return LONG.clone();
}
/**
* Returns a reference on the long primitive type.
*/
public CtTypeReference<Long> longPrimitiveType() {
return LONG_PRIMITIVE.clone();
}
/**
* Returns a reference on the float type.
*/
public CtTypeReference<Float> floatType() {
return FLOAT.clone();
}
/**
* Returns a reference on the float primitive type.
*/
public CtTypeReference<Float> floatPrimitiveType() {
return FLOAT_PRIMITIVE.clone();
}
/**
* Returns a reference on the double type.
*/
public CtTypeReference<Double> doubleType() {
return DOUBLE.clone();
}
/**
* Returns a reference on the double primitive type.
*/
public CtTypeReference<Double> doublePrimitiveType() {
return DOUBLE_PRIMITIVE.clone();
}
/**
* Returns a reference on the short type.
*/
public CtTypeReference<?> shortType() {
return SHORT.clone();
}
/**
* Returns a reference on the short primitive type.
*/
public CtTypeReference<?> shortPrimitiveType() {
return SHORT_PRIMITIVE.clone();
}
/**
* Returns a reference on the date type.
*/
public CtTypeReference<?> dateType() {
return DATE.clone();
}
/**
* Returns a reference on the object type.
*/
public CtTypeReference<?> objectType() {
return OBJECT.clone();
}
/**
* Creates a new type sub-factory.
*
* @param factory
* the parent factory
*/
public TypeFactory(Factory factory) {
super(factory);
}
public TypeFactory() {
this(new FactoryImpl(new DefaultCoreFactory(), new StandardEnvironment()));
}
/**
* Creates a reference to an array of given type.
*
* @param <T>
* type of array
* @param type
* type of array values
*/
public <T> CtArrayTypeReference<T[]> createArrayReference(CtType<T> type) {
CtArrayTypeReference<T[]> array = factory.Core().createArrayTypeReference();
array.setComponentType(createReference(type));
return array;
}
/**
* Creates a reference to a one-dimension array of given type.
*/
public <T> CtArrayTypeReference<T[]> createArrayReference(CtTypeReference<T> reference) {
CtArrayTypeReference<T[]> array = factory.Core().createArrayTypeReference();
array.setComponentType(reference);
return array;
}
/**
* Creates a reference to an n-dimension array of given type.
*/
public CtArrayTypeReference<?> createArrayReference(CtTypeReference<?> reference, int n) {
CtTypeReference<?> componentType = null;
if (n == 1) {
return createArrayReference(reference);
}
componentType = createArrayReference(reference, n - 1);
CtArrayTypeReference<?> array = factory.Core().createArrayTypeReference();
array.setComponentType(componentType);
return array;
}
/**
* Creates a reference to an array of given type.
*/
public <T> CtArrayTypeReference<T> createArrayReference(String qualifiedName) {
CtArrayTypeReference<T> array = factory.Core().createArrayTypeReference();
array.setComponentType(createReference(qualifiedName));
return array;
}
/**
* Creates a reference to a simple type
*/
public <T> CtTypeReference<T> createReference(Class<T> type) {
if (type == null) {
return null;
}
if (type.isArray()) {
CtArrayTypeReference<T> array = factory.Core().createArrayTypeReference();
array.setComponentType(createReference(type.getComponentType()));
return array;
}
return createReference(type.getName());
}
/**
* Create a reference to a simple type
*/
public <T> CtTypeReference<T> createReference(CtType<T> type) {
return createReference(type, false);
}
/**
* @param includingFormalTypeParameter if true then references to formal type parameters
* are added as actual type arguments of returned {@link CtTypeReference}
*/
public <T> CtTypeReference<T> createReference(CtType<T> type, boolean includingFormalTypeParameter) {
CtTypeReference<T> ref = factory.Core().createTypeReference();
if (type.getDeclaringType() != null) {
ref.setDeclaringType(createReference(type.getDeclaringType(), includingFormalTypeParameter));
} else if (type.getPackage() != null) {
ref.setPackage(factory.Package().createReference(type.getPackage()));
}
ref.setSimpleName(type.getSimpleName());
if (includingFormalTypeParameter) {
for (CtTypeParameter formalTypeParam : type.getFormalCtTypeParameters()) {
ref.addActualTypeArgument(formalTypeParam.getReference());
}
}
return ref;
}
/**
* Create a reference to a simple type
*/
public CtTypeParameterReference createReference(CtTypeParameter type) {
CtTypeParameterReference ref = factory.Core().createTypeParameterReference();
if (type.getSuperclass() != null) {
ref.setBoundingType(type.getSuperclass().clone());
}
for (CtAnnotation<? extends Annotation> ctAnnotation : type.getAnnotations()) {
ref.addAnnotation(ctAnnotation.clone());
}
ref.setSimpleName(type.getSimpleName());
ref.setParent(type);
return ref;
}
/**
* Create a reference to a simple type
*/
public <T> CtTypeReference<T> createReference(String qualifiedName) {
if (qualifiedName.endsWith("[]")) {
return createArrayReference(qualifiedName.substring(0, qualifiedName.length() - 2));
}
CtTypeReference<T> ref = factory.Core().createTypeReference();
if (hasInnerType(qualifiedName) > 0) {
ref.setDeclaringType(createReference(getDeclaringTypeName(qualifiedName)));
} else if (hasPackage(qualifiedName) > 0) {
ref.setPackage(factory.Package().createReference(getPackageName(qualifiedName)));
} else if (!NULL_PACKAGE_CLASSES.contains(qualifiedName)) {
ref.setPackage(factory.Package().topLevel());
}
ref.setSimpleName(getSimpleName(qualifiedName));
return ref;
}
/**
* Gets a created type from its qualified name.
*
* @return a found type or null if does not exist
*/
@SuppressWarnings("unchecked")
public <T> CtType<T> get(final String qualifiedName) {
int inertTypeIndex = qualifiedName.lastIndexOf(CtType.INNERTTYPE_SEPARATOR);
if (inertTypeIndex > 0) {
String s = qualifiedName.substring(0, inertTypeIndex);
CtType<T> t = factory.Type().get(s);
if (t == null) {
return null;
}
String className = qualifiedName.substring(inertTypeIndex + 1);
final CtTypeReference<T> reference = t.getReference();
if (reference.isLocalType()) {
final List<CtClass<T>> enclosingClasses = t.getElements(new TypeFilter<CtClass<T>>(CtClass.class) {
@Override
public boolean matches(CtClass<T> element) {
return super.matches(element) && element.getQualifiedName().equals(qualifiedName);
}
});
if (enclosingClasses.size() == 0) {
return null;
}
return enclosingClasses.get(0);
}
try {
// If the class name can't be parsed in integer, the method throws an exception.
// If the class name is an integer, the class is an anonymous class, otherwise,
// it is a standard class.
Integer.parseInt(className);
final List<CtNewClass> anonymousClasses = t.getElements(new TypeFilter<CtNewClass>(CtNewClass.class) {
@Override
public boolean matches(CtNewClass element) {
return super.matches(element) && element.getAnonymousClass().getQualifiedName().equals(qualifiedName);
}
});
if (anonymousClasses.size() == 0) {
return null;
}
return anonymousClasses.get(0).getAnonymousClass();
} catch (NumberFormatException e) {
return t.getNestedType(className);
}
}
int packageIndex = qualifiedName.lastIndexOf(CtPackage.PACKAGE_SEPARATOR);
CtPackage pack;
if (packageIndex > 0) {
pack = factory.Package().get(qualifiedName.substring(0, packageIndex));
} else {
pack = factory.Package().getRootPackage();
}
if (pack == null) {
return null;
}
return (CtType<T>) pack.getType(qualifiedName.substring(packageIndex + 1));
}
/**
* Gets the list of all top-level created types.
*/
public List<CtType<?>> getAll() {
return (List<CtType<?>>) factory.getModel().getAllTypes();
}
/**
* Gets the list of all created types.
*/
public List<CtType<?>> getAll(boolean includeNestedTypes) {
if (!includeNestedTypes) {
return getAll();
}
List<CtType<?>> types = new ArrayList<>();
for (CtPackage pack : factory.Package().getAll()) {
for (CtType<?> type : pack.getTypes()) {
addNestedType(types, type);
}
}
return types;
}
private void addNestedType(List<CtType<?>> list, CtType<?> t) {
list.add(t);
for (CtType<?> nt : t.getNestedTypes()) {
addNestedType(list, nt);
}
}
/**
* Gets a type from its runtime Java class. If the class isn't in the spoon path,
* the class will be build from the Java reflection and will be marked as
* shadow (see {@link spoon.reflect.declaration.CtShadowable}).
*
* @param <T>
* actual type of the class
* @param cl
* the java class: note that this class should be Class<T> but it
* then poses problem when T is a generic type itself
*/
@SuppressWarnings("unchecked")
public <T> CtType<T> get(Class<?> cl) {
final CtType<T> aType = get(cl.getName());
if (aType == null) {
final CtType<T> shadowClass = (CtType<T>) this.shadowCache.get(cl);
if (shadowClass == null) {
CtType<T> newShadowClass;
try {
newShadowClass = new JavaReflectionTreeBuilder(createFactory()).scan((Class<T>) cl);
} catch (Throwable e) {
throw new SpoonClassNotFoundException("cannot create shadow class: " + cl.getName(), e);
}
newShadowClass.setFactory(factory);
newShadowClass.accept(new CtScanner() {
@Override
public void scan(CtElement element) {
if (element != null) {
element.setFactory(factory);
}
}
});
this.shadowCache.put(cl, newShadowClass);
return newShadowClass;
} else {
return shadowClass;
}
}
return aType;
}
/**
* Gets the declaring type name for a given Java qualified name.
*/
protected String getDeclaringTypeName(String qualifiedName) {
return qualifiedName.substring(0, hasInnerType(qualifiedName));
}
/**
* Creates a collection of type references from a collection of classes.
*/
public List<CtTypeReference<?>> createReferences(List<Class<?>> classes) {
List<CtTypeReference<?>> refs = new ArrayList<>(classes.size());
for (Class<?> c : classes) {
refs.add(createReference(c));
}
return refs;
}
/**
* Gets the package name for a given Java qualified name.
*/
protected String getPackageName(String qualifiedName) {
if (hasPackage(qualifiedName) >= 0) {
return qualifiedName.substring(0, hasPackage(qualifiedName));
}
return "";
}
/**
* Gets the simple name for a given Java qualified name.
*/
protected String getSimpleName(String qualifiedName) {
if (hasInnerType(qualifiedName) > 0) {
return qualifiedName.substring(hasInnerType(qualifiedName) + 1);
} else if (hasPackage(qualifiedName) > 0) {
return qualifiedName.substring(hasPackage(qualifiedName) + 1);
} else {
return qualifiedName;
}
}
/**
* Tells if a given Java qualified name is that of an inner type.
*/
protected int hasInnerType(String qualifiedName) {
int ret = qualifiedName.lastIndexOf(CtType.INNERTTYPE_SEPARATOR);
// if (ret < 0) {
// if (hasPackage(qualifiedName) > 0) {
// String buf = qualifiedName.substring(0,
// hasPackage(qualifiedName));
// int tmp = buf.lastIndexOf(CtPackage.PACKAGE_SEPARATOR);
// if (Character.isUpperCase(buf.charAt(tmp + 1))) {
// ret = hasPackage(qualifiedName);
// }
// }
// }
return ret;
}
/**
* Tells if a given Java qualified name contains a package name.
*/
protected int hasPackage(String qualifiedName) {
return qualifiedName.lastIndexOf(CtPackage.PACKAGE_SEPARATOR);
}
/**
* Creates a type parameter reference with no bounds.
*
* @param name
* the name of the formal parameter
*/
public CtTypeParameterReference createTypeParameterReference(String name) {
CtTypeParameterReference typeParam = factory.Core().createTypeParameterReference();
typeParam.setSimpleName(name);
return typeParam;
}
/**
* Create a {@link GenericTypeAdapter} for adapting of formal type parameters from any compatible context to the context of provided `formalTypeDeclarer`
*
* @param formalTypeDeclarer
* the target scope of the returned {@link GenericTypeAdapter}
*/
public GenericTypeAdapter createTypeAdapter(CtFormalTypeDeclarer formalTypeDeclarer) {
class Visitor extends CtAbstractVisitor {
GenericTypeAdapter adapter;
@Override
public <T> void visitCtClass(CtClass<T> ctClass) {
adapter = new ClassTypingContext(ctClass);
}
@Override
public <T> void visitCtInterface(CtInterface<T> intrface) {
adapter = new ClassTypingContext(intrface);
}
@Override
public <T> void visitCtMethod(CtMethod<T> m) {
adapter = new MethodTypingContext().setMethod(m);
}
@Override
public <T> void visitCtConstructor(CtConstructor<T> c) {
adapter = new MethodTypingContext().setConstructor(c);
}
}
Visitor visitor = new Visitor();
((CtElement) formalTypeDeclarer).accept(visitor);
return visitor.adapter;
}
/**
* Creates an intersection type reference.
*
* @param bounds
* List of bounds saved in the intersection type. The first bound will be the intersection type.
* @param <T>
* Type of the first bound.
*/
public <T> CtIntersectionTypeReference<T> createIntersectionTypeReferenceWithBounds(List<CtTypeReference<?>> bounds) {
final CtIntersectionTypeReference<T> intersectionRef = factory.Core().createIntersectionTypeReference();
CtTypeReference<?> firstBound = bounds.toArray(new CtTypeReference<?>[0])[0].clone();
intersectionRef.setSimpleName(firstBound.getSimpleName());
intersectionRef.setDeclaringType(firstBound.getDeclaringType());
intersectionRef.setPackage(firstBound.getPackage());
intersectionRef.setActualTypeArguments(firstBound.getActualTypeArguments());
intersectionRef.setBounds(bounds);
return intersectionRef;
}
}