Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotation change refactor - enable setting type literals #119

Merged
merged 2 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.alfasoftware.astra.core.refactoring.operations.annotations;

import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -20,6 +21,7 @@
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jface.text.BadLocationException;
Expand All @@ -40,15 +42,17 @@ public class AnnotationChangeRefactor implements ASTOperation {
private final AnnotationMatcher fromType;
private final String toType;
private final Map<String, String> membersAndValuesToAdd;
private final Map<String, String> membersAndTypesToAdd;
private final Set<String> namesForMembersToRemove;
private final Map<String, String> memberNameUpdates;
private final Map<String, String> memberNamesToUpdateWithNewValues;
private final Optional<Transform> transform;

public AnnotationChangeRefactor(AnnotationMatcher fromType, String toType, Map<String, String> membersAndValuesToAdd, Set<String> namesForMembersToRemove, Map<String, String> memberNameUpdates, Map<String, String> memberNamesToUpdateWithNewValues, Optional<Transform> transform) {
public AnnotationChangeRefactor(AnnotationMatcher fromType, String toType, Map<String, String> membersAndValuesToAdd, Map<String,String> memberAndTypesToAdd, Set<String> namesForMembersToRemove, Map<String, String> memberNameUpdates, Map<String, String> memberNamesToUpdateWithNewValues, Optional<Transform> transform) {
this.fromType = fromType;
this.toType = toType;
this.membersAndValuesToAdd = membersAndValuesToAdd;
this.membersAndTypesToAdd = memberAndTypesToAdd;
this.namesForMembersToRemove = namesForMembersToRemove;
this.memberNameUpdates = memberNameUpdates;
this.memberNamesToUpdateWithNewValues = memberNamesToUpdateWithNewValues;
Expand Down Expand Up @@ -77,6 +81,7 @@ public static class Builder {
private AnnotationMatcher fromType;
private String toType;
private Map<String, String> membersAndValuesToAdd = Map.of();
private Map<String, String> membersAndTypesToAdd = Map.of();
private Set<String> namesForMembersToRemove = Set.of();
private Map<String, String> memberNameUpdates = Map.of();
private Optional<Transform> transform = Optional.empty();
Expand All @@ -102,6 +107,11 @@ public Builder addMemberNameValuePairs(Map<String, String> membersAndValuesToAdd
return this;
}

public Builder addMemberNameTypePairs(Map<String, String> membersAndTypesToAdd) {
this.membersAndTypesToAdd = membersAndTypesToAdd;
return this;
}

public Builder updateMemberName(Map<String, String> currentToNewName) {
this.memberNameUpdates = currentToNewName;
return this;
Expand All @@ -126,6 +136,7 @@ public AnnotationChangeRefactor build() {
return new AnnotationChangeRefactor(fromType,
toType,
membersAndValuesToAdd,
membersAndTypesToAdd,
namesForMembersToRemove,
memberNameUpdates,
memberNamesToUpdateWithNewValues,
Expand Down Expand Up @@ -199,14 +210,14 @@ private Annotation changeAnnotationName(ASTRewrite rewriter, Annotation annotati


private Annotation addNewMembersToAnnotation(CompilationUnit compilationUnit, ASTRewrite rewriter, Annotation annotation) {
if (!membersAndValuesToAdd.isEmpty()) {
if (!membersAndValuesToAdd.isEmpty() || !membersAndTypesToAdd.isEmpty()) {
final NormalAnnotation normalAnnotation;
if (annotation.isMarkerAnnotation() || annotation.isSingleMemberAnnotation()) {
normalAnnotation = convertAnnotationToNormalAnnotation(rewriter, annotation);
} else {
normalAnnotation = (NormalAnnotation) annotation;
}
addMembersToNormalAnnotation(compilationUnit, rewriter, normalAnnotation, membersAndValuesToAdd);
addMembersToNormalAnnotation(compilationUnit, rewriter, normalAnnotation, membersAndValuesToAdd, membersAndTypesToAdd);
return normalAnnotation;
}
return annotation;
Expand Down Expand Up @@ -332,24 +343,51 @@ private MarkerAnnotation convertAnnotationToMarkerAnnotation(ASTRewrite rewriter
}


private void addMembersToNormalAnnotation(CompilationUnit compilationUnit, ASTRewrite rewriter, NormalAnnotation normalAnnotation, Map<String, String> membersAndValuesToAdd) {
private void addMembersToNormalAnnotation(CompilationUnit compilationUnit, ASTRewrite rewriter, NormalAnnotation normalAnnotation, Map<String, String> membersAndValuesToAdd, Map<String, String> membersAndTypesToAdd) {
final ListRewrite listRewrite = rewriter.getListRewrite(normalAnnotation, NormalAnnotation.VALUES_PROPERTY);
@SuppressWarnings("unchecked")
List<MemberValuePair> originalMembers = listRewrite.getOriginalList();
final List<String> originalMemberNames = originalMembers.stream().map(memberValuePair -> memberValuePair.getName().getIdentifier()).collect(Collectors.toList());
membersAndValuesToAdd.forEach((memberName, value) -> {
if(originalMemberNames.contains(memberName)) {
log.warn("A new member value pair with name " + memberName +
" was not added to annotation in this type " + AstraUtils.getNameForCompilationUnit(compilationUnit) + " as it already exists. " +
"If you wish to overwrite the existing value, please configure the " + AnnotationChangeRefactor.class.getSimpleName() + "to update the value for an existing member name.");

membersAndValuesToAdd.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
if(originalMemberNames.contains(entry.getKey())) {
logWarning(compilationUnit, entry.getKey());
return;
}
MemberValuePair newMemberAndValue = rewriter.getAST().newMemberValuePair();
newMemberAndValue.setName(rewriter.getAST().newSimpleName(memberName));
newMemberAndValue.setName(rewriter.getAST().newSimpleName(entry.getKey()));
final StringLiteral valueLiteral = rewriter.getAST().newStringLiteral();
valueLiteral.setLiteralValue(value);
valueLiteral.setLiteralValue(entry.getValue());
newMemberAndValue.setValue(valueLiteral);
listRewrite.insertLast(newMemberAndValue, null);
});

membersAndTypesToAdd.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
if(originalMemberNames.contains(entry.getKey())) {
logWarning(compilationUnit, entry.getKey());
return;
}
MemberValuePair newMemberAndValue = rewriter.getAST().newMemberValuePair();
newMemberAndValue.setName(rewriter.getAST().newSimpleName(entry.getKey()));
final TypeLiteral typeLiteral = rewriter.getAST().newTypeLiteral();

// Handle both qualified and simple types
if (entry.getValue().contains(".")) {
String[] qualifiedName = entry.getValue().split("\\.");
typeLiteral.setType(rewriter.getAST().newNameQualifiedType(rewriter.getAST().newName(qualifiedName[0]), rewriter.getAST().newSimpleName(qualifiedName[1])));
} else {
typeLiteral.setType(rewriter.getAST().newSimpleType(rewriter.getAST().newSimpleName(entry.getValue())));
}

newMemberAndValue.setValue(typeLiteral);
listRewrite.insertLast(newMemberAndValue, null);
});
}


private static void logWarning(CompilationUnit compilationUnit, String memberName) {
log.warn("A new member value pair with name " + memberName +
" was not added to annotation in this type " + AstraUtils.getNameForCompilationUnit(compilationUnit) + " as it already exists. " +
"If you wish to overwrite the existing value, please configure the " + AnnotationChangeRefactor.class.getSimpleName() + "to update the value for an existing member name.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
import org.alfasoftware.astra.exampleTypes.AnnotationB;
import org.alfasoftware.astra.exampleTypes.AnnotationC;
import org.alfasoftware.astra.exampleTypes.AnnotationD;
import org.alfasoftware.astra.exampleTypes.AnnotationE;
import org.alfasoftware.astra.exampleTypes.B.InnerAnnotationB;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.junit.Test;

import static org.alfasoftware.astra.core.utils.AstraUtils.addImport;

public class TestAnnotationsRefactor extends AbstractRefactorTest {

@Test
Expand Down Expand Up @@ -221,6 +224,28 @@ public void testUpdateMemberValue() {
}


/**
* Example covers:
* - updating the member of an annotation with a simple type literal
* - updating the member of an annotation with a qualified type literal
*/
@Test
public void testUpdateMemberType() {
assertRefactor(
UpdateMemberTypeInAnnotationExample.class,
new HashSet<>(Arrays.asList(
AnnotationChangeRefactor.builder()
.from(AnnotationMatcher.builder()
.withFullyQualifiedName(AnnotationE.class.getName())
.build())
.to(AnnotationE.class.getName())
.addMemberNameTypePairs(Map.of("type", "Integer", "anotherType", "WithNestedClass.NestedClass"))
.withTransform((cu, a, rw) -> addImport(cu, "org.alfasoftware.astra.exampleTypes.WithNestedClass", rw))
.build()
)));
}


/**
* That a custom predicate can be used to identify an annotation to refactor,
* and that a custom transformation can be specified - in this case, that the whole annotation should be removed.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.alfasoftware.astra.core.refactoring.annotations;

import org.alfasoftware.astra.exampleTypes.AnnotationE;

public class UpdateMemberTypeInAnnotationExample {

@AnnotationE
protected long someField;

@AnnotationE(value = "A string of no importance")
protected String anotherField;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.alfasoftware.astra.core.refactoring.annotations;

import org.alfasoftware.astra.exampleTypes.AnnotationE;
import org.alfasoftware.astra.exampleTypes.WithNestedClass;

public class UpdateMemberTypeInAnnotationExampleAfter {

@AnnotationE(anotherType = WithNestedClass.NestedClass.class, type = Integer.class)
protected long someField;

@AnnotationE(value = "A string of no importance", anotherType = WithNestedClass.NestedClass.class, type = Integer.class)
protected String anotherField;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.alfasoftware.astra.exampleTypes;

public @interface AnnotationE {

String value() default "";

Class<?> type() default void.class;

Class<?> anotherType() default void.class;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.alfasoftware.astra.exampleTypes;

public class WithNestedClass {

public static class NestedClass {}
}
Loading