All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.evosuite.seeding.CastClassManager Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite 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 GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see .
 */
package org.evosuite.seeding;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.evosuite.Properties;
import org.evosuite.TestGenerationContext;
import org.evosuite.ga.ConstructionFailedException;
import org.evosuite.setup.*;
import org.evosuite.utils.generic.GenericClass;
import org.evosuite.utils.Randomness;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.gentyref.GenericTypeReflector;

public class CastClassManager {

	private static CastClassManager instance = new CastClassManager();

	private static final Logger logger = LoggerFactory.getLogger(CastClassManager.class);

	private final Map classMap = new LinkedHashMap();

	public static List sortByValue(Map map) {
		List> list = new LinkedList>(
		        map.entrySet());
		Collections.sort(list, new Comparator>() {
			@Override
			public int compare(Map.Entry o1,
			        Map.Entry o2) {
				if (o1.getKey().getNumParameters() == o2.getKey().getNumParameters())
					return (o1.getValue()).compareTo(o2.getValue());
				else
					return o1.getKey().getNumParameters()
					        - o2.getKey().getNumParameters();
			}
		});

		List result = new LinkedList();
		for (Map.Entry entry : list) {
			result.add(entry.getKey());
		}
		return result;
	}

	public static GenericClass selectClass(List candidates) {
		double r = Randomness.nextDouble();
		double d = Properties.RANK_BIAS
		        - Math.sqrt((Properties.RANK_BIAS * Properties.RANK_BIAS)
		                - (4.0 * (Properties.RANK_BIAS - 1.0) * r));
		int length = candidates.size();

		d = d / 2.0 / (Properties.RANK_BIAS - 1.0);

		int index = (int) (length * d);
		return candidates.get(index);
	}

	private CastClassManager() {
		initDefaultClasses();
	}

	private void initDefaultClasses() {
		classMap.put(new GenericClass(Object.class), 0);
		classMap.put(new GenericClass(String.class), 1);
		classMap.put(new GenericClass(Integer.class), 1);
	}

	public static CastClassManager getInstance() {
		return instance;
	}

	public void addCastClass(String className, int depth) {
		try {
			Class clazz = TestGenerationContext.getInstance().getClassLoaderForSUT().loadClass(className);
			GenericClass castClazz = new GenericClass(clazz);
			addCastClass(castClazz.getWithWildcardTypes(), depth);
		} catch (ClassNotFoundException e) {
			// Ignore
			logger.debug("Error including cast class " + className + " because: " + e);
		}
	}

	public void addCastClass(Type type, int depth) {
		GenericClass castClazz = new GenericClass(type);
		addCastClass(castClazz.getWithWildcardTypes(), depth);
	}

	public void addCastClass(GenericClass clazz, int depth) {
		if (clazz.getRawClass() == null) {
			logger.warn("ADDING NULL!");
			assert (false);
		}
		if (clazz.isAbstract()) {
			for (Class concreteClass : ConcreteClassAnalyzer.getInstance().getConcreteClasses(clazz.getRawClass(),
			                                                                      TestCluster.getInheritanceTree())) {
				GenericClass c = new GenericClass(concreteClass);
				if(TestUsageChecker.canUse(c.getRawClass())) {
					classMap.put(c, depth);
				}
			}

			if(Properties.P_FUNCTIONAL_MOCKING > 0.0) {
				if (TestUsageChecker.canUse(clazz.getRawClass()))
					classMap.put(clazz, depth);
			}

		}  else {
			if(TestUsageChecker.canUse(clazz.getRawClass()))
				classMap.put(clazz, depth);
		}
	}

	private void handleComparable() {
		// TODO
	}

	private void handleComparator() {
		// TODO
	}

	private void handleEnum() {
		// TODO
	}

	private void handleIterable() {
		// TODO
	}

	private final List> specialCases = Arrays.asList(new Class[] {
	        Comparable.class, Comparator.class, Iterable.class, Enum.class });

	/**
	 * True if this type variable is one of the java.* special cases
	 *
	 * @return
	 */
	private boolean isSpecialCase(TypeVariable typeVariable) {
		for (Type bound : typeVariable.getBounds()) {
			Class clazz = GenericTypeReflector.erase(bound);
			if (specialCases.contains(clazz))
				return true;
		}
		return false;
	}

