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

framework.src.org.checkerframework.framework.util.TypeArgumentMapper Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.framework.util;

import org.checkerframework.javacutil.Pair;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;


/**
 * Records any mapping between the type parameters of a subtype to the corresponding
 * type parameters of a supertype.
 * e.g., Suppose we have the following classes:
 * 
{@code
 *      class Map
 *      class HashMap extends Map
 * }
* And we pass HashMap and Map to mapTypeArguments, the result would be: *
{@code
 *      Map(H1 -> M1, H2 -> M2)
 * }
* * Note, a single type argument in the subtype can map to multiple type parameters in the supertype. * e.g., *
{@code
 *      class OneTypeMap extends Map
 * }
* would have the result: *
{@code
 *      Map(O1 -> [M1,M2])
 * }
* * This utility only maps between corresponding type parameters, so the following class: *
{@code
 *      class StringMap extends Map
 * }
* would have an empty map as a result: *
{@code
 *      Map() // there are no type argument relationships between the two types
 * }
*/ public class TypeArgumentMapper { /** * Returns a mapping from subtype's type parameter indices to the indices of corresponding * type parameters in supertype. * */ public static Set> mapTypeArgumentIndices(final TypeElement subtype, final TypeElement supertype, final Types types) { Set> result = new HashSet<>(); if (subtype.equals(supertype)) { for (int i = 0; i < subtype.getTypeParameters().size(); i++) { result.add(Pair.of(new Integer(i), new Integer(i))); } } else { Map> subToSuperElements = mapTypeArguments(subtype, supertype, types); Map supertypeIndexes = getElementToIndex(supertype); final List subtypeParams = subtype.getTypeParameters(); for (int subtypeIndex = 0; subtypeIndex < subtypeParams.size(); subtypeIndex++) { final TypeParameterElement subtypeParam = subtypeParams.get(subtypeIndex); final Set correspondingSuperArgs = subToSuperElements.get(subtypeParam); if (correspondingSuperArgs != null) { for (TypeParameterElement supertypeParam : subToSuperElements.get(subtypeParam)) { result.add(Pair.of(subtypeIndex, supertypeIndexes.get(supertypeParam))); } } } } return result; } /** * @return a Map(type parameter symbol → index in type parameter list) */ private static Map getElementToIndex(TypeElement typeElement) { Map result = new LinkedHashMap<>(); List params = typeElement.getTypeParameters(); for (int i = 0; i < params.size(); i++) { result.put(params.get(i), new Integer(i)); } return result; } /** * Returns a mapping from the type parameters of subtype to a list of the type parameters in * supertype that must be the same type as subtype. * * e.g., *
{@code
     * class A
     * class B extends A {}
     * }
* * results in a {@code Map(B1 -> [A1,A2], B2 -> [], B3 -> [A3], B4 -> [])} * * @return a mapping from the type parameters of subtype to the supertype type parameter's * that to which they are a type argument */ public static Map> mapTypeArguments(final TypeElement subtype, final TypeElement supertype, final Types types) { final Map> result = new LinkedHashMap<>(); final List pathToSupertype = depthFirstSearchForSupertype(subtype, supertype, types); if (pathToSupertype != null && !pathToSupertype.isEmpty()) { final Map> intermediate = new LinkedHashMap<>(); final Set currentTypeParams = new HashSet<>(); // takes a type records of the form: // TypeRecord(element = MyMap, type = null) // TypeRecord(element = AbstractMap, type = AbstractMap) // TypeRecord(element = Map, type = AbstractMap) // And makes a map: // Map(Y1 -> [A1], Y2 -> [A2], A1 -> [M1], A2 -> M2] Iterator path = pathToSupertype.iterator(); TypeRecord current = path.next(); while (path.hasNext()) { TypeRecord next = path.next(); final List nextTypeParameter = next.element.getTypeParameters(); final List nextTypeArgs = next.type.getTypeArguments(); currentTypeParams.clear(); currentTypeParams.addAll(current.element.getTypeParameters()); for (int i = 0; i < nextTypeArgs.size(); i++) { final TypeParameterElement correspondingParameter = nextTypeParameter.get(i); final TypeMirror typeArg = nextTypeArgs.get(i); final Element typeArgEle = types.asElement(typeArg); if (currentTypeParams.contains(typeArgEle)) { addToSetMap(intermediate, (TypeParameterElement) typeArgEle, correspondingParameter); } } } List supertypeParams = supertype.getTypeParameters(); // You can think of the map above as a set of links from SubtypeParameter -> Supertype Parameter // e.g. in Map(Y1 - for (TypeParameterElement subtypeParam : subtype.getTypeParameters()) { Set subtypePath = flattenPath(intermediate.get(subtypeParam), intermediate); subtypePath.retainAll(supertypeParams); result.put(subtypeParam, subtypePath); } } return result; } private static Set flattenPath(Set elements, Map> map) { Set result = new HashSet<>(); if (elements != null) { for (final TypeParameterElement oldElement : elements) { Set substitutions = map.get(oldElement); if (substitutions != null) { result.addAll(flattenPath(elements, map)); } else { result.add(oldElement); } } } return result; } private static void addToSetMap(final Map> setMap, final TypeParameterElement element, final TypeParameterElement typeParam) { Set set = setMap.get(element); if (set == null) { set = new HashSet<>(); setMap.put(element, set); } set.add(typeParam); } /** * Create a list of TypeRecord's that form a "path" to target from subtype. * e.g. Suppose I have the types *
{@code
     * interface Map
     * class AbstractMap implements Map, Iterable>
     * class MyMap extends AbstractMap implements List>
     * }
* * The path from MyMap to Map would be: * *
{@code
     * TypeRecord(element = MyMap, type = null)
     * TypeRecord(element = AbstractMap, type = AbstractMap)
     * TypeRecord(element = Map, type = AbstractMap)
     * }
* * Note: You can have an implementation of the same interface inherited multiple times as long * as the parameterization of that interface remains the same * e.g. *
{@code
     * interface List
     * class AbstractList implements List
     * class ArrayList extends AbstractList implements List
     * }
* Notice how ArrayList implements list both by inheriting from AbstractList and from explicitly * listing it in the implements clause. We prioritize finding a path through the list of interfaces first * since this will be the shorter path. * * @return a set of type records that represents the sequence of directSupertypes between * subtype and target */ private static List depthFirstSearchForSupertype(final TypeElement subtype, final TypeElement target, final Types types) { Stack pathFromRoot = new Stack<>(); final TypeRecord pathStart = new TypeRecord(subtype, null); pathFromRoot.push(pathStart); final List result = recursiveDepthFirstSearch(pathFromRoot, target, types); return result; } /** * Computes one level for depthFirstSearchForSupertype then recurses. */ private static List recursiveDepthFirstSearch(final Stack pathFromRoot, final TypeElement target, final Types types) { List path = null; if (!pathFromRoot.isEmpty()) { final TypeRecord currentRecord = pathFromRoot.peek(); final TypeElement currentElement = currentRecord.element; if (currentElement.equals(target)) { return new ArrayList<>(pathFromRoot); } else { final Iterator interfaces = currentElement.getInterfaces().iterator(); final TypeMirror superclassType = currentElement.getSuperclass(); while (path == null && interfaces.hasNext()) { final TypeMirror intface = interfaces.next(); if (intface.getKind() != TypeKind.NONE) { DeclaredType interfaceDeclared = (DeclaredType) intface; pathFromRoot.push(new TypeRecord((TypeElement) types.asElement(interfaceDeclared), interfaceDeclared)); path = recursiveDepthFirstSearch(pathFromRoot, target, types); pathFromRoot.pop(); } } if (path == null && superclassType != null && superclassType.getKind() != TypeKind.NONE) { final DeclaredType superclass = (DeclaredType) superclassType; pathFromRoot.push(new TypeRecord((TypeElement) types.asElement(superclass), superclass)); path = recursiveDepthFirstSearch(pathFromRoot, target, types); pathFromRoot.pop(); } } } return path; } /** * Maps a class or interface's declaration element to the type it would be if viewed from a subtype class or interface * * e.g. suppose we have the elements for the declarations: *
{@code
     * class A
     * class B extends A
     * }
* * The type record of B if it is viewed as class A would bed: *
{@code
     *    TypeRecord( element = A, type = A )
     * }
* * That is, B can be viewed as an object of type A with an type argument of type parameter Tb */ private static class TypeRecord { public final TypeElement element; public final DeclaredType type; TypeRecord(final TypeElement element, final DeclaredType type) { this.element = element; this.type = type; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy