diff --git a/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java b/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java index 2784a08e2cc..7ebb66f6415 100644 --- a/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtAssignmentImpl.java @@ -107,6 +107,9 @@ public > C setTypeCasts(List> casts @Override public > C addTypeCast(CtTypeReference type) { + if (type == null) { + return (C) this; + } if (typeCasts == CtElementImpl.>emptyList()) { 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 009bbb399a4..c9c68021c1d 100644 --- a/src/main/java/spoon/support/reflect/code/CtBlockImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtBlockImpl.java @@ -129,8 +129,7 @@ public > T insertEnd(CtStatementList statements) { } @Override - public > T insertAfter(Filter insertionPoints, - CtStatement statement) { + public > T insertAfter(Filter insertionPoints, CtStatement statement) { for (CtStatement e : Query.getElements(this, insertionPoints)) { e.insertAfter(statement); } @@ -178,6 +177,9 @@ public T setStatements(List statements) @Override public T addStatement(CtStatement statement) { + if (statement == null) { + return (T) this; + } ensureModifiableStatementsList(); statement.setParent(this); this.statements.add(statement); diff --git a/src/main/java/spoon/support/reflect/code/CtCaseImpl.java b/src/main/java/spoon/support/reflect/code/CtCaseImpl.java index 1606e759f81..087eb410ac8 100644 --- a/src/main/java/spoon/support/reflect/code/CtCaseImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtCaseImpl.java @@ -53,7 +53,9 @@ public List getStatements() { @Override public > T setCaseExpression(CtExpression caseExpression) { - caseExpression.setParent(this); + if (caseExpression != null) { + caseExpression.setParent(this); + } this.caseExpression = caseExpression; return (T) this; } @@ -69,6 +71,9 @@ public T setStatements(List statements) @Override public T addStatement(CtStatement statement) { + if (statement == null) { + return (T) this; + } if (statements == CtElementImpl.emptyList()) { statements = new ArrayList(CASE_STATEMENTS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtCatchImpl.java b/src/main/java/spoon/support/reflect/code/CtCatchImpl.java index ab6358490d5..d8eeedc89c4 100644 --- a/src/main/java/spoon/support/reflect/code/CtCatchImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtCatchImpl.java @@ -45,14 +45,18 @@ public CtCatchVariable getParameter() { @Override public T setBody(CtBlock body) { - body.setParent(this); + if (body != null) { + body.setParent(this); + } this.body = body; return (T) this; } @Override public T setParameter(CtCatchVariable parameter) { - parameter.setParent(this); + if (parameter != null) { + parameter.setParent(this); + } this.parameter = parameter; return (T) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java b/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java index 91401864949..ef252416388 100644 --- a/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java @@ -94,6 +94,9 @@ public C setType(CtTypeReference type) { @Override public T addMultiType(CtTypeReference ref) { + if (ref == null) { + return (T) this; + } if (types == CtElementImpl.>emptyList()) { types = new ArrayList>( CATCH_VARIABLE_MULTI_TYPES_CONTAINER_DEFAULT_CAPACITY); diff --git a/src/main/java/spoon/support/reflect/code/CtConditionalImpl.java b/src/main/java/spoon/support/reflect/code/CtConditionalImpl.java index b736eb89af6..a1f880814b5 100644 --- a/src/main/java/spoon/support/reflect/code/CtConditionalImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtConditionalImpl.java @@ -51,21 +51,27 @@ public CtExpression getThenExpression() { @Override public > C setElseExpression(CtExpression elseExpression) { - elseExpression.setParent(this); + if (elseExpression != null) { + elseExpression.setParent(this); + } this.elseExpression = elseExpression; return (C) this; } @Override public > C setCondition(CtExpression condition) { - condition.setParent(this); + if (condition != null) { + condition.setParent(this); + } this.condition = condition; return (C) this; } @Override public > C setThenExpression(CtExpression thenExpression) { - thenExpression.setParent(this); + if (thenExpression != null) { + thenExpression.setParent(this); + } this.thenExpression = thenExpression; return (C) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java b/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java index 65a4a7ca3f1..051719e7117 100644 --- a/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtConstructorCallImpl.java @@ -107,6 +107,9 @@ public > C setArguments(List> @Override public > C addArgument(CtExpression argument) { + if (argument == null) { + return (C) this; + } if (arguments == CtElementImpl.>emptyList()) { arguments = new ArrayList>(PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtDoImpl.java b/src/main/java/spoon/support/reflect/code/CtDoImpl.java index 7853bd95801..6185201f84a 100644 --- a/src/main/java/spoon/support/reflect/code/CtDoImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtDoImpl.java @@ -37,7 +37,9 @@ public CtExpression getLoopingExpression() { @Override public T setLoopingExpression(CtExpression expression) { - expression.setParent(this); + if (expression != null) { + expression.setParent(this); + } this.expression = expression; return (T) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java b/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java index 0ff4f44f9cc..0a0017a9937 100644 --- a/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtExpressionImpl.java @@ -66,6 +66,9 @@ public > C setTypeCasts(List> casts @Override public > C addTypeCast(CtTypeReference type) { + if (type == null) { + return (C) this; + } if (typeCasts == CtElementImpl.>emptyList()) { typeCasts = new ArrayList>( CASTS_CONTAINER_DEFAULT_CAPACITY); diff --git a/src/main/java/spoon/support/reflect/code/CtFieldAccessImpl.java b/src/main/java/spoon/support/reflect/code/CtFieldAccessImpl.java index cc74d13e59a..53316cd8305 100644 --- a/src/main/java/spoon/support/reflect/code/CtFieldAccessImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtFieldAccessImpl.java @@ -19,10 +19,8 @@ import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtFieldAccess; import spoon.reflect.code.CtTargetedExpression; -import spoon.reflect.code.CtVariableAccess; import spoon.reflect.reference.CtFieldReference; import spoon.reflect.reference.CtTypeReference; -import spoon.reflect.reference.CtVariableReference; public abstract class CtFieldAccessImpl extends CtVariableReadImpl implements CtFieldAccess { private static final long serialVersionUID = 1L; @@ -49,11 +47,6 @@ public CtFieldReference getVariable() { return (CtFieldReference) super.getVariable(); } - @Override - public > C setVariable(CtVariableReference variable) { - return super.setVariable(variable); - } - @Override public CtTypeReference getType() { return getVariable() == null ? null : getVariable().getType(); diff --git a/src/main/java/spoon/support/reflect/code/CtForEachImpl.java b/src/main/java/spoon/support/reflect/code/CtForEachImpl.java index 3de0019beb9..e69f52539da 100644 --- a/src/main/java/spoon/support/reflect/code/CtForEachImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtForEachImpl.java @@ -45,14 +45,18 @@ public CtLocalVariable getVariable() { @Override public T setExpression(CtExpression expression) { - expression.setParent(this); + if (expression != null) { + expression.setParent(this); + } this.expression = expression; return (T) this; } @Override public T setVariable(CtLocalVariable variable) { - variable.setParent(this); + if (variable != null) { + variable.setParent(this); + } this.variable = variable; return (T) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtForImpl.java b/src/main/java/spoon/support/reflect/code/CtForImpl.java index 1caa4b4cf02..d9dfb19e252 100644 --- a/src/main/java/spoon/support/reflect/code/CtForImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtForImpl.java @@ -63,6 +63,9 @@ public List getForInit() { @Override public T addForInit(CtStatement statement) { + if (statement == null) { + return (T) this; + } if (forInit == CtElementImpl.emptyList()) { forInit = new ArrayList(FOR_INIT_STATEMENTS_CONTAINER_DEFAULT_CAPACITY); } @@ -92,6 +95,9 @@ public List getForUpdate() { @Override public T addForUpdate(CtStatement statement) { + if (statement == null) { + return (T) this; + } if (forUpdate == CtElementImpl.emptyList()) { forUpdate = new ArrayList( FOR_UPDATE_STATEMENTS_CONTAINER_DEFAULT_CAPACITY); diff --git a/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java b/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java index 999d286b470..469776f6024 100644 --- a/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtInvocationImpl.java @@ -55,6 +55,9 @@ public List> getArguments() { @Override public > C addArgument(CtExpression argument) { + if (argument == null) { + return (C) this; + } if (arguments == CtElementImpl.>emptyList()) { 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 e3b9dfb451e..faad91f0163 100644 --- a/src/main/java/spoon/support/reflect/code/CtLambdaImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtLambdaImpl.java @@ -96,6 +96,9 @@ public > C setParameters(List> params) @Override public > C addParameter(CtParameter parameter) { + if (parameter == null) { + return (C) this; + } if (parameters == CtElementImpl.>emptyList()) { parameters = new ArrayList>( PARAMETERS_CONTAINER_DEFAULT_CAPACITY); @@ -130,6 +133,9 @@ public > C setThrownTypes(Set> C addThrownType(CtTypeReference throwType) { + if (throwType == null) { + return (C) this; + } if (thrownTypes == CtElementImpl.>emptySet()) { 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 9a7f9cb0029..448cc90e7c1 100644 --- a/src/main/java/spoon/support/reflect/code/CtNewArrayImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtNewArrayImpl.java @@ -59,6 +59,9 @@ public > C setDimensionExpressions(List> C addDimensionExpression(CtExpression dimension) { + if (dimension == null) { + return (C) this; + } if (dimensionExpressions == CtElementImpl.>emptyList()) { dimensionExpressions = new ArrayList>( NEW_ARRAY_DEFAULT_EXPRESSIONS_CONTAINER_DEFAULT_CAPACITY); @@ -86,6 +89,9 @@ public > C setElements(List> expressions @Override public > C addElement(CtExpression expression) { + if (expression == null) { + return (C) this; + } if (expressions == CtElementImpl.>emptyList()) { this.expressions = new ArrayList>(); } diff --git a/src/main/java/spoon/support/reflect/code/CtNewClassImpl.java b/src/main/java/spoon/support/reflect/code/CtNewClassImpl.java index 4715469bae2..2a17cb094d9 100644 --- a/src/main/java/spoon/support/reflect/code/CtNewClassImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtNewClassImpl.java @@ -37,7 +37,9 @@ public CtClass getAnonymousClass() { @Override public N setAnonymousClass(CtClass anonymousClass) { - anonymousClass.setParent(this); + if (anonymousClass != null) { + anonymousClass.setParent(this); + } this.anonymousClass = anonymousClass; return (N) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java b/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java index 48a697f6af3..a38ed0b3fad 100644 --- a/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtStatementListImpl.java @@ -56,6 +56,9 @@ public T setStatements(List stmts) { @Override public T addStatement(CtStatement statement) { + if (statement == null) { + return (T) this; + } if (this.statements == CtElementImpl.emptyList()) { this.statements = new ArrayList(BLOCK_STATEMENTS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java b/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java index 4b2707ee921..ccf29a81667 100644 --- a/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtSwitchImpl.java @@ -60,13 +60,18 @@ public > T setCases(List> cases) { @Override public > T setSelector(CtExpression selector) { - selector.setParent(this); + if (selector != null) { + selector.setParent(this); + } this.expression = selector; return (T) this; } @Override public > T addCase(CtCase c) { + if (c == null) { + return (T) this; + } if (cases == CtElementImpl.>emptyList()) { cases = new ArrayList>(SWITCH_CASES_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtSynchronizedImpl.java b/src/main/java/spoon/support/reflect/code/CtSynchronizedImpl.java index 3768fdf39dc..b6166f04137 100644 --- a/src/main/java/spoon/support/reflect/code/CtSynchronizedImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtSynchronizedImpl.java @@ -45,14 +45,18 @@ public CtExpression getExpression() { @Override public T setBlock(CtBlock block) { - block.setParent(this); + if (block != null) { + block.setParent(this); + } this.block = block; return (T) this; } @Override public T setExpression(CtExpression expression) { - expression.setParent(this); + if (expression != null) { + expression.setParent(this); + } this.expression = expression; return (T) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtTryImpl.java b/src/main/java/spoon/support/reflect/code/CtTryImpl.java index 966b096188a..f3281fa15b9 100644 --- a/src/main/java/spoon/support/reflect/code/CtTryImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtTryImpl.java @@ -59,6 +59,9 @@ public T setCatchers(List catchers) { @Override public T addCatcher(CtCatch catcher) { + if (catcher == null) { + return (T) this; + } if (catchers == CtElementImpl.emptyList()) { catchers = new ArrayList(CATCH_CASES_CONTAINER_DEFAULT_CAPACITY); } @@ -79,7 +82,9 @@ public CtBlock getFinalizer() { @Override public T setFinalizer(CtBlock finalizer) { - finalizer.setParent(this); + if (finalizer != null) { + finalizer.setParent(this); + } this.finalizer = finalizer; return (T) this; } @@ -91,7 +96,9 @@ public CtBlock getBody() { @Override public T setBody(CtBlock body) { - body.setParent(this); + if (body != null) { + body.setParent(this); + } this.body = body; return (T) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java b/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java index 27a429bde02..822af40d7c8 100644 --- a/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtTryWithResourceImpl.java @@ -52,6 +52,9 @@ public T setResources(List> res @Override public T addResource(CtLocalVariable resource) { + if (resource == null) { + return (T) this; + } if (resources == CtElementImpl.>emptyList()) { resources = new ArrayList>(RESOURCES_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/code/CtUnaryOperatorImpl.java b/src/main/java/spoon/support/reflect/code/CtUnaryOperatorImpl.java index 30a198589d3..9c050784959 100644 --- a/src/main/java/spoon/support/reflect/code/CtUnaryOperatorImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtUnaryOperatorImpl.java @@ -88,7 +88,9 @@ public void replace(CtElement element) { @Override public C setOperand(CtExpression expression) { - expression.setParent(this); + if (expression != null) { + expression.setParent(this); + } this.operand = expression; return (C) this; } diff --git a/src/main/java/spoon/support/reflect/code/CtWhileImpl.java b/src/main/java/spoon/support/reflect/code/CtWhileImpl.java index 52e271e7894..d1408044678 100644 --- a/src/main/java/spoon/support/reflect/code/CtWhileImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtWhileImpl.java @@ -37,7 +37,9 @@ public CtExpression getLoopingExpression() { @Override public T setLoopingExpression(CtExpression expression) { - expression.setParent(this); + if (expression != null) { + expression.setParent(this); + } this.expression = expression; return (T) this; } diff --git a/src/main/java/spoon/support/reflect/declaration/CtAnnotationTypeImpl.java b/src/main/java/spoon/support/reflect/declaration/CtAnnotationTypeImpl.java index 185a096e035..d01ed2dca9b 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtAnnotationTypeImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtAnnotationTypeImpl.java @@ -49,6 +49,9 @@ public void accept(CtVisitor v) { } private CtMethod createGhostMethod(CtField field) { + if (field == null) { + return null; + } final CtMethod method = factory.Core().createMethod(); method.setImplicit(true); method.setSimpleName(field.getSimpleName()); diff --git a/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java b/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java index a09865c2164..742a6463469 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtClassImpl.java @@ -86,6 +86,9 @@ public Set> getConstructors() { @Override public > C addAnonymousExecutable(CtAnonymousExecutable e) { + if (e == null) { + return (C) this; + } if (anonymousExecutables == CtElementImpl.emptyList()) { anonymousExecutables = new ArrayList( ANONYMOUS_EXECUTABLES_CONTAINER_DEFAULT_CAPACITY); @@ -133,6 +136,9 @@ public > C setConstructors(Set> constructo @Override public > C addConstructor(CtConstructor constructor) { + if (constructor == null) { + return (C) this; + } if (constructors == CtElementImpl.>emptySet()) { constructors = new TreeSet>(); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java index 20167fabf31..173c6aee736 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java @@ -211,6 +211,9 @@ public void delete() { } public E addAnnotation(CtAnnotation annotation) { + if (annotation == null) { + return (E) this; + } if ((List) this.annotations == (List) emptyList()) { this.annotations = new ArrayList>(ANNOTATIONS_CONTAINER_DEFAULT_CAPACITY); } @@ -411,6 +414,9 @@ public List getComments() { @Override public E addComment(CtComment comment) { + if (comment == null) { + return (E) this; + } if ((List) comments == emptyList()) { comments = new ArrayList(COMMENT_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java b/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java index dc2114a4e1a..bdc053cb5c8 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtEnumImpl.java @@ -55,6 +55,9 @@ public boolean isSubtypeOf(CtTypeReference type) { @Override public > C addEnumValue(CtEnumValue enumValue) { + if (enumValue == null) { + return (C) this; + } if (enumValues == CtElementImpl.>emptyList()) { enumValues = new ArrayList>(); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java b/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java index 5def248a301..f9652488d29 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtExecutableImpl.java @@ -82,6 +82,9 @@ public > T setParameters(List> paramete @Override public > T addParameter(CtParameter parameter) { + if (parameter == null) { + return (T) this; + } if (parameters == CtElementImpl.>emptyList()) { parameters = new ArrayList>( PARAMETERS_CONTAINER_DEFAULT_CAPACITY); @@ -115,6 +118,9 @@ public > T setThrownTypes(Set> T addThrownType(CtTypeReference throwType) { + if (throwType == null) { + return (T) this; + } if (thrownTypes == CtElementImpl.>emptySet()) { thrownTypes = new TreeSet>(); } diff --git a/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java b/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java index 53cc67fa7a8..a58353aa502 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java @@ -50,6 +50,9 @@ public void accept(CtVisitor v) { @Override public T addPackage(CtPackage pack) { + if (pack == null) { + return (T) this; + } // they are the same if (this.getQualifiedName().equals(pack.getQualifiedName())) { addAllTypes(pack, this); @@ -170,6 +173,9 @@ public CtPackageReference getReference() { @Override public T addType(CtType type) { + if (type == null) { + return (T) this; + } type.setParent(this); types.add(type); return (T) this; diff --git a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java index ca385e24749..2b5809aa34a 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java @@ -80,6 +80,9 @@ public CtTypeImpl() { @Override public > C addFieldAtTop(CtField field) { + if (field == null) { + return (C) this; + } if (!this.fields.contains(field)) { field.setParent(this); CompilationUnit compilationUnit = null; @@ -96,6 +99,9 @@ public > C addFieldAtTop(CtField field) { @Override public > C addField(CtField field) { + if (field == null) { + return (C) this; + } if (!this.fields.contains(field)) { field.setParent(this); this.fields.add(field); @@ -147,6 +153,9 @@ public List> getFields() { @Override public > C addNestedType(CtType nestedType) { + if (nestedType == null) { + return (C) this; + } if (nestedTypes == CtElementImpl.>emptySet()) { nestedTypes = new TreeSet>(); } @@ -440,6 +449,9 @@ public boolean isAssignableFrom(CtTypeReference type) { @Override public > C addMethod(CtMethod method) { + if (method == null) { + return (C) this; + } if (methods == CtElementImpl.>emptySet()) { methods = new TreeSet>(); } @@ -484,6 +496,9 @@ public boolean removeMethod(CtMethod method) { @Override public > C addSuperInterface(CtTypeReference interfac) { + if (interfac == null) { + return (C) this; + } if (interfaces == CtElementImpl.>emptySet()) { interfaces = new TreeSet>(); } @@ -511,6 +526,9 @@ public boolean removeSuperInterface(CtTypeReference interfac) { @Override public C addFormalTypeParameter(CtTypeParameterReference formalTypeParameter) { + if (formalTypeParameter == null) { + return (C) this; + } if (formalTypeParameters == CtElementImpl.emptyList()) { formalTypeParameters = new ArrayList(TYPE_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } diff --git a/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java index 793be096ccf..a6814d5f5f0 100644 --- a/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtExecutableReferenceImpl.java @@ -409,6 +409,9 @@ private CtExecutableReference getOverloadedExecutable( @Override public C addActualTypeArgument(CtTypeReference actualTypeArgument) { + if (actualTypeArgument == null) { + return (C) this; + } if (actualTypeArguments == CtElementImpl.>emptyList()) { actualTypeArguments = new ArrayList>( METHOD_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); diff --git a/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java index 3813ba77d88..d21309b2ac3 100644 --- a/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtIntersectionTypeReferenceImpl.java @@ -54,6 +54,9 @@ public C setBounds(Set C addBound(CtTypeReference bound) { + if (bound == null) { + return (C) this; + } if (bounds == CtElementImpl.>emptySet()) { bounds = new TreeSet>(new SourcePositionComparator()); } diff --git a/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java index b34145a29c1..8e08f36b0c7 100644 --- a/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java @@ -115,6 +115,9 @@ public Class getActualClass() { @Override public C addActualTypeArgument(CtTypeReference actualTypeArgument) { + if (actualTypeArgument == null) { + return (C) this; + } if (actualTypeArguments == CtElementImpl.>emptyList()) { actualTypeArguments = new ArrayList>( TYPE_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); diff --git a/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java index 180456ad481..0bbbae76b4d 100644 --- a/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtTypeReferenceImpl.java @@ -481,6 +481,9 @@ public boolean isLocalType() { @Override public C addActualTypeArgument(CtTypeReference actualTypeArgument) { + if (actualTypeArgument == null) { + return (C) this; + } if (actualTypeArguments == CtElementImpl.>emptyList()) { 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 new file mode 100644 index 00000000000..a1041e46d63 --- /dev/null +++ b/src/test/java/spoon/reflect/ast/IntercessionScanner.java @@ -0,0 +1,68 @@ +package spoon.reflect.ast; + +import spoon.reflect.code.CtConstructorCall; +import spoon.reflect.code.CtThrow; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.factory.Factory; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.CtScanner; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public abstract class IntercessionScanner extends CtScanner { + protected final Factory factory; + protected final List> COLLECTIONS; + protected final CtTypeReference CTELEMENT_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) // + ); + CTELEMENT_REFERENCE = factory.Type().createReference(CtElement.class); + } + + protected abstract boolean isToBeProcessed(CtMethod candidate); + + protected abstract void process(CtMethod element); + + @Override + public void visitCtMethod(CtMethod m) { + if (isToBeProcessed(m)) { + process(m); + } + super.visitCtMethod(m); + } + + protected boolean avoidThrowUnsupportedOperationException(CtMethod candidate) { + if (candidate.getBody().getStatements().size() != 1) { + return true; + } + if (!(candidate.getBody().getStatement(0) instanceof CtThrow)) { + return true; + } + CtThrow ctThrow = candidate.getBody().getStatement(0); + if (!(ctThrow.getThrownExpression() instanceof CtConstructorCall)) { + return true; + } + final CtConstructorCall thrownExpression = (CtConstructorCall) ctThrow.getThrownExpression(); + if (!thrownExpression.getType().equals(factory.Type().createReference(UnsupportedOperationException.class))) { + return true; + } + return false; + } + + protected boolean takeSetterForCtElement(CtMethod candidate) { + return candidate.getParameters().get(0).getType().isSubtypeOf(CTELEMENT_REFERENCE); + } + + protected boolean avoidInterfaces(CtMethod candidate) { + return candidate.getBody() != null; + } +} diff --git a/src/test/java/spoon/reflect/ast/ParentTest.java b/src/test/java/spoon/reflect/ast/ParentTest.java new file mode 100644 index 00000000000..493073ee1ae --- /dev/null +++ b/src/test/java/spoon/reflect/ast/ParentTest.java @@ -0,0 +1,139 @@ +package spoon.reflect.ast; + +import org.junit.Test; +import spoon.Launcher; +import spoon.reflect.code.BinaryOperatorKind; +import spoon.reflect.code.CtBinaryOperator; +import spoon.reflect.code.CtBlock; +import spoon.reflect.code.CtIf; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.code.CtLiteral; +import spoon.reflect.code.CtStatement; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.declaration.ModifierKind; +import spoon.reflect.declaration.ParentNotInitializedException; +import spoon.reflect.factory.Factory; +import spoon.reflect.reference.CtArrayTypeReference; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.filter.TypeFilter; + +import java.util.List; + +import static org.junit.Assert.fail; + +public class ParentTest { + @Test + public void testParentSetInSetter() throws Exception { + // contract: Check that all setters protect their parameter. + final Launcher launcher = new Launcher(); + final Factory factory = launcher.getFactory(); + launcher.getEnvironment().setNoClasspath(true); + launcher.setSourceOutputDirectory("./target/trash"); + // 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"); + // Utils. + launcher.addInputResource("./src/test/java/spoon/reflect/ast/"); + launcher.buildModel(); + + // 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") // + || candidate.getSimpleName().startsWith("add")) // + && candidate.hasModifier(ModifierKind.PUBLIC) // + && takeSetterForCtElement(candidate) // + && avoidInterfaces(candidate) // + && avoidSpecificMethods(candidate) // + && avoidThrowUnsupportedOperationException(candidate); + } + + @Override + public void process(CtMethod element) { + if (element.getSimpleName().startsWith("add")) { + checkAddStrategy(element); + } else { + checkSetStrategy(element); + } + } + + private void checkAddStrategy(CtMethod element) { + final CtStatement statement = element.getBody().getStatement(0); + if (!(statement instanceof CtIf)) { + fail("First statement should be an if to check the parameter of the setter." + element.getSignature() + " declared in " + element.getDeclaringType().getQualifiedName()); + } + if (!createCheckNull(element.getParameters().get(0)).equals(((CtIf) statement).getCondition())) { + fail("Condition should test if the parameter is null. The condition was " + ((CtIf) statement).getCondition() + "in " + element.getSignature() + " declared in " + element + .getDeclaringType().getQualifiedName()); + } + } + + private void checkSetStrategy(CtMethod element) { + final CtTypeReference type = element.getParameters().get(0).getType(); + if (!COLLECTIONS.contains(type) && !(type instanceof CtArrayTypeReference)) { + CtInvocation setParent = searchSetParent(element.getBody()); + if (setParent == null) { + fail("Missing set parent in " + element.getSignature()); + } + try { + if (setParent.getParent(CtIf.class) == null) { + fail("Missing condition in " + element.getSignature() + " declared in the class " + element.getDeclaringType().getQualifiedName()); + } + } catch (ParentNotInitializedException e) { + fail("Missing parent condition in " + element.getSignature() + " declared in the class " + element.getDeclaringType().getQualifiedName()); + } + } + } + + /** + * Creates parameter == null. + * + * @param ctParameter parameter + */ + private CtBinaryOperator createCheckNull(CtParameter ctParameter) { + final CtLiteral nullLiteral = factory.Code().createLiteral(null); + nullLiteral.setType(factory.Type().NULL_TYPE.clone()); + final CtBinaryOperator operator = factory.Code().createBinaryOperator( // + factory.Code().createVariableRead(ctParameter.getReference(), true), // + nullLiteral, BinaryOperatorKind.EQ); + operator.setType(factory.Type().BOOLEAN_PRIMITIVE); + return operator; + } + + private CtInvocation searchSetParent(CtBlock body) { + final List> ctInvocations = body.getElements(new TypeFilter>(CtInvocation.class) { + @Override + public boolean matches(CtInvocation element) { + return "setParent".equals(element.getExecutable().getSimpleName()) && super.matches(element); + } + }); + if (ctInvocations.size() != 1) { + final CtMethod parent = (CtMethod) body.getParent(); + fail("Have " + ctInvocations.size() + " setParent() in " + parent.getSignature() + " declared in the class " + parent.getDeclaringType().getQualifiedName()); + } + return ctInvocations.get(0); + } + }.scan(launcher.getModel().getRootPackage()); + } +} diff --git a/src/test/java/spoon/test/intercession/IntercessionContractTest.java b/src/test/java/spoon/test/intercession/IntercessionContractTest.java new file mode 100644 index 00000000000..f3b99efc8fc --- /dev/null +++ b/src/test/java/spoon/test/intercession/IntercessionContractTest.java @@ -0,0 +1,137 @@ +package spoon.test.intercession; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import spoon.Launcher; +import spoon.reflect.ast.IntercessionScanner; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.CtTypedElement; +import spoon.reflect.declaration.ModifierKind; +import spoon.reflect.factory.Factory; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.Query; +import spoon.reflect.visitor.filter.TypeFilter; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.fail; + +@RunWith(Parameterized.class) +public class IntercessionContractTest { + + @Parameterized.Parameters(name = "{0}") + public static Collection data() 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(); + + final List values = new ArrayList<>(); + new IntercessionScanner(launcher.getFactory()) { + @Override + protected boolean isToBeProcessed(CtMethod candidate) { + return (candidate.getSimpleName().startsWith("set") // + || candidate.getSimpleName().startsWith("add")) // + && candidate.hasModifier(ModifierKind.PUBLIC) // + && takeSetterForCtElement(candidate) // + && avoidInterfaces(candidate) // + && avoidThrowUnsupportedOperationException(candidate); + } + + @Override + protected void process(CtMethod element) { + values.add(new Object[] { getDeclaringClassConcrete(element), element.getReference().getActualMethod() }); + } + + private Class getDeclaringClassConcrete(CtMethod element) { + final CtType declaringType = element.getDeclaringType(); + if (!declaringType.hasModifier(ModifierKind.ABSTRACT)) { + return declaringType.getActualClass(); + } + final List> superClasses = getSuperClassesOf(declaringType); + superClasses.add(declaringType.getReference()); + final List> elements = Query.getElements(factory, new TypeFilter>(CtClass.class) { + @Override + public boolean matches(CtClass element) { + return super.matches(element) + // Want a concrete class. + && !element.hasModifier(ModifierKind.ABSTRACT) + // Class can't be one of the superclass (or itself) of the declaring class. + && !superClasses.contains(element.getReference()) + // Current class have in its super class hierarchy the given declaring class. + && getSuperClassesOf(element).contains(declaringType.getReference()); + } + }); + if (elements.size() <= 0) { + fail("Can't have an abstract class without any concrete sub class. Error detected with " + declaringType.getQualifiedName()); + } + return takeFirstOneCorrect(element, elements); + } + + private Class takeFirstOneCorrect(CtMethod element, List> potentials) { + for (CtClass potential : potentials) { + final CtMethod method = potential.getMethod( + element.getType(), element.getSimpleName(), + element.getParameters().stream().map(CtTypedElement::getType).toArray(CtTypeReference[]::new)); + if (method == null) { + continue; + } + if (avoidThrowUnsupportedOperationException(method)) { + return potential.getActualClass(); + } + } + // Method don't declared in sub classes. + return potentials.get(0).getActualClass(); + } + + private List> getSuperClassesOf(CtType declaringType) { + final List> superClasses = new ArrayList<>(); + CtTypeReference declaringTypeReference = declaringType.getReference(); + while (declaringTypeReference.getSuperclass() != null) { + superClasses.add(declaringTypeReference.getSuperclass()); + declaringTypeReference = declaringTypeReference.getSuperclass(); + } + return superClasses; + } + }.scan(launcher.getModel().getRootPackage()); + return values; + } + + @Parameterized.Parameter(0) + public Class declaringClass; + + @Parameterized.Parameter(1) + public Method toTest; + + @Test + public void testContract() throws Throwable { + try { + // we invoke the setter + toTest.invoke(declaringClass.newInstance(), new Object[] { null }); + } catch (NullPointerException e) { + fail("Shouldn't throw NPE."); + } catch (InvocationTargetException e) { + if (!(e.getTargetException() instanceof UnsupportedOperationException)) { + fail("Unexpected exception happened with " + toTest.getName() + " in " + declaringClass.getName()); + } + } catch (Exception e) { + fail("Unexpected exception happened with " + toTest.getName() + " in " + declaringClass.getName()); + } + } + +}