	/**
	 * True if this wildcard type is one of the java.* special cases
	 *
	 * @return
	 */
	private boolean isSpecialCase(WildcardType wildcardType) {
		// TODO
		return false;
	}

	private boolean addAssignableClass(WildcardType wildcardType,
	        Map, Type> typeMap) {
		Set> classes = TestCluster.getInstance().getAnalyzedClasses();
		Set> assignableClasses = new LinkedHashSet>();

		for (Class clazz : classes) {
			if (!TestUsageChecker.canUse(clazz))
				continue;

			GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();
			if (!genericClass.satisfiesBoundaries(wildcardType, typeMap)) {
				logger.debug("Not assignable: " + clazz);
			} else {
				logger.debug("Assignable");
				assignableClasses.add(clazz);
			}
		}
		for (Type t : typeMap.values()) {
			if (t instanceof WildcardType)
				continue; // TODO: For now.

			Class clazz = GenericTypeReflector.erase(t);
			if (!TestUsageChecker.canUse(GenericTypeReflector.erase(clazz)))
				continue;

			GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();
			if (!genericClass.satisfiesBoundaries(wildcardType, typeMap)) {
				logger.debug("Not assignable: " + clazz);
			} else {
				logger.debug("Assignable");
				assignableClasses.add(clazz);
			}
		}
		for (Type t : wildcardType.getUpperBounds()) {
			if (typeMap.containsKey(t))
				t = typeMap.get(t);

			Class clazz = GenericTypeReflector.erase(t);
			logger.debug("Checking bound: " + t);

			if (!TestUsageChecker.canUse(clazz))
				continue;

			GenericClass genericClass = new GenericClass(t);
			if (genericClass.hasTypeVariables()) {
				logger.debug("Has type variables: " + genericClass);
				GenericClass wildcardClass = genericClass.getWithWildcardTypes();
				if (!wildcardClass.satisfiesBoundaries(wildcardType, typeMap)) {
					logger.debug("Not assignable: " + clazz);
				} else {
					logger.debug("Assignable");
					assignableClasses.add(clazz);
				}
			} else {
				logger.debug("Adding directly: " + genericClass);
				assignableClasses.add(genericClass.getRawClass());
				classMap.put(genericClass, 10);
			}
		}
		logger.debug("Found assignable classes for wildcardtype " + wildcardType + ": "
		        + assignableClasses.size());
		if (!assignableClasses.isEmpty()) {
			Class clazz = Randomness.choice(assignableClasses);
			GenericClass castClass = new GenericClass(clazz);
			logger.debug("Adding cast class " + castClass);
			classMap.put(castClass, 10);
			return true;
		}

		return false;
	}

