Skip to content

Commit

Permalink
InstanceOf: no action if type casts with specific type params used
Browse files Browse the repository at this point in the history
Wrap up
  • Loading branch information
BoykoAlex committed Sep 18, 2024
1 parent abb0338 commit 581f492
Show file tree
Hide file tree
Showing 2 changed files with 324 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,20 @@ private static class ExpressionAndType {
private final JavaType type;
}

@Data
private static class VariableAndTypeTree {
private final J.VariableDeclarations.NamedVariable variable;
private final TypeTree type;
}

@Data
private static class InstanceOfPatternReplacements {
private final J root;
private final Map<ExpressionAndType, J.InstanceOf> instanceOfs = new HashMap<>();
private final Map<J.InstanceOf, Set<J>> contexts = new HashMap<>();
private final Map<J.InstanceOf, Set<Cursor>> contextScopes = new HashMap<>();
private final Map<J.TypeCast, J.InstanceOf> replacements = new HashMap<>();
private final Map<J.InstanceOf, J.VariableDeclarations.NamedVariable> variablesToDelete = new HashMap<>();
private final Map<J.InstanceOf, VariableAndTypeTree> variablesToDelete = new HashMap<>();

public void registerInstanceOf(J.InstanceOf instanceOf, Set<J> contexts) {
Expression expression = instanceOf.getExpression();
Expand Down Expand Up @@ -192,13 +198,21 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) {
for (Iterator<?> it = cursor.getPath(); it.hasNext(); ) {
Object next = it.next();
if (validContexts.contains(next)) {
if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable
&& !variablesToDelete.containsKey(instanceOf)) {
variablesToDelete.put(instanceOf, parent.getValue());
if (isAcceptableTypeCast(typeCast) && isTheSameAsOtherTypeCasts(typeCast, instanceOf)) {
if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable
&& !variablesToDelete.containsKey(instanceOf)) {
variablesToDelete.put(instanceOf, new VariableAndTypeTree(parent.getValue(), parent.firstEnclosing(J.VariableDeclarations.class).getTypeExpression()));
} else {
replacements.put(typeCast, instanceOf);
}
contextScopes.computeIfAbsent(instanceOf, k -> new HashSet<>()).add(cursor);
} else {
replacements.put(typeCast, instanceOf);
replacements.entrySet().removeIf(e -> e.getValue() == instanceOf);
variablesToDelete.remove(instanceOf);
contextScopes.remove(instanceOf);
contexts.remove(instanceOf);
instanceOfs.entrySet().removeIf(e -> e.getValue() == instanceOf);
}
contextScopes.computeIfAbsent(instanceOf, k -> new HashSet<>()).add(cursor);
break;
} else if (root == next) {
break;
Expand All @@ -207,6 +221,24 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) {
}
}

private boolean isAcceptableTypeCast(J.TypeCast typeCast) {
TypeTree typeTree = typeCast.getClazz().getTree();
if (typeTree instanceof J.ParameterizedType) {
return ((J.ParameterizedType) typeTree).getTypeParameters().stream().allMatch(J.Wildcard.class::isInstance);
}
return true;
}

private boolean isTheSameAsOtherTypeCasts(J.TypeCast typeCast, J.InstanceOf instanceOf) {
return replacements
.entrySet()
.stream()
.filter(e -> e.getValue() == instanceOf)
.findFirst()
.map(e -> e.getKey().getType().equals(typeCast.getType()))
.orElse(true);
}

public boolean isEmpty() {
return replacements.isEmpty() && variablesToDelete.isEmpty();
}
Expand All @@ -225,24 +257,14 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) {
name,
type,
null));
JavaType.FullyQualified fqType = TypeUtils.asFullyQualified(type);
if (fqType != null && !fqType.getTypeParameters().isEmpty() && !(instanceOf.getClazz() instanceof J.ParameterizedType)) {
TypedTree oldTypeTree = (TypedTree) instanceOf.getClazz();

// Each type parameter is turned into a wildcard, i.e. `List` -> `List<?>` or `Map.Entry` -> `Map.Entry<?,?>`
List<Expression> wildcardsList = IntStream.range(0, fqType.getTypeParameters().size())
.mapToObj(i -> new J.Wildcard(randomId(), Space.EMPTY, Markers.EMPTY, null, null))
.collect(Collectors.toList());

J.ParameterizedType newTypeTree = new J.ParameterizedType(
randomId(),
oldTypeTree.getPrefix(),
Markers.EMPTY,
oldTypeTree.withPrefix(Space.EMPTY),
null,
oldTypeTree.getType()
).withTypeParameters(wildcardsList);
result = result.withClazz(newTypeTree);
J currentTypeTree = instanceOf.getClazz();
TypeTree typeCastTypeTree = computeTypeTreeFromTypeCasts(instanceOf);
// If type tree from typa cast is not parameterized then NVM. Instance of should already have proper type
if (typeCastTypeTree != null && typeCastTypeTree instanceof J.ParameterizedType) {
J.ParameterizedType parameterizedType = (J.ParameterizedType) typeCastTypeTree;
result = result.withClazz(parameterizedType.withId(Tree.randomId()).withPrefix(currentTypeTree.getPrefix()));
}

// update entry in replacements to share the pattern variable name
Expand All @@ -254,12 +276,29 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) {
return result;
}

private TypeTree computeTypeTreeFromTypeCasts(J.InstanceOf instanceOf) {
TypeTree typeCastTypeTree = replacements
.entrySet()
.stream()
.filter(e -> e.getValue() == instanceOf)
.findFirst()
.map(e -> e.getKey().getClazz().getTree())
.orElse(null);
if (typeCastTypeTree == null) {
VariableAndTypeTree variable = variablesToDelete.get(instanceOf);
if (variable != null) {
typeCastTypeTree = variable.getType();
}
}
return typeCastTypeTree;
}

private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) {
VariableNameStrategy strategy;
if (root instanceof J.If) {
J.VariableDeclarations.NamedVariable variable = variablesToDelete.get(instanceOf);
strategy = variable != null
? VariableNameStrategy.exact(variable.getSimpleName())
VariableAndTypeTree variableData = variablesToDelete.get(instanceOf);
strategy = variableData != null
? VariableNameStrategy.exact(variableData.getVariable().getSimpleName())
: VariableNameStrategy.normal(contextScopes.get(instanceOf));
} else {
strategy = VariableNameStrategy.short_();
Expand Down Expand Up @@ -288,7 +327,7 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) {
}

public @Nullable J processVariableDeclarations(J.VariableDeclarations multiVariable) {
return multiVariable.getVariables().stream().anyMatch(variablesToDelete::containsValue) ? null : multiVariable;
return multiVariable.getVariables().stream().anyMatch(v -> variablesToDelete.values().stream().anyMatch(vd -> vd.getVariable() == v)) ? null : multiVariable;
}
}

Expand Down
Loading

0 comments on commit 581f492

Please sign in to comment.