diff --git a/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java b/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java index a0527adc20c..6abb401894c 100644 --- a/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java @@ -95,6 +95,10 @@ public C setType(CtTypeReference type) { @Override public > C setTypeCasts(List> casts) { + if (casts == null || casts.isEmpty()) { + this.typeCasts = CtElementImpl.emptyList(); + return (C) this; + } if (this.typeCasts == CtElementImpl.>emptyList()) { this.typeCasts = new ArrayList<>(CASTS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtBlockImpl.java b/src/main/java/spoon/support/reflect/code/CtBlockImpl.java index 17a045b2fb2..8a2f7393159 100644 --- a/src/main/java/spoon/support/reflect/code/CtBlockImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtBlockImpl.java @@ -166,6 +166,10 @@ public > T insertBefore( @Override public T setStatements(List statements) { + if (statements == null || statements.isEmpty()) { + this.statements = CtElementImpl.emptyList(); + return (T) this; + } this.statements.clear(); for (CtStatement s : statements) { addStatement(s); diff --git a/src/main/java/spoon/support/reflect/code/CtCaseImpl.java b/src/main/java/spoon/support/reflect/code/CtCaseImpl.java index c8b7daeb2fb..6894597e7a4 100644 --- a/src/main/java/spoon/support/reflect/code/CtCaseImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtCaseImpl.java @@ -62,6 +62,10 @@ public > T setCaseExpression(CtExpression caseExpression) @Override public T setStatements(List statements) { + if (statements == null || statements.isEmpty()) { + this.statements = CtElementImpl.emptyList(); + return (T) this; + } this.statements.clear(); for (CtStatement stmt : statements) { addStatement(stmt); diff --git a/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java b/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java index 9ec384db983..58b37e5c25d 100644 --- a/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java @@ -95,6 +95,10 @@ public void replace(CtElement element) { @Override public > C setArguments(List> arguments) { + if (arguments == null || arguments.isEmpty()) { + this.arguments = CtElementImpl.emptyList(); + return (C) this; + } if (this.arguments == CtElementImpl.>emptyList()) { this.arguments = new ArrayList<>(PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java b/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java index b41bd77b5a0..06ab0d993bd 100644 --- a/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java @@ -53,6 +53,10 @@ public C setType(CtTypeReference type) { @Override public > C setTypeCasts(List> casts) { + if (casts == null || casts.isEmpty()) { + this.typeCasts = CtElementImpl.emptyList(); + return (C) this; + } if (this.typeCasts == CtElementImpl.>emptyList()) { this.typeCasts = new ArrayList<>(CASTS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtForImpl.java b/src/main/java/spoon/support/reflect/code/CtForImpl.java index 8ade00aa7a2..274bea715d8 100644 --- a/src/main/java/spoon/support/reflect/code/CtForImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtForImpl.java @@ -76,6 +76,10 @@ public T addForInit(CtStatement statement) { @Override public T setForInit(List statements) { + if (statements == null || statements.isEmpty()) { + this.forInit = CtElementImpl.emptyList(); + return (T) this; + } this.forInit.clear(); for (CtStatement stmt : statements) { addForInit(stmt); @@ -108,6 +112,10 @@ public T addForUpdate(CtStatement statement) { @Override public T setForUpdate(List statements) { + if (statements == null || statements.isEmpty()) { + this.forUpdate = CtElementImpl.emptyList(); + return (T) this; + } this.forUpdate.clear(); for (CtStatement stmt : statements) { addForUpdate(stmt); diff --git a/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java b/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java index 2c3bf1aad0a..2612e381697 100644 --- a/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java @@ -104,6 +104,10 @@ public C insertBefore(CtStatementList statements) { @Override public > C setArguments(List> arguments) { + if (arguments == null || arguments.isEmpty()) { + this.arguments = CtElementImpl.emptyList(); + return (C) this; + } if (this.arguments == CtElementImpl.>emptyList()) { this.arguments = new ArrayList<>(PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtLambdaImpl.java b/src/main/java/spoon/support/reflect/code/CtLambdaImpl.java index 1e2c87edca7..daf616bea86 100644 --- a/src/main/java/spoon/support/reflect/code/CtLambdaImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtLambdaImpl.java @@ -83,6 +83,10 @@ public List> getParameters() { @Override public > C setParameters(List> params) { + if (params == null || params.isEmpty()) { + this.parameters = CtElementImpl.emptyList(); + return (C) this; + } if (this.parameters == CtElementImpl.>emptyList()) { this.parameters = new ArrayList<>(PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } @@ -119,6 +123,10 @@ public Set> getThrownTypes() { @Override public > C setThrownTypes(Set> thrownTypes) { + if (thrownTypes == null || thrownTypes.isEmpty()) { + this.thrownTypes = CtElementImpl.emptySet(); + return (C) this; + } if (this.thrownTypes == CtElementImpl.>emptySet()) { this.thrownTypes = new TreeSet<>(); } diff --git a/src/main/java/spoon/support/reflect/code/CtNewArrayImpl.java b/src/main/java/spoon/support/reflect/code/CtNewArrayImpl.java index 93133be6526..0264b60f737 100644 --- a/src/main/java/spoon/support/reflect/code/CtNewArrayImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtNewArrayImpl.java @@ -50,6 +50,10 @@ public List> getElements() { @Override public > C setDimensionExpressions(List> dimensionExpressions) { + if (dimensionExpressions == null || dimensionExpressions.isEmpty()) { + this.dimensionExpressions = CtElementImpl.emptyList(); + return (C) this; + } this.dimensionExpressions.clear(); for (CtExpression expr : dimensionExpressions) { addDimensionExpression(expr); @@ -79,6 +83,10 @@ public boolean removeDimensionExpression(CtExpression dimension) { @Override public > C setElements(List> expressions) { + if (expressions == null || expressions.isEmpty()) { + this.expressions = CtElementImpl.emptyList(); + return (C) this; + } this.expressions.clear(); for (CtExpression expr : expressions) { addElement(expr); diff --git a/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java b/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java index 007ff9da9c2..101ad531347 100644 --- a/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java @@ -47,6 +47,10 @@ public List getStatements() { @Override public T setStatements(List stmts) { + if (stmts == null || stmts.isEmpty()) { + this.statements = CtElementImpl.emptyList(); + return (T) this; + } this.statements.clear(); for (CtStatement stmt : stmts) { addStatement(stmt); diff --git a/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java b/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java index 378725d015b..412ad1d8068 100644 --- a/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java @@ -51,6 +51,10 @@ public CtExpression getSelector() { @Override public > T setCases(List> cases) { + if (cases == null || cases.isEmpty()) { + this.cases = CtElementImpl.emptyList(); + return (T) this; + } this.cases.clear(); for (CtCase aCase : cases) { addCase(aCase); diff --git a/src/main/java/spoon/support/reflect/code/CtTryImpl.java b/src/main/java/spoon/support/reflect/code/CtTryImpl.java index 47c26a770e5..f483742ef6a 100644 --- a/src/main/java/spoon/support/reflect/code/CtTryImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtTryImpl.java @@ -50,6 +50,10 @@ public List getCatchers() { @Override public T setCatchers(List catchers) { + if (catchers == null || catchers.isEmpty()) { + this.catchers = CtElementImpl.emptyList(); + return (T) this; + } this.catchers.clear(); for (CtCatch c : catchers) { addCatcher(c); diff --git a/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java b/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java index 0efd44f692d..fa6963486f3 100644 --- a/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java @@ -43,6 +43,10 @@ public List> getResources() { @Override public T setResources(List> resources) { + if (resources == null || resources.isEmpty()) { + this.resources = CtElementImpl.emptyList(); + return (T) this; + } this.resources.clear(); for (CtLocalVariable l : resources) { addResource(l); diff --git a/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java b/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java index d08df90f2b2..1c0a1de089f 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java @@ -110,6 +110,10 @@ public CtTypeReference getSuperclass() { @Override public > C setAnonymousExecutables(List anonymousExecutables) { + if (anonymousExecutables == null || anonymousExecutables.isEmpty()) { + this.anonymousExecutables = CtElementImpl.emptyList(); + return (C) this; + } if (this.anonymousExecutables == CtElementImpl.emptyList()) { this.anonymousExecutables = new ArrayList<>(ANONYMOUS_EXECUTABLES_CONTAINER_DEFAULT_CAPACITY); } @@ -122,6 +126,10 @@ public > C setAnonymousExecutables(List> C setConstructors(Set> constructors) { + if (constructors == null || constructors.isEmpty()) { + this.constructors = CtElementImpl.emptySet(); + return (C) this; + } if (this.constructors == CtElementImpl.>emptySet()) { this.constructors = new TreeSet<>(); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtConstructorImpl.java b/src/main/java/spoon/support/reflect/declaration/CtConstructorImpl.java index 143e7f8bb12..2da05ccaa1f 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtConstructorImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtConstructorImpl.java @@ -96,6 +96,10 @@ public T addFormalTypeParameter(CtTypeParameter @Override public T setFormalTypeParameters(List formalTypeParameters) { + if (formalTypeParameters == null || formalTypeParameters.isEmpty()) { + this.formalTypeParameters = CtElementImpl.emptyList(); + return (T) this; + } if (this.formalTypeParameters == CtElementImpl.emptyList()) { this.formalTypeParameters = new ArrayList<>(CONSTRUCTOR_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java index aa03addb5e9..b39493d4b74 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java @@ -198,6 +198,10 @@ public int hashCode() { } public E setAnnotations(List> annotations) { + if (annotations == null || annotations.isEmpty()) { + this.annotations = CtElementImpl.emptyList(); + return (E) this; + } this.annotations.clear(); for (CtAnnotation annot : annotations) { addAnnotation(annot); @@ -437,6 +441,10 @@ public E removeComment(CtComment comment) { @Override public E setComments(List comments) { + if (comments == null || comments.isEmpty()) { + this.comments = CtElementImpl.emptyList(); + return (E) this; + } this.comments.clear(); for (CtComment comment : comments) { addComment(comment); diff --git a/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java b/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java index 4dfc83bc9ed..858d79ef357 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java @@ -69,6 +69,10 @@ public List> getParameters() { @Override public > T setParameters(List> parameters) { + if (parameters == null || parameters.isEmpty()) { + this.parameters = CtElementImpl.emptyList(); + return (T) this; + } if (this.parameters == CtElementImpl.>emptyList()) { this.parameters = new ArrayList<>(PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } @@ -104,6 +108,10 @@ public Set> getThrownTypes() { @Override public > T setThrownTypes(Set> thrownTypes) { + if (thrownTypes == null || thrownTypes.isEmpty()) { + this.thrownTypes = CtElementImpl.emptySet(); + return (T) this; + } if (this.thrownTypes == CtElementImpl.>emptySet()) { this.thrownTypes = new TreeSet<>(); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java b/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java index ab1e7910aea..c4d026ebaf3 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java @@ -110,6 +110,10 @@ public T addFormalTypeParameter(CtTypeParameter @Override public T setFormalTypeParameters(List formalTypeParameters) { + if (formalTypeParameters == null || formalTypeParameters.isEmpty()) { + this.formalTypeParameters = CtElementImpl.emptyList(); + return (T) this; + } if (this.formalTypeParameters == CtElementImpl.emptyList()) { this.formalTypeParameters = new ArrayList<>(METHOD_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java b/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java index 828d84dd016..df1cb7033cc 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java @@ -150,6 +150,10 @@ public Set> getTypes() { @Override public T setPackages(Set packs) { + if (packs == null || packs.isEmpty()) { + this.packs = CtElementImpl.emptySet(); + return (T) this; + } this.packs.clear(); for (CtPackage p : packs) { addPackage(p); @@ -159,6 +163,10 @@ public T setPackages(Set packs) { @Override public T setTypes(Set> types) { + if (types == null || types.isEmpty()) { + this.types = CtElementImpl.emptySet(); + return (T) this; + } this.types.clear(); for (CtType t : types) { addType(t); diff --git a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java index c4e90685c6c..43ddb30d39a 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java @@ -124,6 +124,10 @@ public > C addField(int index, CtField field) { @Override public > C setFields(List> fields) { + if (fields == null || fields.isEmpty()) { + this.fields = CtElementImpl.emptyList(); + return (C) this; + } this.fields.clear(); for (CtField field : fields) { addField(field); @@ -182,6 +186,10 @@ public boolean removeNestedType(CtType nestedType) { @Override public > C setNestedTypes(Set> nestedTypes) { + if (nestedTypes == null || nestedTypes.isEmpty()) { + this.nestedTypes = CtElementImpl.emptySet(); + return (C) this; + } if (this.nestedTypes == CtElementImpl.>emptySet()) { this.nestedTypes = new TreeSet<>(); } @@ -673,6 +681,10 @@ public Set> getSuperInterfaces() { @Override public C setFormalTypeParameters(List formalTypeParameters) { + if (formalTypeParameters == null || formalTypeParameters.isEmpty()) { + this.formalTypeParameters = CtElementImpl.emptyList(); + return (C) this; + } if (this.formalTypeParameters == CtElementImpl.emptyList()) { this.formalTypeParameters = new ArrayList<>(TYPE_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } @@ -685,6 +697,10 @@ public C setFormalTypeParameters(List> C setMethods(Set> methods) { + if (methods == null || methods.isEmpty()) { + this.methods = CtElementImpl.emptySet(); + return (C) this; + } this.methods.clear(); for (CtMethod meth : methods) { addMethod(meth); @@ -694,6 +710,10 @@ public > C setMethods(Set> methods) { @Override public > C setSuperInterfaces(Set> interfaces) { + if (interfaces == null || interfaces.isEmpty()) { + this.interfaces = CtElementImpl.emptySet(); + return (C) this; + } if (this.interfaces == CtElementImpl.>emptySet()) { this.interfaces = new TreeSet<>(); } diff --git a/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java index 1f210298929..841f93bfdd1 100644 --- a/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java @@ -184,7 +184,7 @@ public List> getParameters() { @Override public > C setParameters(List> parameters) { - if (parameters.isEmpty()) { + if (parameters == null || parameters.isEmpty()) { this.parameters = CtElementImpl.emptyList(); return (C) this; } @@ -242,8 +242,11 @@ public boolean isOverriding(CtExecutableReference executable) { } @Override - public C setActualTypeArguments( - List> actualTypeArguments) { + public C setActualTypeArguments(List> actualTypeArguments) { + if (actualTypeArguments == null || actualTypeArguments.isEmpty()) { + this.actualTypeArguments = CtElementImpl.emptyList(); + return (C) this; + } if (this.actualTypeArguments == CtElementImpl.>emptyList()) { this.actualTypeArguments = new ArrayList<>(); } diff --git a/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java index 74010dc6ce6..1a1fca3282a 100644 --- a/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java @@ -42,6 +42,10 @@ public Set> getBounds() { @Override public C setBounds(Set> bounds) { + if (bounds == null || bounds.isEmpty()) { + this.bounds = CtElementImpl.emptySet(); + return (C) this; + } if (this.bounds == CtElementImpl.>emptySet()) { this.bounds = new TreeSet<>(new SourcePositionComparator()); } diff --git a/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java index 344a1a0fbf4..a7472a4289c 100644 --- a/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java @@ -269,6 +269,10 @@ public boolean isSubtypeOf(CtTypeReference type) { @Override public C setActualTypeArguments(List> actualTypeArguments) { + if (actualTypeArguments == null || actualTypeArguments.isEmpty()) { + this.actualTypeArguments = CtElementImpl.emptyList(); + return (C) this; + } if (this.actualTypeArguments == CtElementImpl.>emptyList()) { this.actualTypeArguments = new ArrayList<>(TYPE_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/test/java/spoon/reflect/ast/IntercessionScanner.java b/src/test/java/spoon/reflect/ast/IntercessionScanner.java index a1041e46d63..d5e46f7c5dc 100644 --- a/src/test/java/spoon/reflect/ast/IntercessionScanner.java +++ b/src/test/java/spoon/reflect/ast/IntercessionScanner.java @@ -17,14 +17,16 @@ public abstract class IntercessionScanner extends CtScanner { protected final Factory factory; protected final List> COLLECTIONS; protected final CtTypeReference CTELEMENT_REFERENCE; + protected final CtTypeReference COLLECTION_REFERENCE; + protected final CtTypeReference LIST_REFERENCE; + protected final CtTypeReference SET_REFERENCE; public IntercessionScanner(Factory factory) { this.factory = factory; - COLLECTIONS = Arrays.asList( // - factory.Type().createReference(Collection.class), // - factory.Type().createReference(List.class), // - factory.Type().createReference(Set.class) // - ); + COLLECTION_REFERENCE = factory.Type().createReference(Collection.class); + LIST_REFERENCE = factory.Type().createReference(List.class); + SET_REFERENCE = factory.Type().createReference(Set.class); + COLLECTIONS = Arrays.asList(COLLECTION_REFERENCE, LIST_REFERENCE, SET_REFERENCE); CTELEMENT_REFERENCE = factory.Type().createReference(CtElement.class); } @@ -65,4 +67,21 @@ protected boolean takeSetterForCtElement(CtMethod candidate) { protected boolean avoidInterfaces(CtMethod candidate) { return candidate.getBody() != null; } + + protected boolean avoidSpecificMethods(CtMethod candidate) { + return !(candidate.getSimpleName().equals("setType") && candidate.getDeclaringType().getSimpleName().equals("CtConstructorCallImpl")) // + && !(candidate.getSimpleName().equals("addActualTypeArgument") && candidate.getDeclaringType().getSimpleName().equals("CtConstructorCallImpl")) // + && !(candidate.getSimpleName().equals("setActualTypeArguments") && candidate.getDeclaringType().getSimpleName().equals("CtConstructorCallImpl")) // + && !(candidate.getSimpleName().equals("setType") && candidate.getDeclaringType().getSimpleName().equals("CtInvocationImpl")) // + && !(candidate.getSimpleName().equals("addActualTypeArgument") && candidate.getDeclaringType().getSimpleName().equals("CtInvocationImpl")) // + && !(candidate.getSimpleName().equals("setActualTypeArguments") && candidate.getDeclaringType().getSimpleName().equals("CtInvocationImpl")) // + && !(candidate.getSimpleName().equals("setAssignment") && candidate.getDeclaringType().getSimpleName().equals("CtLocalVariableImpl")) // + && !(candidate.getSimpleName().equals("setType") && candidate.getDeclaringType().getSimpleName().equals("CtTypeAccessImpl")) // + && !(candidate.getSimpleName().equals("setAssignment") && candidate.getDeclaringType().getSimpleName().equals("CtFieldImpl")) // + && !(candidate.getSimpleName().equals("addField") && candidate.getDeclaringType().getSimpleName().equals("CtAnnotationTypeImpl")) // + && !(candidate.getSimpleName().equals("addFieldAtTop") && candidate.getDeclaringType().getSimpleName().equals("CtAnnotationTypeImpl")) // + && !(candidate.getSimpleName().equals("setFields") && candidate.getDeclaringType().getSimpleName().equals("CtAnnotationTypeImpl")) // + && !(candidate.getSimpleName().equals("setBounds") && candidate.getDeclaringType().getSimpleName().equals("CtTypeParameterReferenceImpl")) // + && !candidate.getSimpleName().equals("setDeclaration"); + } } diff --git a/src/test/java/spoon/reflect/ast/ParentTest.java b/src/test/java/spoon/reflect/ast/ParentTest.java index 493073ee1ae..e5ea2fd9ced 100644 --- a/src/test/java/spoon/reflect/ast/ParentTest.java +++ b/src/test/java/spoon/reflect/ast/ParentTest.java @@ -45,19 +45,6 @@ public void testParentSetInSetter() throws Exception { // Asserts. new IntercessionScanner(launcher.getFactory()) { - private boolean avoidSpecificMethods(CtMethod candidate) { - return !(candidate.getSimpleName().equals("setType") && candidate.getDeclaringType().getSimpleName().equals("CtConstructorCallImpl")) // - && !(candidate.getSimpleName().equals("addActualTypeArgument") && candidate.getDeclaringType().getSimpleName().equals("CtConstructorCallImpl")) // - && !(candidate.getSimpleName().equals("setType") && candidate.getDeclaringType().getSimpleName().equals("CtInvocationImpl")) // - && !(candidate.getSimpleName().equals("addActualTypeArgument") && candidate.getDeclaringType().getSimpleName().equals("CtInvocationImpl")) // - && !(candidate.getSimpleName().equals("setAssignment") && candidate.getDeclaringType().getSimpleName().equals("CtLocalVariableImpl")) // - && !(candidate.getSimpleName().equals("setType") && candidate.getDeclaringType().getSimpleName().equals("CtTypeAccessImpl")) // - && !(candidate.getSimpleName().equals("setAssignment") && candidate.getDeclaringType().getSimpleName().equals("CtFieldImpl")) // - && !(candidate.getSimpleName().equals("addField") && candidate.getDeclaringType().getSimpleName().equals("CtAnnotationTypeImpl")) // - && !(candidate.getSimpleName().equals("addFieldAtTop") && candidate.getDeclaringType().getSimpleName().equals("CtAnnotationTypeImpl")) // - && !candidate.getSimpleName().equals("setDeclaration"); - } - @Override protected boolean isToBeProcessed(CtMethod candidate) { return (candidate.getSimpleName().startsWith("set") // diff --git a/src/test/java/spoon/test/intercession/IntercessionTest.java b/src/test/java/spoon/test/intercession/IntercessionTest.java index bd81e65a281..2f6e94161ad 100644 --- a/src/test/java/spoon/test/intercession/IntercessionTest.java +++ b/src/test/java/spoon/test/intercession/IntercessionTest.java @@ -3,22 +3,33 @@ import org.junit.Assert; import org.junit.Test; import spoon.Launcher; +import spoon.reflect.ast.IntercessionScanner; +import spoon.reflect.code.BinaryOperatorKind; +import spoon.reflect.code.CtAssignment; +import spoon.reflect.code.CtBinaryOperator; import spoon.reflect.code.CtBlock; import spoon.reflect.code.CtCodeSnippetStatement; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtIf; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.code.CtLiteral; import spoon.reflect.code.CtReturn; import spoon.reflect.code.CtStatement; import spoon.reflect.code.CtThrow; +import spoon.reflect.code.CtVariableAccess; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtConstructor; import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.declaration.ModifierKind; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtTypeParameterReference; +import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.Query; import spoon.reflect.visitor.filter.AbstractFilter; import java.io.File; +import java.util.Collection; import java.util.List; import static org.junit.Assert.assertEquals; @@ -221,4 +232,111 @@ public boolean matches(CtMethod element) { assertTrue("The type of " + methodLog + " don't match with generic types.", isMatch); } } + + @Test + public void testResetCollectionInSetters() throws Exception { + final Launcher launcher = new Launcher(); + final Factory factory = launcher.getFactory(); + launcher.getEnvironment().setNoClasspath(true); + // interfaces. + launcher.addInputResource("./src/main/java/spoon/reflect/code"); + launcher.addInputResource("./src/main/java/spoon/reflect/declaration"); + launcher.addInputResource("./src/main/java/spoon/reflect/reference"); + // implementations. + launcher.addInputResource("./src/main/java/spoon/support/reflect/code"); + launcher.addInputResource("./src/main/java/spoon/support/reflect/declaration"); + launcher.addInputResource("./src/main/java/spoon/support/reflect/reference"); + launcher.buildModel(); + + new IntercessionScanner(factory) { + + @Override + protected boolean isToBeProcessed(CtMethod candidate) { + return candidate.getSimpleName().startsWith("set") // + && candidate.hasModifier(ModifierKind.PUBLIC) // + && takeSetterCollection(candidate) // + && avoidInterfaces(candidate) // + && avoidSpecificMethods(candidate) // + && avoidThrowUnsupportedOperationException(candidate); + } + + private boolean takeSetterCollection(CtMethod candidate) { + final CtTypeReference type = candidate.getParameters().get(0).getType(); + final List> actualTypeArguments = type.getActualTypeArguments(); + return COLLECTIONS.contains(type) && actualTypeArguments.size() == 1 && actualTypeArguments.get(0).isSubtypeOf(CTELEMENT_REFERENCE); + } + + @Override + protected void process(CtMethod element) { + final CtStatement statement = element.getBody().getStatement(0); + if (!(statement instanceof CtIf)) { + fail(log(element, "First statement should be an if to check the parameter of the setter")); + } + final CtIf anIf = (CtIf) statement; + if (!createCheckNull(element.getParameters().get(0)).equals(anIf.getCondition())) { + fail(log(element, "Condition should test if the parameter is null.\nThe condition was " + anIf.getCondition())); + } + + if (!(anIf.getThenStatement() instanceof CtBlock)) { + fail(log(element, "Should have a block in the if condition to have the initialization and the return.")); + } + + if (element.getParameters().get(0).getType().equals(SET_REFERENCE)) { + if (!hasCallEmptyInv(anIf.getThenStatement(), SET_REFERENCE)) { + fail(log(element, "Should initilize the list with CtElementImpl#emptySet().")); + } + } else { + if (!hasCallEmptyInv(anIf.getThenStatement(), LIST_REFERENCE)) { + fail(log(element, "Should initilize the list with CtElementImpl#emptyList().")); + } + } + } + + private boolean hasCallEmptyInv(CtBlock thenStatement, CtTypeReference collectionReference) { + if (!(thenStatement.getStatement(0) instanceof CtAssignment)) { + return false; + } + final CtExpression assignment = ((CtAssignment) thenStatement.getStatement(0)).getAssignment(); + if (!(assignment instanceof CtInvocation)) { + return false; + } + final CtInvocation inv = (CtInvocation) assignment; + if (collectionReference.equals(SET_REFERENCE)) { + if (!inv.getExecutable().getSimpleName().equals("emptySet")) { + return false; + } + } else if (collectionReference.equals(LIST_REFERENCE)) { + if (!inv.getExecutable().getSimpleName().equals("emptyList")) { + return false; + } + } + return true; + } + + /** + * Creates list == null && list.isEmpty(). + * + * @param ctParameter list + */ + private CtBinaryOperator createCheckNull(CtParameter ctParameter) { + final CtVariableAccess variableRead = factory.Code().createVariableRead(ctParameter.getReference(), true); + + final CtLiteral nullLiteral = factory.Code().createLiteral(null); + nullLiteral.setType(factory.Type().nullType()); + + final CtBinaryOperator checkNull = factory.Code().createBinaryOperator(variableRead, nullLiteral, BinaryOperatorKind.EQ); + checkNull.setType(factory.Type().BOOLEAN_PRIMITIVE); + + final CtMethod isEmptyMethod = ctParameter.getType().getTypeDeclaration().getMethod(factory.Type().booleanPrimitiveType(), "isEmpty"); + final CtInvocation isEmpty = factory.Code().createInvocation(variableRead, isEmptyMethod.getReference()); + + final CtBinaryOperator condition = factory.Code().createBinaryOperator(checkNull, isEmpty, BinaryOperatorKind.OR); + return condition.setType(factory.Type().booleanPrimitiveType()); + } + + private String log(CtMethod element, String message) { + return message + "\nin " + element.getSignature() + "\ndeclared in " + element.getDeclaringType().getQualifiedName(); + } + }.scan(factory.getModel().getRootPackage()); + } }