	private boolean addAssignableClass(TypeVariable typeVariable,
	        Map, Type> typeMap) {
		Set> classes = TestCluster.getInstance().getAnalyzedClasses();
		Set> assignableClasses = new LinkedHashSet>();

		for (Class clazz : classes) {
			if (!TestUsageChecker.canUse(clazz))
				continue;

			GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();

			if (!genericClass.satisfiesBoundaries(typeVariable, typeMap)) {
				logger.debug("Not assignable: " + clazz);
			} else {
				logger.debug("Assignable");
				assignableClasses.add(clazz);
			}
		}
		for (Type t : typeMap.values()) {
			if (t instanceof WildcardType)
				continue; // TODO: For now.

			Class clazz = GenericTypeReflector.erase(t);
			if (!TestUsageChecker.canUse(GenericTypeReflector.erase(clazz)))
				continue;

			GenericClass genericClass = new GenericClass(clazz).getWithWildcardTypes();
			if (!genericClass.satisfiesBoundaries(typeVariable, typeMap)) {
				logger.debug("Not assignable: " + clazz);
			} else {
				logger.debug("Assignable");
				assignableClasses.add(clazz);
			}
		}
		/*
		for (Type t : typeVariable.getBounds()) {
			if (typeMap.containsKey(t))
				t = typeMap.get(t);

			Class clazz = GenericTypeReflector.erase(t);
			logger.debug("Checking bound: " + t);

			if (!TestClusterGenerator.canUse(clazz))
				continue;

			GenericClass genericClass = new GenericClass(t);
			//if (genericClass.hasTypeVariables()) {
			logger.debug("Has type variables: " + genericClass
			        + ", checking wildcard version with type map " + typeMap);
			GenericClass wildcardClass = genericClass.getWithWildcardTypes();
			//if (!wildcardClass.satisfiesBoundaries(typeVariable, typeMap)) {
			//	logger.debug("Not assignable: " + clazz);
			//} else {
			//	logger.debug("Assignable");
			assignableClasses.add(clazz);
			//}
			//} else {
			//	logger.debug("Adding directly: " + genericClass);
			//	assignableClasses.add(genericClass.getRawClass());
			//	classMap.put(genericClass, 10);
			//}
		}
		*/
		logger.debug("Found assignable classes for type variable " + typeVariable + ": "
		        + assignableClasses.size());
		if (!assignableClasses.isEmpty()) {
			Class clazz = Randomness.choice(assignableClasses);
			GenericClass castClass = new GenericClass(clazz);
			logger.debug("Adding cast class " + castClass);
			classMap.put(castClass, 10);
			return true;
		} else {
			InheritanceTree inheritanceTree = DependencyAnalysis.getInheritanceTree();
			Set> boundCandidates = new LinkedHashSet>();
			for (Type bound : typeVariable.getBounds()) {
				Class rawBound = GenericTypeReflector.erase(bound);
				boundCandidates.add(rawBound);
				logger.debug("Getting concrete classes for " + rawBound);
				boundCandidates.addAll(ConcreteClassAnalyzer.getInstance().getConcreteClasses(rawBound,
				                                                               inheritanceTree));
			}
			for (Class clazz : boundCandidates) {
				if (!TestUsageChecker.canUse(clazz))
					continue;

				boolean isAssignable = true;
				for (Type bound : typeVariable.getBounds()) {
					if (GenericTypeReflector.erase(bound).equals(Enum.class)) {
						if (clazz.isEnum())
							continue;
					}

					if (!GenericClass.isAssignable(bound, clazz)) {
						isAssignable = false;
						logger.debug("Not assignable: " + clazz + " to bound " + bound);
						break;
					}
					if (bound instanceof ParameterizedType) {
						if (Arrays.asList(((ParameterizedType) bound).getActualTypeArguments()).contains(typeVariable)) {
							isAssignable = false;
							break;
						}
					}
				}
				if (isAssignable) {
					assignableClasses.add(clazz);
				}
			}
			logger.debug("After adding bounds, found "+assignableClasses.size()+" assignable classes for type variable "
			        + typeVariable + ": " + assignableClasses);
			if (!assignableClasses.isEmpty()) {
				// TODO: Add all classes?
//				for(Class clazz : assignableClasses) {
//					GenericClass castClass = new GenericClass(clazz);
//					logger.debug("Adding cast class " + castClass);
//					classMap.put(castClass, 10);
//				}
				Class clazz = Randomness.choice(assignableClasses);
				GenericClass castClass = new GenericClass(clazz);
				logger.debug("Adding cast class " + castClass);
				classMap.put(castClass, 10);
				return true;
			}

		}

		return false;
	}

	private List getAssignableClasses(WildcardType wildcardType,
	        boolean allowRecursion, Map, Type> ownerVariableMap) {
		Map assignableClasses = new LinkedHashMap();

		logger.debug("Getting assignable classes for wildcard type " + wildcardType);
		for (Entry entry : classMap.entrySet()) {
			GenericClass key = entry.getKey();
			logger.debug("Current class for wildcard " + wildcardType + ": " + key);

			if (!key.satisfiesBoundaries(wildcardType, ownerVariableMap)) {
				logger.debug("Does not satisfy boundaries");
				continue;
			}

			if (!allowRecursion && key.hasWildcardOrTypeVariables()) {
				logger.debug("Stopping because of type recursion");
				continue;
			}
			logger.debug("Is assignable");

			assignableClasses.put(entry.getKey(), entry.getValue());
		}

		return sortByValue(assignableClasses);
	}

	private List getAssignableClasses(TypeVariable typeVariable,
	        boolean allowRecursion, Map, Type> ownerVariableMap) {
		Map assignableClasses = new LinkedHashMap();

		logger.debug("Getting assignable classes for type variable " + typeVariable);
		for (Entry entry : classMap.entrySet()) {
			GenericClass key = entry.getKey();
			logger.debug("Current class for type variable " + typeVariable + ": " + key);

			if (!key.satisfiesBoundaries(typeVariable, ownerVariableMap)) {
				logger.debug("Bounds not satisfied");
				continue;
			}
			if (!allowRecursion && key.hasWildcardOrTypeVariables()) {
				logger.debug("Recursion not allowed but type has wildcard or type variables");
				continue;
			}

			assignableClasses.put(entry.getKey(), entry.getValue());
		}
		logger.debug("Found assignable classes: " + assignableClasses.size());

		return sortByValue(assignableClasses);
	}

	public GenericClass selectCastClass() {

		List assignableClasses = sortByValue(classMap);
		return selectClass(assignableClasses);
	}

	public GenericClass selectCastClass(TypeVariable typeVariable,
	        boolean allowRecursion, Map, Type> ownerVariableMap) {

		List assignableClasses = getAssignableClasses(typeVariable,
		                                                            allowRecursion,
		                                                            ownerVariableMap);

		logger.debug("Assignable classes to " + typeVariable + ": " + assignableClasses);

		// FIXME: If we disallow recursion immediately, then we will never actually
		// do recursion since we always have Object, Integer, and String as candidates.
		// Including recursion may influence performance negatively.
		//
		// If we were not able to find an assignable class without recursive types
		// we try again but allowing recursion
//		if(assignableClasses.isEmpty()) {
//			assignableClasses = getAssignableClasses(typeVariable,
//                    allowRecursion,
//                    ownerVariableMap);
//		}

		//special case
		if (assignableClasses.isEmpty()) {
			logger.debug("Trying to add new cast class");
			if (addAssignableClass(typeVariable, ownerVariableMap)) {
				logger.debug("Now trying again to get a class");
				assignableClasses = getAssignableClasses(typeVariable, allowRecursion,
				                                         ownerVariableMap);
				if (assignableClasses.isEmpty()) {
					logger.debug("Nothing is assignable");
					return null;
				}
			} else {
				logger.debug("Nothing is assignable");
				return null;
			}
		}
		logger.debug("Now we've got assignable classes: " + assignableClasses.size());

		return selectClass(assignableClasses);
	}

	public GenericClass selectCastClass(WildcardType wildcardType,
	        boolean allowRecursion, Map, Type> ownerVariableMap)
	        throws ConstructionFailedException {

		logger.debug("Getting assignable classes for wildcard");
		List assignableClasses = getAssignableClasses(wildcardType,
		                                                            false,
		                                                            ownerVariableMap);
		logger.debug("Assignable classes to " + wildcardType + ": " + assignableClasses);

		// If we were not able to find an assignable class without recursive types
		// we try again but allowing recursion
		if (assignableClasses.isEmpty()) {
			if(allowRecursion) {
			assignableClasses.addAll(getAssignableClasses(wildcardType,
                    allowRecursion,
                    ownerVariableMap));
			}
		}

		if (assignableClasses.isEmpty()) {

			logger.debug("Trying to add new cast class");
			if (addAssignableClass(wildcardType, ownerVariableMap)) {
				assignableClasses = getAssignableClasses(wildcardType, allowRecursion,
				                                         ownerVariableMap);
				if (assignableClasses.isEmpty()) {

					logger.debug("Nothing is assignable");
					throw new ConstructionFailedException("Nothing is assignable to "
					        + wildcardType);
				}
			} else {

				logger.debug("Making random choice because nothing is assignable");
				throw new ConstructionFailedException("Nothing is assignable to "
				        + wildcardType);

			}
		}

		return selectClass(assignableClasses);
	}

	public boolean hasClass(String className) {
		for (GenericClass clazz : classMap.keySet()) {
			if (clazz.getClassName().equals(className))
				return true;
		}
		return false;
	}

	public Set getCastClasses() {
		return classMap.keySet();
	}

	public void clear() {
		classMap.clear();
		initDefaultClasses();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy