blob: 883c1e93f15077a3aac7db94211f4523822dbcbb [file] [log] [blame]
package org.checkerframework.framework.type;
import java.util.IdentityHashMap;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
/** Duplicates annotated types and replaces components according to a replacement map. */
public class AnnotatedTypeCopierWithReplacement {
/**
* Return a copy of type after making the specified replacements.
*
* @param type the type that will be copied with replaced components
* @param replacementMap a mapping of {@literal referenceToReplace => referenceOfReplacement}
* @return a duplicate of type in which every reference that was a key in replacementMap has been
* replaced by its corresponding value
*/
public static AnnotatedTypeMirror replace(
AnnotatedTypeMirror type,
IdentityHashMap<? extends AnnotatedTypeMirror, ? extends AnnotatedTypeMirror>
replacementMap) {
return new Visitor(replacementMap).visit(type);
}
/**
* AnnotatedTypeCopier maintains a mapping of {@literal typeVisited => copyOfTypeVisited} When a
* reference, typeVisited, is encountered again, it will use the recorded reference,
* copyOfTypeVisited, instead of generating a new copy of typeVisited. Visitor pre-populates this
* mapping so that references are replaced not by their copies but by those in the replacementMap
* provided in the constructor.
*
* <p>All types NOT in the replacement map are duplicated as per AnnotatedTypeCopier.visit
*/
protected static class Visitor extends AnnotatedTypeCopier {
private final IdentityHashMap<? extends AnnotatedTypeMirror, ? extends AnnotatedTypeMirror>
originalMappings;
public Visitor(
final IdentityHashMap<? extends AnnotatedTypeMirror, ? extends AnnotatedTypeMirror>
mappings) {
originalMappings = new IdentityHashMap<>(mappings);
}
@Override
public AnnotatedTypeMirror visit(AnnotatedTypeMirror type) {
return type.accept(this, new IdentityHashMap<>(originalMappings));
}
@Override
public AnnotatedTypeMirror visitTypeVariable(
AnnotatedTypeVariable original,
IdentityHashMap<AnnotatedTypeMirror, AnnotatedTypeMirror> originalToCopy) {
// AnnotatedTypeCopier will visit the type parameters of a method and copy them.
// Without this flag, any mappings in originalToCopy would replace the type parameters.
// However, we do not replace the type parameters in an AnnotatedExecutableType. Also,
// AnnotatedExecutableType.typeVarTypes is of type List<AnnotatedTypeVariable> so if the
// mapping contained a type parameter -> (Non-type variable AnnotatedTypeMirror) then a
// runtime exception would occur.
if (visitingExecutableTypeParam) {
visitingExecutableTypeParam = false;
final AnnotatedTypeVariable copy =
(AnnotatedTypeVariable)
AnnotatedTypeMirror.createType(
original.getUnderlyingType(), original.atypeFactory, original.isDeclaration());
maybeCopyPrimaryAnnotations(original, copy);
originalToCopy.put(original, copy);
if (original.getUpperBoundField() != null) {
copy.setUpperBound(visit(original.getUpperBoundField(), originalToCopy));
}
if (original.getLowerBoundField() != null) {
copy.setLowerBound(visit(original.getLowerBoundField(), originalToCopy));
}
return copy;
}
return super.visitTypeVariable(original, originalToCopy);
}
}
}