extraDimensions= declaration.extraDimensions();
if (!extraDimensions.isEmpty()) {
ArrayType arrayType;
@@ -281,14 +318,14 @@
/**
* Returns the new type node representing the return type of lambdaExpression
* including the extra dimensions.
- *
+ *
* @param lambdaExpression the lambda expression
* @param ast the AST to create the return type with
* @param importRewrite the import rewrite to use, or null
* @param context the import rewrite context, or null
* @return a new type node created with the given AST representing the return type of
* lambdaExpression
- *
+ *
* @since 3.10
*/
public static Type newReturnType(LambdaExpression lambdaExpression, AST ast, ImportRewrite importRewrite, ImportRewriteContext context) {
@@ -344,12 +381,18 @@
public static Expression newDefaultExpression(AST ast, ITypeBinding type) {
if (type.isPrimitive()) {
String name= type.getName();
- if ("boolean".equals(name)) { //$NON-NLS-1$
+ boolean nomatch= false;
+ if (name != null) switch (name) {
+ case "boolean": //$NON-NLS-1$
return ast.newBooleanLiteral(false);
- } else if ("void".equals(name)) { //$NON-NLS-1$
+ case "void": //$NON-NLS-1$
return null;
- } else {
- return ast.newNumberLiteral("0"); //$NON-NLS-1$
+ default:
+ nomatch= true;
+ break;
+ }
+ if (nomatch) {
+ return ast.newNumberLiteral("0");//$NON-NLS-1$
}
}
return ast.newNullLiteral();
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java" 2020-02-26 15:31:57.000000000 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -18,10 +18,16 @@
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=51540).
* Stephan Herrmann - Configuration for
* Bug 463360 - [override method][null] generating method override should not create redundant null annotations
+ * Fabrice TIERCELIN - Methods to identify a signature
+ * Pierre-Yves B. (pyvesdev@gmail.com) - contributed fix for
+ * Bug 434747 - [inline] Inlining a local variable leads to ambiguity with overloaded methods
*******************************************************************************/
package org.eclipse.jdt.internal.corext.dom;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -53,11 +59,14 @@
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CharacterLiteral;
@@ -93,6 +102,7 @@
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
@@ -100,10 +110,12 @@
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.UnionType;
import org.eclipse.jdt.core.dom.VariableDeclaration;
@@ -117,6 +129,8 @@
import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
import org.eclipse.jdt.internal.core.manipulation.util.Strings;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TypeEnvironment;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
@@ -236,7 +250,7 @@
* To improve type-safety, callers can add the expected element type as explicit type argument, e.g.:
*
* {@code ASTNodes.getChildListProperty(typeDecl, bodyDeclarationsProperty)}
- *
+ *
* @param node the node
* @param propertyDescriptor the child list property to get
* @return the child list
@@ -320,7 +334,7 @@
/**
* Returns the type node for the given declaration.
- *
+ *
* @param declaration the declaration
* @return the type node or null
if the given declaration represents a type
* inferred parameter in lambda expression
@@ -394,7 +408,7 @@
public static boolean isLiteral(Expression expression) {
int type= expression.getNodeType();
return type == ASTNode.BOOLEAN_LITERAL || type == ASTNode.CHARACTER_LITERAL || type == ASTNode.NULL_LITERAL ||
- type == ASTNode.NUMBER_LITERAL || type == ASTNode.STRING_LITERAL || type == ASTNode.TYPE_LITERAL;
+ type == ASTNode.NUMBER_LITERAL || type == ASTNode.STRING_LITERAL || type == ASTNode.TYPE_LITERAL || type == ASTNode.TEXT_BLOCK;
}
public static boolean isLabel(SimpleName name) {
@@ -408,6 +422,24 @@
return Modifier.isStatic(declaration.getModifiers());
}
+ /**
+ * True if the method is static, false if it is not or null if it is unknown.
+ *
+ * @param method The method
+ * @return True if the method is static, false if it is not or null if it is unknown.
+ */
+ public static Boolean isStatic(final MethodInvocation method) {
+ Expression calledType= method.getExpression();
+
+ if (method.resolveMethodBinding() != null) {
+ return (method.resolveMethodBinding().getModifiers() & Modifier.STATIC) != 0;
+ } else if ((calledType instanceof Name) && ((Name) calledType).resolveBinding().getKind() == IBinding.TYPE) {
+ return Boolean.TRUE;
+ }
+
+ return null;
+ }
+
public static List getBodyDeclarations(ASTNode node) {
if (node instanceof AbstractTypeDeclaration) {
return ((AbstractTypeDeclaration)node).bodyDeclarations();
@@ -422,7 +454,7 @@
/**
* Returns the structural property descriptor for the "bodyDeclarations" property
* of this node (element type: {@link BodyDeclaration}).
- *
+ *
* @param node the node, either an {@link AbstractTypeDeclaration} or an {@link AnonymousClassDeclaration}
* @return the property descriptor
*/
@@ -442,7 +474,7 @@
* Skips qualifiers, type arguments, and type annotations.
*
* Does not work for WildcardTypes, etc.!
- *
+ *
* @param type a type that has a simple name
* @return the simple name, followed by array dimensions
* @see #getSimpleNameIdentifier(Name)
@@ -490,7 +522,7 @@
/**
* Returns the (potentially qualified) name of a type, followed by array dimensions.
* Skips type arguments and type annotations.
- *
+ *
* @param type a type that has a name
* @return the name, followed by array dimensions
* @since 3.10
@@ -532,7 +564,201 @@
type.accept(visitor);
return buffer.toString();
}
-
+
+ /**
+ * Returns the {@link Boolean} object value represented by the provided expression.
+ *
+ * @param expression the expression to analyze
+ * @return the {@link Boolean} object value if the provided expression represents one, null
+ * otherwise
+ */
+ public static Boolean getBooleanLiteral(Expression expression) {
+ final BooleanLiteral bl= as(expression, BooleanLiteral.class);
+ if (bl != null) {
+ return Boolean.valueOf(bl.booleanValue());
+ }
+ final QualifiedName qn= as(expression, QualifiedName.class);
+ if (hasType(qn, Boolean.class.getCanonicalName())) {
+ return getBooleanObject(qn);
+ }
+ return null;
+ }
+
+ /**
+ * Casts the provided expression to an object of the provided type if type matches.
+ *
+ * @param the required expression type
+ * @param expression the expression to cast
+ * @param exprClass the class representing the required expression type
+ * @return the provided expression as an object of the provided type if type matches, null
+ * otherwise
+ */
+ @SuppressWarnings("unchecked")
+ public static T as(Expression expression, Class exprClass) {
+ if (expression != null) {
+ if (exprClass.isAssignableFrom(expression.getClass())) {
+ return (T) expression;
+ } else if (expression instanceof ParenthesizedExpression) {
+ return as(((ParenthesizedExpression) expression).getExpression(), exprClass);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the provided statement as a non null list of statements:
+ *
+ * - if the statement is null, then an empty list is returned
+ * - if the statement is a {@link Block}, then its children are returned
+ * - otherwise, the current node is returned wrapped in a list
+ *
+ *
+ * @param statement the statement to analyze
+ * @return the provided statement as a non null list of statements
+ */
+ public static List asList(Statement statement) {
+ if (statement == null) {
+ return Collections.emptyList();
+ }
+
+ if (statement instanceof Block) {
+ return ((Block) statement).statements();
+ }
+
+ return Arrays.asList(statement);
+ }
+
+ /**
+ * Returns all the operands from the provided infix expressions.
+ *
+ * @param node the infix expression
+ * @return a List of expressions
+ */
+ public static List allOperands(InfixExpression node) {
+ final List extOps= node.extendedOperands();
+ final List results= new ArrayList<>(2 + extOps.size());
+ results.add(node.getLeftOperand());
+ results.add(node.getRightOperand());
+ results.addAll(extOps);
+
+ return results;
+ }
+
+ /**
+ * Returns the {@link Boolean} object constant value represented by the provided qualified name.
+ *
+ * @param qualifiedName the qualified name that must represent a Boolean object constant
+ * @return the {@link Boolean} object constant value represented by the provided qualified name,
+ * or null if the qualified name does not represent a {@link Boolean} object constant
+ * value.
+ */
+ public static Boolean getBooleanObject(final QualifiedName qualifiedName) {
+ final String fqn= qualifiedName.getFullyQualifiedName();
+ if ("Boolean.TRUE".equals(fqn)) { //$NON-NLS-1$
+ return Boolean.TRUE;
+ } else if ("Boolean.FALSE".equals(fqn)) { //$NON-NLS-1$
+ return Boolean.FALSE;
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the provided operator is the same as the one of provided node.
+ *
+ * @param node the node for which to test the operator
+ * @param anOperator the first operator to test
+ * @param operators the other operators to test
+ * @return true if the provided node has the provided operator, false otherwise.
+ */
+ public static boolean hasOperator(InfixExpression node, InfixExpression.Operator anOperator, InfixExpression.Operator... operators) {
+ return node != null && isOperatorInList(node.getOperator(), anOperator, operators);
+ }
+
+ /**
+ * Returns whether the provided operator is the same as the one of provided node.
+ *
+ * @param node the node for which to test the operator
+ * @param anOperator the first operator to test
+ * @param operators the other operators to test
+ * @return true if the provided node has the provided operator, false otherwise.
+ */
+ public static boolean hasOperator(PrefixExpression node, PrefixExpression.Operator anOperator, PrefixExpression.Operator... operators) {
+ return node != null && isOperatorInList(node.getOperator(), anOperator, operators);
+ }
+
+ private static boolean isOperatorInList(Object realOperator, Object anOperator, Object[] operators) {
+ return realOperator != null && (realOperator.equals(anOperator) || Arrays.asList(operators).contains(realOperator));
+ }
+
+ /**
+ * Returns whether the provided expression evaluates to exactly one of the provided type.
+ *
+ * @param expression the expression to analyze
+ * @param oneOfQualifiedTypeNames the type binding qualified name must be equal to one of these
+ * qualified type names
+ * @return true if the provided expression evaluates to exactly one of the provided type, false
+ * otherwise
+ */
+ public static boolean hasType(Expression expression, String... oneOfQualifiedTypeNames) {
+ return expression != null && hasType(expression.resolveTypeBinding(), oneOfQualifiedTypeNames);
+ }
+
+ /**
+ * Returns whether the provided type binding is exactly one of the provided type.
+ *
+ * @param typeBinding the type binding to analyze
+ * @param oneOfQualifiedTypeNames the type binding qualified name must be equal to one of these
+ * qualified type names
+ * @return {@code true} if the provided type binding is exactly one of the provided type,
+ * {@code false} otherwise
+ */
+ public static boolean hasType(final ITypeBinding typeBinding, String... oneOfQualifiedTypeNames) {
+ if (typeBinding != null) {
+ final String qualifiedName= typeBinding.getErasure().getQualifiedName();
+ for (String qualifiedTypeName : oneOfQualifiedTypeNames) {
+ if (qualifiedTypeName.equals(qualifiedName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the opposite infix operator. For boolean operators, the operands should be negated
+ * too.
+ *
+ * @param operator the infix operator
+ * @return the opposite infix operator
+ */
+ public static InfixExpression.Operator oppositeInfixOperator(InfixExpression.Operator operator) {
+ if (InfixExpression.Operator.LESS.equals(operator))
+ return InfixExpression.Operator.GREATER_EQUALS;
+
+ if (InfixExpression.Operator.LESS_EQUALS.equals(operator))
+ return InfixExpression.Operator.GREATER;
+
+ if (InfixExpression.Operator.GREATER.equals(operator))
+ return InfixExpression.Operator.LESS_EQUALS;
+
+ if (InfixExpression.Operator.GREATER_EQUALS.equals(operator))
+ return InfixExpression.Operator.LESS;
+
+ if (InfixExpression.Operator.EQUALS.equals(operator))
+ return InfixExpression.Operator.NOT_EQUALS;
+
+ if (InfixExpression.Operator.NOT_EQUALS.equals(operator))
+ return InfixExpression.Operator.EQUALS;
+
+ if (InfixExpression.Operator.CONDITIONAL_AND.equals(operator))
+ return InfixExpression.Operator.CONDITIONAL_OR;
+
+ if (InfixExpression.Operator.CONDITIONAL_OR.equals(operator))
+ return InfixExpression.Operator.CONDITIONAL_AND;
+
+ return null;
+ }
+
public static InfixExpression.Operator convertToInfixOperator(Assignment.Operator operator) {
if (operator.equals(Assignment.Operator.PLUS_ASSIGN))
return InfixExpression.Operator.PLUS;
@@ -591,7 +817,7 @@
/**
* Returns the type to which an inlined variable initializer should be cast, or
* null
if no cast is necessary.
- *
+ *
* @param initializer the initializer expression of the variable to inline
* @param reference the reference to the variable (which is to be inlined)
* @return a type binding to which the initializer should be cast, or null
iff no cast is necessary
@@ -602,22 +828,22 @@
ITypeBinding referenceType= reference.resolveTypeBinding();
if (initializerType == null || referenceType == null)
return null;
-
+
if (initializerType.isPrimitive() && referenceType.isPrimitive() && ! referenceType.isEqualTo(initializerType)) {
return referenceType;
-
+
} else if (initializerType.isPrimitive() && ! referenceType.isPrimitive()) { // initializer is autoboxed
ITypeBinding unboxedReferenceType= Bindings.getUnboxedTypeBinding(referenceType, reference.getAST());
if (!unboxedReferenceType.isEqualTo(initializerType))
return unboxedReferenceType;
else if (needsExplicitBoxing(reference))
return referenceType;
-
+
} else if (! initializerType.isPrimitive() && referenceType.isPrimitive()) { // initializer is autounboxed
ITypeBinding unboxedInitializerType= Bindings.getUnboxedTypeBinding(initializerType, reference.getAST());
if (!unboxedInitializerType.isEqualTo(referenceType))
return referenceType;
-
+
} else if (initializerType.isRawType() && referenceType.isParameterizedType()) {
return referenceType; // don't lose the unchecked conversion
@@ -634,8 +860,13 @@
} else if (! TypeRules.canAssign(initializerType, referenceType)) {
if (!Bindings.containsTypeVariables(referenceType))
return referenceType;
+
+ } else if (!initializerType.isEqualTo(referenceType)) {
+ if (isTargetAmbiguous(reference, initializerType)) {
+ return referenceType;
+ }
}
-
+
return null;
}
@@ -643,16 +874,81 @@
* Checks whether overloaded methods can result in an ambiguous method call or a semantic change when the
* expression
argument is replaced with a poly expression form of the functional
* interface instance.
- *
+ *
* @param expression the method argument, which is a functional interface instance
* @param expressionIsExplicitlyTyped true
iff the intended replacement for expression
* is an explicitly typed lambda expression (JLS8 15.27.1)
* @return true
if overloaded methods can result in an ambiguous method call or a semantic change,
* false
otherwise
- *
+ *
* @since 3.10
*/
public static boolean isTargetAmbiguous(Expression expression, boolean expressionIsExplicitlyTyped) {
+ ParentSummary targetSummary= getParentSummary(expression);
+ if (targetSummary == null) {
+ return false;
+ }
+
+ if (targetSummary.methodBinding != null) {
+ ITypeBinding invocationTargetType= getInvocationType(expression.getParent(), targetSummary.methodBinding, targetSummary.invocationQualifier);
+ if (invocationTargetType != null) {
+ TypeBindingVisitor visitor= new FunctionalInterfaceAmbiguousMethodAnalyzer(invocationTargetType, targetSummary.methodBinding, targetSummary.argumentIndex,
+ targetSummary.argumentCount, expressionIsExplicitlyTyped);
+ return !(visitor.visit(invocationTargetType) && Bindings.visitHierarchy(invocationTargetType, visitor));
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks whether overloaded methods can result in an ambiguous method call or a semantic change
+ * when the expression
argument is inlined.
+ *
+ * @param expression the method argument, which is a functional interface instance
+ * @param initializerType the initializer type of the variable to inline
+ * @return true
if overloaded methods can result in an ambiguous method call or a
+ * semantic change, false
otherwise
+ *
+ * @since 3.19
+ */
+ public static boolean isTargetAmbiguous(Expression expression, ITypeBinding initializerType) {
+ ParentSummary parentSummary= getParentSummary(expression);
+ if (parentSummary == null) {
+ return false;
+ }
+
+ IMethodBinding methodBinding= parentSummary.methodBinding;
+ if (methodBinding != null) {
+ ITypeBinding[] parameterTypes= methodBinding.getParameterTypes();
+ int argumentIndex= parentSummary.argumentIndex;
+ if (methodBinding.isVarargs() && argumentIndex >= parameterTypes.length - 1) {
+ argumentIndex= parameterTypes.length - 1;
+ initializerType= initializerType.createArrayType(1);
+ }
+ parameterTypes[argumentIndex]= initializerType;
+
+ ITypeBinding invocationType= getInvocationType(expression.getParent(), methodBinding, parentSummary.invocationQualifier);
+ if (invocationType != null) {
+ TypeEnvironment typeEnvironment= new TypeEnvironment();
+ TypeBindingVisitor visitor= new AmbiguousMethodAnalyzer(typeEnvironment, methodBinding, typeEnvironment.create(parameterTypes));
+ if (!visitor.visit(invocationType)) {
+ return true;
+ } else if (invocationType.isInterface()) {
+ return !Bindings.visitInterfaces(invocationType, visitor);
+ } else if (Modifier.isAbstract(invocationType.getModifiers())) {
+ return !Bindings.visitHierarchy(invocationType, visitor);
+ } else {
+ // it is not needed to visit interfaces if receiver is a concrete class
+ return !Bindings.visitSuperclasses(invocationType, visitor);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static ParentSummary getParentSummary(Expression expression) {
StructuralPropertyDescriptor locationInParent= expression.getLocationInParent();
while (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY
@@ -661,7 +957,7 @@
expression= (Expression) expression.getParent();
locationInParent= expression.getLocationInParent();
}
-
+
ASTNode parent= expression.getParent();
IMethodBinding methodBinding;
int argumentIndex;
@@ -700,24 +996,33 @@
argumentIndex= enumConstantDecl.arguments().indexOf(expression);
argumentCount= enumConstantDecl.arguments().size();
} else {
- return false;
+ return null;
}
- if (methodBinding != null) {
- ITypeBinding invocationTargetType;
- invocationTargetType= getInvocationType(parent, methodBinding, invocationQualifier);
- if (invocationTargetType != null) {
- TypeBindingVisitor visitor= new AmbiguousTargetMethodAnalyzer(invocationTargetType, methodBinding, argumentIndex, argumentCount, expressionIsExplicitlyTyped);
- return !(visitor.visit(invocationTargetType) && Bindings.visitHierarchy(invocationTargetType, visitor));
- }
- }
+ return new ParentSummary(methodBinding, argumentIndex, argumentCount, invocationQualifier);
+ }
- return true;
+ private static class ParentSummary {
+
+ private final IMethodBinding methodBinding;
+
+ private final int argumentIndex;
+
+ private final int argumentCount;
+
+ private final Expression invocationQualifier;
+
+ ParentSummary(IMethodBinding methodBinding, int argumentIndex, int argumentCount, Expression invocationQualifier) {
+ this.methodBinding= methodBinding;
+ this.argumentIndex= argumentIndex;
+ this.argumentCount= argumentCount;
+ this.invocationQualifier= invocationQualifier;
+ }
}
/**
* Returns the binding of the type which declares the method being invoked.
- *
+ *
* @param invocationNode the method invocation node
* @param methodBinding binding of the method being invoked
* @param invocationQualifier the qualifier used for method invocation, or null
if
@@ -756,7 +1061,56 @@
return invocationType;
}
- private static class AmbiguousTargetMethodAnalyzer implements TypeBindingVisitor {
+ private static class AmbiguousMethodAnalyzer implements TypeBindingVisitor {
+ private TypeEnvironment fTypeEnvironment;
+ private TType[] fTypes;
+ private IMethodBinding fOriginal;
+
+ public AmbiguousMethodAnalyzer(TypeEnvironment typeEnvironment, IMethodBinding original, TType[] types) {
+ fTypeEnvironment= typeEnvironment;
+ fOriginal= original;
+ fTypes= types;
+ }
+
+ @Override
+ public boolean visit(ITypeBinding node) {
+ IMethodBinding[] methods= node.getDeclaredMethods();
+ for (int i= 0; i < methods.length; i++) {
+ IMethodBinding candidate= methods[i];
+ if (candidate == fOriginal) {
+ continue;
+ }
+ if (fOriginal.getName().equals(candidate.getName())) {
+ if (canImplicitlyCall(candidate)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true
if the method can be called without explicit casts; otherwise
+ * false
.
+ *
+ * @param candidate the method to test
+ * @return true
if the method can be called without explicit casts
+ */
+ private boolean canImplicitlyCall(IMethodBinding candidate) {
+ ITypeBinding[] parameters= candidate.getParameterTypes();
+ if (parameters.length != fTypes.length) {
+ return false;
+ }
+ for (int i= 0; i < parameters.length; i++) {
+ if (!fTypes[i].canAssignTo(fTypeEnvironment.create(parameters[i]))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static class FunctionalInterfaceAmbiguousMethodAnalyzer implements TypeBindingVisitor {
private ITypeBinding fDeclaringType;
private IMethodBinding fOriginalMethod;
private int fArgIndex;
@@ -772,7 +1126,7 @@
* @param expressionIsExplicitlyTyped true
iff the intended replacement for expression
* is an explicitly typed lambda expression (JLS8 15.27.1)
*/
- public AmbiguousTargetMethodAnalyzer(ITypeBinding declaringType, IMethodBinding originalMethod, int argumentIndex, int argumentCount, boolean expressionIsExplicitlyTyped) {
+ public FunctionalInterfaceAmbiguousMethodAnalyzer(ITypeBinding declaringType, IMethodBinding originalMethod, int argumentIndex, int argumentCount, boolean expressionIsExplicitlyTyped) {
fDeclaringType= declaringType;
fOriginalMethod= originalMethod;
fArgIndex= argumentIndex;
@@ -782,9 +1136,7 @@
@Override
public boolean visit(ITypeBinding type) {
- IMethodBinding[] methods= type.getDeclaredMethods();
- for (int i= 0; i < methods.length; i++) {
- IMethodBinding candidate= methods[i];
+ for (IMethodBinding candidate : type.getDeclaredMethods()) {
if (candidate.getMethodDeclaration() == fOriginalMethod.getMethodDeclaration()) {
continue;
}
@@ -801,7 +1153,7 @@
if (fOriginalMethod.getName().equals(candidate.getName()) && !fOriginalMethod.overrides(candidate)) {
ITypeBinding[] originalParameterTypes= fOriginalMethod.getParameterTypes();
ITypeBinding[] candidateParameterTypes= candidate.getParameterTypes();
-
+
boolean couldBeAmbiguous;
if (originalParameterTypes.length == candidateParameterTypes.length) {
couldBeAmbiguous= true;
@@ -847,12 +1199,12 @@
/**
* Derives the target type defined at the location of the given expression if the target context
* supports poly expressions.
- *
+ *
* @param expression the expression at whose location the target type is required
* @return the type binding of the target type defined at the location of the given expression
* if the target context supports poly expressions, or null
if the target
* type could not be derived
- *
+ *
* @since 3.10
*/
public static ITypeBinding getTargetType(Expression expression) {
@@ -871,6 +1223,24 @@
} else if (locationInParent == ArrayInitializer.EXPRESSIONS_PROPERTY) {
return getTargetTypeForArrayInitializer((ArrayInitializer) parent);
+ } else if (locationInParent == ArrayAccess.INDEX_PROPERTY) {
+ return parent.getAST().resolveWellKnownType(int.class.getSimpleName());
+
+ } else if (locationInParent == ConditionalExpression.EXPRESSION_PROPERTY
+ || locationInParent == IfStatement.EXPRESSION_PROPERTY
+ || locationInParent == WhileStatement.EXPRESSION_PROPERTY
+ || locationInParent == DoStatement.EXPRESSION_PROPERTY) {
+ return parent.getAST().resolveWellKnownType(boolean.class.getSimpleName());
+
+ } else if (locationInParent == SwitchStatement.EXPRESSION_PROPERTY) {
+ final ITypeBinding discriminentType= expression.resolveTypeBinding();
+ if (discriminentType.isPrimitive() || discriminentType.isEnum()
+ || discriminentType.getQualifiedName().equals(String.class.getCanonicalName())) {
+ return discriminentType;
+ } else {
+ return Bindings.getUnboxedTypeBinding(discriminentType, parent.getAST());
+ }
+
} else if (locationInParent == MethodInvocation.ARGUMENTS_PROPERTY) {
MethodInvocation methodInvocation= (MethodInvocation) parent;
IMethodBinding methodBinding= methodInvocation.resolveMethodBinding();
@@ -969,7 +1339,7 @@
/**
* Returns whether an expression at the given location needs explicit boxing.
- *
+ *
* @param expression the expression
* @return true
iff an expression at the given location needs explicit boxing
* @since 3.6
@@ -978,18 +1348,18 @@
StructuralPropertyDescriptor locationInParent= expression.getLocationInParent();
if (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY)
return needsExplicitBoxing((ParenthesizedExpression) expression.getParent());
-
+
if (locationInParent == ClassInstanceCreation.EXPRESSION_PROPERTY
|| locationInParent == FieldAccess.EXPRESSION_PROPERTY
|| locationInParent == MethodInvocation.EXPRESSION_PROPERTY)
return true;
-
+
return false;
}
/**
* Checks whether the given expression is a lambda expression with explicitly typed parameters.
- *
+ *
* @param expression the expression to check
* @return true
if the expression is a lambda expression with explicitly typed
* parameters or no parameters, false
otherwise
@@ -1041,8 +1411,7 @@
}
public static ASTNode findParent(ASTNode node, StructuralPropertyDescriptor[][] pathes) {
- for (int p= 0; p < pathes.length; p++) {
- StructuralPropertyDescriptor[] path= pathes[p];
+ for (StructuralPropertyDescriptor[] path : pathes) {
ASTNode current= node;
int d= path.length - 1;
for (; d >= 0 && current != null; d--) {
@@ -1060,7 +1429,7 @@
/**
* For {@link Name} or {@link Type} nodes, returns the topmost {@link Type} node
* that shares the same type binding as the given node.
- *
+ *
* @param node an ASTNode
* @return the normalized {@link Type} node or the original node
*/
@@ -1083,10 +1452,39 @@
return current;
}
+ /**
+ * Returns the same node after removing any parentheses around it.
+ *
+ * @param node the node around which parentheses must be removed
+ * @return the same node after removing any parentheses around it. If there are
+ * no parentheses around it then the exact same node is returned
+ */
+ public static ASTNode getUnparenthesedExpression(ASTNode node) {
+ if (node instanceof Expression) {
+ return getUnparenthesedExpression((Expression) node);
+ }
+ return node;
+ }
+
+ /**
+ * Returns the same expression after removing any parentheses around it.
+ *
+ * @param expression the expression around which parentheses must be removed
+ * @return the same expression after removing any parentheses around it If there
+ * are no parentheses around it then the exact same expression is
+ * returned
+ */
+ public static Expression getUnparenthesedExpression(Expression expression) {
+ if (expression != null && expression.getNodeType() == ASTNode.PARENTHESIZED_EXPRESSION) {
+ return getUnparenthesedExpression(((ParenthesizedExpression) expression).getExpression());
+ }
+ return expression;
+ }
+
/**
* Returns true
iff parent
is a true ancestor of node
* (i.e. returns false
if parent == node
).
- *
+ *
* @param node node to test
* @param parent assumed parent
* @return true
iff parent
is a true ancestor of node
@@ -1187,8 +1585,7 @@
return problems;
final int iterations= computeIterations(scope);
List result= new ArrayList<>(5);
- for (int i= 0; i < problems.length; i++) {
- IProblem problem= problems[i];
+ for (IProblem problem : problems) {
boolean consider= false;
if ((severity & PROBLEMS) == PROBLEMS)
consider= true;
@@ -1225,8 +1622,7 @@
return messages;
final int iterations= computeIterations(flags);
List result= new ArrayList<>(5);
- for (int i= 0; i < messages.length; i++) {
- Message message= messages[i];
+ for (Message message : messages) {
ASTNode temp= node;
int count= iterations;
do {
@@ -1281,7 +1677,7 @@
* Returns the topmost ancestor of name
that is still a {@link Name}.
*
* Note: The returned node may resolve to a different binding than the given name
!
- *
+ *
* @param name a name node
* @return the topmost name
* @see #getNormalizedNode(ASTNode)
@@ -1298,7 +1694,7 @@
* Returns the topmost ancestor of node
that is a {@link Type} (but not a {@link UnionType}).
*
* Note: The returned node often resolves to a different binding than the given node
!
- *
+ *
* @param node the starting node, can be null
* @return the topmost type or null
if the node is not a descendant of a type node
* @see #getNormalizedNode(ASTNode)
@@ -1312,10 +1708,10 @@
result= node;
node= node.getParent();
}
-
+
if (result instanceof Type)
return (Type) result;
-
+
return null;
}
@@ -1361,6 +1757,301 @@
}
}
+ /**
+ * Returns whether the provided method invocation invokes a method with the
+ * provided method signature. The method signature is compared against the
+ * erasure of the invoked method.
+ *
+ * @param node the method invocation to compare
+ * @param typeQualifiedName the qualified name of the type declaring
+ * the method
+ * @param methodName the method name
+ * @param parameterTypesQualifiedNames the qualified names of the parameter
+ * types
+ * @return true if the provided method invocation matches the provided method
+ * signature, false otherwise
+ */
+ public static boolean usesGivenSignature(final MethodInvocation node, final String typeQualifiedName, final String methodName,
+ final String... parameterTypesQualifiedNames) {
+ return node != null
+ && usesGivenSignature(node.resolveMethodBinding(), typeQualifiedName, methodName, parameterTypesQualifiedNames);
+ }
+
+ /**
+ * Returns whether the provided method binding has the provided method signature. The method
+ * signature is compared against the erasure of the invoked method.
+ *
+ * @param methodBinding the method binding to compare
+ * @param typeQualifiedName the qualified name of the type declaring the method
+ * @param methodName the method name
+ * @param parameterTypesQualifiedNames the qualified names of the parameter types
+ * @return true if the provided method invocation matches the provided method signature, false
+ * otherwise
+ */
+ public static boolean usesGivenSignature(final IMethodBinding methodBinding, final String typeQualifiedName, final String methodName,
+ final String... parameterTypesQualifiedNames) {
+ // Let's do the fast checks first
+ if (methodBinding == null || !methodName.equals(methodBinding.getName())
+ || methodBinding.getParameterTypes().length != parameterTypesQualifiedNames.length) {
+ return false;
+ }
+
+ // OK more heavy checks now
+ ITypeBinding declaringClass= methodBinding.getDeclaringClass();
+ ITypeBinding implementedType= findImplementedType(declaringClass, typeQualifiedName);
+
+ if (parameterTypesMatch(implementedType, methodBinding, parameterTypesQualifiedNames)) {
+ return true;
+ }
+
+ // A lot more heavy checks
+ IMethodBinding overriddenMethod= findOverridenMethod(declaringClass, typeQualifiedName, methodName,
+ parameterTypesQualifiedNames);
+
+ if (overriddenMethod != null && methodBinding.overrides(overriddenMethod)) {
+ return true;
+ }
+
+ IMethodBinding methodDeclaration= methodBinding.getMethodDeclaration();
+ return methodDeclaration != null && methodDeclaration != methodBinding
+ && usesGivenSignature(methodDeclaration, typeQualifiedName, methodName, parameterTypesQualifiedNames);
+ }
+
+ private static boolean parameterTypesMatch(final ITypeBinding implementedType, final IMethodBinding methodBinding,
+ final String[] parameterTypesQualifiedNames) {
+ if (implementedType != null && !implementedType.isRawType()) {
+ ITypeBinding erasure= implementedType.getErasure();
+
+ if (erasure.isGenericType() || erasure.isParameterizedType()) {
+ return parameterizedTypesMatch(implementedType, erasure, methodBinding);
+ }
+ }
+
+ return implementedType != null && concreteTypesMatch(methodBinding.getParameterTypes(), parameterTypesQualifiedNames);
+ }
+
+ private static IMethodBinding findOverridenMethod(final ITypeBinding typeBinding, final String typeQualifiedName,
+ final String methodName, final String[] parameterTypesQualifiedNames) {
+ // Superclass
+ ITypeBinding superclassBinding= typeBinding.getSuperclass();
+
+ if (superclassBinding != null) {
+ superclassBinding= superclassBinding.getErasure();
+
+ if (typeQualifiedName.equals(superclassBinding.getErasure().getQualifiedName())) {
+ // Found the type
+ return findOverridenMethod(methodName, parameterTypesQualifiedNames,
+ superclassBinding.getDeclaredMethods());
+ }
+
+ IMethodBinding overridenMethod= findOverridenMethod(superclassBinding, typeQualifiedName, methodName,
+ parameterTypesQualifiedNames);
+
+ if (overridenMethod != null) {
+ return overridenMethod;
+ }
+ }
+
+ // Interfaces
+ for (ITypeBinding itfBinding : typeBinding.getInterfaces()) {
+ itfBinding= itfBinding.getErasure();
+
+ if (typeQualifiedName.equals(itfBinding.getQualifiedName())) {
+ // Found the type
+ return findOverridenMethod(methodName, parameterTypesQualifiedNames, itfBinding.getDeclaredMethods());
+ }
+
+ IMethodBinding overridenMethod= findOverridenMethod(itfBinding, typeQualifiedName, methodName,
+ parameterTypesQualifiedNames);
+
+ if (overridenMethod != null) {
+ return overridenMethod;
+ }
+ }
+
+ return null;
+ }
+
+ private static IMethodBinding findOverridenMethod(final String methodName, final String[] parameterTypesQualifiedNames,
+ final IMethodBinding[] declaredMethods) {
+ for (IMethodBinding methodBinding : declaredMethods) {
+ IMethodBinding methodDecl= methodBinding.getMethodDeclaration();
+
+ if (methodBinding.getName().equals(methodName) && methodDecl != null
+ && concreteTypesMatch(methodDecl.getParameterTypes(), parameterTypesQualifiedNames)) {
+ return methodBinding;
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean concreteTypesMatch(final ITypeBinding[] typeBindings, final String... typesQualifiedNames) {
+ if (typeBindings.length != typesQualifiedNames.length) {
+ return false;
+ }
+
+ for (int i= 0; i < typesQualifiedNames.length; i++) {
+ if (!typesQualifiedNames[i].equals(typeBindings[i].getQualifiedName())
+ && !typesQualifiedNames[i].equals(Bindings.getBoxedTypeName(typeBindings[i].getQualifiedName()))
+ && !typesQualifiedNames[i].equals(Bindings.getUnboxedTypeName(typeBindings[i].getQualifiedName()))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean parameterizedTypesMatch(final ITypeBinding clazz, final ITypeBinding clazzErasure,
+ IMethodBinding methodBinding) {
+ if (clazz.isParameterizedType() && !clazz.equals(clazzErasure)) {
+ Map genericToConcreteTypeParamsFromClass= getGenericToConcreteTypeParamsMap(
+ clazz, clazzErasure);
+
+ for (IMethodBinding declaredMethod : clazzErasure.getDeclaredMethods()) {
+ if (declaredMethod.getName().equals(methodBinding.getName())) {
+ Map genericToConcreteTypeParams= getGenericToConcreteTypeParamsMap(
+ methodBinding, declaredMethod);
+ genericToConcreteTypeParams.putAll(genericToConcreteTypeParamsFromClass);
+
+ if (parameterizedTypesMatch(genericToConcreteTypeParams, methodBinding, declaredMethod)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static Map getGenericToConcreteTypeParamsMap(final IMethodBinding method,
+ final IMethodBinding methodErasure) {
+ return getGenericToConcreteTypeParamsMap(method.getTypeArguments(), methodErasure.getTypeParameters());
+ }
+
+ private static Map getGenericToConcreteTypeParamsMap(final ITypeBinding clazz,
+ final ITypeBinding clazzErasure) {
+ return getGenericToConcreteTypeParamsMap(clazz.getTypeArguments(), clazzErasure.getTypeParameters());
+ }
+
+ private static Map getGenericToConcreteTypeParamsMap(final ITypeBinding[] typeParams,
+ final ITypeBinding[] genericTypeParams) {
+ final Map results= new HashMap<>();
+ for (int i= 0; i < genericTypeParams.length && i < typeParams.length; i++) {
+ results.put(genericTypeParams[i], typeParams[i]);
+ }
+ return results;
+ }
+
+ private static boolean parameterizedTypesMatch(final Map genericToConcreteTypeParams,
+ final IMethodBinding parameterizedMethod, final IMethodBinding genericMethod) {
+ ITypeBinding[] paramTypes= parameterizedMethod.getParameterTypes();
+ ITypeBinding[] genericParamTypes= genericMethod.getParameterTypes();
+
+ if (paramTypes.length != genericParamTypes.length) {
+ return false;
+ }
+
+ for (int i= 0; i < genericParamTypes.length; i++) {
+ ITypeBinding genericParamType= genericParamTypes[i];
+ ITypeBinding concreteParamType= null;
+
+ if (genericParamType.isArray()) {
+ ITypeBinding concreteElementType= genericToConcreteTypeParams.get(genericParamType.getElementType());
+
+ if (concreteElementType != null) {
+ concreteParamType= concreteElementType.createArrayType(genericParamType.getDimensions());
+ }
+ } else {
+ concreteParamType= genericToConcreteTypeParams.get(genericParamType);
+ }
+
+ if (concreteParamType == null) {
+ concreteParamType= genericParamType;
+ }
+
+ final ITypeBinding erasure1= paramTypes[i].getErasure();
+ final String erasureName1;
+ if (erasure1.isPrimitive()) {
+ erasureName1= Bindings.getBoxedTypeName(erasure1.getQualifiedName());
+ } else {
+ erasureName1= erasure1.getQualifiedName();
+ }
+
+ final ITypeBinding erasure2= concreteParamType.getErasure();
+ final String erasureName2;
+ if (erasure2.isPrimitive()) {
+ erasureName2= Bindings.getBoxedTypeName(erasure2.getQualifiedName());
+ } else {
+ erasureName2= erasure2.getQualifiedName();
+ }
+
+ if (!erasureName1.equals(erasureName2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the type binding for the provided qualified type name if it can be found in the type
+ * hierarchy of the provided type binding.
+ *
+ * @param typeBinding the type binding to analyze
+ * @param qualifiedTypeName the qualified type name to find
+ * @return the type binding for the provided qualified type name if it can be found in the type
+ * hierarchy of the provided type binding, or {@code null} otherwise
+ */
+ private static ITypeBinding findImplementedType(final ITypeBinding typeBinding, final String qualifiedTypeName) {
+ if (typeBinding == null) {
+ return null;
+ }
+
+ ITypeBinding typeErasure= typeBinding.getErasure();
+
+ if (qualifiedTypeName.equals(typeBinding.getQualifiedName())
+ || qualifiedTypeName.equals(typeErasure.getQualifiedName())) {
+ return typeBinding;
+ }
+
+ return findImplementedType2(typeBinding, qualifiedTypeName);
+ }
+
+ private static ITypeBinding findImplementedType2(final ITypeBinding typeBinding, final String qualifiedTypeName) {
+ final ITypeBinding superclass= typeBinding.getSuperclass();
+
+ if (superclass != null) {
+ String superClassQualifiedName= superclass.getErasure().getQualifiedName();
+
+ if (qualifiedTypeName.equals(superClassQualifiedName)) {
+ return superclass;
+ }
+
+ ITypeBinding implementedType= findImplementedType2(superclass, qualifiedTypeName);
+
+ if (implementedType != null) {
+ return implementedType;
+ }
+ }
+
+ for (ITypeBinding itfBinding : typeBinding.getInterfaces()) {
+ String itfQualifiedName= itfBinding.getErasure().getQualifiedName();
+
+ if (qualifiedTypeName.equals(itfQualifiedName)) {
+ return itfBinding;
+ }
+
+ ITypeBinding implementedType= findImplementedType2(itfBinding, qualifiedTypeName);
+
+ if (implementedType != null) {
+ return implementedType;
+ }
+ }
+
+ return null;
+ }
+
public static Modifier findModifierNode(int flag, List modifiers) {
for (int i= 0; i < modifiers.size(); i++) {
Object curr= modifiers.get(i);
@@ -1396,26 +2087,26 @@
/**
* Escapes a string value to a literal that can be used in Java source.
- *
+ *
* @param stringValue the string value
* @return the escaped string
* @see StringLiteral#getEscapedValue()
*/
public static String getEscapedStringLiteral(String stringValue) {
- StringLiteral stringLiteral= AST.newAST(IASTSharedValues.SHARED_AST_LEVEL).newStringLiteral();
+ StringLiteral stringLiteral= AST.newAST(IASTSharedValues.SHARED_AST_LEVEL, false).newStringLiteral();
stringLiteral.setLiteralValue(stringValue);
return stringLiteral.getEscapedValue();
}
/**
* Escapes a character value to a literal that can be used in Java source.
- *
+ *
* @param ch the character value
* @return the escaped string
* @see CharacterLiteral#getEscapedValue()
*/
public static String getEscapedCharacterLiteral(char ch) {
- CharacterLiteral characterLiteral= AST.newAST(IASTSharedValues.SHARED_AST_LEVEL).newCharacterLiteral();
+ CharacterLiteral characterLiteral= AST.newAST(IASTSharedValues.SHARED_AST_LEVEL, false).newCharacterLiteral();
characterLiteral.setCharValue(ch);
return characterLiteral.getEscapedValue();
}
@@ -1424,7 +2115,7 @@
* If the given node
has already been rewritten, undo that rewrite and return the
* replacement version of the node. Otherwise, return the result of
* {@link ASTRewrite#createCopyTarget(ASTNode)}.
- *
+ *
* @param rewrite ASTRewrite for the given node
* @param node the node to get the replacement or to create a copy placeholder for
* @param group the edit group which collects the corresponding text edits, or null
@@ -1444,7 +2135,7 @@
/**
* Type-safe variant of {@link ASTRewrite#createMoveTarget(ASTNode)}.
- *
+ *
* @param rewrite ASTRewrite for the given node
* @param node the node to create a move placeholder for
* @return the new placeholder node
@@ -1458,7 +2149,7 @@
/**
* Type-safe variant of {@link ASTNode#copySubtree(AST, ASTNode)}.
- *
+ *
* @param target the AST that is to own the nodes in the result
* @param node the node to copy, or null
if none
* @return the copied node, or null
if node
@@ -1490,7 +2181,7 @@
/**
* Checks whether the given exprStatement
has a semicolon at the end.
- *
+ *
* @param exprStatement the {@link ExpressionStatement} to check the semicolon
* @param cu the compilation unit
* @return true
if the given exprStatement
has a semicolon at the end,
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/Bindings.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/Bindings.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/Bindings.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/Bindings.java" 2020-02-26 15:31:57.000000000 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -19,6 +19,7 @@
package org.eclipse.jdt.internal.corext.dom;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -141,7 +142,7 @@
* Note: this method is for debugging and testing purposes only.
* There are tests whose pre-computed test results rely on the returned String's format.
* Use org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider to present a binding to the user.
- *
+ *
* @param binding the binding
* @return a string representation of given binding
*/
@@ -162,7 +163,7 @@
Assert.isTrue(variableBinding.getName().equals("length"));//$NON-NLS-1$
return ARRAY_LENGTH_FIELD_BINDING_STRING;
}
- StringBuffer result= new StringBuffer();
+ StringBuilder result= new StringBuilder();
result.append(variableBinding.getDeclaringClass().getName());
result.append(':');
result.append(variableBinding.getName());
@@ -170,7 +171,7 @@
}
private static String asString(IMethodBinding method) {
- StringBuffer result= new StringBuffer();
+ StringBuilder result= new StringBuilder();
result.append(method.getDeclaringClass().getName());
result.append(':');
result.append(method.getName());
@@ -191,7 +192,7 @@
List result= new ArrayList<>(5);
createName(type, false, result);
- StringBuffer buffer= new StringBuffer();
+ StringBuilder buffer= new StringBuilder();
for (int i= 0; i < result.size(); i++) {
if (i > 0) {
buffer.append('.');
@@ -252,9 +253,7 @@
createName(declaringType, includePackage, list);
} else if (includePackage && !baseType.getPackage().isUnnamed()) {
String[] components= baseType.getPackage().getNameComponents();
- for (int i= 0; i < components.length; i++) {
- list.add(components[i]);
- }
+ list.addAll(Arrays.asList(components));
}
}
if (!baseType.isAnonymous()) {
@@ -310,9 +309,7 @@
public static IVariableBinding findFieldInType(ITypeBinding type, String fieldName) {
if (type.isPrimitive())
return null;
- IVariableBinding[] fields= type.getDeclaredFields();
- for (int i= 0; i < fields.length; i++) {
- IVariableBinding field= fields[i];
+ for (IVariableBinding field : type.getDeclaredFields()) {
if (field.getName().equals(fieldName))
return field;
}
@@ -338,9 +335,8 @@
if (field != null)
return field;
}
- ITypeBinding[] interfaces= type.getInterfaces();
- for (int i= 0; i < interfaces.length; i++) {
- field= findFieldInHierarchy(interfaces[i], fieldName);
+ for (ITypeBinding intf : type.getInterfaces()) {
+ field= findFieldInHierarchy(intf, fieldName);
if (field != null) // no private fields in interfaces
return field;
}
@@ -359,14 +355,15 @@
public static IMethodBinding findMethodInType(ITypeBinding type, String methodName, ITypeBinding[] parameters) {
if (type.isPrimitive())
return null;
- IMethodBinding[] methods= type.getDeclaredMethods();
- for (int i= 0; i < methods.length; i++) {
+ for (IMethodBinding method : type.getDeclaredMethods()) {
if (parameters == null) {
- if (methodName.equals(methods[i].getName()))
- return methods[i];
+ if (methodName.equals(method.getName())) {
+ return method;
+ }
} else {
- if (isEqualMethod(methods[i], methodName, parameters))
- return methods[i];
+ if (isEqualMethod(method, methodName, parameters)) {
+ return method;
+ }
}
}
return null;
@@ -377,7 +374,7 @@
* the type hierarchy denoted by the given type. Returns null
if no such method
* exists. If the method is defined in more than one super type only the first match is
* returned. First the super class is examined and then the implemented interfaces.
- *
+ *
* @param type The type to search the method in
* @param methodName The name of the method to find
* @param parameters The parameter types of the method to find. If null
is passed, only the name is matched and parameters are ignored.
@@ -393,9 +390,8 @@
if (method != null)
return method;
}
- ITypeBinding[] interfaces= type.getInterfaces();
- for (int i= 0; i < interfaces.length; i++) {
- method= findMethodInHierarchy(interfaces[i], methodName, parameters);
+ for (ITypeBinding intf : type.getInterfaces()) {
+ method= findMethodInHierarchy(intf, methodName, parameters);
if (method != null)
return method;
}
@@ -413,14 +409,15 @@
public static IMethodBinding findMethodInType(ITypeBinding type, String methodName, String[] parameters) {
if (type.isPrimitive())
return null;
- IMethodBinding[] methods= type.getDeclaredMethods();
- for (int i= 0; i < methods.length; i++) {
+ for (IMethodBinding method : type.getDeclaredMethods()) {
if (parameters == null) {
- if (methodName.equals(methods[i].getName()))
- return methods[i];
+ if (methodName.equals(method.getName())) {
+ return method;
+ }
} else {
- if (isEqualMethod(methods[i], methodName, parameters))
- return methods[i];
+ if (isEqualMethod(method, methodName, parameters)) {
+ return method;
+ }
}
}
return null;
@@ -434,7 +431,7 @@
* whose {@link IMethodBinding#getMethodDeclaration() declaration}'s parameters matches the
* given parameters.
*
- *
+ *
* @param type The type to search the method in
* @param methodName The name of the method to find
* @param parameters The parameter types of the method to find. If null
is passed, only the name is matched and parameters are ignored.
@@ -443,19 +440,20 @@
public static IMethodBinding findMethodWithDeclaredParameterTypesInType(ITypeBinding type, String methodName, String[] parameters) {
if (type.isPrimitive())
return null;
- IMethodBinding[] methods= type.getDeclaredMethods();
- for (int i= 0; i < methods.length; i++) {
+ for (IMethodBinding method : type.getDeclaredMethods()) {
if (parameters == null) {
- if (methodName.equals(methods[i].getName()))
- return methods[i];
+ if (methodName.equals(method.getName())) {
+ return method;
+ }
} else {
- if (isEqualMethod(methods[i].getMethodDeclaration(), methodName, parameters))
- return methods[i];
+ if (isEqualMethod(method.getMethodDeclaration(), methodName, parameters)) {
+ return method;
+ }
}
}
return null;
}
-
+
/**
* Finds the method specified by methodName
and parameters
in
* the type hierarchy denoted by the given type. Returns null
if no such method
@@ -476,9 +474,8 @@
if (method != null)
return method;
}
- ITypeBinding[] interfaces= type.getInterfaces();
- for (int i= 0; i < interfaces.length; i++) {
- method= findMethodInHierarchy(interfaces[i], methodName, parameters);
+ for (ITypeBinding intf : type.getInterfaces()) {
+ method= findMethodInHierarchy(intf, methodName, parameters);
if (method != null)
return method;
}
@@ -493,10 +490,10 @@
* @return the method binding of the method that is overridden by the specified method
, or null
*/
public static IMethodBinding findOverriddenMethodInType(ITypeBinding type, IMethodBinding method) {
- IMethodBinding[] methods= type.getDeclaredMethods();
- for (int i= 0; i < methods.length; i++) {
- if (isSubsignature(method, methods[i]))
- return methods[i];
+ for (IMethodBinding curr : type.getDeclaredMethods()) {
+ if (isSubsignature(method, curr)) {
+ return curr;
+ }
}
return null;
}
@@ -519,9 +516,8 @@
if (method != null)
return method;
}
- ITypeBinding[] interfaces= type.getInterfaces();
- for (int i= 0; i < interfaces.length; i++) {
- method= findOverriddenMethodInHierarchy(interfaces[i], binding);
+ for (ITypeBinding intf : type.getInterfaces()) {
+ method= findOverriddenMethodInHierarchy(intf, binding);
if (method != null)
return method;
}
@@ -543,11 +539,11 @@
}
return findOverriddenMethods.get(0);
}
-
+
/**
* Finds all methods that are overridden by the given method. The search is bottom-up, so this
* returns the nearest defining/declaring methods in order.
- *
+ *
* @param overriding overriding method
* @param testVisibility if true
the result is tested on visibility
* @param firstOnly if true
this method will return when the first overridden
@@ -576,9 +572,8 @@
}
}
}
- ITypeBinding[] interfaces= type.getInterfaces();
- for (int i= 0; i < interfaces.length; i++) {
- IMethodBinding res= findOverriddenMethodInHierarchy(interfaces[i], overriding);
+ for (ITypeBinding intf : type.getInterfaces()) {
+ IMethodBinding res= findOverriddenMethodInHierarchy(intf, overriding);
if (res != null) {
methodList.add(res); // methods from interfaces are always public and therefore visible
if (firstOnly) {
@@ -615,9 +610,8 @@
private static void collectSuperTypes(ITypeBinding curr, Set collection) {
if (collection.add(curr)) {
- ITypeBinding[] interfaces= curr.getInterfaces();
- for (int i= 0; i < interfaces.length; i++) {
- collectSuperTypes(interfaces[i], collection);
+ for (ITypeBinding intf : curr.getInterfaces()) {
+ collectSuperTypes(intf, collection);
}
ITypeBinding superClass= curr.getSuperclass();
if (superClass != null) {
@@ -662,12 +656,11 @@
boolean unvisited= visited.add(type);
if (!unvisited)
return true;
- ITypeBinding[] interfaces= type.getInterfaces();
- for (int i= 0; i < interfaces.length; i++) {
- if (!visitor.visit(interfaces[i])) {
+ for (ITypeBinding intf : type.getInterfaces()) {
+ if (!visitor.visit(intf)) {
return false;
}
- if (!visitInterfaces(interfaces[i], visitor, visited)) {
+ if (!visitInterfaces(intf, visitor, visited)) {
return false;
}
}
@@ -813,9 +806,11 @@
}
private static boolean containsTypeVariables(ITypeBinding[] types) {
- for (int i= 0; i < types.length; i++)
- if (containsTypeVariables(types[i]))
+ for (ITypeBinding type : types) {
+ if (containsTypeVariables(type)) {
return true;
+ }
+ }
return false;
}
@@ -826,8 +821,7 @@
return Collections.emptySet();
Set result= new HashSet<>(typeBounds.length);
- for (int i= 0; i < typeBounds.length; i++) {
- ITypeBinding bound= typeBounds[i];
+ for (ITypeBinding bound : typeBounds) {
if ("java.lang.Object".equals(typeBounds[0].getQualifiedName())) //$NON-NLS-1$
continue;
else if (containsTypeVariables(bound))
@@ -843,7 +837,7 @@
/**
* Checks whether a method with the given name and parameter types
* is a subsignature of the given method binding.
- *
+ *
* @param method a method
* @param methodName method name to match
* @param parameters the parameter types of the method to find. If null
is passed, only the name is matched and parameters are ignored.
@@ -865,7 +859,7 @@
index= first.indexOf('<');
if (index > 0){
int lastIndex= first.lastIndexOf('>');
- StringBuffer buf= new StringBuffer();
+ StringBuilder buf= new StringBuilder();
buf.append(first.substring(0, index));
if (lastIndex < first.length() - 1)
buf.append(first.substring(lastIndex + 1, first.length()));
@@ -903,8 +897,8 @@
}
}
ITypeBinding[] superInterfaces= hierarchyType.getInterfaces();
- for (int i= 0; i < superInterfaces.length; i++) {
- ITypeBinding res= findTypeInHierarchy(superInterfaces[i], fullyQualifiedTypeName);
+ for (ITypeBinding superInterface : superInterfaces) {
+ ITypeBinding res= findTypeInHierarchy(superInterface, fullyQualifiedTypeName);
if (res != null) {
return res;
}
@@ -930,16 +924,15 @@
return superClass;
}
}
- ITypeBinding[] superInterfaces= hierarchyType.getInterfaces();
- for (int i= 0; i < superInterfaces.length; i++) {
- ITypeBinding res= findTypeInHierarchy(superInterfaces[i], fullyQualifiedTypeName);
+ for (ITypeBinding superInterface : hierarchyType.getInterfaces()) {
+ ITypeBinding res= findTypeInHierarchy(superInterface, fullyQualifiedTypeName);
if (res != null) {
- return superInterfaces[i];
+ return superInterface;
}
}
return null;
}
-
+
/**
* Returns the binding of the variable written in an Assignment.
* @param assignment The assignment
@@ -964,13 +957,13 @@
/**
* Returns true
if the given type is a super type of a candidate.
* true
is returned if the two type bindings are identical.
- *
+ *
* Warning: With the addition of generics, this method is valid in less
* cases than before. Consider using {@link TypeRules#canAssign(ITypeBinding, ITypeBinding)}
* if you're dealing with types of variables. The classical notion of supertypes
* only makes sense if you really need to walk the type hierarchy but don't need to play
* the assignment rules.
- *
+ *
* @param possibleSuperType the type to inspect
* @param type the type whose super types are looked at
* @return true
iff possibleSuperType
is
@@ -979,7 +972,7 @@
public static boolean isSuperType(ITypeBinding possibleSuperType, ITypeBinding type) {
return isSuperType(possibleSuperType, type, true);
}
-
+
/**
* Returns true
if the given type is a super type of a candidate.
* true
is returned if the two type bindings are identical (TODO)
@@ -1007,9 +1000,8 @@
}
if (possibleSuperType.isInterface()) {
- ITypeBinding[] superInterfaces= type.getInterfaces();
- for (int i= 0; i < superInterfaces.length; i++) {
- if (isSuperType(possibleSuperType, superInterfaces[i], considerTypeArguments)) {
+ for (ITypeBinding superInterface : type.getInterfaces()) {
+ if (isSuperType(possibleSuperType, superInterface, considerTypeArguments)) {
return true;
}
}
@@ -1047,9 +1039,7 @@
public static IMethod findMethod(IMethodBinding method, IType type) throws JavaModelException {
method= method.getMethodDeclaration();
- IMethod[] candidates= type.getMethods();
- for (int i= 0; i < candidates.length; i++) {
- IMethod candidate= candidates[i];
+ for (IMethod candidate : type.getMethods()) {
if (candidate.getElementName().equals(method.getName()) && sameParameters(method, candidate)) {
return candidate;
}
@@ -1102,10 +1092,9 @@
return false;
String packageName= type.getPackage().isUnnamed() ? "" : type.getPackage().getName(); //$NON-NLS-1$
String typeName= getTypeQualifiedName(type);
- for (int i= 0; i < qualifiedCandidates.length; i++) {
- String[] qualifiedCandidate= qualifiedCandidates[i];
+ for (String[] qualifiedCandidate : qualifiedCandidates) {
if ( qualifiedCandidate[0].equals(packageName) &&
- qualifiedCandidate[1].equals(typeName))
+ qualifiedCandidate[1].equals(typeName))
return true;
}
}
@@ -1120,10 +1109,10 @@
* Anonymous types are normalized to the super class or interface. For
* null or void bindings, null
is returned.
*
- *
+ *
* @param binding the binding to normalize
* @return the normalized binding, can be null
- *
+ *
* @see #normalizeForDeclarationUse(ITypeBinding, AST)
*/
public static ITypeBinding normalizeTypeBinding(ITypeBinding binding) {
@@ -1152,7 +1141,7 @@
* declaration, method return type, parameter type, ...).
* For null bindings, java.lang.Object is returned.
* For void bindings, null
is returned.
- *
+ *
* @param binding binding to normalize
* @param ast current AST
* @return the normalized type to be used in declarations, or null
@@ -1184,7 +1173,7 @@
/**
* Returns the type binding of the node's enclosing type declaration.
- *
+ *
* @param node an AST node
* @return the type binding of the node's parent type declaration, or null
*/
@@ -1204,7 +1193,7 @@
* Returns the type binding of the node's type context or null if the node is inside
* an annotation, type parameter, super type declaration, or Javadoc of a top level type.
* The result of this method is equal to the result of {@link #getBindingOfParentType(ASTNode)} for nodes in the type's body.
- *
+ *
* @param node an AST node
* @return the type binding of the node's parent type context, or null
*/
@@ -1256,7 +1245,7 @@
if (binding.isArray()) {
String elementTypeQualifiedName = getRawQualifiedName(binding.getElementType());
if (elementTypeQualifiedName.length() != 0) {
- StringBuffer stringBuffer= new StringBuffer(elementTypeQualifiedName);
+ StringBuilder stringBuffer= new StringBuilder(elementTypeQualifiedName);
stringBuffer.append('[').append(']');
return stringBuffer.toString();
} else {
@@ -1266,7 +1255,7 @@
if (binding.isMember()) {
String outerName= getRawQualifiedName(binding.getDeclaringClass());
if (outerName.length() > 0) {
- StringBuffer buffer= new StringBuffer();
+ StringBuilder buffer= new StringBuilder();
buffer.append(outerName);
buffer.append('.');
buffer.append(getRawName(binding));
@@ -1277,7 +1266,7 @@
} else if (binding.isTopLevel()) {
IPackageBinding packageBinding= binding.getPackage();
- StringBuffer buffer= new StringBuffer();
+ StringBuilder buffer= new StringBuilder();
if (packageBinding != null && packageBinding.getName().length() > 0) {
buffer.append(packageBinding.getName()).append('.');
}
@@ -1330,9 +1319,10 @@
*/
@Deprecated
public static boolean containsSignatureEquivalentConstructor(IMethodBinding[] candidates, IMethodBinding overridable) {
- for (int index= 0; index < candidates.length; index++) {
- if (isSignatureEquivalentConstructor(candidates[index], overridable))
+ for (IMethodBinding candidate : candidates) {
+ if (isSignatureEquivalentConstructor(candidate, overridable)) {
return true;
+ }
}
return false;
}
@@ -1387,14 +1377,13 @@
if (!overridableErasure.isSubTypeCompatible(overriddenErasure) || !overridableErasure.getKey().equals(overriddenErasure.getKey()))
return false;
}
- ITypeBinding[] overriddenExceptions= overridden.getExceptionTypes();
- ITypeBinding[] overridableExceptions= overridable.getExceptionTypes();
boolean checked= false;
- for (int index= 0; index < overriddenExceptions.length; index++) {
+ for (ITypeBinding overriddenException : overridden.getExceptionTypes()) {
checked= false;
- for (int offset= 0; offset < overridableExceptions.length; offset++) {
- if (overriddenExceptions[index].isSubTypeCompatible(overridableExceptions[offset]))
+ for (ITypeBinding overridableException : overridable.getExceptionTypes()) {
+ if (overriddenException.isSubTypeCompatible(overridableException)) {
checked= true;
+ }
}
if (!checked)
return false;
@@ -1422,33 +1411,36 @@
return boxed;
}
- private static String getBoxedTypeName(String primitiveName) {
- if ("long".equals(primitiveName)) //$NON-NLS-1$
- return "java.lang.Long"; //$NON-NLS-1$
+ /**
+ * Returns the boxed type binding according to JLS3 5.1.7, or null.
+ *
+ * @param primitiveName a type name
+ * @return the boxed type, or null if no boxed type found
+ */
+ public static String getBoxedTypeName(String primitiveName) {
+ if (null == primitiveName)
+ return null;
- else if ("int".equals(primitiveName)) //$NON-NLS-1$
+ switch (primitiveName) {
+ case "long": //$NON-NLS-1$
+ return "java.lang.Long"; //$NON-NLS-1$
+ case "int": //$NON-NLS-1$
return "java.lang.Integer"; //$NON-NLS-1$
-
- else if ("short".equals(primitiveName)) //$NON-NLS-1$
+ case "short": //$NON-NLS-1$
return "java.lang.Short"; //$NON-NLS-1$
-
- else if ("char".equals(primitiveName)) //$NON-NLS-1$
+ case "char": //$NON-NLS-1$
return "java.lang.Character"; //$NON-NLS-1$
-
- else if ("byte".equals(primitiveName)) //$NON-NLS-1$
+ case "byte": //$NON-NLS-1$
return "java.lang.Byte"; //$NON-NLS-1$
-
- else if ("boolean".equals(primitiveName)) //$NON-NLS-1$
+ case "boolean": //$NON-NLS-1$
return "java.lang.Boolean"; //$NON-NLS-1$
-
- else if ("float".equals(primitiveName)) //$NON-NLS-1$
+ case "float": //$NON-NLS-1$
return "java.lang.Float"; //$NON-NLS-1$
-
- else if ("double".equals(primitiveName)) //$NON-NLS-1$
+ case "double": //$NON-NLS-1$
return "java.lang.Double"; //$NON-NLS-1$
-
- else
+ default:
return null;
+ }
}
/**
@@ -1470,73 +1462,77 @@
return type;
return unboxed;
}
-
- private static String getUnboxedTypeName(String boxedName) {
- if ("java.lang.Long".equals(boxedName)) //$NON-NLS-1$
+
+ /**
+ * Returns the unboxed type binding according to JLS3 5.1.7, or null if the given type is not a
+ * boxed type.
+ *
+ * @param boxedName a type name
+ * @return the unboxed type, or null if no unboxed type found
+ */
+ public static String getUnboxedTypeName(String boxedName) {
+ if (null == boxedName)
+ return null;
+
+ switch (boxedName) {
+ case "java.lang.Long": //$NON-NLS-1$
return "long"; //$NON-NLS-1$
-
- else if ("java.lang.Integer".equals(boxedName)) //$NON-NLS-1$
+ case "java.lang.Integer": //$NON-NLS-1$
return "int"; //$NON-NLS-1$
-
- else if ("java.lang.Short".equals(boxedName)) //$NON-NLS-1$
+ case "java.lang.Short": //$NON-NLS-1$
return "short"; //$NON-NLS-1$
-
- else if ("java.lang.Character".equals(boxedName)) //$NON-NLS-1$
+ case "java.lang.Character": //$NON-NLS-1$
return "char"; //$NON-NLS-1$
-
- else if ("java.lang.Byte".equals(boxedName)) //$NON-NLS-1$
+ case "java.lang.Byte": //$NON-NLS-1$
return "byte"; //$NON-NLS-1$
-
- else if ("java.lang.Boolean".equals(boxedName)) //$NON-NLS-1$
+ case "java.lang.Boolean": //$NON-NLS-1$
return "boolean"; //$NON-NLS-1$
-
- else if ("java.lang.Float".equals(boxedName)) //$NON-NLS-1$
+ case "java.lang.Float": //$NON-NLS-1$
return "float"; //$NON-NLS-1$
-
- else if ("java.lang.Double".equals(boxedName)) //$NON-NLS-1$
+ case "java.lang.Double": //$NON-NLS-1$
return "double"; //$NON-NLS-1$
-
- else
+ default:
return null;
+ }
}
-
+
/**
* Resolve the binding (not the type binding) for the expression or a nested expression
* (e.g. nested in parentheses, cast, ...).
- *
+ *
* @param expression an expression node
* @param goIntoCast iff true
, go into a CastExpression's expression to resolve
* @return the expression binding, or null
if the expression has no binding or the
* binding could not be resolved
- *
+ *
* @since 3.5
*/
public static IBinding resolveExpressionBinding(Expression expression, boolean goIntoCast) {
//TODO: search for callers of resolve*Binding() methods and replace with call to this method
-
+
// similar to StubUtility#getVariableNameSuggestions(int, IJavaProject, ITypeBinding, Expression, Collection)
switch (expression.getNodeType()) {
case ASTNode.SIMPLE_NAME:
case ASTNode.QUALIFIED_NAME:
return ((Name) expression).resolveBinding();
-
+
case ASTNode.FIELD_ACCESS:
return ((FieldAccess) expression).resolveFieldBinding();
case ASTNode.SUPER_FIELD_ACCESS:
return ((SuperFieldAccess) expression).resolveFieldBinding();
-
+
case ASTNode.METHOD_INVOCATION:
return ((MethodInvocation) expression).resolveMethodBinding();
case ASTNode.SUPER_METHOD_INVOCATION:
return ((SuperMethodInvocation) expression).resolveMethodBinding();
case ASTNode.CLASS_INSTANCE_CREATION:
return ((ClassInstanceCreation) expression).resolveConstructorBinding();
-
+
case ASTNode.MARKER_ANNOTATION:
case ASTNode.SINGLE_MEMBER_ANNOTATION:
case ASTNode.NORMAL_ANNOTATION:
return ((Annotation) expression).resolveAnnotationBinding();
-
+
case ASTNode.ARRAY_ACCESS:
return resolveExpressionBinding(((ArrayAccess) expression).getArray(), goIntoCast);
case ASTNode.CAST_EXPRESSION:
@@ -1570,7 +1566,7 @@
/**
* Returns the n-th component type of the given type, or null
if
* the type binding is not an array type or has not that many dimensions.
- *
+ *
* @param arrayType an array type binding
* @param n number of dimensions to cut
* @return arrayType with n dimensions removed, or null
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/BodyDeclarationRewrite.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/BodyDeclarationRewrite.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/BodyDeclarationRewrite.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/BodyDeclarationRewrite.java" 2020-02-26 15:31:57.000000000 +0000
@@ -31,9 +31,9 @@
/**
* Rewrite helper for body declarations.
- *
+ *
* @see ASTNodes#getBodyDeclarationsProperty(ASTNode)
- *
+ *
* @since 1.10
*/
public class BodyDeclarationRewrite {
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/CodeScopeBuilder.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/CodeScopeBuilder.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/CodeScopeBuilder.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/CodeScopeBuilder.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+
+
+public class CodeScopeBuilder extends ASTVisitor {
+
+ public static class Scope {
+ private Scope fParent;
+ private int fStart;
+ private int fLength;
+ private List fNames;
+ private List fChildren;
+ private int fCursorOffset;
+ Scope(Scope parent, int start, int length) {
+ fParent= parent;
+ fStart= start;
+ fLength= length;
+ if (fParent != null)
+ fParent.addChild(this);
+ }
+ public void setCursor(int offset) {
+ fCursorOffset= offset;
+ }
+ private void addChild(Scope child) {
+ if (fChildren == null)
+ fChildren= new ArrayList<>(2);
+ fChildren.add(child);
+ }
+ private void addName(String name) {
+ if (fNames == null)
+ fNames= new ArrayList<>(2);
+ fNames.add(name);
+ }
+ public Scope findScope(int start, int length) {
+ if (fStart <= start && start + length <= fStart + fLength) {
+ if (fChildren == null)
+ return this;
+ for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
+ Scope scope= iter.next().findScope(start, length);
+ if (scope != null)
+ return scope;
+ }
+ return this;
+ }
+ return null;
+ }
+ public String createName(String candidate, boolean add) {
+ int i= 1;
+ String result= candidate;
+ while(isInUse(result)) {
+ result= candidate + i++;
+ }
+ if (add)
+ addName(result);
+ return result;
+ }
+ public boolean isInUse(String name) {
+ if (internalIsInUse(name))
+ return true;
+ if (fChildren != null) {
+ for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
+ Scope child= iter.next();
+ if (fCursorOffset < child.fStart && child.isInUseDown(name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ private boolean internalIsInUse(String name) {
+ if (fNames != null && fNames.contains(name))
+ return true;
+ if (fParent != null)
+ return fParent.internalIsInUse(name);
+ return false;
+
+ }
+ private boolean isInUseDown(String name) {
+ if (fNames != null && fNames.contains(name))
+ return true;
+ if (fChildren == null)
+ return false;
+ for (Iterator iter= fChildren.iterator(); iter.hasNext();) {
+ Scope scope= iter.next();
+ if (scope.isInUseDown(name))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private IBinding fIgnoreBinding;
+ private Selection fIgnoreRange;
+ private Scope fScope;
+ private List fScopes;
+
+ public static Scope perform(BodyDeclaration node, IBinding ignore) {
+ CodeScopeBuilder collector= new CodeScopeBuilder(node, ignore);
+ node.accept(collector);
+ return collector.fScope;
+ }
+
+ public static Scope perform(BodyDeclaration node, Selection ignore) {
+ CodeScopeBuilder collector= new CodeScopeBuilder(node, ignore);
+ node.accept(collector);
+ return collector.fScope;
+ }
+
+ private CodeScopeBuilder(ASTNode node, IBinding ignore) {
+ fScope= new Scope(null, node.getStartPosition(), node.getLength());
+ fScopes= new ArrayList<>();
+ fIgnoreBinding= ignore;
+ }
+
+ private CodeScopeBuilder(ASTNode node, Selection ignore) {
+ fScope= new Scope(null, node.getStartPosition(), node.getLength());
+ fScopes= new ArrayList<>();
+ fIgnoreRange= ignore;
+ }
+
+ @Override
+ public boolean visit(CatchClause node) {
+ // open a new scope for the exception declaration.
+ fScopes.add(fScope);
+ fScope= new Scope(fScope, node.getStartPosition(), node.getLength());
+ return true;
+ }
+
+ @Override
+ public void endVisit(CatchClause node) {
+ fScope= fScopes.remove(fScopes.size() - 1);
+ }
+
+ @Override
+ public boolean visit(SimpleName node) {
+ if (fIgnoreBinding != null && Bindings.equals(fIgnoreBinding, node.resolveBinding()))
+ return false;
+ if (fIgnoreRange != null && fIgnoreRange.covers(node))
+ return false;
+ fScope.addName(node.getIdentifier());
+ return false;
+ }
+
+ @Override
+ public boolean visit(QualifiedName node) {
+ // only consider the left most identifier.
+ node.getQualifier().accept(this);
+ return false;
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ Expression receiver= node.getExpression();
+ if (receiver == null) {
+ SimpleName name= node.getName();
+ if (fIgnoreBinding == null || !Bindings.equals(fIgnoreBinding, name.resolveBinding()))
+ node.getName().accept(this);
+ } else {
+ receiver.accept(this);
+ }
+ accept(node.arguments());
+ return false;
+ }
+
+ @Override
+ public boolean visit(TypeDeclarationStatement node) {
+ fScope.addName(node.getDeclaration().getName().getIdentifier());
+ return false;
+ }
+
+ @Override
+ public boolean visit(Block node) {
+ fScopes.add(fScope);
+ fScope= new Scope(fScope, node.getStartPosition(), node.getLength());
+ return true;
+ }
+
+ @Override
+ public void endVisit(Block node) {
+ fScope= fScopes.remove(fScopes.size() - 1);
+ }
+
+ @Override
+ public boolean visit(ForStatement node) {
+ fScopes.add(fScope);
+ fScope= new Scope(fScope, node.getStartPosition(), node.getLength());
+ return true;
+ }
+
+ @Override
+ public void endVisit(ForStatement node) {
+ fScope= fScopes.remove(fScopes.size() - 1);
+ }
+
+ private void accept(List list) {
+ int size;
+ if (list == null || (size= list.size()) == 0)
+ return;
+ for (int i= 0; i < size; i++) {
+ list.get(i).accept(this);
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/DimensionRewrite.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/DimensionRewrite.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/DimensionRewrite.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/DimensionRewrite.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2013, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
+import org.eclipse.jdt.core.dom.Dimension;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+/**
+ * Rewrite helper for {@link Dimension} node lists and {@link ArrayType}s.
+ *
+ * @see JDTUIHelperClasses
+ * @since 3.10
+ */
+public class DimensionRewrite {
+
+ /**
+ * Creates a {@link ASTRewrite#createCopyTarget(ASTNode) copy} of type
+ * and adds extraDimensions
to it.
+ *
+ * @param type the type to copy
+ * @param extraDimensions the dimensions to add
+ * @param rewrite the ASTRewrite with which to create new nodes
+ * @return the copy target with added dimensions
+ */
+ public static Type copyTypeAndAddDimensions(Type type, List extraDimensions, ASTRewrite rewrite) {
+ AST ast= rewrite.getAST();
+ if (extraDimensions.isEmpty()) {
+ return (Type) rewrite.createCopyTarget(type);
+ }
+
+ ArrayType result;
+ if (type instanceof ArrayType) {
+ ArrayType arrayType= (ArrayType) type;
+ Type varElementType= (Type) rewrite.createCopyTarget(arrayType.getElementType());
+ result= ast.newArrayType(varElementType, 0);
+ result.dimensions().addAll(copyDimensions(extraDimensions, rewrite));
+ result.dimensions().addAll(copyDimensions(arrayType.dimensions(), rewrite));
+ } else {
+ Type elementType= (Type) rewrite.createCopyTarget(type);
+ result= ast.newArrayType(elementType, 0);
+ result.dimensions().addAll(copyDimensions(extraDimensions, rewrite));
+ }
+ return result;
+ }
+
+ /**
+ * Returns {@link ASTRewrite#createCopyTarget(ASTNode) copies} of the given dimensions
.
+ *
+ * @param dimensions the dimensions to copy
+ * @param rewrite the ASTRewrite with which to create new nodes
+ *
+ * @return list of copy targets
+ */
+ public static List copyDimensions(List dimensions, ASTRewrite rewrite) {
+ ArrayList result= new ArrayList<>();
+ for (int i= 0; i < dimensions.size(); i++) {
+ result.add((Dimension) rewrite.createCopyTarget(dimensions.get(i)));
+ }
+ return result;
+ }
+
+ /**
+ * Removes all children in node
's childListProperty
.
+ *
+ * @param node ASTNode
+ * @param childListProperty child list property
+ * @param rewrite rewrite that removes the nodes
+ * @param editGroup the edit group in which to collect the corresponding text edits, or null if ungrouped
+ */
+ public static void removeAllChildren(ASTNode node, ChildListPropertyDescriptor childListProperty, ASTRewrite rewrite, TextEditGroup editGroup) {
+ ListRewrite listRewrite= rewrite.getListRewrite(node, childListProperty);
+ @SuppressWarnings("unchecked")
+ List extends ASTNode> children= (List extends ASTNode>) node.getStructuralProperty(childListProperty);
+ for (ASTNode child : children) {
+ listRewrite.remove(child, editGroup);
+ }
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/AssociativeInfixExpressionFragment.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/AssociativeInfixExpressionFragment.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/AssociativeInfixExpressionFragment.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/AssociativeInfixExpressionFragment.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,464 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.SourceRange;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+import org.eclipse.jdt.internal.corext.SourceRangeFactory;
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+import org.eclipse.jdt.internal.corext.dom.JdtASTMatcher;
+
+class AssociativeInfixExpressionFragment extends ASTFragment implements IExpressionFragment {
+
+ private final List fOperands;
+ private final InfixExpression fGroupRoot;
+
+ public static IExpressionFragment createSubPartFragmentBySourceRange(InfixExpression node, ISourceRange range, ICompilationUnit cu) throws JavaModelException {
+ Assert.isNotNull(node);
+ Assert.isNotNull(range);
+ Assert.isTrue(!Util.covers(range, node));
+ Assert.isTrue(Util.covers(SourceRangeFactory.create(node), range));
+
+ if(!isAssociativeInfix(node))
+ return null;
+
+ InfixExpression groupRoot= findGroupRoot(node);
+ Assert.isTrue(isAGroupRoot(groupRoot));
+
+ List groupMembers= AssociativeInfixExpressionFragment.findGroupMembersInOrderFor(groupRoot);
+ List subGroup= findSubGroupForSourceRange(groupMembers, range);
+ if(subGroup.isEmpty() || rangeIncludesExtraNonWhitespace(range, subGroup, cu))
+ return null;
+
+ return new AssociativeInfixExpressionFragment(groupRoot, subGroup);
+ }
+
+ public static IExpressionFragment createFragmentForFullSubtree(InfixExpression node) {
+ Assert.isNotNull(node);
+
+ if(!isAssociativeInfix(node))
+ return null;
+
+ InfixExpression groupRoot= findGroupRoot(node);
+ Assert.isTrue(isAGroupRoot(groupRoot));
+
+ List groupMembers= AssociativeInfixExpressionFragment.findGroupMembersInOrderFor(node);
+
+ return new AssociativeInfixExpressionFragment(groupRoot, groupMembers);
+ }
+
+ private static InfixExpression findGroupRoot(InfixExpression node) {
+ Assert.isTrue(isAssociativeInfix(node));
+
+ while(!isAGroupRoot(node)) {
+ ASTNode parent= node.getParent();
+
+ Assert.isNotNull(parent);
+ Assert.isTrue(isAssociativeInfix(parent));
+ Assert.isTrue(((InfixExpression) parent).getOperator() == node.getOperator());
+
+ node= (InfixExpression) parent;
+ }
+
+ return node;
+ }
+
+ private static List findSubGroupForSourceRange(List group, ISourceRange range) {
+ Assert.isTrue(!group.isEmpty());
+
+ List subGroup= new ArrayList<>();
+
+ boolean entered= false, exited= false;
+ if(range.getOffset() == group.get(0).getStartPosition())
+ entered= true;
+ for(int i= 0; i < group.size() - 1; i++) {
+ Expression member= group.get(i);
+ Expression nextMember= group.get(i + 1);
+
+ if(entered) {
+ subGroup.add(member);
+ if(rangeEndsBetween(range, member, nextMember)) {
+ exited= true;
+ break;
+ }
+
+ } else {
+ if(rangeStartsBetween(range, member, nextMember))
+ entered= true;
+ }
+ }
+ Expression lastGroupMember= group.get(group.size() - 1);
+ if (Util.getEndExclusive(range) == Util.getEndExclusive(SourceRangeFactory.create(lastGroupMember))) {
+ subGroup.add(lastGroupMember);
+ exited= true;
+ }
+
+ if(!exited)
+ return new ArrayList<>(0);
+ return subGroup;
+ }
+
+
+ private static boolean rangeStartsBetween(ISourceRange range, ASTNode first, ASTNode next) {
+ int pos= range.getOffset();
+ return first.getStartPosition() + first.getLength() <= pos
+ && pos <= next.getStartPosition();
+ }
+
+ private static boolean rangeEndsBetween(ISourceRange range, ASTNode first, ASTNode next) {
+ int pos= Util.getEndExclusive(range);
+ return first.getStartPosition() + first.getLength() <= pos
+ && pos <= next.getStartPosition();
+ }
+
+ private static boolean rangeIncludesExtraNonWhitespace(ISourceRange range, List operands, ICompilationUnit cu) throws JavaModelException {
+ return Util.rangeIncludesNonWhitespaceOutsideRange(range, getRangeOfOperands(operands), cu.getBuffer());
+ }
+
+ private static ISourceRange getRangeOfOperands(List operands) {
+ Expression first= operands.get(0);
+ Expression last= operands.get(operands.size() - 1);
+ return new SourceRange(first.getStartPosition(), last.getStartPosition() + last.getLength() - first.getStartPosition());
+ }
+
+ @Override
+ public IASTFragment[] getMatchingFragmentsWithNode(ASTNode node) {
+ IASTFragment fragmentForNode= ASTFragmentFactory.createFragmentForFullSubtree(node);
+ if (fragmentForNode instanceof AssociativeInfixExpressionFragment) {
+ AssociativeInfixExpressionFragment kin= (AssociativeInfixExpressionFragment)fragmentForNode;
+ return kin.getSubFragmentsWithMyNodeMatching(this);
+ } else {
+ return new IASTFragment[0];
+ }
+ }
+
+ /**
+ * Returns all matching subsequences of toMatch
in source
.
+ *
+ * @param source the source to look for matching subsequences
+ * @param toMatch the sequence to match
+ * @return returns a List of Lists of Expression
s
+ */
+ private static List> getMatchingContiguousNodeSubsequences(List source, List toMatch) {
+ //naive implementation:
+
+ List> subsequences= new ArrayList<>();
+
+ for(int i= 0; i < source.size();) {
+ if(matchesAt(i, source, toMatch)) {
+ subsequences.add(source.subList(i, i + toMatch.size()));
+ i += toMatch.size();
+ } else
+ i++;
+ }
+
+ return subsequences;
+ }
+
+ private static boolean matchesAt(int index, List subject, List toMatch) {
+ if(index + toMatch.size() > subject.size())
+ return false;
+ for(int i= 0; i < toMatch.size(); i++, index++) {
+ if(!JdtASTMatcher.doNodesMatch(
+ subject.get(index), toMatch.get(i)
+ )
+ )
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean isAGroupRoot(ASTNode node) {
+ Assert.isNotNull(node);
+
+ return isAssociativeInfix(node)
+ && !isParentInfixWithSameOperator((InfixExpression) node);
+ }
+
+ private static boolean isAssociativeInfix(ASTNode node) {
+ return node instanceof InfixExpression && isOperatorAssociative(((InfixExpression)node).getOperator());
+ }
+
+ private static boolean isParentInfixWithSameOperator(InfixExpression node) {
+ return node.getParent() instanceof InfixExpression
+ && ((InfixExpression) node.getParent()).getOperator() == node.getOperator();
+ }
+
+ private static boolean isOperatorAssociative(InfixExpression.Operator operator) {
+ return operator == InfixExpression.Operator.PLUS
+ || operator == InfixExpression.Operator.TIMES
+ || operator == InfixExpression.Operator.XOR
+ || operator == InfixExpression.Operator.OR
+ || operator == InfixExpression.Operator.AND
+ || operator == InfixExpression.Operator.CONDITIONAL_OR
+ || operator == InfixExpression.Operator.CONDITIONAL_AND;
+ }
+
+ private AssociativeInfixExpressionFragment(InfixExpression groupRoot, List operands) {
+ Assert.isTrue(isAGroupRoot(groupRoot));
+ Assert.isTrue(operands.size() >= 2);
+ fGroupRoot= groupRoot;
+ fOperands= Collections.unmodifiableList(operands);
+ }
+
+ @Override
+ public boolean matches(IASTFragment other) {
+ if (! other.getClass().equals(getClass()))
+ return false;
+
+ AssociativeInfixExpressionFragment otherOfKind= (AssociativeInfixExpressionFragment) other;
+ return getOperator() == otherOfKind.getOperator()
+ && doOperandsMatch(otherOfKind);
+ }
+
+ private boolean doOperandsMatch(AssociativeInfixExpressionFragment other) {
+ if (getOperands().size() != other.getOperands().size())
+ return false;
+
+ Iterator myOperands= getOperands().iterator();
+ Iterator othersOperands= other.getOperands().iterator();
+
+ while (myOperands.hasNext() && othersOperands.hasNext()) {
+ Expression myOperand= myOperands.next();
+ Expression othersOperand= othersOperands.next();
+
+ if (! JdtASTMatcher.doNodesMatch(myOperand, othersOperand))
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public IASTFragment[] getSubFragmentsMatching(IASTFragment toMatch) {
+ return union(
+ getSubFragmentsWithMyNodeMatching(toMatch),
+ getSubFragmentsWithAnotherNodeMatching(toMatch)
+ );
+ }
+
+ private IASTFragment[] getSubFragmentsWithMyNodeMatching(IASTFragment toMatch) {
+ if(toMatch.getClass() != getClass())
+ return new IASTFragment[0];
+
+ AssociativeInfixExpressionFragment kinToMatch= (AssociativeInfixExpressionFragment) toMatch;
+ if(kinToMatch.getOperator() != getOperator())
+ return new IASTFragment[0];
+
+ List> matchingSubsequences=
+ getMatchingContiguousNodeSubsequences(
+ getOperands(),
+ kinToMatch.getOperands()
+ );
+
+ IASTFragment[] matches= new IASTFragment[matchingSubsequences.size()];
+ for(int i= 0; i < matchingSubsequences.size(); i++) {
+ IASTFragment match= new AssociativeInfixExpressionFragment(
+ fGroupRoot,
+ matchingSubsequences.get(i)
+ );
+ Assert.isTrue(match.matches(toMatch) || toMatch.matches(match));
+ matches[i]= match;
+ }
+ return matches;
+ }
+
+ private IASTFragment[] getSubFragmentsWithAnotherNodeMatching(IASTFragment toMatch) {
+ IASTFragment[] result= new IASTFragment[0];
+ for (Iterator iter= getOperands().iterator(); iter.hasNext();) {
+ ASTNode operand= iter.next();
+ result= union(result, ASTMatchingFragmentFinder.findMatchingFragments(operand, (ASTFragment)toMatch));
+ }
+ return result;
+ }
+ private static IASTFragment[] union(IASTFragment[] a1, IASTFragment[] a2) {
+ IASTFragment[] union= new IASTFragment[a1.length + a2.length];
+ System.arraycopy(a1, 0, union, 0, a1.length);
+ System.arraycopy(a2, 0, union, a1.length, a2.length);
+ return union;
+
+ //TODO: this would be a REAL union...:
+// ArrayList union= new ArrayList();
+// for (int i= 0; i < a1.length; i++) {
+// union.add(a1[i]);
+// }
+// for (int i= 0; i < a2.length; i++) {
+// if (! union.contains(a2[i]))
+// union.add(a2[i]);
+// }
+// return (IASTFragment[]) union.toArray(new IASTFragment[union.size()]);
+ }
+
+
+ /**
+ * Note that this fragment does not directly
+ * represent this expression node, but rather
+ * a part of it.
+ * @return returns the associated expression.
+ */
+ @Override
+ public Expression getAssociatedExpression() {
+ return fGroupRoot;
+ }
+
+ /**
+ * Note that this fragment does not directly
+ * represent this node, but rather a particular sort of
+ * part of its subtree.
+ * @return returns the associated node.
+ */
+ @Override
+ public ASTNode getAssociatedNode() {
+ return fGroupRoot;
+ }
+
+ public InfixExpression getGroupRoot() {
+ return fGroupRoot;
+ }
+
+ @Override
+ public int getLength() {
+ return getEndPositionExclusive() - getStartPosition();
+ }
+
+ private int getEndPositionExclusive() {
+ List operands= getOperands();
+ ASTNode lastNode= operands.get(operands.size() - 1);
+ return lastNode.getStartPosition() + lastNode.getLength();
+ }
+
+ @Override
+ public int getStartPosition() {
+ return getOperands().get(0).getStartPosition();
+ }
+
+ public List getOperands() {
+ return fOperands;
+ }
+
+ public InfixExpression.Operator getOperator() {
+ return fGroupRoot.getOperator();
+ }
+
+ @Override
+ public Expression createCopyTarget(ASTRewrite rewrite, boolean removeSurroundingParenthesis) throws JavaModelException {
+ List allOperands= findGroupMembersInOrderFor(fGroupRoot);
+ if (allOperands.size() == fOperands.size()) {
+ return (Expression) rewrite.createCopyTarget(fGroupRoot);
+ }
+
+ CompilationUnit root= (CompilationUnit) fGroupRoot.getRoot();
+ ICompilationUnit cu= (ICompilationUnit) root.getJavaElement();
+ String source= cu.getBuffer().getText(getStartPosition(), getLength());
+ return (Expression) rewrite.createStringPlaceholder(source, ASTNode.INFIX_EXPRESSION);
+
+// //Todo: see whether we could copy bigger chunks of the original selection
+// // (probably only possible from extendedOperands list or from nested InfixExpressions)
+// InfixExpression result= rewrite.getAST().newInfixExpression();
+// result.setOperator(getOperator());
+// Expression first= (Expression) fOperands.get(0);
+// Expression second= (Expression) fOperands.get(1);
+// result.setLeftOperand((Expression) rewrite.createCopyTarget(first));
+// result.setRightOperand((Expression) rewrite.createCopyTarget(second));
+// for (int i= 2; i < fOperands.size(); i++) {
+// Expression next= (Expression) fOperands.get(i);
+// result.extendedOperands().add(rewrite.createCopyTarget(next));
+// }
+// return result;
+ }
+
+ @Override
+ public void replace(ASTRewrite rewrite, ASTNode replacement, TextEditGroup textEditGroup) {
+ ASTNode groupNode= getGroupRoot();
+
+ List allOperands= findGroupMembersInOrderFor(getGroupRoot());
+ if (allOperands.size() == fOperands.size()) {
+ if (replacement instanceof Name && groupNode.getParent() instanceof ParenthesizedExpression) {
+ // replace including the parenthesized expression around it
+ rewrite.replace(groupNode.getParent(), replacement, textEditGroup);
+ } else {
+ rewrite.replace(groupNode, replacement, textEditGroup);
+ }
+ return;
+ }
+
+ rewrite.replace(fOperands.get(0), replacement, textEditGroup);
+ int first= allOperands.indexOf(fOperands.get(0));
+ int after= first + fOperands.size();
+ for (int i= first + 1; i < after; i++) {
+ rewrite.remove(allOperands.get(i), textEditGroup);
+ }
+ }
+
+ private static ArrayList findGroupMembersInOrderFor(InfixExpression groupRoot) {
+ return new GroupMemberFinder(groupRoot).fMembersInOrder;
+ }
+
+ private static class GroupMemberFinder extends GenericVisitor {
+ private ArrayList fMembersInOrder= new ArrayList<>();
+ private InfixExpression fGroupRoot;
+
+ public GroupMemberFinder(InfixExpression groupRoot) {
+ super(true);
+ Assert.isTrue(isAssociativeInfix(groupRoot));
+ fGroupRoot= groupRoot;
+ fGroupRoot.accept(this);
+ }
+ @Override
+ protected boolean visitNode(ASTNode node) {
+ if (node instanceof InfixExpression && ((InfixExpression) node).getOperator() == fGroupRoot.getOperator())
+ return true;
+
+ fMembersInOrder.add((Expression) node);
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return fGroupRoot.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AssociativeInfixExpressionFragment other= (AssociativeInfixExpressionFragment) obj;
+ return fGroupRoot.equals(other.fGroupRoot) && fOperands.equals(other.fOperands);
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragmentFactory.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragmentFactory.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragmentFactory.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragmentFactory.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.InfixExpression;
+
+import org.eclipse.jdt.internal.corext.SourceRangeFactory;
+import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
+import org.eclipse.jdt.internal.corext.dom.Selection;
+import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer;
+
+/**
+ * Creates various differing kinds of IASTFragments, all through
+ * a very narrow interface. The kind of IASTFragment produced will depend
+ * on properties of the parameters supplied to the factory methods, such
+ * as the types and characteristics of AST nodes, or the location of
+ * source ranges.
+ *
+ * In general, the client will not be aware of exactly what kind of
+ * fragment is obtained from these methods. Beyond the functionality
+ * provided by the IASTFragment interface, the client can know, however,
+ * based on the parameters passed, some things about the created fragment.
+ * See the documentation of the factory methods.
+ *
+ * @see IASTFragment
+ *
+ */
+public class ASTFragmentFactory {
+
+ // Factory Methods: /////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Creates and returns a fragment representing the entire subtree
+ * rooted at node
. It is not true in general that
+ * the node to which the produced IASTFragment maps (see {@link org.eclipse.jdt.internal.corext.dom.fragments.IASTFragment IASTFragment})
+ * will be node
.
+ *
+ * XXX: more doc (current assertions about input vs. output)
+ */
+ public static IASTFragment createFragmentForFullSubtree(ASTNode node) {
+ IASTFragment result= FragmentForFullSubtreeFactory.createFragmentFor(node);
+ Assert.isNotNull(result);
+ return result;
+ }
+
+ /**
+ * If possible, this method creates a fragment whose source code
+ * range is range
within compilation unit cu
,
+ * and which resides somewhere within the subtree identified by
+ * scope
.
+ *
+ * XXX: more doc (current assertions about input vs. output)
+ *
+ * @param range The source range which the create fragment must have.
+ * @param scope A node identifying the AST subtree in which the fragment must lie.
+ * @param cu The compilation unit to which the source range applies, and to which the AST corresponds.
+ * @return IASTFragment A fragment whose source range is range
within
+ * compilation unit cu
, residing somewhere within the
+ * AST subtree identified by scope
.
+ * @throws JavaModelException
+ */
+ public static IASTFragment createFragmentForSourceRange(ISourceRange range, ASTNode scope, ICompilationUnit cu) throws JavaModelException {
+ SelectionAnalyzer sa= new SelectionAnalyzer(Selection.createFromStartLength(range.getOffset(), range.getLength()), false);
+ scope.accept(sa);
+
+ if (isSingleNodeSelected(sa, range, cu))
+ return ASTFragmentFactory.createFragmentForFullSubtree(sa.getFirstSelectedNode());
+ if (isEmptySelectionCoveredByANode(range, sa))
+ return ASTFragmentFactory.createFragmentForFullSubtree(sa.getLastCoveringNode());
+ return ASTFragmentFactory.createFragmentForSubPartBySourceRange(sa.getLastCoveringNode(), range, cu);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+ private static boolean isEmptySelectionCoveredByANode(ISourceRange range, SelectionAnalyzer sa) {
+ return range.getLength() == 0 && sa.getFirstSelectedNode() == null && sa.getLastCoveringNode() != null;
+ }
+
+ private static boolean isSingleNodeSelected(SelectionAnalyzer sa, ISourceRange range, ICompilationUnit cu) throws JavaModelException {
+ return sa.getSelectedNodes().length == 1 && !rangeIncludesNonWhitespaceOutsideNode(range, sa.getFirstSelectedNode(), cu);
+ }
+
+ private static boolean rangeIncludesNonWhitespaceOutsideNode(ISourceRange range, ASTNode node, ICompilationUnit cu) throws JavaModelException {
+ return Util.rangeIncludesNonWhitespaceOutsideRange(range, SourceRangeFactory.create(node), cu.getBuffer());
+ }
+
+
+ /**
+ * Returns null
if the indices, taken with respect to
+ * the node, do not correspond to a valid node-sub-part
+ * fragment.
+ */
+ private static IASTFragment createFragmentForSubPartBySourceRange(ASTNode node, ISourceRange range, ICompilationUnit cu) throws JavaModelException {
+ return FragmentForSubPartBySourceRangeFactory.createFragmentFor(node, range, cu);
+ }
+
+ private static class FragmentForFullSubtreeFactory extends FragmentFactory {
+ public static IASTFragment createFragmentFor(ASTNode node) {
+ return new FragmentForFullSubtreeFactory().createFragment(node);
+ }
+
+ @Override
+ public boolean visit(InfixExpression node) {
+ /* Try creating an associative infix expression fragment
+ /* for the full subtree. If this is not applicable,
+ * try something more generic.
+ */
+ IASTFragment fragment= AssociativeInfixExpressionFragment.createFragmentForFullSubtree(node);
+ if(fragment == null)
+ return visit((Expression) node);
+
+ setFragment(fragment);
+ return false;
+ }
+ @Override
+ public boolean visit(Expression node) {
+ setFragment(new SimpleExpressionFragment(node));
+ return false;
+ }
+ @Override
+ public boolean visit(ASTNode node) {
+ setFragment(new SimpleFragment(node));
+ return false;
+ }
+ }
+ private static class FragmentForSubPartBySourceRangeFactory extends FragmentFactory {
+ private ISourceRange fRange;
+ private ICompilationUnit fCu;
+
+ private JavaModelException javaModelException= null;
+
+ public static IASTFragment createFragmentFor(ASTNode node, ISourceRange range, ICompilationUnit cu) throws JavaModelException {
+ return new FragmentForSubPartBySourceRangeFactory().createFragment(node, range, cu);
+ }
+
+ @Override
+ public boolean visit(InfixExpression node) {
+ try {
+ setFragment(createInfixExpressionSubPartFragmentBySourceRange(node, fRange, fCu));
+ } catch(JavaModelException e) {
+ javaModelException= e;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(ASTNode node) {
+ //let fragment be null
+ return false;
+ }
+
+ protected IASTFragment createFragment(ASTNode node, ISourceRange range, ICompilationUnit cu) throws JavaModelException {
+ fRange= range;
+ fCu= cu;
+ IASTFragment result= createFragment(node);
+ if(javaModelException != null)
+ throw javaModelException;
+ return result;
+ }
+
+ private static IExpressionFragment createInfixExpressionSubPartFragmentBySourceRange(InfixExpression node, ISourceRange range, ICompilationUnit cu) throws JavaModelException {
+ return AssociativeInfixExpressionFragment.createSubPartFragmentBySourceRange(node, range, cu);
+ }
+ }
+ private static abstract class FragmentFactory extends HierarchicalASTVisitor {
+ private IASTFragment fFragment;
+
+ protected IASTFragment createFragment(ASTNode node) {
+ fFragment= null;
+ node.accept(this);
+ return fFragment;
+ }
+
+ protected final IASTFragment getFragment() {
+ return fFragment;
+ }
+ protected final void setFragment(IASTFragment fragment) {
+ Assert.isTrue(!isFragmentSet());
+ fFragment= fragment;
+ }
+ protected final boolean isFragmentSet() {
+ return getFragment() != null;
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragment.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragment.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragment.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTFragment.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+
+/**
+ * @see org.eclipse.jdt.internal.corext.dom.fragments.IASTFragment
+ * @see org.eclipse.jdt.internal.corext.dom.fragments.ASTFragmentFactory
+ */
+abstract class ASTFragment implements IASTFragment {
+
+ /**
+ * Tries to create or find as many fragments as possible
+ * such that each fragment f matches
+ * this fragment and f.getNode() is node
+ */
+ abstract IASTFragment[] getMatchingFragmentsWithNode(ASTNode node);
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTMatchingFragmentFinder.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTMatchingFragmentFinder.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTMatchingFragmentFinder.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/ASTMatchingFragmentFinder.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Javadoc;
+
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+
+class ASTMatchingFragmentFinder extends GenericVisitor {
+
+ public static IASTFragment[] findMatchingFragments(ASTNode scope, ASTFragment toMatch) {
+ return new ASTMatchingFragmentFinder(toMatch).findMatches(scope);
+ }
+
+ private ASTFragment fFragmentToMatch;
+ private Set fMatches= new HashSet<>();
+
+ private ASTMatchingFragmentFinder(ASTFragment toMatch) {
+ super(true);
+ fFragmentToMatch= toMatch;
+ }
+ private IASTFragment[] findMatches(ASTNode scope) {
+ fMatches.clear();
+ scope.accept(this);
+ return getMatches();
+ }
+ private IASTFragment[] getMatches() {
+ return fMatches.toArray(new IASTFragment[fMatches.size()]);
+ }
+
+ @Override
+ public boolean visit(Javadoc node) {
+ return false;
+ }
+
+ @Override
+ protected boolean visitNode(ASTNode node) {
+ IASTFragment[] localMatches= fFragmentToMatch.getMatchingFragmentsWithNode(node);
+ Collections.addAll(fMatches, localMatches);
+ return true;
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IASTFragment.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IASTFragment.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IASTFragment.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IASTFragment.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+/**
+ * An IASTFragment represents 'part' of an AST, but
+ * not necessarily a subtree of the AST. A fragment
+ * may simply be an instance of some sort of pattern
+ * or formation in an AST.
+ * Such "fragments", however, do correspond to a
+ * contiguous source code region, and, thus, posses a
+ * source start position, and a source length. Every
+ * fragment maps to an ASTNode, although this mapping is
+ * not necessarily straightforward, and more than one
+ * fragment may map to a given node.
+ *
+ * Fragments support abstract operations, which
+ * support the notion of 'matching' fragments.
+ * One operation determines whether a fragment 'matches'
+ * a given fragment. Another operation finds all
+ * sub-fragments (fragments contained within a
+ * parent fragment, including the parent itself)
+ * which 'match' another given fragment.
+ *
+ */
+public interface IASTFragment {
+
+ /**
+ * Determines whether other
+ * 'matches' this
.
+ * This binary operation should be reflexive,
+ * symmetric, and transitive.
+ *
+ * That two node match does not imply that their source ranges
+ * are the same, or that they map (via getAssociatedNode()) to the
+ * same node.
+ * @param other the element to test with
+ * @return return true if the passed element matches the current element.
+ */
+ public boolean matches(IASTFragment other);
+
+ /**
+ * Returns (at least some approximation of) a maximal set of
+ * sub-fragments of this fragment which match toMatch
+ * @param toMatch the fragment to match
+ * @return set of sub fragments that match
+ */
+ public IASTFragment[] getSubFragmentsMatching(IASTFragment toMatch);
+
+ /**
+ * Every fragment maps to a node.
+ * Multiple fragments can map to the same node.
+ *
+ * @return ASTNode The node to which this fragment maps.
+ */
+ public ASTNode getAssociatedNode();
+
+ /**
+ * Every fragment has a source start position.
+ *
+ * @return int The source start position.
+ */
+ public int getStartPosition();
+
+ /**
+ * Every fragment has a source length.
+ *
+ * @return int The source length.
+ */
+ public int getLength();
+
+ /**
+ * Replaces this fragment with the given replacement node.
+ *
+ * @param rewrite an ASTRewrite
+ * @param replacement replacement for this fragment
+ * @param textEditGroup a description or null
+ */
+ public void replace(ASTRewrite rewrite, ASTNode replacement, TextEditGroup textEditGroup);
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IExpressionFragment.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IExpressionFragment.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IExpressionFragment.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/IExpressionFragment.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+/**
+ * Represents a fragment (@see IASTFragment) for which the node
+ * to which the fragment maps is an Expression.
+ */
+public interface IExpressionFragment extends IASTFragment {
+
+ /**
+ * Every IASTFragment maps to an ASTNode, although this mapping may
+ * not be straightforward, and more than one fragment may map to the
+ * same node.
+ * An IExpressionFragment maps, specifically, to an Expression.
+ *
+ * @return Expression The node to which this fragment maps.
+ */
+ public Expression getAssociatedExpression();
+
+ /**
+ * Creates a copy of this IExpressionFragment.
+ *
+ * @param rewrite an ASTRewrite
+ * @param removeSurroundingParenthesis if set to true, a surrounding ParenthesizedExpression will not be part of
+ * the copied node.
+ * @return a copy of this IExpressionFragment, ready for use in the given
+ * rewrite
+ * @throws JavaModelException
+ */
+ public Expression createCopyTarget(ASTRewrite rewrite, boolean removeSurroundingParenthesis) throws JavaModelException;
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleExpressionFragment.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleExpressionFragment.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleExpressionFragment.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleExpressionFragment.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+class SimpleExpressionFragment extends SimpleFragment implements IExpressionFragment {
+ SimpleExpressionFragment(Expression node) {
+ super(node);
+ }
+
+ @Override
+ public Expression getAssociatedExpression() {
+ return (Expression) getAssociatedNode();
+ }
+
+ @Override
+ public Expression createCopyTarget(ASTRewrite rewrite, boolean removeSurroundingParenthesis) {
+ Expression node= getAssociatedExpression();
+ if (removeSurroundingParenthesis && node instanceof ParenthesizedExpression) {
+ node= ((ParenthesizedExpression) node).getExpression();
+ }
+ return (Expression) rewrite.createCopyTarget(node);
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleFragment.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleFragment.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleFragment.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/SimpleFragment.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+import org.eclipse.jdt.internal.corext.dom.JdtASTMatcher;
+
+class SimpleFragment extends ASTFragment {
+ private final ASTNode fNode;
+
+ SimpleFragment(ASTNode node) {
+ Assert.isNotNull(node);
+ fNode= node;
+ }
+
+ @Override
+ public IASTFragment[] getMatchingFragmentsWithNode(ASTNode node) {
+ if (! JdtASTMatcher.doNodesMatch(getAssociatedNode(), node))
+ return new IASTFragment[0];
+
+ IASTFragment match= ASTFragmentFactory.createFragmentForFullSubtree(node);
+ Assert.isTrue(match.matches(this) || this.matches(match));
+ return new IASTFragment[] { match };
+ }
+
+ @Override
+ public boolean matches(IASTFragment other) {
+ return other.getClass().equals(getClass()) && JdtASTMatcher.doNodesMatch(other.getAssociatedNode(), getAssociatedNode());
+ }
+
+ @Override
+ public IASTFragment[] getSubFragmentsMatching(IASTFragment toMatch) {
+ return ASTMatchingFragmentFinder.findMatchingFragments(getAssociatedNode(), (ASTFragment) toMatch);
+ }
+
+ @Override
+ public int getStartPosition() {
+ return fNode.getStartPosition();
+ }
+
+ @Override
+ public int getLength() {
+ return fNode.getLength();
+ }
+
+ @Override
+ public ASTNode getAssociatedNode() {
+ return fNode;
+ }
+
+ @Override
+ public void replace(ASTRewrite rewrite, ASTNode replacement, TextEditGroup textEditGroup) {
+ if (replacement instanceof Name && fNode.getParent() instanceof ParenthesizedExpression) {
+ // replace including the parenthesized expression around it
+ rewrite.replace(fNode.getParent(), replacement, textEditGroup);
+ } else {
+ rewrite.replace(fNode, replacement, textEditGroup);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return fNode.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SimpleFragment other= (SimpleFragment) obj;
+ return fNode.equals(other.fNode);
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/Util.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/Util.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/Util.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/fragments/Util.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom.fragments;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.IBuffer;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.ToolFactory;
+import org.eclipse.jdt.core.compiler.IScanner;
+import org.eclipse.jdt.core.compiler.ITerminalSymbols;
+import org.eclipse.jdt.core.compiler.InvalidInputException;
+import org.eclipse.jdt.core.dom.ASTNode;
+
+import org.eclipse.jdt.internal.corext.SourceRangeFactory;
+
+
+/**
+ * This class houses a collection of static methods which do not refer to,
+ * or otherwise depend on, other classes in this package. Each
+ * package-visible method is called by more than one other class in this
+ * package. Since they do not depend on other classes in this package,
+ * they could be moved to some less specialized package.
+ */
+class Util {
+ static boolean rangeIncludesNonWhitespaceOutsideRange(ISourceRange selection, ISourceRange nodes, IBuffer buffer) {
+ if (!covers(selection, nodes))
+ return false;
+
+ //TODO: skip leading comments. Consider that leading line comment must be followed by newline!
+ if(!isJustWhitespace(selection.getOffset(), nodes.getOffset(), buffer))
+ return true;
+ if(!isJustWhitespaceOrComment(nodes.getOffset() + nodes.getLength(), selection.getOffset() + selection.getLength(), buffer))
+ return true;
+ return false;
+ }
+ private static boolean isJustWhitespace(int start, int end, IBuffer buffer) {
+ if (start == end)
+ return true;
+ Assert.isTrue(start <= end);
+ return 0 == buffer.getText(start, end - start).trim().length();
+ }
+ private static boolean isJustWhitespaceOrComment(int start, int end, IBuffer buffer) {
+ if (start == end)
+ return true;
+ Assert.isTrue(start <= end);
+ String trimmedText= buffer.getText(start, end - start).trim();
+ if (0 == trimmedText.length()) {
+ return true;
+ } else {
+ IScanner scanner= ToolFactory.createScanner(false, false, false, null);
+ scanner.setSource(trimmedText.toCharArray());
+ try {
+ return scanner.getNextToken() == ITerminalSymbols.TokenNameEOF;
+ } catch (InvalidInputException e) {
+ return false;
+ }
+ }
+ }
+
+ public static int getEndExclusive(ISourceRange sourceRange) {
+ return sourceRange.getOffset() + sourceRange.getLength();
+ }
+
+ public static int getEndInclusive(ISourceRange sourceRange) {
+ return getEndExclusive(sourceRange) - 1;
+ }
+
+ public static boolean covers(ISourceRange sourceRange, ASTNode astNode) {
+ return covers(sourceRange, SourceRangeFactory.create(astNode));
+ }
+
+ public static boolean covers(ISourceRange thisRange, ISourceRange otherRange) {
+ return thisRange.getOffset() <= otherRange.getOffset()
+ && getEndInclusive(thisRange) >= getEndInclusive(otherRange);
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/GenericVisitor.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/GenericVisitor.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/GenericVisitor.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/GenericVisitor.java" 2020-02-26 15:31:57.000000000 +0000
@@ -7,7 +7,7 @@
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
- *
+ *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
@@ -43,7 +43,7 @@
/**
* Visits the given type-specific AST node.
- *
+ *
* @param node the AST note to visit
* @return true
if the children of this node should be visited, and
* false
if the children of this node should be skipped
@@ -54,7 +54,7 @@
/**
* Visits the given type-specific AST node.
- *
+ *
* @param node the AST note to visit
*/
protected void endVisitNode(ASTNode node) {
@@ -838,6 +838,10 @@
return visitNode(node);
}
@Override
+ public boolean visit(TextBlock node) {
+ return visitNode(node);
+ }
+ @Override
public boolean visit(UnionType node) {
return visitNode(node);
}
@@ -865,4 +869,8 @@
public boolean visit(WildcardType node) {
return visitNode(node);
}
+ @Override
+ public boolean visit(YieldStatement node) {
+ return visitNode(node);
+ }
}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java" 2020-02-26 15:31:57.000000000 +0000
@@ -479,6 +479,16 @@
endVisit((Expression)node);
}
+ @Override
+ public boolean visit(YieldStatement node) {
+ return visit((Statement)node);
+ }
+
+ @Override
+ public void endVisit(YieldStatement node) {
+ endVisit((Statement)node);
+ }
+
//---- Begin MethodReference Hierarchy ----------------------------------
public boolean visit(MethodReference node) {
return visit((Expression)node);
@@ -637,11 +647,21 @@
}
@Override
+ public boolean visit(TextBlock node) {
+ return visit((Expression)node);
+ }
+
+ @Override
public void endVisit(SuperMethodInvocation node) {
endVisit((Expression)node);
}
@Override
+ public void endVisit(TextBlock node) {
+ endVisit((Expression)node);
+ }
+
+ @Override
public boolean visit(ThisExpression node) {
return visit((Expression)node);
}
@@ -737,12 +757,12 @@
public boolean visit(ModuleDeclaration node) {
return visit((ASTNode)node);
}
-
+
@Override
public void endVisit(ModuleDeclaration node) {
endVisit((ASTNode)node);
}
-
+
//---- Begin ModuleDirective Hierarchy ----------------------------------
public boolean visit(ModuleDirective node) {
@@ -754,11 +774,11 @@
}
//---- Begin ModulePackageAccess Hierarchy ----------------------------------
-
+
public boolean visit(ModulePackageAccess node) {
return visit((ModuleDirective)node);
}
-
+
public void endVisit(ModulePackageAccess node) {
endVisit((ModuleDirective)node);
}
@@ -767,7 +787,7 @@
public boolean visit(ExportsDirective node) {
return visit((ModulePackageAccess)node);
}
-
+
@Override
public void endVisit(ExportsDirective node) {
endVisit((ModulePackageAccess)node);
@@ -777,24 +797,24 @@
public boolean visit(OpensDirective node) {
return visit((ModulePackageAccess)node);
}
-
+
@Override
public void endVisit(OpensDirective node) {
endVisit((ModulePackageAccess)node);
}
-
+
//---- End ModulePackageAccess Hierarchy ------------------------------------
-
+
@Override
public boolean visit(ProvidesDirective node) {
return visit((ModuleDirective)node);
}
-
+
@Override
public void endVisit(ProvidesDirective node) {
endVisit((ModuleDirective)node);
}
-
+
@Override
public boolean visit(RequiresDirective node) {
return visit((ModuleDirective)node);
@@ -809,24 +829,24 @@
public boolean visit(UsesDirective node) {
return visit((ModuleDirective)node);
}
-
+
@Override
public void endVisit(UsesDirective node) {
endVisit((ModuleDirective)node);
}
-
+
//---- End ModuleDirective Hierarchy ------------------------------------
-
+
@Override
public boolean visit(ModuleModifier node) {
return visit((ASTNode)node);
}
-
+
@Override
public void endVisit(ModuleModifier node) {
endVisit((ASTNode)node);
}
-
+
@Override
public boolean visit(PackageDeclaration node) {
return visit((ASTNode)node);
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/IASTSharedValues.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/IASTSharedValues.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/IASTSharedValues.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/IASTSharedValues.java" 2020-02-26 15:31:57.000000000 +0000
@@ -21,7 +21,7 @@
* This value is subject to change with every release. JDT-UI-internal code typically supports
* the latest available {@link AST#apiLevel() AST level} exclusively.
*/
- public static final int SHARED_AST_LEVEL= AST.JLS12;
+ public static final int SHARED_AST_LEVEL= AST.JLS13;
public static final boolean SHARED_AST_STATEMENT_RECOVERY= true;
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/JdtASTMatcher.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/JdtASTMatcher.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/JdtASTMatcher.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/JdtASTMatcher.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - refactored to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.dom.ASTMatcher;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.SimpleName;
+
+public class JdtASTMatcher extends ASTMatcher {
+
+ @Override
+ public boolean match(SimpleName node, Object other) {
+ boolean isomorphic= super.match(node, other);
+ if (! isomorphic || !(other instanceof SimpleName))
+ return false;
+ SimpleName name= (SimpleName)other;
+ IBinding nodeBinding= node.resolveBinding();
+ IBinding otherBinding= name.resolveBinding();
+ if (nodeBinding == null) {
+ if (otherBinding != null) {
+ return false;
+ }
+ } else {
+ if (nodeBinding != otherBinding) {
+ return false;
+ }
+ }
+ if (node.resolveTypeBinding() != name.resolveTypeBinding())
+ return false;
+ return true;
+ }
+
+ public static boolean doNodesMatch(ASTNode one, ASTNode other) {
+ Assert.isNotNull(one);
+ Assert.isNotNull(other);
+
+ return one.subtreeMatch(new JdtASTMatcher(), other);
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LinkedNodeFinder.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LinkedNodeFinder.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LinkedNodeFinder.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LinkedNodeFinder.java" 2020-02-26 15:31:57.000000000 +0000
@@ -114,8 +114,7 @@
int nameOffset= nameNode.getStartPosition();
int nameInclEnd= nameOffset + nameNode.getLength() - 1;
- for (int i= 0; i < problems.length; i++) {
- IProblem curr= problems[i];
+ for (IProblem curr : problems) {
if (curr.getSourceStart() == nameOffset && curr.getSourceEnd() == nameInclEnd) {
int kind= getProblemKind(curr);
if (kind != 0) {
@@ -149,8 +148,7 @@
String name= nameNode.getIdentifier();
- for (int i= 0; i < problems.length; i++) {
- IProblem curr= problems[i];
+ for (IProblem curr : problems) {
int probStart= curr.getSourceStart();
int probEnd= curr.getSourceEnd() + 1;
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LocalVariableIndex.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LocalVariableIndex.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LocalVariableIndex.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/LocalVariableIndex.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.Initializer;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+
+public class LocalVariableIndex extends ASTVisitor {
+
+ private int fTopIndex;
+
+ /**
+ * Computes the maximum number of local variable declarations in the
+ * given body declaration.
+ *
+ * @param declaration the body declaration. Must either be a method
+ * declaration, or an initializer, or a field declaration.
+ * @return the maximum number of local variables
+ */
+ public static int perform(BodyDeclaration declaration) {
+ Assert.isTrue(declaration != null);
+ switch (declaration.getNodeType()) {
+ case ASTNode.METHOD_DECLARATION:
+ case ASTNode.FIELD_DECLARATION:
+ case ASTNode.INITIALIZER:
+ return internalPerform(declaration);
+ default:
+ throw new IllegalArgumentException(declaration.toString());
+ }
+ }
+
+ private static int internalPerform(BodyDeclaration methodOrInitializer) {
+ // we have to find the outermost method/initializer/field declaration since a local or anonymous
+ // type can reference final variables from the outer scope.
+ BodyDeclaration target= methodOrInitializer;
+ ASTNode parent= target.getParent();
+ while (parent != null) {
+ if (parent instanceof MethodDeclaration || parent instanceof Initializer || parent instanceof FieldDeclaration) {
+ target= (BodyDeclaration)parent;
+ }
+ parent= parent.getParent();
+ }
+
+ return doPerform(target);
+ }
+
+ private static int doPerform(BodyDeclaration node) {
+ LocalVariableIndex counter= new LocalVariableIndex();
+ node.accept(counter);
+ return counter.fTopIndex;
+ }
+
+ @Override
+ public boolean visit(SingleVariableDeclaration node) {
+ handleVariableBinding(node.resolveBinding());
+ return true;
+ }
+
+ @Override
+ public boolean visit(VariableDeclarationFragment node) {
+ handleVariableBinding(node.resolveBinding());
+ return true;
+ }
+
+ private void handleVariableBinding(IVariableBinding binding) {
+ if (binding == null)
+ return;
+ fTopIndex= Math.max(fTopIndex, binding.getVariableId());
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ModifierRewrite.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ModifierRewrite.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ModifierRewrite.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ModifierRewrite.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroupCore;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroupCore.PositionInformation;
+
+/**
+ * Rewrite helper for modifier lists.
+ *
+ * @see JDTUIHelperClasses
+ */
+public class ModifierRewrite {
+
+ public static final int VISIBILITY_MODIFIERS= Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
+
+ private ListRewrite fModifierRewrite;
+ private AST fAst;
+
+
+ public static ModifierRewrite create(ASTRewrite rewrite, ASTNode declNode) {
+ return new ModifierRewrite(rewrite, declNode);
+ }
+
+ private ModifierRewrite(ASTRewrite rewrite, ASTNode declNode) {
+ fModifierRewrite= evaluateListRewrite(rewrite, declNode);
+ fAst= declNode.getAST();
+ }
+
+ private ListRewrite evaluateListRewrite(ASTRewrite rewrite, ASTNode declNode) {
+ switch (declNode.getNodeType()) {
+ case ASTNode.METHOD_DECLARATION:
+ return rewrite.getListRewrite(declNode, MethodDeclaration.MODIFIERS2_PROPERTY);
+ case ASTNode.FIELD_DECLARATION:
+ return rewrite.getListRewrite(declNode, FieldDeclaration.MODIFIERS2_PROPERTY);
+ case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
+ return rewrite.getListRewrite(declNode, VariableDeclarationExpression.MODIFIERS2_PROPERTY);
+ case ASTNode.VARIABLE_DECLARATION_STATEMENT:
+ return rewrite.getListRewrite(declNode, VariableDeclarationStatement.MODIFIERS2_PROPERTY);
+ case ASTNode.SINGLE_VARIABLE_DECLARATION:
+ return rewrite.getListRewrite(declNode, SingleVariableDeclaration.MODIFIERS2_PROPERTY);
+ case ASTNode.TYPE_DECLARATION:
+ return rewrite.getListRewrite(declNode, TypeDeclaration.MODIFIERS2_PROPERTY);
+ case ASTNode.ENUM_DECLARATION:
+ return rewrite.getListRewrite(declNode, EnumDeclaration.MODIFIERS2_PROPERTY);
+ case ASTNode.ANNOTATION_TYPE_DECLARATION:
+ return rewrite.getListRewrite(declNode, AnnotationTypeDeclaration.MODIFIERS2_PROPERTY);
+ case ASTNode.ENUM_CONSTANT_DECLARATION:
+ return rewrite.getListRewrite(declNode, EnumConstantDeclaration.MODIFIERS2_PROPERTY);
+ case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
+ return rewrite.getListRewrite(declNode, AnnotationTypeMemberDeclaration.MODIFIERS2_PROPERTY);
+ default:
+ throw new IllegalArgumentException("node has no modifiers: " + declNode.getClass().getName()); //$NON-NLS-1$
+ }
+ }
+
+ public ListRewrite getModifierRewrite() {
+ return fModifierRewrite;
+ }
+
+ /**
+ * Sets the given modifiers. Removes all other flags, but leaves annotations in place.
+ *
+ * @param modifiers the modifiers to set
+ * @param editGroup the edit group in which to collect the corresponding text edits, or
+ * null
if ungrouped
+ * @return a tracked position that contains the changed modifiers
+ */
+ public PositionInformation setModifiers(int modifiers, TextEditGroup editGroup) {
+ return internalSetModifiers(modifiers, -1, editGroup);
+ }
+
+ /**
+ * Sets the included modifiers and removes the excluded modifiers. Does not touch other flags
+ * and leaves annotations in place.
+ *
+ * @param included the modifiers to set
+ * @param excluded the modifiers to remove
+ * @param editGroup the edit group in which to collect the corresponding text edits, or
+ * null
if ungrouped
+ * @return a tracked position that contains the changed modifiers
+ */
+ public PositionInformation setModifiers(int included, int excluded, TextEditGroup editGroup) {
+ return internalSetModifiers(included, included | excluded, editGroup);
+ }
+
+ /**
+ * Sets the included visibility modifiers and removes existing visibility modifiers. Does not
+ * touch other flags and leaves annotations in place.
+ *
+ * @param visibilityFlags the new visibility modifiers
+ * @param editGroup the edit group in which to collect the corresponding text edits, or
+ * null
if ungrouped
+ * @return a tracked position that contains the changed modifiers, or null
iff editGroup == null
+ */
+ public PositionInformation setVisibility(int visibilityFlags, TextEditGroup editGroup) {
+ return internalSetModifiers(visibilityFlags, VISIBILITY_MODIFIERS, editGroup);
+ }
+
+ public void copyAllModifiers(ASTNode otherDecl, TextEditGroup editGroup) {
+ copyAllModifiers(otherDecl, editGroup, false);
+ }
+
+ public void copyAllModifiers(ASTNode otherDecl, TextEditGroup editGroup, boolean copyIndividually) {
+ ListRewrite modifierList= evaluateListRewrite(fModifierRewrite.getASTRewrite(), otherDecl);
+ List originalList= modifierList.getOriginalList();
+ if (originalList.isEmpty()) {
+ return;
+ }
+
+ if (copyIndividually) {
+ for (Iterator iterator= originalList.iterator(); iterator.hasNext();) {
+ ASTNode modifier= (ASTNode) iterator.next();
+ ASTNode copy= fModifierRewrite.getASTRewrite().createCopyTarget(modifier);
+ if (copy != null) { //paranoia check (only left here because we're in RC1)
+ fModifierRewrite.insertLast(copy, editGroup);
+ }
+ }
+ } else {
+ ASTNode copy= modifierList.createCopyTarget((ASTNode) originalList.get(0), (ASTNode) originalList.get(originalList.size() - 1));
+ if (copy != null) { //paranoia check (only left here because we're in RC1)
+ fModifierRewrite.insertLast(copy, editGroup);
+ }
+ }
+ }
+
+ public void copyAllAnnotations(ASTNode otherDecl, TextEditGroup editGroup) {
+ ListRewrite modifierList= evaluateListRewrite(fModifierRewrite.getASTRewrite(), otherDecl);
+ List originalList= modifierList.getOriginalList();
+
+ for (Iterator iterator= originalList.iterator(); iterator.hasNext();) {
+ IExtendedModifier modifier= iterator.next();
+ if (modifier.isAnnotation()) {
+ fModifierRewrite.insertLast(fModifierRewrite.getASTRewrite().createCopyTarget((Annotation) modifier), editGroup);
+ }
+ }
+ }
+
+ /**
+ * Sets the given modifiers and removes all other modifiers that match the consideredFlags mask.
+ * Does not touch other flags and leaves annotations in place.
+ *
+ * @param modifiers the modifiers to set
+ * @param consideredFlags mask of modifiers to consider
+ * @param editGroup the edit group in which to collect the corresponding text edits, or
+ * null
if ungrouped
+ * @return a tracked position that contains the changed modifiers
+ */
+ private PositionInformation internalSetModifiers(int modifiers, int consideredFlags, TextEditGroup editGroup) {
+ int newModifiers= modifiers & consideredFlags;
+
+ ITrackedNodePosition trackedFallback= null;
+ List trackedNodes= new ArrayList<>();
+
+ // remove modifiers
+ List originalList= fModifierRewrite.getOriginalList();
+ for (int i= 0; i < originalList.size(); i++) {
+ ASTNode curr= (ASTNode) originalList.get(i);
+ if (curr instanceof Modifier) {
+ int flag= ((Modifier)curr).getKeyword().toFlagValue();
+ if ((consideredFlags & flag) != 0) {
+ if ((newModifiers & flag) == 0) {
+ fModifierRewrite.remove(curr, editGroup);
+ if (trackedFallback == null)
+ trackedFallback= fModifierRewrite.getASTRewrite().track(curr);
+ }
+ newModifiers &= ~flag;
+ }
+ }
+ }
+
+ // find last annotation
+ IExtendedModifier lastAnnotation= null;
+ List extendedList= fModifierRewrite.getRewrittenList();
+ for (int i= 0; i < extendedList.size(); i++) {
+ IExtendedModifier curr= extendedList.get(i);
+ if (curr.isAnnotation())
+ lastAnnotation= curr;
+ }
+
+ // add modifiers
+ List newNodes= ASTNodeFactory.newModifiers(fAst, newModifiers);
+ for (int i= 0; i < newNodes.size(); i++) {
+ Modifier curr= newNodes.get(i);
+ if ((curr.getKeyword().toFlagValue() & VISIBILITY_MODIFIERS) != 0) {
+ if (lastAnnotation != null)
+ fModifierRewrite.insertAfter(curr, (ASTNode) lastAnnotation, editGroup);
+ else
+ fModifierRewrite.insertFirst(curr, editGroup);
+ } else {
+ fModifierRewrite.insertLast(curr, editGroup);
+ }
+ trackedNodes.add(fModifierRewrite.getASTRewrite().track(curr));
+ }
+
+ if (trackedNodes.isEmpty()) {
+ if (trackedFallback == null) {
+ // out of tricks...
+ trackedFallback= fModifierRewrite.getASTRewrite().track(fModifierRewrite.getParent());
+ }
+ return new LinkedProposalPositionGroupCore.StartPositionInformation(trackedFallback);
+ } else {
+ return new LinkedProposalPositionGroupCore.TrackedNodesPosition(trackedNodes);
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ReplaceRewrite.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ReplaceRewrite.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ReplaceRewrite.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ReplaceRewrite.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - moved to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
+import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+
+
+/**
+ * Rewrite helper for situations where one node can be replaced by many new nodes.
+ *
+ * see JDTUIHelperClasses
+ */
+public class ReplaceRewrite {
+
+ protected ASTRewrite fRewrite;
+ protected ASTNode[] fToReplace;
+ protected StructuralPropertyDescriptor fDescriptor;
+
+ public static ReplaceRewrite create(ASTRewrite rewrite, ASTNode[] nodes) {
+ return new ReplaceRewrite(rewrite, nodes);
+ }
+
+ protected ReplaceRewrite(ASTRewrite rewrite, ASTNode[] nodes) {
+ Assert.isNotNull(rewrite);
+ Assert.isNotNull(nodes);
+ Assert.isTrue(nodes.length > 0);
+ fRewrite= rewrite;
+ fToReplace= nodes;
+ fDescriptor= fToReplace[0].getLocationInParent();
+ if (nodes.length > 1) {
+ Assert.isTrue(fDescriptor instanceof ChildListPropertyDescriptor);
+ }
+ }
+
+ public void replace(ASTNode[] replacements, TextEditGroup description) {
+ if (fToReplace.length == 1) {
+ if (replacements.length == 1) {
+ handleOneOne(replacements, description);
+ } else {
+ handleOneMany(replacements, description);
+ }
+ } else {
+ handleManyMany(replacements, description);
+ }
+ }
+
+ protected void handleOneOne(ASTNode[] replacements, TextEditGroup description) {
+ fRewrite.replace(fToReplace[0], replacements[0], description);
+ }
+
+ protected void handleOneMany(ASTNode[] replacements, TextEditGroup description) {
+ handleManyMany(replacements, description);
+ }
+
+ protected void handleManyMany(ASTNode[] replacements, TextEditGroup description) {
+ ListRewrite container= fRewrite.getListRewrite(fToReplace[0].getParent(), (ChildListPropertyDescriptor)fDescriptor);
+ if (fToReplace.length == replacements.length) {
+ for (int i= 0; i < fToReplace.length; i++) {
+ container.replace(fToReplace[i], replacements[i], description);
+ }
+ } else if (fToReplace.length < replacements.length) {
+ for (int i= 0; i < fToReplace.length; i++) {
+ container.replace(fToReplace[i], replacements[i], description);
+ }
+ for (int i= fToReplace.length; i < replacements.length; i++) {
+ container.insertAfter(replacements[i], replacements[i - 1], description);
+ }
+ } else if (fToReplace.length > replacements.length) {
+ int delta= fToReplace.length - replacements.length;
+ for(int i= 0; i < delta; i++) {
+ container.remove(fToReplace[i], description);
+ }
+ for (int i= delta, r= 0; i < fToReplace.length; i++, r++) {
+ container.replace(fToReplace[i], replacements[r], description);
+ }
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ScopeAnalyzer.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ScopeAnalyzer.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ScopeAnalyzer.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ScopeAnalyzer.java" 2020-02-26 15:31:57.000000000 +0000
@@ -18,7 +18,6 @@
import java.util.HashSet;
import java.util.List;
-import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
@@ -164,7 +163,7 @@
if (binding != null) {
switch (binding.getKind()) {
case IBinding.METHOD:
- StringBuffer buf= new StringBuffer();
+ StringBuilder buf= new StringBuilder();
buf.append('M');
buf.append(binding.getName()).append('(');
ITypeBinding[] parameters= ((IMethodBinding) binding).getParameterTypes();
@@ -177,6 +176,7 @@
}
buf.append(')');
return buf.toString();
+
case IBinding.VARIABLE:
if (hasFlag(NO_FIELDS, flags) && ((IVariableBinding) binding).isField()) {
return 'F' + binding.getName();
@@ -209,17 +209,15 @@
return false;
}
if (hasFlag(VARIABLES, flags)) {
- IVariableBinding[] variableBindings= binding.getDeclaredFields();
- for (int i= 0; i < variableBindings.length; i++) {
- if (requestor.acceptBinding(variableBindings[i]))
+ for (IVariableBinding variableBinding : binding.getDeclaredFields()) {
+ if (requestor.acceptBinding(variableBinding)) {
return true;
+ }
}
}
if (hasFlag(METHODS, flags)) {
- IMethodBinding[] methodBindings= binding.getDeclaredMethods();
- for (int i= 0; i < methodBindings.length; i++) {
- IMethodBinding curr= methodBindings[i];
+ for (IMethodBinding curr : binding.getDeclaredMethods()) {
if (isSuperInterfaceBinding && Modifier.isStatic(curr.getModifiers())) {
continue;
}
@@ -231,9 +229,7 @@
}
if (hasFlag(TYPES, flags)) {
- ITypeBinding[] typeBindings= binding.getDeclaredTypes();
- for (int i= 0; i < typeBindings.length; i++) {
- ITypeBinding curr= typeBindings[i];
+ for (ITypeBinding curr : binding.getDeclaredTypes()) {
if (requestor.acceptBinding(curr))
return true;
}
@@ -249,10 +245,10 @@
return true;
}
- ITypeBinding[] interfaces= binding.getInterfaces(); // includes looking for methods: abstract, unimplemented methods
- for (int i= 0; i < interfaces.length; i++) {
- if (addInherited(interfaces[i], true, flags, requestor)) // recursive
+ for (ITypeBinding intf : binding.getInterfaces()) {// includes looking for methods: abstract, unimplemented methods
+ if (addInherited(intf, true, flags, requestor)) {
return true;
+ }
}
return false;
}
@@ -270,10 +266,10 @@
if (requestor.acceptBinding(binding))
return true;
- ITypeBinding[] typeParameters= binding.getTypeParameters();
- for (int i= 0; i < typeParameters.length; i++) {
- if (requestor.acceptBinding(typeParameters[i]))
+ for (ITypeBinding typeParameter : binding.getTypeParameters()) {
+ if (requestor.acceptBinding(typeParameter)) {
return true;
+ }
}
}
@@ -516,8 +512,7 @@
private IVariableBinding[] getEnumContants(ITypeBinding binding) {
IVariableBinding[] declaredFields= binding.getDeclaredFields();
ArrayList res= new ArrayList<>(declaredFields.length);
- for (int i= 0; i < declaredFields.length; i++) {
- IVariableBinding curr= declaredFields[i];
+ for (IVariableBinding curr : declaredFields) {
if (curr.isEnumConstant()) {
res.add(curr);
}
@@ -526,9 +521,7 @@
}
private boolean hasEnumContants(IBinding declaration, ITypeBinding binding) {
- IVariableBinding[] declaredFields= binding.getDeclaredFields();
- for (int i= 0; i < declaredFields.length; i++) {
- IVariableBinding curr= declaredFields[i];
+ for (IVariableBinding curr : binding.getDeclaredFields()) {
if (curr == declaration)
return true;
}
@@ -638,9 +631,8 @@
}
}
if (possibleSuperTypeDecl.isInterface()) {
- ITypeBinding[] superInterfaces= type.getInterfaces();
- for (int i= 0; i < superInterfaces.length; i++) {
- if (isInSuperTypeHierarchy(possibleSuperTypeDecl, superInterfaces[i].getTypeDeclaration())) {
+ for (ITypeBinding superInterface : type.getInterfaces()) {
+ if (isInSuperTypeHierarchy(possibleSuperTypeDecl, superInterface.getTypeDeclaration())) {
return true;
}
}
@@ -723,7 +715,7 @@
public boolean visit(SwitchCase node) {
// switch on enum allows to use enum constants without qualification
if (hasFlag(VARIABLES, fFlags) && !node.isDefault()) {
- if (node.getAST().apiLevel() >= AST.JLS12) {
+ if (node.getAST().isPreviewEnabled()) {
List expressions= node.expressions();
for (Expression expression : expressions) {
visitExpression(node, expression);
@@ -748,9 +740,7 @@
binding= switchExpression.getExpression().resolveTypeBinding();
}
if (binding != null && binding.isEnum()) {
- IVariableBinding[] declaredFields= binding.getDeclaredFields();
- for (int i= 0; i < declaredFields.length; i++) {
- IVariableBinding curr= declaredFields[i];
+ for (IVariableBinding curr : binding.getDeclaredFields()) {
if (curr.isEnumConstant()) {
fBreak= fRequestor.acceptBinding(curr);
if (fBreak)
@@ -770,7 +760,7 @@
public boolean visit(FieldDeclaration node) {
return !fBreak && isInside(node);
}
-
+
@Override
public boolean visit(Expression node) {
return !fBreak && isInside(node);
@@ -913,13 +903,11 @@
public Collection getUsedVariableNames(int offset, int length) {
HashSet result= new HashSet<>();
- IBinding[] bindingsBefore= getDeclarationsInScope(offset, VARIABLES | CHECK_VISIBILITY);
- for (int i= 0; i < bindingsBefore.length; i++) {
- result.add(bindingsBefore[i].getName());
- }
- IBinding[] bindingsAfter= getDeclarationsAfter(offset + length, VARIABLES | CHECK_VISIBILITY);
- for (int i= 0; i < bindingsAfter.length; i++) {
- result.add(bindingsAfter[i].getName());
+ for (IBinding b : getDeclarationsInScope(offset, VARIABLES | CHECK_VISIBILITY)) {
+ result.add(b.getName());
+ }
+ for (IBinding b : getDeclarationsAfter(offset + length, VARIABLES | CHECK_VISIBILITY)) {
+ result.add(b.getName());
}
List imports= fRoot.imports();
for (int i= 0; i < imports.size(); i++) {
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/StatementRewrite.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/StatementRewrite.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/StatementRewrite.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/StatementRewrite.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - moved to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+
+/**
+ * Rewrite helper for {@link Statement}s. Ensures that
+ * the replacement nodes are enclosed in a Block if necessary.
+ *
+ * see JDTUIHelperClasses
+ */
+public class StatementRewrite extends ReplaceRewrite {
+
+ public StatementRewrite(ASTRewrite rewrite, ASTNode[] nodes) {
+ super(rewrite, nodes);
+ }
+
+ @Override
+ protected void handleOneMany(ASTNode[] replacements, TextEditGroup description) {
+ AST ast= fToReplace[0].getAST();
+ // to replace == 1, but more than one replacement. Have to check if we
+ // need to insert a block to not change structure
+ if (ASTNodes.isControlStatementBody(fDescriptor)) {
+ Block block= ast.newBlock();
+ ListRewrite statements= fRewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
+ for (int i= 0; i < replacements.length; i++) {
+ statements.insertLast(replacements[i], description);
+ }
+ fRewrite.replace(fToReplace[0], block, description);
+ } else {
+ ListRewrite container= fRewrite.getListRewrite(fToReplace[0].getParent(), (ChildListPropertyDescriptor)fDescriptor);
+ container.replace(fToReplace[0], replacements[0], description);
+ for (int i= 1; i < replacements.length; i++) {
+ container.insertAfter(replacements[i], replacements[i - 1], description);
+ }
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/TypeRules.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/TypeRules.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/TypeRules.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/TypeRules.java" 2020-02-26 15:31:57.000000000 +0000
@@ -32,7 +32,7 @@
/**
* Tests if two types are assign compatible. Void types are never compatible.
- *
+ *
* @param typeToAssign The binding of the type to assign
* @param definedType The type of the object that is assigned
* @return true
iff definedType = typeToAssign is a valid assignment
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/VariableDeclarationRewrite.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/VariableDeclarationRewrite.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/VariableDeclarationRewrite.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/VariableDeclarationRewrite.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.dom;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+/**
+ * Rewrite helper for variable declarations.
+ *
+ * see JDTUIHelperClasses
+ */
+public class VariableDeclarationRewrite {
+
+ public static void rewriteModifiers(final SingleVariableDeclaration declarationNode, final int includedModifiers, final int excludedModifiers, final ASTRewrite rewrite, final TextEditGroup group) {
+ ModifierRewrite listRewrite= ModifierRewrite.create(rewrite, declarationNode);
+ listRewrite.setModifiers(includedModifiers, excludedModifiers, group);
+ }
+
+ public static void rewriteModifiers(final VariableDeclarationExpression declarationNode, final int includedModifiers, final int excludedModifiers, final ASTRewrite rewrite, final TextEditGroup group) {
+ ModifierRewrite listRewrite= ModifierRewrite.create(rewrite, declarationNode);
+ listRewrite.setModifiers(includedModifiers, excludedModifiers, group);
+ }
+
+ public static void rewriteModifiers(final FieldDeclaration declarationNode, final VariableDeclarationFragment[] toChange, final int includedModifiers, final int excludedModifiers, final ASTRewrite rewrite, final TextEditGroup group) {
+ final List fragmentsToChange= Arrays.asList(toChange);
+ AST ast= declarationNode.getAST();
+/*
+ * Problem: Same declarationNode can be the subject of multiple calls to this method.
+ * For the 2nd++ calls, the original declarationNode has already been rewritten, and this has to be taken into account.
+ *
+ * Assumption:
+ * - Modifiers for each VariableDeclarationFragment are modified at most once.
+ *
+ * Solution:
+ * - Maintain a map from original VariableDeclarationFragments to their new FieldDeclaration.
+ * - Original modifiers in declarationNode belong to the first fragment.
+ * - When a later fragment needs different modifiers, we create a new FieldDeclaration and move all successive fragments into that declaration
+ * - When a fragment has been moved to a new declaration, make sure we don't create a new move target again, but instead use the already created one
+ */
+ List fragments= declarationNode.fragments();
+ Iterator iter= fragments.iterator();
+
+ ListRewrite blockRewrite;
+ if (declarationNode.getParent() instanceof AbstractTypeDeclaration) {
+ blockRewrite= rewrite.getListRewrite(declarationNode.getParent(), ((AbstractTypeDeclaration)declarationNode.getParent()).getBodyDeclarationsProperty());
+ } else {
+ blockRewrite= rewrite.getListRewrite(declarationNode.getParent(), AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY);
+ }
+
+ VariableDeclarationFragment lastFragment= iter.next();
+ ASTNode lastStatement= declarationNode;
+
+ if (fragmentsToChange.contains(lastFragment)) {
+ ModifierRewrite modifierRewrite= ModifierRewrite.create(rewrite, declarationNode);
+ modifierRewrite.setModifiers(includedModifiers, excludedModifiers, group);
+ }
+
+ ListRewrite fragmentsRewrite= null;
+ while (iter.hasNext()) {
+ VariableDeclarationFragment currentFragment= iter.next();
+
+ @SuppressWarnings("unchecked")
+ Map lookup= (Map) rewrite.getProperty(MovedFragment.class.getName());
+ if (lookup == null) {
+ lookup= new HashMap<>();
+ rewrite.setProperty(MovedFragment.class.getName(), lookup);
+ }
+ MovedFragment currentMovedFragment= lookup.get(currentFragment);
+
+ boolean changeLast= fragmentsToChange.contains(lastFragment);
+ boolean changeCurrent= fragmentsToChange.contains(currentFragment);
+ if (changeLast != changeCurrent || lookup.containsKey(lastFragment)) {
+ ModifierRewrite modifierRewrite= null;
+ if (currentMovedFragment != null) {
+ // Current fragment has already been moved.
+
+ if (currentMovedFragment.fUsesOriginalModifiers) {
+ // Need to put in the right modifiers (removing any existing ones).
+ modifierRewrite= ModifierRewrite.create(rewrite, currentMovedFragment.fDeclaration);
+ ListRewrite listRewrite= rewrite.getListRewrite(currentMovedFragment.fDeclaration, FieldDeclaration.MODIFIERS2_PROPERTY);
+ List extendedList= listRewrite.getRewrittenList();
+ for (int i= 0; i < extendedList.size(); i++) {
+ ASTNode curr= (ASTNode)extendedList.get(i);
+ if (curr instanceof Modifier)
+ rewrite.remove(curr, group);
+ }
+ }
+ // otherwise, don't need to touch the modifiers, so leave modifierRewrite null
+
+ } else { // need to split an existing field declaration
+ VariableDeclarationFragment moveTarget;
+ moveTarget= (VariableDeclarationFragment)rewrite.createMoveTarget(currentFragment);
+
+ FieldDeclaration newStatement= (FieldDeclaration)ast.createInstance(FieldDeclaration.class);
+ rewrite.getListRewrite(newStatement, FieldDeclaration.FRAGMENTS_PROPERTY).insertLast(moveTarget, group);
+ lookup.put(currentFragment, new MovedFragment(moveTarget, newStatement, !changeCurrent));
+ rewrite.set(newStatement, FieldDeclaration.TYPE_PROPERTY, rewrite.createCopyTarget(declarationNode.getType()), group);
+
+ modifierRewrite= ModifierRewrite.create(rewrite, newStatement);
+ modifierRewrite.copyAllAnnotations(declarationNode, group);
+ blockRewrite.insertAfter(newStatement, lastStatement, group);
+
+ fragmentsRewrite= rewrite.getListRewrite(newStatement, FieldDeclaration.FRAGMENTS_PROPERTY);
+ lastStatement= newStatement;
+ }
+
+ if (modifierRewrite != null) {
+ if (changeCurrent) {
+ int newModifiers= (declarationNode.getModifiers() & ~excludedModifiers) | includedModifiers;
+ modifierRewrite.setModifiers(newModifiers, excludedModifiers, group);
+ } else {
+ int newModifiers= declarationNode.getModifiers();
+ modifierRewrite.setModifiers(newModifiers, Modifier.NONE, group);
+ }
+ }
+
+ } else if (fragmentsRewrite != null) {
+ VariableDeclarationFragment fragment0;
+ boolean usesOriginalModifiers= true;
+ if (currentMovedFragment != null) {
+ fragment0= currentMovedFragment.fMoveTarget;
+ usesOriginalModifiers= currentMovedFragment.fUsesOriginalModifiers;
+ rewrite.getListRewrite(currentMovedFragment.fDeclaration, FieldDeclaration.FRAGMENTS_PROPERTY).remove(fragment0, group);
+ } else {
+ fragment0= (VariableDeclarationFragment)rewrite.createMoveTarget(currentFragment);
+ }
+ lookup.put(currentFragment, new MovedFragment(fragment0, lastStatement, usesOriginalModifiers));
+ fragmentsRewrite.insertLast(fragment0, group);
+ }
+ lastFragment= currentFragment;
+ }
+ }
+
+ private static class MovedFragment {
+ final VariableDeclarationFragment fMoveTarget;
+ final ASTNode fDeclaration;
+ boolean fUsesOriginalModifiers;
+
+ public MovedFragment(VariableDeclarationFragment moveTarget, ASTNode declaration, boolean usesOriginalModifiers) {
+ fMoveTarget= moveTarget;
+ fDeclaration= declaration;
+ fUsesOriginalModifiers= usesOriginalModifiers;
+ }
+ }
+
+ public static void rewriteModifiers(final VariableDeclarationStatement declarationNode, final VariableDeclarationFragment[] toChange, final int includedModifiers, final int excludedModifiers, ASTRewrite rewrite, final TextEditGroup group) {
+ final List fragmentsToChange= Arrays.asList(toChange);
+ AST ast= declarationNode.getAST();
+
+ List fragments= declarationNode.fragments();
+ Iterator iter= fragments.iterator();
+
+ ListRewrite blockRewrite= null;
+ ASTNode parentStatement= declarationNode.getParent();
+ if (parentStatement instanceof SwitchStatement) {
+ blockRewrite= rewrite.getListRewrite(parentStatement, SwitchStatement.STATEMENTS_PROPERTY);
+ } else if (parentStatement instanceof Block) {
+ blockRewrite= rewrite.getListRewrite(parentStatement, Block.STATEMENTS_PROPERTY);
+ } else {
+ // should not happen. VariableDeclaration's can not be in a control statement body
+ Assert.isTrue(false);
+ }
+
+ VariableDeclarationFragment lastFragment= iter.next();
+ ASTNode lastStatement= declarationNode;
+
+ boolean modifiersModified= false;
+ if (fragmentsToChange.contains(lastFragment)) {
+ ModifierRewrite modifierRewrite= ModifierRewrite.create(rewrite, declarationNode);
+ modifierRewrite.setModifiers(includedModifiers, excludedModifiers, group);
+ modifiersModified= true;
+ }
+
+ ListRewrite fragmentsRewrite= null;
+ while (iter.hasNext()) {
+ VariableDeclarationFragment currentFragment= iter.next();
+
+ if (fragmentsToChange.contains(lastFragment) != fragmentsToChange.contains(currentFragment)) {
+
+ VariableDeclarationStatement newStatement= ast.newVariableDeclarationStatement((VariableDeclarationFragment)rewrite.createMoveTarget(currentFragment));
+ newStatement.setType((Type)rewrite.createCopyTarget(declarationNode.getType()));
+
+ ModifierRewrite modifierRewrite= ModifierRewrite.create(rewrite, newStatement);
+ if (fragmentsToChange.contains(currentFragment)) {
+ modifierRewrite.copyAllAnnotations(declarationNode, group);
+ int newModifiers= (declarationNode.getModifiers() & ~excludedModifiers) | includedModifiers;
+ modifierRewrite.setModifiers(newModifiers, excludedModifiers, group);
+ } else {
+ modifierRewrite.copyAllModifiers(declarationNode, group, modifiersModified);
+ }
+ blockRewrite.insertAfter(newStatement, lastStatement, group);
+
+ fragmentsRewrite= rewrite.getListRewrite(newStatement, VariableDeclarationStatement.FRAGMENTS_PROPERTY);
+ lastStatement= newStatement;
+ } else if (fragmentsRewrite != null) {
+ ASTNode fragment0= rewrite.createMoveTarget(currentFragment);
+ fragmentsRewrite.insertLast(fragment0, group);
+ }
+ lastFragment= currentFragment;
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AbstractSerialVersionOperationCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AbstractSerialVersionOperationCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AbstractSerialVersionOperationCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AbstractSerialVersionOperationCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - copied to jdt.core.manipulation core class
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.manipulation.CodeGeneration;
+
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+
+/**
+ * Partial implementation of a serial version correction proposal.
+ *
+ * @since 3.1
+ */
+public abstract class AbstractSerialVersionOperationCore extends CompilationUnitRewriteOperation {
+
+ /** The long literal suffix */
+ protected static final String LONG_SUFFIX= "L"; //$NON-NLS-1$
+
+ /** The default serial value */
+ public static final long SERIAL_VALUE= 1;
+
+ /** The default serial id expression */
+ protected static final String DEFAULT_EXPRESSION= SERIAL_VALUE + LONG_SUFFIX;
+
+ /** The name of the serial version field */
+ protected static final String NAME_FIELD= "serialVersionUID"; //$NON-NLS-1$
+
+ /** The originally selected node */
+ private final ASTNode[] fNodes;
+ private final ICompilationUnit fUnit;
+
+ protected AbstractSerialVersionOperationCore(final ICompilationUnit unit, final ASTNode[] node) {
+ fUnit= unit;
+ fNodes= node;
+ }
+
+ /**
+ * Adds an initializer to the specified variable declaration fragment.
+ *
+ * @param fragment the variable declaration fragment to add an initializer
+ * @param declarationNode the declartion node
+ * @return false if no id could be calculated
+ */
+ protected abstract boolean addInitializer(final VariableDeclarationFragment fragment, final ASTNode declarationNode);
+
+ /**
+ * Adds the necessary linked positions for the specified fragment.
+ *
+ * @param rewrite the ast rewrite to operate on
+ * @param fragment the fragment to add linked positions to
+ * @param positionGroups the list of {@link LinkedProposalPositionGroupCore}s
+ */
+ protected abstract void addLinkedPositions(final ASTRewrite rewrite, final VariableDeclarationFragment fragment, final LinkedProposalModelCore positionGroups);
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore positionGroups) throws CoreException {
+ final ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ VariableDeclarationFragment fragment= null;
+ for (int i= 0; i < fNodes.length; i++) {
+ final ASTNode node= fNodes[i];
+
+ final AST ast= node.getAST();
+
+ fragment= ast.newVariableDeclarationFragment();
+ fragment.setName(ast.newSimpleName(NAME_FIELD));
+
+ final FieldDeclaration declaration= ast.newFieldDeclaration(fragment);
+ declaration.setType(ast.newPrimitiveType(PrimitiveType.LONG));
+ declaration.modifiers().addAll(ASTNodeFactory.newModifiers(ast, Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL));
+
+ if (!addInitializer(fragment, node))
+ continue;
+
+ if (fragment.getInitializer() != null) {
+
+ final TextEditGroup editGroup= createTextEditGroup(FixMessages.SerialVersion_group_description, cuRewrite);
+ if (node instanceof AbstractTypeDeclaration)
+ rewrite.getListRewrite(node, ((AbstractTypeDeclaration) node).getBodyDeclarationsProperty()).insertAt(declaration, 0, editGroup);
+ else if (node instanceof AnonymousClassDeclaration)
+ rewrite.getListRewrite(node, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY).insertAt(declaration, 0, editGroup);
+ else if (node instanceof ParameterizedType) {
+ final ParameterizedType type= (ParameterizedType) node;
+ final ASTNode parent= type.getParent();
+ if (parent instanceof ClassInstanceCreation) {
+ final ClassInstanceCreation creation= (ClassInstanceCreation) parent;
+ final AnonymousClassDeclaration anonymous= creation.getAnonymousClassDeclaration();
+ if (anonymous != null)
+ rewrite.getListRewrite(anonymous, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY).insertAt(declaration, 0, editGroup);
+ }
+ } else
+ Assert.isTrue(false);
+
+ addLinkedPositions(rewrite, fragment, positionGroups);
+ }
+
+ final String comment= CodeGeneration.getFieldComment(fUnit, declaration.getType().toString(), NAME_FIELD, StubUtility.getLineDelimiterUsed(fUnit));
+ if (comment != null && comment.length() > 0 && !"/**\n *\n */\n".equals(comment)) { //$NON-NLS-1$
+ final Javadoc doc= (Javadoc) rewrite.createStringPlaceholder(comment, ASTNode.JAVADOC);
+ declaration.setJavadoc(doc);
+ }
+ }
+ if (fragment == null)
+ return;
+
+ positionGroups.setEndPosition(rewrite.track(fragment));
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddUnimplementedMethodsOperation.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddUnimplementedMethodsOperation.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddUnimplementedMethodsOperation.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddUnimplementedMethodsOperation.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Mateusz Wenus - [override method] generate in declaration order [code generation] - https://bugs.eclipse.org/bugs/show_bug.cgi?id=140971
+ * Stephan Herrmann - Contribution for Bug 463360 - [override method][null] generating method override should not create redundant null annotations
+ * Red Hat Inc. - moved to jdt.core.manipulation
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.Arrays;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+import org.eclipse.jdt.internal.core.manipulation.BindingLabelProviderCore;
+import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
+import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2Core;
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.Messages;
+import org.eclipse.jdt.internal.corext.util.MethodsSourcePositionComparator;
+
+import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
+import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages;
+
+public class AddUnimplementedMethodsOperation extends CompilationUnitRewriteOperation {
+
+ private ASTNode fTypeNode;
+
+ /**
+ * Create a {@link AddUnimplementedMethodsOperation}
+ * @param typeNode must be one of the following types:
+ * - AnonymousClassDeclaration
+ * - AbstractTypeDeclaration
+ * - EnumConstantDeclaration
+ */
+ public AddUnimplementedMethodsOperation(ASTNode typeNode) {
+ fTypeNode= typeNode;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ IMethodBinding[] unimplementedMethods= getUnimplementedMethods(fTypeNode);
+ if (unimplementedMethods.length == 0)
+ return;
+
+ ImportRewriteContext context= new ContextSensitiveImportRewriteContext(fTypeNode, cuRewrite.getImportRewrite());
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ ICompilationUnit unit= cuRewrite.getCu();
+ CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(unit.getJavaProject());
+
+ ListRewrite listRewrite;
+
+ if (fTypeNode instanceof AnonymousClassDeclaration) {
+ AnonymousClassDeclaration decl= (AnonymousClassDeclaration) fTypeNode;
+ listRewrite= rewrite.getListRewrite(decl, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY);
+ settings.createComments= false;
+ } else if (fTypeNode instanceof AbstractTypeDeclaration) {
+ AbstractTypeDeclaration decl= (AbstractTypeDeclaration) fTypeNode;
+ listRewrite= rewrite.getListRewrite(decl, decl.getBodyDeclarationsProperty());
+ } else if (fTypeNode instanceof EnumConstantDeclaration) {
+ EnumConstantDeclaration enumConstantDeclaration= (EnumConstantDeclaration) fTypeNode;
+ AnonymousClassDeclaration anonymousClassDeclaration= enumConstantDeclaration.getAnonymousClassDeclaration();
+ if (anonymousClassDeclaration == null) {
+ anonymousClassDeclaration= rewrite.getAST().newAnonymousClassDeclaration();
+ rewrite.set(enumConstantDeclaration, EnumConstantDeclaration.ANONYMOUS_CLASS_DECLARATION_PROPERTY, anonymousClassDeclaration, createTextEditGroup(
+ CorrectionMessages.AddUnimplementedMethodsOperation_AddMissingMethod_group, cuRewrite));
+ }
+ listRewrite= rewrite.getListRewrite(anonymousClassDeclaration, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY);
+ settings.createComments= false;
+ } else {
+ Assert.isTrue(false, "Unknown type node"); //$NON-NLS-1$
+ return;
+ }
+
+ ImportRewrite imports= cuRewrite.getImportRewrite();
+
+ for (int i= 0; i < unimplementedMethods.length; i++) {
+ IMethodBinding curr= unimplementedMethods[i];
+ MethodDeclaration newMethodDecl= StubUtility2Core.createImplementationStubCore(unit, rewrite, imports, context, curr, curr.getDeclaringClass(), settings, false, fTypeNode, false);
+ listRewrite.insertLast(newMethodDecl, createTextEditGroup(CorrectionMessages.AddUnimplementedMethodsOperation_AddMissingMethod_group, cuRewrite));
+ }
+ }
+
+ @Override
+ public String getAdditionalInfo() {
+ if (fTypeNode instanceof EnumDeclaration)
+ return CorrectionMessages.UnimplementedMethodsCorrectionProposal_enum_info;
+
+ IMethodBinding[] methodsToOverride= getMethodsToImplement();
+ StringBuilder buf= new StringBuilder();
+ buf.append(""); //$NON-NLS-1$
+ if (methodsToOverride.length == 1) {
+ buf.append(CorrectionMessages.UnimplementedMethodsCorrectionProposal_info_singular);
+ } else {
+ buf.append(Messages.format(CorrectionMessages.UnimplementedMethodsCorrectionProposal_info_plural, String.valueOf(methodsToOverride.length)));
+ }
+ buf.append(""); //$NON-NLS-1$
+ for (int i= 0; i < methodsToOverride.length; i++) {
+ buf.append("- "); //$NON-NLS-1$
+ buf.append(BindingLabelProviderCore.getBindingLabel(methodsToOverride[i], JavaElementLabelsCore.ALL_FULLY_QUALIFIED));
+ buf.append("
"); //$NON-NLS-1$
+ }
+ buf.append("
"); //$NON-NLS-1$
+ return buf.toString();
+ }
+
+ public IMethodBinding[] getMethodsToImplement() {
+ return getUnimplementedMethods(fTypeNode);
+ }
+
+ private IMethodBinding[] getUnimplementedMethods(ASTNode typeNode) {
+ ITypeBinding binding= null;
+ boolean implementAbstractsOfInput= false;
+ if (typeNode instanceof AnonymousClassDeclaration) {
+ AnonymousClassDeclaration decl= (AnonymousClassDeclaration) typeNode;
+ binding= decl.resolveBinding();
+ } else if (typeNode instanceof AbstractTypeDeclaration) {
+ AbstractTypeDeclaration decl= (AbstractTypeDeclaration) typeNode;
+ binding= decl.resolveBinding();
+ } else if (typeNode instanceof EnumConstantDeclaration) {
+ EnumConstantDeclaration enumConstantDeclaration= (EnumConstantDeclaration) typeNode;
+ if (enumConstantDeclaration.getAnonymousClassDeclaration() != null) {
+ binding= enumConstantDeclaration.getAnonymousClassDeclaration().resolveBinding();
+ } else {
+ IVariableBinding varBinding= enumConstantDeclaration.resolveVariable();
+ if (varBinding != null) {
+ binding= varBinding.getDeclaringClass();
+ implementAbstractsOfInput= true;
+ }
+ }
+ }
+ if (binding == null)
+ return new IMethodBinding[0];
+
+ IMethodBinding[] unimplementedMethods= StubUtility2Core.getUnimplementedMethods(binding, implementAbstractsOfInput);
+ Arrays.sort(unimplementedMethods, new MethodsSourcePositionComparator(binding));
+ return unimplementedMethods;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java" 2020-02-26 15:31:57.000000000 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -16,6 +16,7 @@
package org.eclipse.jdt.internal.corext.fix;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
+import org.eclipse.jdt.core.manipulation.CleanUpOptionsCore;
public class CleanUpConstants {
@@ -34,9 +35,9 @@
*
* Possible values: {TRUE, FALSE}
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String FORMAT_SOURCE_CODE= "cleanup.format_source_code"; //$NON-NLS-1$
@@ -47,11 +48,11 @@
*
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.4
*/
public static final String FORMAT_SOURCE_CODE_CHANGES_ONLY= "cleanup.format_source_code_changes_only"; //$NON-NLS-1$
@@ -61,11 +62,11 @@
* {@link #FORMAT_MULTI_LINE_COMMENT}
{@link #FORMAT_SINGLE_LINE_COMMENT}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
* @deprecated replaced by {@link #FORMAT_SOURCE_CODE}
*/
@@ -76,11 +77,11 @@
* Format single line comments. Only has an effect if {@link #FORMAT_COMMENT} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
* @deprecated replaced by
* {@link DefaultCodeFormatterConstants#FORMATTER_COMMENT_FORMAT_LINE_COMMENT}
@@ -92,11 +93,11 @@
* Format multi line comments. Only has an effect if {@link #FORMAT_COMMENT} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
* @deprecated replaced by
* {@link DefaultCodeFormatterConstants#FORMATTER_COMMENT_FORMAT_BLOCK_COMMENT}
@@ -108,11 +109,11 @@
* Format javadoc comments. Only has an effect if {@link #FORMAT_COMMENT} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
* @deprecated replaced by
* {@link DefaultCodeFormatterConstants#FORMATTER_COMMENT_FORMAT_JAVADOC_COMMENT}
@@ -124,11 +125,11 @@
* Removes trailing whitespace in compilation units
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String FORMAT_REMOVE_TRAILING_WHITESPACES= "cleanup.remove_trailing_whitespaces"; //$NON-NLS-1$
@@ -138,11 +139,11 @@
* Only has an effect if {@link #FORMAT_REMOVE_TRAILING_WHITESPACES} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String FORMAT_REMOVE_TRAILING_WHITESPACES_ALL= "cleanup.remove_trailing_whitespaces_all"; //$NON-NLS-1$
@@ -153,11 +154,11 @@
* Only has an effect if {@link #FORMAT_REMOVE_TRAILING_WHITESPACES} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String FORMAT_REMOVE_TRAILING_WHITESPACES_IGNORE_EMPTY= "cleanup.remove_trailing_whitespaces_ignore_empty"; //$NON-NLS-1$
@@ -166,11 +167,11 @@
* Correct indentation in compilation units on all lines
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.4
*/
public static final String FORMAT_CORRECT_INDENTATION= "cleanup.correct_indentation"; //$NON-NLS-1$
@@ -181,11 +182,11 @@
* {@link #MEMBER_ACCESSES_NON_STATIC_FIELD_USE_THIS_IF_NECESSARY}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_NON_STATIC_FIELD_USE_THIS= "cleanup.use_this_for_non_static_field_access"; //$NON-NLS-1$
@@ -194,20 +195,20 @@
* Adds a 'this' qualifier to field accesses.
*
* Example:
- *
+ *
*
* int fField;
* void foo() {fField= 10;} -> void foo() {this.fField= 10;}
*
- *
+ *
* Only has an effect if {@link #MEMBER_ACCESSES_NON_STATIC_FIELD_USE_THIS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_NON_STATIC_FIELD_USE_THIS_ALWAYS= "cleanup.always_use_this_for_non_static_field_access"; //$NON-NLS-1$
@@ -216,20 +217,20 @@
* Removes 'this' qualifier to field accesses.
*
* Example:
- *
+ *
*
* int fField;
* void foo() {this.fField= 10;} -> void foo() {fField= 10;}
*
- *
+ *
* Only has an effect if {@link #MEMBER_ACCESSES_NON_STATIC_FIELD_USE_THIS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_NON_STATIC_FIELD_USE_THIS_IF_NECESSARY= "cleanup.use_this_for_non_static_field_access_only_if_necessary"; //$NON-NLS-1$
@@ -240,11 +241,11 @@
* {@link #MEMBER_ACCESSES_NON_STATIC_METHOD_USE_THIS_IF_NECESSARY}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_NON_STATIC_METHOD_USE_THIS= "cleanup.use_this_for_non_static_method_access"; //$NON-NLS-1$
@@ -253,20 +254,20 @@
* Adds a 'this' qualifier to method accesses.
*
* Example:
- *
+ *
*
* int method(){};
* void foo() {method()} -> void foo() {this.method();}
*
- *
+ *
* Only has an effect if {@link #MEMBER_ACCESSES_NON_STATIC_METHOD_USE_THIS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_NON_STATIC_METHOD_USE_THIS_ALWAYS= "cleanup.always_use_this_for_non_static_method_access"; //$NON-NLS-1$
@@ -275,20 +276,20 @@
* Removes 'this' qualifier from field accesses.
*
* Example:
- *
+ *
*
* int fField;
* void foo() {this.fField= 10;} -> void foo() {fField= 10;}
*
- *
+ *
* Only has an effect if {@link #MEMBER_ACCESSES_NON_STATIC_METHOD_USE_THIS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_NON_STATIC_METHOD_USE_THIS_IF_NECESSARY= "cleanup.use_this_for_non_static_method_access_only_if_necessary"; //$NON-NLS-1$
@@ -301,11 +302,11 @@
* {@link #MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS_SUBTYPE_ACCESS}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS= "cleanup.qualify_static_member_accesses_with_declaring_class"; //$NON-NLS-1$
@@ -314,23 +315,23 @@
* Qualify static field accesses with declaring type.
*
* Example:
- *
+ *
*
* class E {
* public static int i;
* void foo() {i= 10;} -> void foo() {E.i= 10;}
* }
*
- *
+ *
*
* Only has an effect if {@link #MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS_FIELD= "cleanup.qualify_static_field_accesses_with_declaring_class"; //$NON-NLS-1$
@@ -339,22 +340,22 @@
* Qualifies static method accesses with declaring type.
*
* Example:
- *
+ *
*
* class E {
* public static int m();
* void foo() {m();} -> void foo() {E.m();}
* }
*
- *
+ *
* Only has an effect if {@link #MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS_METHOD= "cleanup.qualify_static_method_accesses_with_declaring_class"; //$NON-NLS-1$
@@ -363,22 +364,22 @@
* Changes indirect accesses to static members to direct ones.
*
* Example:
- *
+ *
*
* class E {public static int i;}
* class ESub extends E {
* void foo() {ESub.i= 10;} -> void foo() {E.i= 10;}
* }
*
- *
+ *
* Only has an effect if {@link #MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS_SUBTYPE_ACCESS= "cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class"; //$NON-NLS-1$
@@ -387,22 +388,22 @@
* Changes non static accesses to static members to static accesses.
*
* Example:
- *
+ *
*
* class E {
* public static int i;
* void foo() {(new E()).i= 10;} -> void foo() {E.i= 10;}
* }
*
- *
+ *
* Only has an effect if {@link #MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String MEMBER_ACCESSES_STATIC_QUALIFY_WITH_DECLARING_CLASS_INSTANCE_ACCESS= "cleanup.qualify_static_member_accesses_through_instances_with_declaring_class"; //$NON-NLS-1$
@@ -413,11 +414,11 @@
* {@link #CONTROL_STATMENTS_USE_BLOCKS_NO_FOR_RETURN_AND_THROW}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String CONTROL_STATEMENTS_USE_BLOCKS= "cleanup.use_blocks"; //$NON-NLS-1$
@@ -426,19 +427,19 @@
* Adds block to control statement body if the body is not a block.
*
* Example:
- *
+ *
*
* if (b) foo(); -> if (b) {foo();}
*
- *
+ *
* Only has an effect if {@link #CONTROL_STATEMENTS_USE_BLOCKS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String CONTROL_STATMENTS_USE_BLOCKS_ALWAYS= "cleanup.always_use_blocks"; //$NON-NLS-1$
@@ -448,19 +449,19 @@
* throw statement.
*
* Example:
- *
+ *
*
* if (b) {return;} -> if (b) return;
*
- *
+ *
* Only has an effect if {@link #CONTROL_STATEMENTS_USE_BLOCKS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String CONTROL_STATMENTS_USE_BLOCKS_NO_FOR_RETURN_AND_THROW= "cleanup.use_blocks_only_for_return_and_throw"; //$NON-NLS-1$
@@ -469,19 +470,19 @@
* Remove unnecessary blocks in control statement bodies.
*
* Example:
- *
+ *
*
* if (b) {foo();} -> if (b) foo();
*
- *
+ *
* Only has an effect if {@link #CONTROL_STATEMENTS_USE_BLOCKS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String CONTROL_STATMENTS_USE_BLOCKS_NEVER= "cleanup.never_use_blocks"; //$NON-NLS-1$
@@ -490,17 +491,17 @@
* Convert for loops to enhanced for loops.
*
* Example:
- *
+ *
*
* for (int i = 0; i < array.length; i++) {} -> for (int element : array) {}
*
- *
+ *
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String CONTROL_STATMENTS_CONVERT_FOR_LOOP_TO_ENHANCED= "cleanup.convert_to_enhanced_for_loop"; //$NON-NLS-1$
@@ -511,11 +512,11 @@
*
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String EXPRESSIONS_USE_PARENTHESES= "cleanup.use_parentheses_in_expressions"; //$NON-NLS-1$
@@ -524,21 +525,21 @@
* Add paranoiac parentheses around conditional expressions.
*
* Example:
- *
+ *
*
* boolean b= i > 10 && i < 100 || i > 20;
* ->
* boolean b= ((i > 10) && (i < 100)) || (i > 20);
*
- *
+ *
* Only has an effect if {@link #EXPRESSIONS_USE_PARENTHESES} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String EXPRESSIONS_USE_PARENTHESES_ALWAYS= "cleanup.always_use_parentheses_in_expressions"; //$NON-NLS-1$
@@ -547,21 +548,21 @@
* Remove unnecessary parenthesis around conditional expressions.
*
* Example:
- *
+ *
*
* boolean b= ((i > 10) && (i < 100)) || (i > 20);
* ->
* boolean b= i > 10 && i < 100 || i > 20;
*
- *
+ *
* Only has an effect if {@link #EXPRESSIONS_USE_PARENTHESES} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String EXPRESSIONS_USE_PARENTHESES_NEVER= "cleanup.never_use_parentheses_in_expressions"; //$NON-NLS-1$
@@ -573,68 +574,68 @@
* {@link #VARIABLE_DECLARATIONS_USE_FINAL_PRIVATE_FIELDS}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String VARIABLE_DECLARATIONS_USE_FINAL= "cleanup.make_variable_declarations_final"; //$NON-NLS-1$
/**
* Add a final modifier to private fields where possible i.e.:
- *
+ *
*
* private int field= 0; -> private final int field= 0;
*
- *
+ *
* Only has an effect if {@link #VARIABLE_DECLARATIONS_USE_FINAL} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String VARIABLE_DECLARATIONS_USE_FINAL_PRIVATE_FIELDS= "cleanup.make_private_fields_final"; //$NON-NLS-1$
/**
* Add a final modifier to method parameters where possible i.e.:
- *
+ *
*
* void foo(int i) {} -> void foo(final int i) {}
*
- *
+ *
* Only has an effect if {@link #VARIABLE_DECLARATIONS_USE_FINAL} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String VARIABLE_DECLARATIONS_USE_FINAL_PARAMETERS= "cleanup.make_parameters_final"; //$NON-NLS-1$
/**
* Add a final modifier to local variables where possible i.e.:
- *
+ *
*
* int i= 0; -> final int i= 0;
*
- *
+ *
* Only has an effect if {@link #VARIABLE_DECLARATIONS_USE_FINAL} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String VARIABLE_DECLARATIONS_USE_FINAL_LOCAL_VARIABLES= "cleanup.make_local_variable_final"; //$NON-NLS-1$
@@ -644,9 +645,9 @@
* For detailed settings, use {@link #USE_LAMBDA} or {@link #USE_ANONYMOUS_CLASS_CREATION}
*
* Possible values: {TRUE, FALSE}
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String CONVERT_FUNCTIONAL_INTERFACES= "cleanup.convert_functional_interfaces"; //$NON-NLS-1$
@@ -657,9 +658,9 @@
* Possible values: {TRUE, FALSE}
*
* Only has an effect if {@link #CONVERT_FUNCTIONAL_INTERFACES} is TRUE.
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.10
*/
public static final String USE_LAMBDA= "cleanup.use_lambda"; //$NON-NLS-1$
@@ -670,26 +671,39 @@
* Possible values: {TRUE, FALSE}
*
* Only has an effect if {@link #CONVERT_FUNCTIONAL_INTERFACES} is TRUE.
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.10
*/
public static final String USE_ANONYMOUS_CLASS_CREATION= "cleanup.use_anonymous_class_creation"; //$NON-NLS-1$
/**
+ * Removes useless parenthesis, return statements and brackets from lambda expressions and
+ * method references.
+ *
+ * Possible values: {TRUE, FALSE}
+ *
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 4.15
+ */
+ public static final String SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF= "cleanup.simplify_lambda_expression_and_method_ref"; //$NON-NLS-1$
+
+ /**
* Adds type parameters to raw type references.
*
* Example:
- *
+ *
*
* List l; -> List<Object> l;
*
- *
+ *
* Possible values: {TRUE, FALSE}
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String VARIABLE_DECLARATION_USE_TYPE_ARGUMENTS_FOR_RAW_TYPE_REFERENCES= "cleanup.use_arguments_for_raw_type_references"; //$NON-NLS-1$
@@ -698,11 +712,11 @@
* Removes unused imports.
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNUSED_CODE_IMPORTS= "cleanup.remove_unused_imports"; //$NON-NLS-1$
@@ -713,11 +727,11 @@
* {@link #REMOVE_UNUSED_CODE_PRIVATE_METHODS}
{@link #REMOVE_UNUSED_CODE_PRIVATE_TYPES}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNUSED_CODE_PRIVATE_MEMBERS= "cleanup.remove_unused_private_members"; //$NON-NLS-1$
@@ -727,11 +741,11 @@
* Only has an effect if {@link #REMOVE_UNUSED_CODE_PRIVATE_MEMBERS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNUSED_CODE_PRIVATE_TYPES= "cleanup.remove_unused_private_types"; //$NON-NLS-1$
@@ -741,11 +755,11 @@
* Only has an effect if {@link #REMOVE_UNUSED_CODE_PRIVATE_MEMBERS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNUSED_CODE_PRIVATE_CONSTRUCTORS= "cleanup.remove_private_constructors"; //$NON-NLS-1$
@@ -755,11 +769,11 @@
* Only has an effect if {@link #REMOVE_UNUSED_CODE_PRIVATE_MEMBERS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNUSED_CODE_PRIVATE_FELDS= "cleanup.remove_unused_private_fields"; //$NON-NLS-1$
@@ -769,11 +783,11 @@
* Only has an effect if {@link #REMOVE_UNUSED_CODE_PRIVATE_MEMBERS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNUSED_CODE_PRIVATE_METHODS= "cleanup.remove_unused_private_methods"; //$NON-NLS-1$
@@ -782,11 +796,11 @@
* Removes unused local variables.
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNUSED_CODE_LOCAL_VARIABLES= "cleanup.remove_unused_local_variables"; //$NON-NLS-1$
@@ -795,11 +809,11 @@
* Removes unused casts.
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNNECESSARY_CASTS= "cleanup.remove_unnecessary_casts"; //$NON-NLS-1$
@@ -808,19 +822,19 @@
* Remove unnecessary '$NON-NLS$' tags.
*
* Example:
- *
+ *
*
* String s; //$NON-NLS-1$ -> String s;
*
- *
+ *
*
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String REMOVE_UNNECESSARY_NLS_TAGS= "cleanup.remove_unnecessary_nls_tags"; //$NON-NLS-1$
@@ -831,8 +845,8 @@
* Possible values: {TRUE, FALSE}
*
*
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.10
*/
public static final String INSERT_INFERRED_TYPE_ARGUMENTS= "cleanup.insert_inferred_type_arguments"; //$NON-NLS-1$
@@ -843,8 +857,8 @@
* Possible values: {TRUE, FALSE}
*
*
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.10
*/
public static final String REMOVE_REDUNDANT_TYPE_ARGUMENTS= "cleanup.remove_redundant_type_arguments"; //$NON-NLS-1$
@@ -855,12 +869,61 @@
* Possible values: {TRUE, FALSE}
*
*
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.14
*/
public static final String REMOVE_REDUNDANT_MODIFIERS= "cleanup.remove_redundant_modifiers"; //$NON-NLS-1$
+ /**
+ * Uses Autoboxing.
+ *
+ * Possible values: {TRUE, FALSE}
+ *
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 4.13
+ */
+ public static final String USE_AUTOBOXING= "cleanup.use_autoboxing"; //$NON-NLS-1$
+
+ /**
+ * Uses unboxing.
+ *
+ * Possible values: {TRUE, FALSE}
+ *
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 4.13
+ */
+ public static final String USE_UNBOXING= "cleanup.use_unboxing"; //$NON-NLS-1$
+
+ /**
+ * Push down negation.
+ *
+ * Possible values: {TRUE, FALSE}
+ *
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 4.13
+ */
+ public static final String PUSH_DOWN_NEGATION= "cleanup.push_down_negation"; //$NON-NLS-1$
+
+
+ /**
+ * Use directly map method.
+ *
+ * Possible values: {TRUE, FALSE}
+ *
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 4.14
+ */
+ public static final String USE_DIRECTLY_MAP_METHOD= "cleanup.use_directly_map_method"; //$NON-NLS-1$
+
/**
* Removes redundant semicolons.
@@ -868,47 +931,58 @@
* Possible values: {TRUE, FALSE}
*
*
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.14
*/
public static final String REMOVE_REDUNDANT_SEMICOLONS= "cleanup.remove_redundant_semicolons"; //$NON-NLS-1$
+ /**
+ * Removes unnecessary array creation for varargs.
+ *
+ * Possible values: {TRUE, FALSE}
+ *
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 3.19
+ */
+ public static final String REMOVE_UNNECESSARY_ARRAY_CREATION= "cleanup.remove_unnecessary_array_creation"; //$NON-NLS-1$
/**
* Controls whether missing annotations should be added to the code. For detailed settings use:
* {@link #ADD_MISSING_ANNOTATIONS_DEPRECATED}
{@value #ADD_MISSING_ANNOTATIONS_OVERRIDE}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ADD_MISSING_ANNOTATIONS= "cleanup.add_missing_annotations"; //$NON-NLS-1$
-
+
/**
* Add '@Override' annotation in front of overriding methods.
*
* Example:
- *
+ *
*
* class E1 {void foo();}
* class E2 extends E1 {
* void foo(); -> @Override void foo();
* }
*
- *
+ *
* Only has an effect if {@link #ADD_MISSING_ANNOTATIONS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ADD_MISSING_ANNOTATIONS_OVERRIDE= "cleanup.add_missing_override_annotations"; //$NON-NLS-1$
@@ -917,23 +991,23 @@
* Add '@Override' annotation in front of methods that override or implement a superinterface method.
*
* Example:
- *
+ *
*
* interface I {void foo();}
* class E implements I {
* void foo(); -> @Override void foo();
* }
*
- *
+ *
* Only has an effect if {@link #ADD_MISSING_ANNOTATIONS} and {@link #ADD_MISSING_ANNOTATIONS_OVERRIDE} are TRUE and
* the compiler compliance is 1.6 or higher.
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.6
*/
public static final String ADD_MISSING_ANNOTATIONS_OVERRIDE_FOR_INTERFACE_METHOD_IMPLEMENTATION= "cleanup.add_missing_override_annotations_interface_methods"; //$NON-NLS-1$
@@ -942,7 +1016,7 @@
* Add '@Deprecated' annotation in front of deprecated members.
*
* Example:
- *
+ *
*
* /**@deprecated* /
* int i;
@@ -951,15 +1025,15 @@
* @Deprecated
* int i;
*
- *
+ *
* Only has an effect if {@link #ADD_MISSING_ANNOTATIONS} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ADD_MISSING_ANNOTATIONS_DEPRECATED= "cleanup.add_missing_deprecated_annotations"; //$NON-NLS-1$
@@ -970,11 +1044,11 @@
* {@link #ADD_MISSING_SERIAL_VERSION_ID_GENERATED}
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ADD_MISSING_SERIAL_VERSION_ID= "cleanup.add_serial_version_id"; //$NON-NLS-1$
@@ -982,17 +1056,17 @@
/**
* Adds a generated serial version id to subtypes of java.io.Serializable and
* java.io.Externalizable
- *
+ *
* public class E implements Serializable {} -> public class E implements Serializable { private
* static final long serialVersionUID = 4381024239L; }
* Only has an effect if {@link #ADD_MISSING_SERIAL_VERSION_ID} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ADD_MISSING_SERIAL_VERSION_ID_GENERATED= "cleanup.add_generated_serial_version_id"; //$NON-NLS-1$
@@ -1000,51 +1074,64 @@
/**
* Adds a default serial version it to subtypes of java.io.Serializable and
* java.io.Externalizable
- *
+ *
* public class E implements Serializable {} -> public class E implements Serializable { private
* static final long serialVersionUID = 1L; }
* Only has an effect if {@link #ADD_MISSING_SERIAL_VERSION_ID} is TRUE
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ADD_MISSING_SERIAL_VERSION_ID_DEFAULT= "cleanup.add_default_serial_version_id"; //$NON-NLS-1$
/**
+ * Controls whether long literal suffix should be rewritten in uppercase.
+ *
+ * Possible values: {TRUE, FALSE}
+ *
+ *
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
+ * @since 4.13
+ */
+ public static final String NUMBER_SUFFIX= "cleanup.number_suffix"; //$NON-NLS-1$
+
+ /**
* Add '$NON-NLS$' tags to non externalized strings.
*
* Example:
- *
+ *
*
* String s= ""; -> String s= ""; //$NON-NLS-1$
*
- *
+ *
*
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ADD_MISSING_NLS_TAGS= "cleanup.add_missing_nls_tags"; //$NON-NLS-1$
/**
* If true the imports are organized while cleaning up code.
- *
+ *
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String ORGANIZE_IMPORTS= "cleanup.organize_imports"; //$NON-NLS-1$
@@ -1053,12 +1140,12 @@
* Should members be sorted?
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
+ *
* @see #SORT_MEMBERS_ALL
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String SORT_MEMBERS= "cleanup.sort_members"; //$NON-NLS-1$
@@ -1068,12 +1155,12 @@
* This has only an effect if {@link #SORT_MEMBERS} is also enabled.
*
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
+ *
* @see #SORT_MEMBERS
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.3
*/
public static final String SORT_MEMBERS_ALL= "cleanup.sort_members_all"; //$NON-NLS-1$
@@ -1082,11 +1169,11 @@
* If enabled method stubs are added to all non abstract classes which require to implement some
* methods.
* Possible values: {TRUE, FALSE}
- *
+ *
*
- *
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
+ *
+ * @see CleanUpOptionsCore#TRUE
+ * @see CleanUpOptionsCore#FALSE
* @since 3.4
*/
public static final String ADD_MISSING_METHODES= "cleanup.add_missing_methods"; //$NON-NLS-1$
@@ -1097,7 +1184,7 @@
* Possible values: {true
, false
}
* Default value: true
*
- *
+ *
* @since 3.3
*/
public static final String SHOW_CLEAN_UP_WIZARD= "cleanup.showwizard"; //$NON-NLS-1$
@@ -1105,11 +1192,11 @@
/**
* A key to a serialized string in the InstanceScope
containing all the profiles.
* Following code snippet can load the profiles:
- *
+ *
*
* List profiles= new ProfileStore(CLEANUP_PROFILES, new CleanUpVersioner()).readProfiles(InstanceScope.INSTANCE);
*
- *
+ *
* @since 3.3
*/
public static final String CLEANUP_PROFILES= "org.eclipse.jdt.ui.cleanupprofiles"; //$NON-NLS-1$
@@ -1119,7 +1206,7 @@
*
* Possible values: String value
* Default value: {@link #DEFAULT_PROFILE}
- *
+ *
* @since 3.3
*/
public final static String CLEANUP_PROFILE= "cleanup_profile"; //$NON-NLS-1$$
@@ -1129,7 +1216,7 @@
*
* Possible values: String value
* Default value: {@link #DEFAULT_SAVE_PARTICIPANT_PROFILE}
- *
+ *
* @since 3.3
*/
public static final String CLEANUP_ON_SAVE_PROFILE= "cleanup.on_save_profile_id"; //$NON-NLS-1$
@@ -1139,7 +1226,7 @@
*
* Possible values: Integer value
* Default value: CleanUpProfileVersioner#CURRENT_VERSION
- *
+ *
* @since 3.3
*/
public final static String CLEANUP_SETTINGS_VERSION_KEY= "cleanup_settings_version"; //$NON-NLS-1$
@@ -1147,7 +1234,7 @@
/**
* Id of the 'Eclipse [built-in]' profile.
*
- *
+ *
* @since 3.3
*/
public final static String ECLIPSE_PROFILE= "org.eclipse.jdt.ui.default.eclipse_clean_up_profile"; //$NON-NLS-1$
@@ -1155,7 +1242,7 @@
/**
* Id of the 'Save Participant [built-in]' profile.
*
- *
+ *
* @since 3.3
*/
public final static String SAVE_PARTICIPANT_PROFILE= "org.eclipse.jdt.ui.default.save_participant_clean_up_profile"; //$NON-NLS-1$
@@ -1167,7 +1254,7 @@
*
* Possible values: String value
* Default value: {@link #ECLIPSE_PROFILE}
- *
+ *
* @since 3.3
*/
public final static String DEFAULT_PROFILE= ECLIPSE_PROFILE;
@@ -1177,7 +1264,7 @@
*
* Possible values: String value
* Default value: {@link #SAVE_PARTICIPANT_PROFILE}
- *
+ *
* @since 3.3
*/
public final static String DEFAULT_SAVE_PARTICIPANT_PROFILE= SAVE_PARTICIPANT_PROFILE;
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpContext.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpContext.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpContext.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpContext.java" 1970-01-01 00:00:00.000000000 +0000
@@ -1,73 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2018 IBM Corporation and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- * Red Hat Inc. - refactored to jdt.core.manipultion
- *******************************************************************************/
-package org.eclipse.jdt.internal.corext.fix;
-
-import org.eclipse.core.runtime.Assert;
-
-import org.eclipse.jdt.core.ICompilationUnit;
-import org.eclipse.jdt.core.dom.CompilationUnit;
-
-
-/**
- * The context that contains all information required by a clean up to create a fix.
- *
- * @since 3.5
- */
-public class CleanUpContext {
-
- private final ICompilationUnit fUnit;
-
- private final CompilationUnit fAst;
-
- /**
- * Creates a new clean up context.
- *
- * @param unit the compilation unit
- * @param ast the AST, can be null
if {@link CleanUpRequirements#requiresAST()}
- * returns false
. The AST is guaranteed to contain changes made by
- * previous clean ups only if {@link CleanUpRequirements#requiresFreshAST()} returns
- * true
.
- */
- public CleanUpContext(ICompilationUnit unit, CompilationUnit ast) {
- Assert.isLegal(unit != null);
- fUnit= unit;
- fAst= ast;
- }
-
- /**
- * The compilation unit to clean up.
- *
- * @return the compilation unit to clean up
- */
- public ICompilationUnit getCompilationUnit() {
- return fUnit;
- }
-
- /**
- * An AST built from the compilation unit to fix.
- *
- * Can be null
if {@link CleanUpRequirements#requiresAST()} returns
- * false
. The AST is guaranteed to contain changes made by previous clean ups only
- * if {@link CleanUpRequirements#requiresFreshAST()} returns true
.
- *
- * Clients should check the AST API level and do nothing if they are given an AST
- * they can't handle (see {@link org.eclipse.jdt.core.dom.AST#apiLevel()}).
- *
- * @return an AST or null
if none required
- */
- public CompilationUnit getAST() {
- return fAst;
- }
-}
\ No newline at end of file
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpOptions.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpOptions.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpOptions.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpOptions.java" 1970-01-01 00:00:00.000000000 +0000
@@ -1,115 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2018 IBM Corporation and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- * Red Hat Inc. - refactored to jdt.core.manipulation
- *******************************************************************************/
-package org.eclipse.jdt.internal.corext.fix;
-
-import java.util.Collections;
-import java.util.Hashtable;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.core.runtime.Assert;
-
-import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
-
-
-/**
- * Allows to set and retrieve clean up settings for given options keys.
- *
- * @since 3.5
- * @noextend This class is not intended to be subclassed by clients.
- */
-public class CleanUpOptions {
-
- private final Map fOptions;
-
- /**
- * True value
- */
- public static final String TRUE= "true"; //$NON-NLS-1$
-
- /**
- * False value
- */
- public static final String FALSE= "false"; //$NON-NLS-1$
-
- /**
- * Creates a new CleanUpOptions instance with the given options.
- *
- * @param options map that maps clean ups keys (String
) to a non-null
- * string value
- */
- public CleanUpOptions(Map options) {
- fOptions= options;
- }
-
- /**
- * Creates a new instance.
- */
- public CleanUpOptions() {
- fOptions= new Hashtable<>();
- }
-
- /**
- * Tells whether the option with the given key
is enabled.
- *
- * @param key the name of the option
- * @return true
if enabled, false
if not enabled or unknown key
- * @throws IllegalArgumentException if the key is null
- * @see CleanUpConstants
- */
- public boolean isEnabled(String key) {
- Assert.isLegal(key != null);
- Object value= fOptions.get(key);
- return CleanUpOptions.TRUE == value || CleanUpOptions.TRUE.equals(value);
- }
-
- /**
- * Returns the value for the given key.
- *
- * @param key the key of the value
- * @return the value associated with the key
- * @throws IllegalArgumentException if the key is null or unknown
- */
- public String getValue(String key) {
- Assert.isLegal(key != null);
- String value= fOptions.get(key);
- Assert.isLegal(value != null);
- return value;
- }
-
- /**
- * Sets the option for the given key to the given value.
- *
- * @param key the name of the option to set
- * @param value the value of the option
- * @throws IllegalArgumentException if the key is null
- * @see CleanUpOptions#TRUE
- * @see CleanUpOptions#FALSE
- */
- public void setOption(String key, String value) {
- Assert.isLegal(key != null);
- Assert.isLegal(value != null);
- fOptions.put(key, value);
- }
-
- /**
- * Returns an unmodifiable set of all known keys.
- *
- * @return an unmodifiable set of all keys
- */
- public Set getKeys() {
- return Collections.unmodifiableSet(fOptions.keySet());
- }
-}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRequirements.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRequirements.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRequirements.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRequirements.java" 1970-01-01 00:00:00.000000000 +0000
@@ -1,113 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2018 IBM Corporation and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- * Red Hat Inc. - refactored to jdt.core.manipulation
- *******************************************************************************/
-package org.eclipse.jdt.internal.corext.fix;
-
-import java.util.Map;
-
-import org.eclipse.core.runtime.Assert;
-
-import org.eclipse.jdt.core.JavaCore;
-
-
-/**
- * Specifies the requirements of a clean up.
- *
- * @since 3.5
- */
-public final class CleanUpRequirements {
-
- private final boolean fRequiresAST;
-
- private final Map fCompilerOptions;
-
- private final boolean fRequiresFreshAST;
-
- private final boolean fRequiresChangedRegions;
-
-
- /**
- * Create a new instance
- *
- * @param requiresAST true
if an AST is required
- * @param requiresFreshAST true
if a fresh AST is required
- * @param requiresChangedRegions true
if changed regions are required
- * @param compilerOptions map of compiler options or null
if no requirements
- */
- public CleanUpRequirements(boolean requiresAST, boolean requiresFreshAST, boolean requiresChangedRegions, Map compilerOptions) {
- Assert.isLegal(!requiresFreshAST || requiresAST, "Must not request fresh AST if no AST is required"); //$NON-NLS-1$
- Assert.isLegal(compilerOptions == null || requiresAST, "Must not provide options if no AST is required"); //$NON-NLS-1$
- fRequiresAST= requiresAST;
- fRequiresFreshAST= requiresFreshAST;
- fRequiresChangedRegions= requiresChangedRegions;
-
- fCompilerOptions= compilerOptions;
- // Make sure that compile warnings are not suppressed since some clean ups work on reported warnings
- if (fCompilerOptions != null)
- fCompilerOptions.put(JavaCore.COMPILER_PB_SUPPRESS_WARNINGS, JavaCore.DISABLED);
- }
-
- /**
- * Tells whether the clean up requires an AST.
- *
- * Note: This should return false
whenever possible because
- * creating an AST is expensive.
- *
- *
- * @return true
if the {@linkplain CleanUpContext context} must provide an AST
- */
- public boolean requiresAST() {
- return fRequiresAST;
- }
-
- /**
- * Tells whether a fresh AST, containing all the changes from previous clean ups, will be
- * needed.
- *
- * @return true
if the caller needs an up to date AST
- */
- public boolean requiresFreshAST() {
- return fRequiresFreshAST;
- }
-
- /**
- * Required compiler options.
- *
- * @return the compiler options map or null
if none
- * @see JavaCore
- */
- public Map getCompilerOptions() {
- return fCompilerOptions;
- }
-
- /**
- * Tells whether this clean up requires to be informed about changed regions. The changed regions are the
- * regions which have been changed between the last save state of the compilation unit and its
- * current state.
- *
- * Has only an effect if the clean up is used as save action.
- *
- *
- * Note:: This should return false
whenever possible because
- * calculating the changed regions is expensive.
- *
- *
- * @return true
if the {@linkplain CleanUpContext context} must provide changed
- * regions
- */
- public boolean requiresChangedRegions() {
- return fRequiresChangedRegions;
- }
-
-}
\ No newline at end of file
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CodeStyleFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CodeStyleFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CodeStyleFixCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CodeStyleFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,813 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - created core version based on CodeStyleFix
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.Initializer;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SwitchCase;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore;
+import org.eclipse.jdt.internal.ui.text.correction.ProblemLocationCore;
+
+/**
+ * A fix which fixes code style issues.
+ */
+public class CodeStyleFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ public final static class CodeStyleVisitor extends GenericVisitor {
+
+ private final List fResult;
+ private final ImportRewrite fImportRewrite;
+ private final boolean fFindUnqualifiedAccesses;
+ private final boolean fFindUnqualifiedStaticAccesses;
+ private final boolean fFindUnqualifiedMethodAccesses;
+ private final boolean fFindUnqualifiedStaticMethodAccesses;
+
+ public CodeStyleVisitor(CompilationUnit compilationUnit,
+ boolean findUnqualifiedAccesses,
+ boolean findUnqualifiedStaticAccesses,
+ boolean findUnqualifiedMethodAccesses,
+ boolean findUnqualifiedStaticMethodAccesses,
+ List resultingCollection) {
+
+ fFindUnqualifiedAccesses= findUnqualifiedAccesses;
+ fFindUnqualifiedStaticAccesses= findUnqualifiedStaticAccesses;
+ fFindUnqualifiedMethodAccesses= findUnqualifiedMethodAccesses;
+ fFindUnqualifiedStaticMethodAccesses= findUnqualifiedStaticMethodAccesses;
+ fImportRewrite= StubUtility.createImportRewrite(compilationUnit, true);
+ fResult= resultingCollection;
+ }
+
+ @Override
+ public boolean visit(TypeDeclaration node) {
+ if (!fFindUnqualifiedStaticAccesses && !fFindUnqualifiedStaticMethodAccesses
+ && !fFindUnqualifiedAccesses && !fFindUnqualifiedMethodAccesses && node.isInterface())
+ return false;
+
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(QualifiedName node) {
+ if (fFindUnqualifiedAccesses || fFindUnqualifiedStaticAccesses) {
+ ASTNode simpleName= node;
+ while (simpleName instanceof QualifiedName) {
+ simpleName= ((QualifiedName) simpleName).getQualifier();
+ }
+ if (simpleName instanceof SimpleName) {
+ handleSimpleName((SimpleName)simpleName);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(SimpleName node) {
+ if (fFindUnqualifiedAccesses || fFindUnqualifiedStaticAccesses) {
+ handleSimpleName(node);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ if (!fFindUnqualifiedMethodAccesses && !fFindUnqualifiedStaticMethodAccesses)
+ return true;
+
+ if (node.getExpression() != null)
+ return true;
+
+ IBinding binding= node.getName().resolveBinding();
+ if (!(binding instanceof IMethodBinding))
+ return true;
+
+ handleMethod(node.getName(), (IMethodBinding)binding);
+ return true;
+ }
+
+ private void handleSimpleName(SimpleName node) {
+ ASTNode firstExpression= node.getParent();
+ if (firstExpression instanceof FieldAccess) {
+ while (firstExpression instanceof FieldAccess) {
+ firstExpression= ((FieldAccess)firstExpression).getExpression();
+ }
+ if (!(firstExpression instanceof SimpleName))
+ return;
+
+ node= (SimpleName)firstExpression;
+ } else if (firstExpression instanceof SuperFieldAccess)
+ return;
+
+ StructuralPropertyDescriptor parentDescription= node.getLocationInParent();
+ if (parentDescription == VariableDeclarationFragment.NAME_PROPERTY || parentDescription == SwitchCase.EXPRESSION_PROPERTY || parentDescription == SwitchCase.EXPRESSIONS2_PROPERTY)
+ return;
+
+ IBinding binding= node.resolveBinding();
+ if (!(binding instanceof IVariableBinding))
+ return;
+
+ handleVariable(node, (IVariableBinding) binding);
+ }
+
+ private void handleVariable(SimpleName node, IVariableBinding varbinding) {
+ if (!varbinding.isField())
+ return;
+
+ if (varbinding.isEnumConstant())
+ return;
+
+ ITypeBinding declaringClass= varbinding.getDeclaringClass();
+ if (Modifier.isStatic(varbinding.getModifiers())) {
+ if (fFindUnqualifiedStaticAccesses) {
+ Initializer initializer= ASTNodes.getParent(node, Initializer.class);
+ //Do not qualify assignments to static final fields in static initializers (would result in compile error)
+ StructuralPropertyDescriptor parentDescription= node.getLocationInParent();
+ if (initializer != null && Modifier.isStatic(initializer.getModifiers())
+ && Modifier.isFinal(varbinding.getModifiers()) && parentDescription == Assignment.LEFT_HAND_SIDE_PROPERTY)
+ return;
+
+ //Do not qualify static fields if defined inside an anonymous class
+ if (declaringClass.isAnonymous())
+ return;
+
+ fResult.add(new AddStaticQualifierOperation(declaringClass, node));
+ }
+ } else if (fFindUnqualifiedAccesses){
+ String qualifier= getThisExpressionQualifier(declaringClass, fImportRewrite, node);
+ if (qualifier == null)
+ return;
+
+ if (qualifier.length() == 0)
+ qualifier= null;
+
+ fResult.add(new AddThisQualifierOperation(qualifier, node));
+ }
+ }
+
+ private void handleMethod(SimpleName node, IMethodBinding binding) {
+ ITypeBinding declaringClass= binding.getDeclaringClass();
+ if (Modifier.isStatic(binding.getModifiers())) {
+ if (fFindUnqualifiedStaticMethodAccesses) {
+ //Do not qualify static fields if defined inside an anonymous class
+ if (declaringClass.isAnonymous())
+ return;
+
+ fResult.add(new AddStaticQualifierOperation(declaringClass, node));
+ }
+ } else {
+ if (fFindUnqualifiedMethodAccesses) {
+ String qualifier= getThisExpressionQualifier(declaringClass, fImportRewrite, node);
+ if (qualifier == null)
+ return;
+
+ if (qualifier.length() == 0)
+ qualifier= null;
+
+ fResult.add(new AddThisQualifierOperation(qualifier, node));
+ }
+ }
+ }
+ }
+
+ public static class ThisQualifierVisitor extends GenericVisitor {
+
+ private final CompilationUnit fCompilationUnit;
+ private final List fOperations;
+ private final boolean fRemoveFieldQualifiers;
+ private final boolean fRemoveMethodQualifiers;
+
+ public ThisQualifierVisitor(boolean removeFieldQualifiers,
+ boolean removeMethodQualifiers,
+ CompilationUnit compilationUnit,
+ List result) {
+ fRemoveFieldQualifiers= removeFieldQualifiers;
+ fRemoveMethodQualifiers= removeMethodQualifiers;
+ fCompilationUnit= compilationUnit;
+ fOperations= result;
+ }
+
+ @Override
+ public boolean visit(final FieldAccess node) {
+ if (!fRemoveFieldQualifiers)
+ return true;
+
+ Expression expression= node.getExpression();
+ if (!(expression instanceof ThisExpression))
+ return true;
+
+ final SimpleName name= node.getName();
+ if (hasConflict(expression.getStartPosition(), name, ScopeAnalyzer.VARIABLES | ScopeAnalyzer.CHECK_VISIBILITY))
+ return true;
+
+ Name qualifier= ((ThisExpression) expression).getQualifier();
+ if (qualifier != null) {
+ ITypeBinding outerClass= (ITypeBinding) qualifier.resolveBinding();
+ if (outerClass == null)
+ return true;
+
+ IVariableBinding nameBinding= (IVariableBinding) name.resolveBinding();
+ if (nameBinding == null)
+ return true;
+
+ ITypeBinding variablesDeclaringClass= nameBinding.getDeclaringClass();
+ if (outerClass != variablesDeclaringClass)
+ //be conservative: We have a reference to a field of an outer type, and this type inherited
+ //the field. It's possible that the inner type inherits the same field. We must not remove
+ //the qualifier in this case.
+ return true;
+
+ ITypeBinding enclosingTypeBinding= Bindings.getBindingOfParentType(node);
+ if (enclosingTypeBinding == null || Bindings.isSuperType(variablesDeclaringClass, enclosingTypeBinding))
+ //We have a reference to a field of an outer type, and this type inherited
+ //the field. The inner type inherits the same field. We must not remove
+ //the qualifier in this case.
+ return true;
+ }
+
+ fOperations.add(new CompilationUnitRewriteOperation() {
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ TextEditGroup group= createTextEditGroup(FixMessages.CodeStyleFix_removeThis_groupDescription, cuRewrite);
+ rewrite.replace(node, rewrite.createCopyTarget(name), group);
+ }
+ });
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(final MethodInvocation node) {
+ if (!fRemoveMethodQualifiers)
+ return true;
+
+ Expression expression= node.getExpression();
+ if (!(expression instanceof ThisExpression))
+ return true;
+
+ final SimpleName name= node.getName();
+ if (name.resolveBinding() == null)
+ return true;
+
+ if (hasConflict(expression.getStartPosition(), name, ScopeAnalyzer.METHODS | ScopeAnalyzer.CHECK_VISIBILITY))
+ return true;
+
+ Name qualifier= ((ThisExpression)expression).getQualifier();
+ if (qualifier != null) {
+ ITypeBinding declaringClass= ((IMethodBinding)name.resolveBinding()).getDeclaringClass();
+ if (declaringClass == null)
+ return true;
+
+ ITypeBinding caller= getDeclaringType(node);
+ if (caller == null)
+ return true;
+
+ ITypeBinding callee= (ITypeBinding)qualifier.resolveBinding();
+ if (callee == null)
+ return true;
+
+ if (callee.isAssignmentCompatible(declaringClass) && caller.isAssignmentCompatible(declaringClass))
+ return true;
+ }
+
+ fOperations.add(new CompilationUnitRewriteOperation() {
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ TextEditGroup group= createTextEditGroup(FixMessages.CodeStyleFix_removeThis_groupDescription, cuRewrite);
+ rewrite.remove(node.getExpression(), group);
+ }
+ });
+ return super.visit(node);
+ }
+
+ private ITypeBinding getDeclaringType(MethodInvocation node) {
+ ASTNode p= node;
+ while (p != null) {
+ p= p.getParent();
+ if (p instanceof AbstractTypeDeclaration) {
+ return ((AbstractTypeDeclaration)p).resolveBinding();
+ }
+ }
+ return null;
+ }
+
+ private boolean hasConflict(int startPosition, SimpleName name, int flag) {
+ ScopeAnalyzer analyzer= new ScopeAnalyzer(fCompilationUnit);
+ IBinding[] declarationsInScope= analyzer.getDeclarationsInScope(startPosition, flag);
+ for (int i= 0; i < declarationsInScope.length; i++) {
+ IBinding decl= declarationsInScope[i];
+ if (decl.getName().equals(name.getIdentifier()) && name.resolveBinding() != decl)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public final static class AddThisQualifierOperation extends CompilationUnitRewriteOperation {
+
+ private final String fQualifier;
+ private final SimpleName fName;
+
+ public AddThisQualifierOperation(String qualifier, SimpleName name) {
+ fQualifier= qualifier;
+ fName= name;
+ }
+
+ public String getDescription() {
+ String nameLabel= BasicElementLabels.getJavaElementName(fName.getIdentifier());
+ String qualifierLabel;
+ if (fQualifier == null) {
+ qualifierLabel= "this"; //$NON-NLS-1$
+ } else {
+ qualifierLabel= BasicElementLabels.getJavaElementName(fQualifier + ".this"); //$NON-NLS-1$
+ }
+
+ return Messages.format(FixMessages.CodeStyleFix_QualifyWithThis_description, new Object[] {nameLabel, qualifierLabel});
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ TextEditGroup group= createTextEditGroup(getDescription(), cuRewrite);
+ AST ast= rewrite.getAST();
+
+ FieldAccess fieldAccess= ast.newFieldAccess();
+
+ ThisExpression thisExpression= ast.newThisExpression();
+ if (fQualifier != null)
+ thisExpression.setQualifier(ast.newName(fQualifier));
+
+ fieldAccess.setExpression(thisExpression);
+ fieldAccess.setName((SimpleName) rewrite.createMoveTarget(fName));
+
+ rewrite.replace(fName, fieldAccess, group);
+ }
+ }
+
+ public final static class AddStaticQualifierOperation extends CompilationUnitRewriteOperation {
+
+ private final SimpleName fName;
+ private final ITypeBinding fDeclaringClass;
+
+ public AddStaticQualifierOperation(ITypeBinding declaringClass, SimpleName name) {
+ super();
+ fDeclaringClass= declaringClass;
+ fName= name;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ CompilationUnit compilationUnit= cuRewrite.getRoot();
+ importType(fDeclaringClass, fName, cuRewrite.getImportRewrite(), compilationUnit, TypeLocation.OTHER);
+ TextEditGroup group;
+ if (fName.resolveBinding() instanceof IMethodBinding) {
+ group= createTextEditGroup(FixMessages.CodeStyleFix_QualifyMethodWithDeclClass_description, cuRewrite);
+ } else {
+ group= createTextEditGroup(FixMessages.CodeStyleFix_QualifyFieldWithDeclClass_description, cuRewrite);
+ }
+ IJavaElement javaElement= fDeclaringClass.getJavaElement();
+ if (javaElement instanceof IType) {
+ Name qualifierName= compilationUnit.getAST().newName(((IType)javaElement).getElementName());
+ SimpleName simpleName= (SimpleName)rewrite.createMoveTarget(fName);
+ QualifiedName qualifiedName= compilationUnit.getAST().newQualifiedName(qualifierName, simpleName);
+ rewrite.replace(fName, qualifiedName, group);
+ }
+ }
+
+ }
+
+ public final static class ToStaticAccessOperation extends CompilationUnitRewriteOperation {
+
+ private final ITypeBinding fDeclaringTypeBinding;
+ private final Expression fQualifier;
+ private final HashMap fCreatedBlocks;
+
+ public ToStaticAccessOperation(ITypeBinding declaringTypeBinding, Expression qualifier, HashMap createdBlocks) {
+ fDeclaringTypeBinding= declaringTypeBinding;
+ fQualifier= qualifier;
+ fCreatedBlocks= createdBlocks;
+ }
+
+ public String getAccessorName() {
+ return fDeclaringTypeBinding.getName();
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ TextEditGroup group= createTextEditGroup(FixMessages.CodeStyleFix_ChangeAccessUsingDeclaring_description, cuRewrite);
+
+ if (fQualifier instanceof MethodInvocation || fQualifier instanceof ClassInstanceCreation)
+ extractQualifier(fQualifier, cuRewrite, group);
+
+ Type type= importType(fDeclaringTypeBinding, fQualifier, cuRewrite.getImportRewrite(), cuRewrite.getRoot(), TypeLocation.UNKNOWN);
+ cuRewrite.getASTRewrite().replace(fQualifier, type, group);
+ }
+
+ private void extractQualifier(Expression qualifier, CompilationUnitRewrite cuRewrite, TextEditGroup group) {
+ Statement statement= ASTResolving.findParentStatement(qualifier);
+ if (statement == null)
+ return;
+
+ ASTRewrite astRewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getAST();
+
+ Expression expression= (Expression) astRewrite.createMoveTarget(qualifier);
+ ExpressionStatement newStatement= ast.newExpressionStatement(expression);
+
+ if (statement.getParent() instanceof Block) {
+ Block block= (Block) statement.getParent();
+ ListRewrite listRewrite= astRewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
+
+ listRewrite.insertBefore(newStatement, statement, group);
+ } else {
+ Block block;
+ if (fCreatedBlocks.containsKey(statement.getParent())) {
+ block= fCreatedBlocks.get(statement.getParent());
+ } else {
+ block= ast.newBlock();
+ }
+
+ ListRewrite listRewrite= astRewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
+
+ ASTNode lastStatement;
+ if (!fCreatedBlocks.containsKey(statement.getParent())) {
+ fCreatedBlocks.put(statement.getParent(), block);
+
+ lastStatement= astRewrite.createMoveTarget(statement);
+ listRewrite.insertLast(lastStatement, group);
+
+ ASTNode parent= statement.getParent();
+ astRewrite.set(parent, statement.getLocationInParent(), block, group);
+ } else {
+ List> rewrittenList= listRewrite.getRewrittenList();
+ lastStatement= (ASTNode) rewrittenList.get(rewrittenList.size() - 1);
+ }
+
+ listRewrite.insertBefore(newStatement, lastStatement, group);
+ }
+ }
+ }
+
+ public static CompilationUnitRewriteOperationsFixCore[] createNonStaticAccessFixes(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ if (!isNonStaticAccess(problem))
+ return null;
+
+ ToStaticAccessOperation operations[]= createToStaticAccessOperations(compilationUnit, new HashMap(), problem, false);
+ if (operations == null)
+ return null;
+
+ String label1= Messages.format(FixMessages.CodeStyleFix_ChangeAccessToStatic_description, operations[0].getAccessorName());
+ CompilationUnitRewriteOperationsFixCore fix1= new CompilationUnitRewriteOperationsFixCore(label1, compilationUnit, operations[0]);
+
+ if (operations.length > 1) {
+ String label2= Messages.format(FixMessages.CodeStyleFix_ChangeAccessToStaticUsingInstanceType_description, operations[1].getAccessorName());
+ CompilationUnitRewriteOperationsFixCore fix2= new CompilationUnitRewriteOperationsFixCore(label2, compilationUnit, operations[1]);
+ return new CompilationUnitRewriteOperationsFixCore[] {fix1, fix2};
+ }
+ return new CompilationUnitRewriteOperationsFixCore[] {fix1};
+ }
+
+ public static CompilationUnitRewriteOperationsFixCore createAddFieldQualifierFix(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ if (IProblem.UnqualifiedFieldAccess != problem.getProblemId())
+ return null;
+
+ AddThisQualifierOperation operation= getUnqualifiedFieldAccessResolveOperation(compilationUnit, problem);
+ if (operation == null)
+ return null;
+
+ String groupName= operation.getDescription();
+ return new CodeStyleFixCore(groupName, compilationUnit, new CompilationUnitRewriteOperation[] {operation});
+ }
+
+ public static CompilationUnitRewriteOperationsFixCore createIndirectAccessToStaticFix(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ if (!isIndirectStaticAccess(problem))
+ return null;
+
+ ToStaticAccessOperation operations[]= createToStaticAccessOperations(compilationUnit, new HashMap(), problem, false);
+ if (operations == null)
+ return null;
+
+ String label= Messages.format(FixMessages.CodeStyleFix_ChangeStaticAccess_description, operations[0].getAccessorName());
+ return new CodeStyleFixCore(label, compilationUnit, new CompilationUnitRewriteOperation[] {operations[0]});
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit,
+ boolean addThisQualifier,
+ boolean changeNonStaticAccessToStatic,
+ boolean qualifyStaticFieldAccess,
+ boolean changeIndirectStaticAccessToDirect,
+ boolean qualifyMethodAccess,
+ boolean qualifyStaticMethodAccess,
+ boolean removeFieldQualifier,
+ boolean removeMethodQualifier) {
+
+ if (!addThisQualifier && !changeNonStaticAccessToStatic && !qualifyStaticFieldAccess && !changeIndirectStaticAccessToDirect && !qualifyMethodAccess && !qualifyStaticMethodAccess && !removeFieldQualifier && !removeMethodQualifier)
+ return null;
+
+ List operations= new ArrayList<>();
+ if (addThisQualifier || qualifyStaticFieldAccess || qualifyMethodAccess || qualifyStaticMethodAccess) {
+ CodeStyleVisitor codeStyleVisitor= new CodeStyleVisitor(compilationUnit, addThisQualifier, qualifyStaticFieldAccess, qualifyMethodAccess, qualifyStaticMethodAccess, operations);
+ compilationUnit.accept(codeStyleVisitor);
+ }
+
+ IProblem[] problems= compilationUnit.getProblems();
+ IProblemLocationCore[] locations= new IProblemLocationCore[problems.length];
+ for (int i= 0; i < problems.length; i++) {
+ locations[i]= new ProblemLocationCore(problems[i]);
+ }
+ addToStaticAccessOperations(compilationUnit, locations, changeNonStaticAccessToStatic, changeIndirectStaticAccessToDirect, operations);
+
+ if (removeFieldQualifier || removeMethodQualifier) {
+ ThisQualifierVisitor visitor= new ThisQualifierVisitor(removeFieldQualifier, removeMethodQualifier, compilationUnit, operations);
+ compilationUnit.accept(visitor);
+ }
+
+ if (operations.isEmpty())
+ return null;
+
+ CompilationUnitRewriteOperation[] operationsArray= operations.toArray(new CompilationUnitRewriteOperation[operations.size()]);
+ return new CodeStyleFixCore(FixMessages.CodeStyleFix_change_name, compilationUnit, operationsArray);
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, IProblemLocationCore[] problems,
+ boolean addThisQualifier,
+ boolean changeNonStaticAccessToStatic,
+ boolean changeIndirectStaticAccessToDirect) {
+
+ if (!addThisQualifier && !changeNonStaticAccessToStatic && !changeIndirectStaticAccessToDirect)
+ return null;
+
+ List operations= new ArrayList<>();
+ if (addThisQualifier) {
+ for (int i= 0; i < problems.length; i++) {
+ IProblemLocationCore problem= problems[i];
+ if (problem.getProblemId() == IProblem.UnqualifiedFieldAccess) {
+ AddThisQualifierOperation operation= getUnqualifiedFieldAccessResolveOperation(compilationUnit, problem);
+ if (operation != null)
+ operations.add(operation);
+ }
+ }
+ }
+
+ addToStaticAccessOperations(compilationUnit, problems, changeNonStaticAccessToStatic, changeIndirectStaticAccessToDirect, operations);
+
+ if (operations.isEmpty())
+ return null;
+
+ CompilationUnitRewriteOperation[] operationsArray= operations.toArray(new CompilationUnitRewriteOperation[operations.size()]);
+ return new CodeStyleFixCore(FixMessages.CodeStyleFix_change_name, compilationUnit, operationsArray);
+ }
+
+ public static void addToStaticAccessOperations(CompilationUnit compilationUnit, IProblemLocationCore[] problems, boolean changeNonStaticAccessToStatic, boolean changeIndirectStaticAccessToDirect, List result) {
+ if (!changeNonStaticAccessToStatic && !changeIndirectStaticAccessToDirect)
+ return;
+
+ List operations= new ArrayList<>();
+ HashMap createdBlocks= new HashMap<>();
+ for (int i= 0; i < problems.length; i++) {
+ IProblemLocationCore problem= problems[i];
+ boolean isNonStaticAccess= changeNonStaticAccessToStatic && isNonStaticAccess(problem);
+ boolean isIndirectStaticAccess= changeIndirectStaticAccessToDirect && isIndirectStaticAccess(problem);
+ if (isNonStaticAccess || isIndirectStaticAccess) {
+ ToStaticAccessOperation[] nonStaticAccessInformation= createToStaticAccessOperations(compilationUnit, createdBlocks, problem, true);
+ if (nonStaticAccessInformation != null) {
+ ToStaticAccessOperation op= nonStaticAccessInformation[0];
+
+ Expression qualifier= op.fQualifier;
+ for (Iterator it= result.iterator(); it.hasNext();) { // see bug 346230
+ CompilationUnitRewriteOperation oper= it.next();
+ if (oper instanceof CodeStyleFixCore.AddThisQualifierOperation
+ && ((CodeStyleFixCore.AddThisQualifierOperation) oper).fName.equals(qualifier)) {
+ result.remove(oper);
+ break;
+ }
+ }
+ operations.add(op);
+ }
+ }
+ }
+ // Make sure qualifiers are processed inside-out and left-to-right, so that
+ // ToStaticAccessOperation#extractQualifier(..) extracts qualifiers in execution order:
+ Collections.sort(operations, new Comparator() {
+ @Override
+ public int compare(ToStaticAccessOperation o1, ToStaticAccessOperation o2) {
+ if (ASTNodes.isParent(o1.fQualifier, o2.fQualifier)) {
+ return -1;
+ } else if (ASTNodes.isParent(o2.fQualifier, o1.fQualifier)) {
+ return 1;
+ } else {
+ return o1.fQualifier.getStartPosition() - o2.fQualifier.getStartPosition();
+ }
+ }
+ });
+ result.addAll(operations);
+ }
+
+ public static boolean isIndirectStaticAccess(IProblemLocationCore problem) {
+ return (problem.getProblemId() == IProblem.IndirectAccessToStaticField
+ || problem.getProblemId() == IProblem.IndirectAccessToStaticMethod);
+ }
+
+ public static boolean isNonStaticAccess(IProblemLocationCore problem) {
+ return (problem.getProblemId() == IProblem.NonStaticAccessToStaticField
+ || problem.getProblemId() == IProblem.NonStaticAccessToStaticMethod
+ || problem.getProblemId() == IProblem.NonStaticOrAlienTypeReceiver);
+ }
+
+ public static ToStaticAccessOperation[] createToStaticAccessOperations(CompilationUnit astRoot, HashMap createdBlocks, IProblemLocationCore problem, boolean conservative) {
+ ASTNode selectedNode= problem.getCoveringNode(astRoot);
+ if (selectedNode == null) {
+ return null;
+ }
+
+ Expression qualifier= null;
+ IBinding accessBinding= null;
+
+ if (selectedNode instanceof SimpleName) {
+ selectedNode= selectedNode.getParent();
+ }
+ if (selectedNode instanceof QualifiedName) {
+ QualifiedName name= (QualifiedName) selectedNode;
+ qualifier= name.getQualifier();
+ accessBinding= name.resolveBinding();
+ } else if (selectedNode instanceof MethodInvocation) {
+ MethodInvocation methodInvocation= (MethodInvocation) selectedNode;
+ qualifier= methodInvocation.getExpression();
+ accessBinding= methodInvocation.getName().resolveBinding();
+ } else if (selectedNode instanceof FieldAccess) {
+ FieldAccess fieldAccess= (FieldAccess) selectedNode;
+ qualifier= fieldAccess.getExpression();
+ accessBinding= fieldAccess.getName().resolveBinding();
+ }
+
+ if (accessBinding != null && qualifier != null) {
+ if (conservative && ASTResolving.findParentStatement(qualifier) == null)
+ return null;
+
+ ToStaticAccessOperation declaring= null;
+ ITypeBinding declaringTypeBinding= getDeclaringTypeBinding(accessBinding);
+ if (declaringTypeBinding != null) {
+ declaringTypeBinding= declaringTypeBinding.getTypeDeclaration(); // use generic to avoid any type arguments
+
+ declaring= new ToStaticAccessOperation(declaringTypeBinding, qualifier, createdBlocks);
+ }
+ ToStaticAccessOperation instance= null;
+ ITypeBinding instanceTypeBinding= Bindings.normalizeTypeBinding(qualifier.resolveTypeBinding());
+ if (instanceTypeBinding != null) {
+ instanceTypeBinding= instanceTypeBinding.getTypeDeclaration(); // use generic to avoid any type arguments
+ if (instanceTypeBinding.getTypeDeclaration() != declaringTypeBinding) {
+ instance= new ToStaticAccessOperation(instanceTypeBinding, qualifier, createdBlocks);
+ }
+ }
+ if (declaring != null && instance != null) {
+ return new ToStaticAccessOperation[] {declaring, instance};
+ } else {
+ return new ToStaticAccessOperation[] {declaring};
+ }
+ }
+ return null;
+ }
+
+ private static ITypeBinding getDeclaringTypeBinding(IBinding accessBinding) {
+ if (accessBinding instanceof IMethodBinding) {
+ return ((IMethodBinding) accessBinding).getDeclaringClass();
+ } else if (accessBinding instanceof IVariableBinding) {
+ return ((IVariableBinding) accessBinding).getDeclaringClass();
+ }
+ return null;
+ }
+
+ public static AddThisQualifierOperation getUnqualifiedFieldAccessResolveOperation(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ SimpleName name= getName(compilationUnit, problem);
+ if (name == null)
+ return null;
+
+ IBinding binding= name.resolveBinding();
+ if (binding == null || binding.getKind() != IBinding.VARIABLE)
+ return null;
+
+ ImportRewrite imports= StubUtility.createImportRewrite(compilationUnit, true);
+
+ String replacement= getThisExpressionQualifier(((IVariableBinding) binding).getDeclaringClass(), imports, name);
+ if (replacement == null)
+ return null;
+
+ if (replacement.length() == 0)
+ replacement= null;
+
+ return new AddThisQualifierOperation(replacement, name);
+ }
+
+ private static String getThisExpressionQualifier(ITypeBinding declaringClass, ImportRewrite imports, SimpleName name) {
+ ITypeBinding parentType= Bindings.getBindingOfParentType(name);
+ ITypeBinding currType= parentType;
+ while (currType != null && !Bindings.isSuperType(declaringClass, currType)) {
+ currType= currType.getDeclaringClass();
+ }
+ if (currType == null) {
+ declaringClass= declaringClass.getTypeDeclaration();
+ currType= parentType;
+ while (currType != null && !Bindings.isSuperType(declaringClass, currType)) {
+ currType= currType.getDeclaringClass();
+ }
+ }
+ if (currType != parentType) {
+ if (currType == null)
+ return null;
+
+ if (currType.isAnonymous())
+ //If we access a field of a super class of an anonymous class
+ //then we can only qualify with 'this' but not with outer.this
+ //see bug 115277
+ return null;
+
+ return imports.addImport(currType);
+ } else {
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ private static SimpleName getName(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+
+ while (selectedNode instanceof QualifiedName) {
+ selectedNode= ((QualifiedName) selectedNode).getQualifier();
+ }
+ if (!(selectedNode instanceof SimpleName)) {
+ return null;
+ }
+ return (SimpleName) selectedNode;
+ }
+
+ private CodeStyleFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] operations) {
+ super(name, compilationUnit, operations);
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFixCore.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CompilationUnitRewriteOperationsFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -94,8 +94,7 @@
CompilationUnitRewrite cuRewrite= new CompilationUnitRewrite((ICompilationUnit)fCompilationUnit.getJavaElement(), fCompilationUnit);
fLinkedProposalModel.clear();
- for (int i= 0; i < fOperations.length; i++) {
- CompilationUnitRewriteOperation operation= fOperations[i];
+ for (CompilationUnitRewriteOperation operation : fOperations) {
operation.rewriteAST(cuRewrite, fLinkedProposalModel);
}
@@ -109,8 +108,7 @@
@Override
public String getAdditionalProposalInfo(){
StringBuilder sb= new StringBuilder();
- for (int i= 0; i < fOperations.length; i++) {
- CompilationUnitRewriteOperation operation= fOperations[i];
+ for (CompilationUnitRewriteOperation operation : fOperations) {
String info= operation.getAdditionalInfo();
if (info != null)
sb.append(info);
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertForLoopOperation.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertForLoopOperation.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertForLoopOperation.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertForLoopOperation.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,941 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTMatcher;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayAccess;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ContinueStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.InfixExpression.Operator;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.PostfixExpression;
+import org.eclipse.jdt.core.dom.PrefixExpression;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+import org.eclipse.jdt.internal.corext.dom.JdtASTMatcher;
+import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+
+
+public class ConvertForLoopOperation extends ConvertLoopOperation {
+
+ private static final String LENGTH_QUERY= "length"; //$NON-NLS-1$
+ private static final String SIZE_QUERY= "size"; //$NON-NLS-1$
+ private static final String GET_QUERY= "get"; //$NON-NLS-1$
+ private static final String ISEMPTY_QUERY= "isEmpty"; //$NON-NLS-1$
+ private static final String LITERAL_0= "0"; //$NON-NLS-1$
+ private static final String LITERAL_1= "1"; //$NON-NLS-1$
+ private static final class InvalidBodyError extends Error {
+ private static final long serialVersionUID= 1L;
+ }
+
+ private IVariableBinding fIndexBinding;
+ private IVariableBinding fLengthBinding;
+ private IBinding fArrayBinding;
+ private Expression fArrayAccess;
+ private VariableDeclarationFragment fElementDeclaration;
+ private boolean fMakeFinal;
+ private boolean fIsCollection;
+ private IMethodBinding fSizeMethodBinding;
+ private IMethodBinding fGetMethodBinding;
+ private MethodInvocation fSizeMethodAccess;
+
+ public ConvertForLoopOperation(ForStatement forStatement) {
+ this(forStatement, new String[0], false);
+ }
+
+ public ConvertForLoopOperation(ForStatement forStatement, String[] usedNames, boolean makeFinal) {
+ super(forStatement, usedNames);
+ fMakeFinal= makeFinal;
+ }
+
+ @Override
+ public IStatus satisfiesPreconditions() {
+ ForStatement statement= getForStatement();
+ CompilationUnit ast= (CompilationUnit)statement.getRoot();
+
+ IJavaElement javaElement= ast.getJavaElement();
+ if (javaElement == null)
+ return ERROR_STATUS;
+
+ if (!JavaModelUtil.is50OrHigher(javaElement.getJavaProject()))
+ return ERROR_STATUS;
+
+ if (!validateInitializers(statement))
+ return ERROR_STATUS;
+
+ if (!validateExpression(statement))
+ return ERROR_STATUS;
+
+ if (!validateUpdaters(statement))
+ return ERROR_STATUS;
+
+ if (!validateBody(statement))
+ return ERROR_STATUS;
+
+ return Status.OK_STATUS;
+ }
+
+
+ /*
+ * Must be one of:
+ *
+ * - int [result]= 0;
+ * - int [result]= 0, [lengthBinding]= [arrayBinding].length;
+ * - int , [result]= 0;
+ *
+ */
+ private boolean validateInitializers(ForStatement statement) {
+ List initializers= statement.initializers();
+ if (initializers.size() != 1)
+ return false;
+
+ Expression expression= initializers.get(0);
+ if (!(expression instanceof VariableDeclarationExpression))
+ return false;
+
+ VariableDeclarationExpression declaration= (VariableDeclarationExpression)expression;
+ ITypeBinding declarationBinding= declaration.resolveTypeBinding();
+ if (declarationBinding == null)
+ return false;
+
+ if (!declarationBinding.isPrimitive())
+ return false;
+
+ if (!PrimitiveType.INT.toString().equals(declarationBinding.getQualifiedName()))
+ return false;
+
+ List fragments= declaration.fragments();
+ if (fragments.size() == 1) {
+ IVariableBinding indexBinding= getIndexBindingFromFragment(fragments.get(0));
+ if (indexBinding == null)
+ return false;
+
+ fIndexBinding= indexBinding;
+ return true;
+ } else if (fragments.size() == 2) {
+ IVariableBinding indexBinding= getIndexBindingFromFragment(fragments.get(0));
+ if (indexBinding == null) {
+ indexBinding= getIndexBindingFromFragment(fragments.get(1));
+ if (indexBinding == null)
+ return false;
+
+ if (!validateLengthFragment(fragments.get(0)))
+ return false;
+ } else {
+ if (!validateLengthFragment(fragments.get(1)))
+ return false;
+ }
+
+ fIndexBinding= indexBinding;
+ return true;
+ }
+ return false;
+ }
+
+
+ /*
+ * [lengthBinding]= [arrayBinding].length
+ */
+ private boolean validateLengthFragment(VariableDeclarationFragment fragment) {
+ Expression initializer= fragment.getInitializer();
+ if (initializer == null)
+ return false;
+
+ if (!validateLengthQuery(initializer))
+ return false;
+
+ IVariableBinding lengthBinding= (IVariableBinding)fragment.getName().resolveBinding();
+ if (lengthBinding == null)
+ return false;
+ fLengthBinding= lengthBinding;
+
+ return true;
+ }
+
+
+ /*
+ * Must be one of:
+ *
+ */
+ private IVariableBinding getIndexBindingFromFragment(VariableDeclarationFragment fragment) {
+ Expression initializer= fragment.getInitializer();
+ if (!(initializer instanceof NumberLiteral))
+ return null;
+
+ NumberLiteral number= (NumberLiteral)initializer;
+ if (!LITERAL_0.equals(number.getToken()))
+ return null;
+
+ return (IVariableBinding)fragment.getName().resolveBinding();
+ }
+
+
+ /*
+ * Must be one of:
+ *
+ * - [indexBinding] < [result].length;
+ * - [result].length > [indexBinding];
+ * - [indexBinding] < [lengthBinding];
+ * - [lengthBinding] > [indexBinding];
+ *
+ */
+ private boolean validateExpression(ForStatement statement) {
+ Expression expression= statement.getExpression();
+ if (!(expression instanceof InfixExpression))
+ return false;
+
+ InfixExpression infix= (InfixExpression)expression;
+
+ Expression left= infix.getLeftOperand();
+ Expression right= infix.getRightOperand();
+ if (left instanceof SimpleName && right instanceof SimpleName) {
+ IVariableBinding lengthBinding= fLengthBinding;
+ if (lengthBinding == null)
+ return false;
+
+ IBinding leftBinding= ((SimpleName)left).resolveBinding();
+ IBinding righBinding= ((SimpleName)right).resolveBinding();
+
+ if (fIndexBinding.equals(leftBinding)) {
+ return lengthBinding.equals(righBinding);
+ } else if (fIndexBinding.equals(righBinding)) {
+ return lengthBinding.equals(leftBinding);
+ }
+
+ return false;
+ } else if (left instanceof SimpleName) {
+ if (!fIndexBinding.equals(((SimpleName)left).resolveBinding()))
+ return false;
+
+ if (!Operator.LESS.equals(infix.getOperator()))
+ return false;
+
+ return validateLengthQuery(right);
+ } else if (right instanceof SimpleName) {
+ if (!fIndexBinding.equals(((SimpleName)right).resolveBinding()))
+ return false;
+
+ if (!Operator.GREATER.equals(infix.getOperator()))
+ return false;
+
+ return validateLengthQuery(left);
+ }
+
+ return false;
+ }
+
+
+ /*
+ * Must be one of:
+ *
+ * - [result].length
+ * - [result].size()
+ *
+ */
+ private boolean validateLengthQuery(Expression lengthQuery) {
+ if (lengthQuery instanceof QualifiedName) {
+ QualifiedName qualifiedName= (QualifiedName)lengthQuery;
+ SimpleName name= qualifiedName.getName();
+ if (!LENGTH_QUERY.equals(name.getIdentifier()))
+ return false;
+
+ Name arrayAccess= qualifiedName.getQualifier();
+ ITypeBinding accessType= arrayAccess.resolveTypeBinding();
+ if (accessType == null)
+ return false;
+
+ if (!accessType.isArray())
+ return false;
+
+ IBinding arrayBinding= arrayAccess.resolveBinding();
+ if (arrayBinding == null)
+ return false;
+
+ fArrayBinding= arrayBinding;
+ fArrayAccess= arrayAccess;
+ return true;
+ } else if (lengthQuery instanceof FieldAccess) {
+ FieldAccess fieldAccess= (FieldAccess)lengthQuery;
+ SimpleName name= fieldAccess.getName();
+ if (!LENGTH_QUERY.equals(name.getIdentifier()))
+ return false;
+
+ Expression arrayAccess= fieldAccess.getExpression();
+ ITypeBinding accessType= arrayAccess.resolveTypeBinding();
+ if (accessType == null)
+ return false;
+
+ if (!accessType.isArray())
+ return false;
+
+ IBinding arrayBinding= getBinding(arrayAccess);
+ if (arrayBinding == null)
+ return false;
+
+ fArrayBinding= arrayBinding;
+ fArrayAccess= arrayAccess;
+ return true;
+ } else if (lengthQuery instanceof MethodInvocation) {
+ MethodInvocation methodCall= (MethodInvocation)lengthQuery;
+ SimpleName name= methodCall.getName();
+ if (!SIZE_QUERY.equals(name.getIdentifier())
+ || !methodCall.arguments().isEmpty()) {
+ return false;
+ }
+ IMethodBinding methodBinding= methodCall.resolveMethodBinding();
+ if (methodBinding == null) {
+ return false;
+ }
+ ITypeBinding classBinding= methodBinding.getDeclaringClass();
+
+ if (isCollection(classBinding)) {
+ fIsCollection= true;
+ fSizeMethodBinding= methodBinding;
+ fSizeMethodAccess= methodCall;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isCollection(ITypeBinding classBinding) {
+ ITypeBinding[] interfaces= classBinding.getInterfaces();
+ for (ITypeBinding binding : interfaces) {
+ if (binding.getErasure().getQualifiedName().startsWith("java.util.Collection")) { //$NON-NLS-1$
+ return true;
+ }
+ }
+ ITypeBinding superClass= classBinding.getSuperclass();
+ if (superClass != null && isCollection(superClass)) {
+ return true;
+ }
+ for (ITypeBinding binding : interfaces) {
+ if (isCollection(binding)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Must be one of:
+ *
+ * - [indexBinding]++
+ * - ++[indexBinding]
+ * - [indexBinding]+= 1
+ * - [indexBinding]= [indexBinding] + 1
+ * - [indexBinding]= 1 + [indexBinding]
+ *
+ */
+ private boolean validateUpdaters(ForStatement statement) {
+ List updaters= statement.updaters();
+ if (updaters.size() != 1)
+ return false;
+
+ Expression updater= updaters.get(0);
+ if (updater instanceof PostfixExpression) {
+ PostfixExpression postfix= (PostfixExpression)updater;
+
+ if (!PostfixExpression.Operator.INCREMENT.equals(postfix.getOperator()))
+ return false;
+
+ IBinding binding= getBinding(postfix.getOperand());
+ if (!fIndexBinding.equals(binding))
+ return false;
+
+ return true;
+ } else if (updater instanceof PrefixExpression) {
+ PrefixExpression prefix= (PrefixExpression) updater;
+
+ if (!PrefixExpression.Operator.INCREMENT.equals(prefix.getOperator()))
+ return false;
+
+ IBinding binding= getBinding(prefix.getOperand());
+ if (!fIndexBinding.equals(binding))
+ return false;
+
+ return true;
+ } else if (updater instanceof Assignment) {
+ Assignment assignment= (Assignment)updater;
+ Expression left= assignment.getLeftHandSide();
+ IBinding binding= getBinding(left);
+ if (!fIndexBinding.equals(binding))
+ return false;
+
+ if (Assignment.Operator.PLUS_ASSIGN.equals(assignment.getOperator())) {
+ return isOneLiteral(assignment.getRightHandSide());
+ } else if (Assignment.Operator.ASSIGN.equals(assignment.getOperator())) {
+ Expression right= assignment.getRightHandSide();
+ if (!(right instanceof InfixExpression))
+ return false;
+
+ InfixExpression infixExpression= (InfixExpression)right;
+ Expression leftOperand= infixExpression.getLeftOperand();
+ IBinding leftBinding= getBinding(leftOperand);
+ Expression rightOperand= infixExpression.getRightOperand();
+ IBinding rightBinding= getBinding(rightOperand);
+
+ if (fIndexBinding.equals(leftBinding)) {
+ return isOneLiteral(rightOperand);
+ } else if (fIndexBinding.equals(rightBinding)) {
+ return isOneLiteral(leftOperand);
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isOneLiteral(Expression expression) {
+ if (!(expression instanceof NumberLiteral))
+ return false;
+
+ NumberLiteral literal= (NumberLiteral)expression;
+ return LITERAL_1.equals(literal.getToken());
+ }
+
+
+ /*
+ * returns false iff
+ *
+ * indexBinding
is used for anything else then accessing
+ * an element of arrayBinding
or as a parameter to getBinding
+ * arrayBinding
is assigned
+ * - an element of
arrayBinding
is assigned
+ * lengthBinding
is referenced
+ * - a method call is made to anything but get(
indexBinding
) or size() or isEmpty()
+ *
+ * within body
+ */
+ private boolean validateBody(ForStatement statement) {
+ Statement body= statement.getBody();
+ try {
+ body.accept(new GenericVisitor() {
+ @Override
+ protected boolean visitNode(ASTNode node) {
+ if (node instanceof ContinueStatement) {
+ return false;
+ }
+ if (node instanceof Name) {
+ Name name= (Name)node;
+ IBinding nameBinding= name.resolveBinding();
+ if (nameBinding == null)
+ throw new InvalidBodyError();
+
+ if (nameBinding.equals(fIndexBinding)) {
+ if (node.getLocationInParent() == ArrayAccess.INDEX_PROPERTY) {
+ if (fIsCollection)
+ throw new InvalidBodyError();
+ ArrayAccess arrayAccess= (ArrayAccess)node.getParent();
+ Expression array= arrayAccess.getArray();
+ if (array instanceof QualifiedName) {
+ if (!(fArrayAccess instanceof QualifiedName))
+ throw new InvalidBodyError();
+
+ IBinding varBinding1= ((QualifiedName) array).getQualifier().resolveBinding();
+ if (varBinding1 == null)
+ throw new InvalidBodyError();
+
+ IBinding varBinding2= ((QualifiedName) fArrayAccess).getQualifier().resolveBinding();
+ if (!varBinding1.equals(varBinding2))
+ throw new InvalidBodyError();
+ } else if (array instanceof FieldAccess) {
+ Expression arrayExpression= ((FieldAccess) array).getExpression();
+ if (arrayExpression instanceof ThisExpression) {
+ if (fArrayAccess instanceof FieldAccess) {
+ Expression arrayAccessExpression= ((FieldAccess) fArrayAccess).getExpression();
+ if (!(arrayAccessExpression instanceof ThisExpression))
+ throw new InvalidBodyError();
+ } else if (fArrayAccess instanceof QualifiedName) {
+ throw new InvalidBodyError();
+ }
+ } else {
+ if (!(fArrayAccess instanceof FieldAccess))
+ throw new InvalidBodyError();
+
+ Expression arrayAccessExpression= ((FieldAccess) fArrayAccess).getExpression();
+ if (!arrayExpression.subtreeMatch(new JdtASTMatcher(), arrayAccessExpression)) {
+ throw new InvalidBodyError();
+ }
+ }
+ } else {
+ if (fArrayAccess instanceof QualifiedName) {
+ throw new InvalidBodyError();
+ }
+ if (fArrayAccess instanceof FieldAccess) {
+ Expression arrayAccessExpression= ((FieldAccess) fArrayAccess).getExpression();
+ if (!(arrayAccessExpression instanceof ThisExpression))
+ throw new InvalidBodyError();
+ }
+ }
+
+ IBinding binding= getBinding(array);
+ if (binding == null)
+ throw new InvalidBodyError();
+
+ if (!fArrayBinding.equals(binding))
+ throw new InvalidBodyError();
+
+ } else if (node.getLocationInParent() == MethodInvocation.ARGUMENTS_PROPERTY) {
+ MethodInvocation method= (MethodInvocation)node.getParent();
+ IMethodBinding methodBinding= method.resolveMethodBinding();
+ if (methodBinding == null)
+ throw new InvalidBodyError();
+ ITypeBinding[] parms= methodBinding.getParameterTypes();
+ if (!fIsCollection || !GET_QUERY.equals(method.getName().getFullyQualifiedName()) ||
+ parms.length != 1 || !parms[0].getName().equals("int") || //$NON-NLS-1$
+ !fSizeMethodBinding.getDeclaringClass().equals(methodBinding.getDeclaringClass()) ||
+ fSizeMethodAccess.getExpression() == null ||
+ !fSizeMethodAccess.getExpression().subtreeMatch(new ASTMatcher(), method.getExpression()))
+ throw new InvalidBodyError();
+ fGetMethodBinding= methodBinding;
+ } else {
+ throw new InvalidBodyError();
+ }
+ } else if (nameBinding.equals(fArrayBinding)) {
+ if (isAssigned(node))
+ throw new InvalidBodyError();
+ } else if (nameBinding.equals(fLengthBinding)) {
+ throw new InvalidBodyError();
+ } else if (fElementDeclaration != null && nameBinding.equals(fElementDeclaration.getName().resolveBinding())) {
+ if (isAssigned(node))
+ fElementDeclaration= null;
+ }
+ } else if (fIsCollection && node instanceof MethodInvocation) {
+ MethodInvocation method= (MethodInvocation)node;
+ IMethodBinding binding= method.resolveMethodBinding();
+ if (binding == null) {
+ throw new InvalidBodyError();
+ }
+ if (fSizeMethodBinding.getDeclaringClass().equals(binding.getDeclaringClass())) {
+ String methodName= method.getName().getFullyQualifiedName();
+ if (!SIZE_QUERY.equals(methodName) &&
+ !GET_QUERY.equals(methodName) &&
+ !ISEMPTY_QUERY.equals(methodName)) {
+ throw new InvalidBodyError();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private boolean isAssigned(ASTNode current) {
+ while (current != null && !(current instanceof Statement)) {
+ if (current.getLocationInParent() == Assignment.LEFT_HAND_SIDE_PROPERTY)
+ return true;
+
+ if (current instanceof PrefixExpression
+ && !(((PrefixExpression) current).getOperand() instanceof MethodInvocation && ((PrefixExpression) current).getOperator().equals(PrefixExpression.Operator.NOT)))
+ return true;
+
+ if (current instanceof PostfixExpression)
+ return true;
+
+ current= current.getParent();
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(ArrayAccess node) {
+ if (fElementDeclaration != null)
+ return super.visit(node);
+
+ IBinding binding= getBinding(node.getArray());
+ if (!fIsCollection && fArrayBinding.equals(binding)) {
+ IBinding index= getBinding(node.getIndex());
+ if (fIndexBinding.equals(index)) {
+ if (node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
+ fElementDeclaration= (VariableDeclarationFragment)node.getParent();
+ }
+ }
+ }
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ if (fElementDeclaration != null || !fIsCollection)
+ return super.visit(node);
+
+ IMethodBinding nodeBinding= node.resolveMethodBinding();
+ if (nodeBinding == null) {
+ return super.visit(node);
+ }
+ ITypeBinding[] args= nodeBinding.getParameterTypes();
+ if (GET_QUERY.equals(nodeBinding.getName()) && args.length == 1 &&
+ args[0].getName().equals("int") && //$NON-NLS-1$
+ nodeBinding.getDeclaringClass().equals(fSizeMethodBinding.getDeclaringClass())) {
+ IBinding index= getBinding((Expression)node.arguments().get(0));
+ if (fIndexBinding.equals(index)) {
+ if (node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
+ fElementDeclaration= (VariableDeclarationFragment)node.getParent();
+ }
+ }
+ }
+ return super.visit(node);
+ }
+ });
+ } catch (InvalidBodyError e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static IBinding getBinding(Expression expression) {
+ if (expression instanceof FieldAccess) {
+ return ((FieldAccess)expression).resolveFieldBinding();
+ } else if (expression instanceof Name) {
+ return ((Name)expression).resolveBinding();
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getIntroducedVariableName() {
+ if (fElementDeclaration != null) {
+ return fElementDeclaration.getName().getIdentifier();
+ } else {
+ ForStatement forStatement= getForStatement();
+ IJavaProject javaProject= ((CompilationUnit)forStatement.getRoot()).getJavaElement().getJavaProject();
+ String[] proposals= null;
+ if (this.fIsCollection) {
+ proposals= getVariableNameProposalsCollection(fSizeMethodAccess, javaProject);
+ } else {
+ proposals= getVariableNameProposals(fArrayAccess.resolveTypeBinding(), javaProject);
+ }
+ return proposals[0];
+ }
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore positionGroups) throws CoreException {
+ TextEditGroup group= createTextEditGroup(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description, cuRewrite);
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+
+ TightSourceRangeComputer rangeComputer;
+ if (rewrite.getExtendedSourceRangeComputer() instanceof TightSourceRangeComputer) {
+ rangeComputer= (TightSourceRangeComputer)rewrite.getExtendedSourceRangeComputer();
+ } else {
+ rangeComputer= new TightSourceRangeComputer();
+ }
+ rangeComputer.addTightSourceNode(getForStatement());
+ rewrite.setTargetSourceRangeComputer(rangeComputer);
+
+ Statement statement= convert(cuRewrite, group, positionGroups);
+ rewrite.replace(getForStatement(), statement, group);
+ }
+
+ @Override
+ protected Statement convert(CompilationUnitRewrite cuRewrite, TextEditGroup group, LinkedProposalModelCore positionGroups) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+
+ ForStatement forStatement= getForStatement();
+
+ IJavaProject javaProject= ((CompilationUnit)forStatement.getRoot()).getJavaElement().getJavaProject();
+ String[] proposals= null;
+
+ if (this.fIsCollection) {
+ proposals= getVariableNameProposalsCollection(fSizeMethodAccess, javaProject);
+ } else {
+ proposals= getVariableNameProposals(fArrayAccess.resolveTypeBinding(), javaProject);
+ }
+
+ String parameterName;
+ if (fElementDeclaration != null) {
+ parameterName= fElementDeclaration.getName().getIdentifier();
+ } else {
+ parameterName= proposals[0];
+ }
+
+ LinkedProposalPositionGroupCore pg= positionGroups.getPositionGroup(parameterName, true);
+ if (fElementDeclaration != null)
+ pg.addProposal(parameterName, 10);
+ for (int i= 0; i < proposals.length; i++) {
+ pg.addProposal(proposals[i], 10);
+ }
+
+ AST ast= forStatement.getAST();
+ EnhancedForStatement result= ast.newEnhancedForStatement();
+
+ SingleVariableDeclaration parameterDeclaration= null;
+ Expression parameterExpression= null;
+
+ if (this.fIsCollection) {
+ parameterExpression= fSizeMethodAccess.getExpression();
+ parameterDeclaration= createParameterDeclarationCollection(parameterName, fElementDeclaration, fSizeMethodAccess, forStatement, importRewrite, rewrite, group, pg, fMakeFinal);
+ } else {
+ parameterExpression= fArrayAccess;
+ parameterDeclaration= createParameterDeclaration(parameterName, fElementDeclaration, fArrayAccess, forStatement, importRewrite, rewrite, group, pg, fMakeFinal);
+ }
+ result.setParameter(parameterDeclaration);
+
+ result.setExpression((Expression)rewrite.createCopyTarget(parameterExpression));
+
+ if (this.fIsCollection) {
+ convertBodyCollection(forStatement.getBody(), fIndexBinding, fGetMethodBinding, parameterName, rewrite, group, pg);
+ } else {
+ convertBody(forStatement.getBody(), fIndexBinding, fArrayBinding, parameterName, rewrite, group, pg);
+ }
+ result.setBody(getBody(cuRewrite, group, positionGroups));
+
+ positionGroups.setEndPosition(rewrite.track(result));
+
+ return result;
+ }
+
+ private void convertBody(Statement body, final IBinding indexBinding, final IBinding arrayBinding, final String parameterName, final ASTRewrite rewrite, final TextEditGroup editGroup, final LinkedProposalPositionGroupCore pg) {
+ final AST ast= body.getAST();
+
+ body.accept(new GenericVisitor() {
+ @Override
+ public boolean visit(ArrayAccess node) {
+ IBinding binding= getBinding(node.getArray());
+ if (arrayBinding.equals(binding)) {
+ IBinding index= getBinding(node.getIndex());
+ if (indexBinding.equals(index)) {
+ replaceAccess(node);
+ }
+ }
+
+ return super.visit(node);
+ }
+
+ private void replaceAccess(ASTNode node) {
+ if (fElementDeclaration != null && node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
+ VariableDeclarationFragment fragment= (VariableDeclarationFragment)node.getParent();
+ IBinding targetBinding= fragment.getName().resolveBinding();
+ if (targetBinding != null) {
+ VariableDeclarationStatement statement= (VariableDeclarationStatement)fragment.getParent();
+
+ if (statement.fragments().size() == 1) {
+ rewrite.remove(statement, editGroup);
+ } else {
+ ListRewrite listRewrite= rewrite.getListRewrite(statement, VariableDeclarationStatement.FRAGMENTS_PROPERTY);
+ listRewrite.remove(fragment, editGroup);
+ }
+
+ } else {
+ SimpleName name= ast.newSimpleName(parameterName);
+ rewrite.replace(node, name, editGroup);
+ pg.addPosition(rewrite.track(name), true);
+ }
+ } else {
+ SimpleName name= ast.newSimpleName(parameterName);
+ rewrite.replace(node, name, editGroup);
+ pg.addPosition(rewrite.track(name), true);
+ }
+ }
+ });
+ }
+
+ private SingleVariableDeclaration createParameterDeclaration(String parameterName, VariableDeclarationFragment fragement, Expression arrayAccess, ForStatement statement, ImportRewrite importRewrite, ASTRewrite rewrite, TextEditGroup group, LinkedProposalPositionGroupCore pg, boolean makeFinal) {
+ CompilationUnit compilationUnit= (CompilationUnit)arrayAccess.getRoot();
+ AST ast= compilationUnit.getAST();
+
+ SingleVariableDeclaration result= ast.newSingleVariableDeclaration();
+
+ SimpleName name= ast.newSimpleName(parameterName);
+ pg.addPosition(rewrite.track(name), true);
+ result.setName(name);
+
+ ITypeBinding arrayTypeBinding= arrayAccess.resolveTypeBinding();
+ Type type= importType(arrayTypeBinding.getElementType(), statement, importRewrite, compilationUnit,
+ arrayTypeBinding.getDimensions() == 1 ? TypeLocation.LOCAL_VARIABLE : TypeLocation.ARRAY_CONTENTS);
+ if (arrayTypeBinding.getDimensions() != 1) {
+ type= ast.newArrayType(type, arrayTypeBinding.getDimensions() - 1);
+ }
+ result.setType(type);
+
+ if (fragement != null) {
+ VariableDeclarationStatement declaration= (VariableDeclarationStatement)fragement.getParent();
+ ModifierRewrite.create(rewrite, result).copyAllModifiers(declaration, group);
+ }
+ if (makeFinal && (fragement == null || ASTNodes.findModifierNode(Modifier.FINAL, ASTNodes.getModifiers(fragement)) == null)) {
+ ModifierRewrite.create(rewrite, result).setModifiers(Modifier.FINAL, 0, group);
+ }
+
+ return result;
+ }
+
+ private String[] getVariableNameProposals(ITypeBinding arrayTypeBinding, IJavaProject project) {
+ String[] variableNames= getUsedVariableNames();
+ String baseName= FOR_LOOP_ELEMENT_IDENTIFIER;
+ String name= fArrayBinding.getName();
+ if (name.length() > 2 && name.charAt(name.length() - 1) == 's') {
+ baseName= name.substring(0, name.length() - 1);
+ }
+ String[] elementSuggestions= StubUtility.getLocalNameSuggestions(project, baseName, 0, variableNames);
+
+ String type= arrayTypeBinding.getElementType().getName();
+ String[] typeSuggestions= StubUtility.getLocalNameSuggestions(project, type, arrayTypeBinding.getDimensions() - 1, variableNames);
+
+ String[] result= new String[elementSuggestions.length + typeSuggestions.length];
+ System.arraycopy(elementSuggestions, 0, result, 0, elementSuggestions.length);
+ System.arraycopy(typeSuggestions, 0, result, elementSuggestions.length, typeSuggestions.length);
+ return result;
+ }
+
+ private String[] getVariableNameProposalsCollection(MethodInvocation sizeMethodAccess, IJavaProject project) {
+ String[] variableNames= getUsedVariableNames();
+ String baseName= FOR_LOOP_ELEMENT_IDENTIFIER;
+ Expression exp= sizeMethodAccess.getExpression();
+ String name= exp instanceof SimpleName ? ((SimpleName)exp).getFullyQualifiedName() : ""; //$NON-NLS-1$
+ if (name.length() > 2 && name.charAt(name.length() - 1) == 's') {
+ baseName= name.substring(0, name.length() - 1);
+ }
+ String[] elementSuggestions= StubUtility.getLocalNameSuggestions(project, baseName, 0, variableNames);
+
+ ITypeBinding[] typeArgs= fSizeMethodBinding.getDeclaringClass().getTypeArguments();
+ String type= "Object"; //$NON-NLS-1$
+ if (typeArgs != null && typeArgs.length > 0) {
+ type= typeArgs[0].getName();
+ }
+ String[] typeSuggestions= StubUtility.getLocalNameSuggestions(project, type, 0, variableNames);
+
+ String[] result= new String[elementSuggestions.length + typeSuggestions.length];
+ System.arraycopy(elementSuggestions, 0, result, 0, elementSuggestions.length);
+ System.arraycopy(typeSuggestions, 0, result, elementSuggestions.length, typeSuggestions.length);
+ return result;
+ }
+
+ private void convertBodyCollection(Statement body, final IBinding indexBinding, final IBinding getBinding, final String parameterName, final ASTRewrite rewrite, final TextEditGroup editGroup, final LinkedProposalPositionGroupCore pg) {
+ final AST ast= body.getAST();
+
+ body.accept(new GenericVisitor() {
+ @Override
+ public boolean visit(MethodInvocation node) {
+ IBinding binding= node.resolveMethodBinding();
+ if (binding != null && binding.equals(getBinding)) {
+ List args = node.arguments();
+ if (args.size() == 1 && args.get(0) instanceof SimpleName
+ && indexBinding.equals(((SimpleName)args.get(0)).resolveBinding())) {
+ replaceAccess(node);
+ }
+ }
+
+ return super.visit(node);
+ }
+
+ private void replaceAccess(ASTNode node) {
+ if (fElementDeclaration != null && node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
+ VariableDeclarationFragment fragment= (VariableDeclarationFragment)node.getParent();
+ IBinding targetBinding= fragment.getName().resolveBinding();
+ if (targetBinding != null) {
+ VariableDeclarationStatement statement= (VariableDeclarationStatement)fragment.getParent();
+
+ if (statement.fragments().size() == 1) {
+ rewrite.remove(statement, editGroup);
+ } else {
+ ListRewrite listRewrite= rewrite.getListRewrite(statement, VariableDeclarationStatement.FRAGMENTS_PROPERTY);
+ listRewrite.remove(fragment, editGroup);
+ }
+
+ } else {
+ SimpleName name= ast.newSimpleName(parameterName);
+ rewrite.replace(node, name, editGroup);
+ pg.addPosition(rewrite.track(name), true);
+ }
+ } else {
+ SimpleName name= ast.newSimpleName(parameterName);
+ rewrite.replace(node, name, editGroup);
+ pg.addPosition(rewrite.track(name), true);
+ }
+ }
+ });
+ }
+
+ private SingleVariableDeclaration createParameterDeclarationCollection(String parameterName, VariableDeclarationFragment fragement, Expression sizeAccess, ForStatement statement, ImportRewrite importRewrite, ASTRewrite rewrite, TextEditGroup group, LinkedProposalPositionGroupCore pg, boolean makeFinal) {
+ CompilationUnit compilationUnit= (CompilationUnit)sizeAccess.getRoot();
+ AST ast= compilationUnit.getAST();
+
+ SingleVariableDeclaration result= ast.newSingleVariableDeclaration();
+
+ SimpleName name= ast.newSimpleName(parameterName);
+ pg.addPosition(rewrite.track(name), true);
+ result.setName(name);
+
+ IMethodBinding sizeTypeBinding= ((MethodInvocation)sizeAccess).resolveMethodBinding();
+ ITypeBinding[] sizeTypeArguments= sizeTypeBinding.getDeclaringClass().getTypeArguments();
+ Type type= importType(sizeTypeArguments != null && sizeTypeArguments.length > 0
+ ? sizeTypeArguments[0]
+ : fSizeMethodAccess.getAST().resolveWellKnownType("java.lang.Object"), //$NON-NLS-1$
+ statement, importRewrite, compilationUnit,
+ TypeLocation.LOCAL_VARIABLE);
+ result.setType(type);
+
+ if (fragement != null) {
+ VariableDeclarationStatement declaration= (VariableDeclarationStatement)fragement.getParent();
+ ModifierRewrite.create(rewrite, result).copyAllModifiers(declaration, group);
+ }
+ if (makeFinal && (fragement == null || ASTNodes.findModifierNode(Modifier.FINAL, ASTNodes.getModifiers(fragement)) == null)) {
+ ModifierRewrite.create(rewrite, result).setModifiers(Modifier.FINAL, 0, group);
+ }
+
+ return result;
+ }
+
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertIterableLoopOperation.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertIterableLoopOperation.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertIterableLoopOperation.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertIterableLoopOperation.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,612 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - refactored to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NullLiteral;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ImportRemover;
+import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
+
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+
+/**
+ * Operation to convert for loops over iterables to enhanced for loops.
+ *
+ * @since 3.1
+ */
+public final class ConvertIterableLoopOperation extends ConvertLoopOperation {
+
+ private static final StatusInfo SEMANTIC_CHANGE_WARNING_STATUS= new StatusInfo(IStatus.WARNING, FixMessages.ConvertIterableLoopOperation_semanticChangeWarning);
+
+ /**
+ * Returns the supertype of the given type with the qualified name.
+ *
+ * @param binding
+ * the binding of the type
+ * @param name
+ * the qualified name of the supertype
+ * @return the supertype, or null
+ */
+ private static ITypeBinding getSuperType(final ITypeBinding binding, final String name) {
+
+ if (binding.isArray() || binding.isPrimitive())
+ return null;
+
+ if (binding.getQualifiedName().startsWith(name))
+ return binding;
+
+ final ITypeBinding type= binding.getSuperclass();
+ if (type != null) {
+ final ITypeBinding result= getSuperType(type, name);
+ if (result != null)
+ return result;
+ }
+ final ITypeBinding[] types= binding.getInterfaces();
+ for (int index= 0; index < types.length; index++) {
+ final ITypeBinding result= getSuperType(types[index], name);
+ if (result != null)
+ return result;
+ }
+ return null;
+ }
+
+ /** Has the element variable been assigned outside the for statement? */
+ private boolean fAssigned= false;
+
+ /** The binding of the element variable */
+ private IBinding fElementVariable= null;
+
+ /** The node of the iterable expression */
+ private Expression fExpression= null;
+
+ /** The type binding of the iterable expression */
+ private IBinding fIterable= null;
+
+ /** Is the iterator method invoked on this
? */
+ private boolean fThis= false;
+
+ /** The binding of the iterator variable */
+ private IVariableBinding fIteratorVariable= null;
+
+ /** The nodes of the element variable occurrences */
+ private final List fOccurrences= new ArrayList<>(2);
+
+ private EnhancedForStatement fEnhancedForLoop;
+
+ private boolean fMakeFinal;
+
+ public ConvertIterableLoopOperation(ForStatement statement) {
+ this(statement, new String[0], false);
+ }
+
+ public ConvertIterableLoopOperation(ForStatement statement, String[] usedNames, boolean makeFinal) {
+ super(statement, usedNames);
+ fMakeFinal= makeFinal;
+ }
+
+ @Override
+ public String getIntroducedVariableName() {
+ if (fElementVariable != null) {
+ return fElementVariable.getName();
+ } else {
+ return getVariableNameProposals()[0];
+ }
+ }
+
+ private String[] getVariableNameProposals() {
+
+ String[] variableNames= getUsedVariableNames();
+ String[] elementSuggestions= StubUtility.getLocalNameSuggestions(getJavaProject(), FOR_LOOP_ELEMENT_IDENTIFIER, 0, variableNames);
+
+ final ITypeBinding binding= fIteratorVariable.getType();
+ if (binding != null && binding.isParameterizedType()) {
+ String type= binding.getTypeArguments()[0].getName();
+ String[] typeSuggestions= StubUtility.getLocalNameSuggestions(getJavaProject(), type, 0, variableNames);
+
+ String[] result= new String[elementSuggestions.length + typeSuggestions.length];
+ System.arraycopy(typeSuggestions, 0, result, 0, typeSuggestions.length);
+ System.arraycopy(elementSuggestions, 0, result, typeSuggestions.length, elementSuggestions.length);
+ return result;
+ } else {
+ return elementSuggestions;
+ }
+ }
+
+ private IJavaProject getJavaProject() {
+ return getRoot().getJavaElement().getJavaProject();
+ }
+
+ private CompilationUnit getRoot() {
+ return (CompilationUnit)getForStatement().getRoot();
+ }
+
+ /**
+ * Returns the expression for the enhanced for statement.
+ *
+ * @param rewrite
+ * the AST rewrite to use
+ * @return the expression node, or null
+ */
+ private Expression getExpression(final ASTRewrite rewrite) {
+ if (fThis)
+ return rewrite.getAST().newThisExpression();
+ if (fExpression instanceof MethodInvocation)
+ return (MethodInvocation)rewrite.createMoveTarget(fExpression);
+ return (Expression)ASTNode.copySubtree(rewrite.getAST(), fExpression);
+ }
+
+ /**
+ * Returns the type of elements returned by the iterator.
+ *
+ * @param iterator
+ * the iterator type binding, or null
+ * @return the element type
+ */
+ private ITypeBinding getElementType(final ITypeBinding iterator) {
+ if (iterator != null) {
+ final ITypeBinding[] bindings= iterator.getTypeArguments();
+ if (bindings.length > 0) {
+ ITypeBinding arg= bindings[0];
+ if (arg.isWildcardType()) {
+ arg= ASTResolving.normalizeWildcardType(arg, true, getRoot().getAST());
+ }
+ return arg;
+ }
+ }
+ return getRoot().getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore positionGroups) throws CoreException {
+ final TextEditGroup group= createTextEditGroup(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description, cuRewrite);
+
+ final ASTRewrite astRewrite= cuRewrite.getASTRewrite();
+
+ TightSourceRangeComputer rangeComputer;
+ if (astRewrite.getExtendedSourceRangeComputer() instanceof TightSourceRangeComputer) {
+ rangeComputer= (TightSourceRangeComputer)astRewrite.getExtendedSourceRangeComputer();
+ } else {
+ rangeComputer= new TightSourceRangeComputer();
+ }
+ rangeComputer.addTightSourceNode(getForStatement());
+ astRewrite.setTargetSourceRangeComputer(rangeComputer);
+
+ Statement statement= convert(cuRewrite, group, positionGroups);
+ astRewrite.replace(getForStatement(), statement, group);
+ }
+
+ @Override
+ protected Statement convert(CompilationUnitRewrite cuRewrite, final TextEditGroup group, final LinkedProposalModelCore positionGroups) throws CoreException {
+ final AST ast= cuRewrite.getAST();
+ final ASTRewrite astRewrite= cuRewrite.getASTRewrite();
+ final ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+ final ImportRemover remover= cuRewrite.getImportRemover();
+
+ fEnhancedForLoop= ast.newEnhancedForStatement();
+ String[] names= getVariableNameProposals();
+
+ String name;
+ if (fElementVariable != null) {
+ name= fElementVariable.getName();
+ } else {
+ name= names[0];
+ }
+ final LinkedProposalPositionGroupCore pg= positionGroups.getPositionGroup(name, true);
+ if (fElementVariable != null)
+ pg.addProposal(name, 10);
+ for (int i= 0; i < names.length; i++) {
+ pg.addProposal(names[i], 10);
+ }
+
+ final Statement body= getForStatement().getBody();
+ if (body != null) {
+ final ListRewrite list;
+ if (body instanceof Block) {
+ list= astRewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY);
+ for (final Iterator iterator= fOccurrences.iterator(); iterator.hasNext();) {
+ final Statement parent= ASTNodes.getParent(iterator.next(), Statement.class);
+ if (parent != null && list.getRewrittenList().contains(parent)) {
+ list.remove(parent, null);
+ remover.registerRemovedNode(parent);
+ }
+ }
+ } else {
+ list= null;
+ }
+ final String text= name;
+ body.accept(new ASTVisitor() {
+
+ private boolean replace(final Expression expression) {
+ final SimpleName node= ast.newSimpleName(text);
+ astRewrite.replace(expression, node, group);
+ remover.registerRemovedNode(expression);
+ pg.addPosition(astRewrite.track(node), false);
+ return false;
+ }
+
+ @Override
+ public final boolean visit(final MethodInvocation node) {
+ final IMethodBinding binding= node.resolveMethodBinding();
+ if (binding != null && (binding.getName().equals("next") || binding.getName().equals("nextElement"))) { //$NON-NLS-1$ //$NON-NLS-2$
+ final Expression expression= node.getExpression();
+ if (expression instanceof Name) {
+ final IBinding result= ((Name)expression).resolveBinding();
+ if (result != null && result.equals(fIteratorVariable))
+ return replace(node);
+ } else if (expression instanceof FieldAccess) {
+ final IBinding result= ((FieldAccess)expression).resolveFieldBinding();
+ if (result != null && result.equals(fIteratorVariable))
+ return replace(node);
+ }
+ }
+ return super.visit(node);
+ }
+
+ @Override
+ public final boolean visit(final SimpleName node) {
+ if (fElementVariable != null) {
+ final IBinding binding= node.resolveBinding();
+ if (binding != null && binding.equals(fElementVariable)) {
+ final Statement parent= ASTNodes.getParent(node, Statement.class);
+ if (parent != null && (list == null || list.getRewrittenList().contains(parent)))
+ pg.addPosition(astRewrite.track(node), false);
+ }
+ }
+ return false;
+ }
+ });
+
+ fEnhancedForLoop.setBody(getBody(cuRewrite, group, positionGroups));
+ }
+ final SingleVariableDeclaration declaration= ast.newSingleVariableDeclaration();
+ final SimpleName simple= ast.newSimpleName(name);
+ pg.addPosition(astRewrite.track(simple), true);
+ declaration.setName(simple);
+ final ITypeBinding elementType= getElementType(fIteratorVariable.getType());
+ Type importType= importType(elementType, getForStatement(), importRewrite, getRoot(), TypeLocation.LOCAL_VARIABLE);
+ remover.registerAddedImports(importType);
+ declaration.setType(importType);
+ if (fMakeFinal) {
+ ModifierRewrite.create(astRewrite, declaration).setModifiers(Modifier.FINAL, 0, group);
+ }
+ remover.registerAddedImport(elementType.getQualifiedName());
+ fEnhancedForLoop.setParameter(declaration);
+ fEnhancedForLoop.setExpression(getExpression(astRewrite));
+
+ for (Iterator iterator= getForStatement().initializers().iterator(); iterator.hasNext();) {
+ ASTNode node= iterator.next();
+ if (node instanceof VariableDeclarationExpression) {
+ VariableDeclarationExpression variableDeclarationExpression= (VariableDeclarationExpression) node;
+ remover.registerRemovedNode(variableDeclarationExpression.getType());
+ } else {
+ remover.registerRemovedNode(node);
+ }
+ }
+
+ for (Iterator iterator= getForStatement().updaters().iterator(); iterator.hasNext();) {
+ ASTNode node= iterator.next();
+ remover.registerRemovedNode(node);
+ }
+
+ return fEnhancedForLoop;
+ }
+
+ /**
+ * Is this proposal applicable?
+ *
+ * @return A status with severity IStatus.Error
if not
+ * applicable
+ */
+ @Override
+ public final IStatus satisfiesPreconditions() {
+ IStatus resultStatus= StatusInfo.OK_STATUS;
+ if (JavaModelUtil.is50OrHigher(getJavaProject())) {
+ resultStatus= checkExpressionCondition();
+ if (resultStatus.getSeverity() == IStatus.ERROR)
+ return resultStatus;
+
+ List updateExpressions= getForStatement().updaters();
+ if (updateExpressions.size() == 1) {
+ resultStatus= new StatusInfo(IStatus.WARNING, Messages.format(FixMessages.ConvertIterableLoopOperation_RemoveUpdateExpression_Warning, BasicElementLabels.getJavaCodeString(updateExpressions.get(0).toString())));
+ } else if (updateExpressions.size() > 1) {
+ resultStatus= new StatusInfo(IStatus.WARNING, FixMessages.ConvertIterableLoopOperation_RemoveUpdateExpressions_Warning);
+ }
+
+ for (final Iterator outer= getForStatement().initializers().iterator(); outer.hasNext();) {
+ final Expression initializer= outer.next();
+ if (initializer instanceof VariableDeclarationExpression) {
+ final VariableDeclarationExpression declaration= (VariableDeclarationExpression)initializer;
+ List fragments= declaration.fragments();
+ if (fragments.size() != 1) {
+ return new StatusInfo(IStatus.ERROR, ""); //$NON-NLS-1$
+ } else {
+ final VariableDeclarationFragment fragment= fragments.get(0);
+ fragment.accept(new ASTVisitor() {
+
+ @Override
+ public final boolean visit(final MethodInvocation node) {
+ final IMethodBinding binding= node.resolveMethodBinding();
+ if (binding != null) {
+ final ITypeBinding type= binding.getReturnType();
+ if (type != null) {
+ final String qualified= type.getQualifiedName();
+ if (qualified.startsWith("java.util.Enumeration<") || qualified.startsWith("java.util.Iterator<")) { //$NON-NLS-1$ //$NON-NLS-2$
+ final Expression qualifier= node.getExpression();
+ if (qualifier != null) {
+ final ITypeBinding resolved= qualifier.resolveTypeBinding();
+ if (resolved != null) {
+ final ITypeBinding iterable= getSuperType(resolved, "java.lang.Iterable"); //$NON-NLS-1$
+ if (iterable != null) {
+ fExpression= qualifier;
+ fIterable= resolved;
+ }
+ }
+ } else {
+ final ITypeBinding declaring= binding.getDeclaringClass();
+ if (declaring != null) {
+ final ITypeBinding superBinding= getSuperType(declaring, "java.lang.Iterable"); //$NON-NLS-1$
+ if (superBinding != null) {
+ fIterable= superBinding;
+ fThis= true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public final boolean visit(final VariableDeclarationFragment node) {
+ final IVariableBinding binding= node.resolveBinding();
+ if (binding != null) {
+ final ITypeBinding type= binding.getType();
+ if (type != null) {
+ ITypeBinding iterator= getSuperType(type, "java.util.Iterator"); //$NON-NLS-1$
+ if (iterator != null)
+ fIteratorVariable= binding;
+ else {
+ iterator= getSuperType(type, "java.util.Enumeration"); //$NON-NLS-1$
+ if (iterator != null)
+ fIteratorVariable= binding;
+ }
+ }
+ }
+ return true;
+ }
+ });
+ }
+ }
+ }
+ final Statement statement= getForStatement().getBody();
+ final boolean[] otherInvocationThenNext= new boolean[] {false};
+ final int[] nextInvocationCount= new int[] {0};
+ if (statement != null && fIteratorVariable != null) {
+ final ITypeBinding elementType= getElementType(fIteratorVariable.getType());
+ statement.accept(new ASTVisitor() {
+
+ @Override
+ public boolean visit(SimpleName node) {
+ IBinding nodeBinding= node.resolveBinding();
+ if (fElementVariable != null && fElementVariable.equals(nodeBinding)) {
+ fMakeFinal= false;
+ }
+
+ if (nodeBinding == fIteratorVariable) {
+ if (node.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY) {
+ MethodInvocation invocation= (MethodInvocation) node.getParent();
+ String name= invocation.getName().getIdentifier();
+ if (name.equals("next") || name.equals("nextElement")) { //$NON-NLS-1$ //$NON-NLS-2$
+ nextInvocationCount[0]++;
+
+ Expression left= null;
+ if (invocation.getLocationInParent() == Assignment.RIGHT_HAND_SIDE_PROPERTY) {
+ left= ((Assignment) invocation.getParent()).getLeftHandSide();
+ } else if (invocation.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
+ left= ((VariableDeclarationFragment) invocation.getParent()).getName();
+ }
+
+ return visitElementVariable(left);
+ }
+ }
+ otherInvocationThenNext[0]= true;
+ }
+ return true;
+ }
+
+ private boolean visitElementVariable(final Expression node) {
+ if (node != null) {
+ final ITypeBinding binding= node.resolveTypeBinding();
+ if (binding != null && elementType.equals(binding)) {
+ if (node instanceof Name) {
+ final Name name= (Name)node;
+ final IBinding result= name.resolveBinding();
+ if (result != null) {
+ fOccurrences.add(node);
+ fElementVariable= result;
+ return false;
+ }
+ } else if (node instanceof FieldAccess) {
+ final FieldAccess access= (FieldAccess)node;
+ final IBinding result= access.resolveFieldBinding();
+ if (result != null) {
+ fOccurrences.add(node);
+ fElementVariable= result;
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ });
+ if (otherInvocationThenNext[0])
+ return ERROR_STATUS;
+
+ if (nextInvocationCount[0] > 1)
+ return ERROR_STATUS;
+
+ if (fElementVariable != null) {
+ statement.accept(new ASTVisitor() {
+ @Override
+ public final boolean visit(final VariableDeclarationFragment node) {
+ if (node.getInitializer() instanceof NullLiteral) {
+ SimpleName name= node.getName();
+ if (elementType.equals(name.resolveTypeBinding()) && fElementVariable.equals(name.resolveBinding())) {
+ fOccurrences.add(name);
+ }
+ }
+
+ return true;
+ }
+ });
+ }
+ }
+ final ASTNode root= getForStatement().getRoot();
+ if (root != null) {
+ root.accept(new ASTVisitor() {
+
+ @Override
+ public final boolean visit(final ForStatement node) {
+ if (node.equals(getForStatement()))
+ return false;
+ else
+ return true;
+ }
+
+ @Override
+ public final boolean visit(final SimpleName node) {
+ final IBinding binding= node.resolveBinding();
+ if (binding != null && binding.equals(fElementVariable))
+ fAssigned= true;
+ return false;
+ }
+ });
+ }
+ }
+ if ((fExpression != null || fThis) && fIterable != null && fIteratorVariable != null && !fAssigned) {
+ return resultStatus;
+ } else {
+ return ERROR_STATUS;
+ }
+ }
+
+ private IStatus checkExpressionCondition() {
+ Expression expression= getForStatement().getExpression();
+ if (!(expression instanceof MethodInvocation))
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+
+ MethodInvocation invoc= (MethodInvocation)expression;
+ IMethodBinding methodBinding= invoc.resolveMethodBinding();
+ if (methodBinding == null)
+ return ERROR_STATUS;
+
+ ITypeBinding declaringClass= methodBinding.getDeclaringClass();
+ if (declaringClass == null)
+ return ERROR_STATUS;
+
+ String qualifiedName= declaringClass.getQualifiedName();
+ String methodName= invoc.getName().getIdentifier();
+ if (qualifiedName.startsWith("java.util.Enumeration")) { //$NON-NLS-1$
+ if (!methodName.equals("hasMoreElements")) //$NON-NLS-1$
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+ } else if (qualifiedName.startsWith("java.util.Iterator")) { //$NON-NLS-1$
+ if (!methodName.equals("hasNext")) //$NON-NLS-1$
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+ return checkIteratorCondition();
+ } else {
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+ }
+
+ return StatusInfo.OK_STATUS;
+ }
+
+ private IStatus checkIteratorCondition() {
+
+ List initializers= getForStatement().initializers();
+ if (initializers.size() != 1)
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+
+ Expression expression= initializers.get(0);
+ if (!(expression instanceof VariableDeclarationExpression))
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+
+ VariableDeclarationExpression declaration= (VariableDeclarationExpression)expression;
+ List variableDeclarationFragments= declaration.fragments();
+ if (variableDeclarationFragments.size() != 1)
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+
+ VariableDeclarationFragment declarationFragment= variableDeclarationFragments.get(0);
+
+ Expression initializer= declarationFragment.getInitializer();
+ if (!(initializer instanceof MethodInvocation))
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+
+ MethodInvocation methodInvocation= (MethodInvocation)initializer;
+ String methodName= methodInvocation.getName().getIdentifier();
+ if (!"iterator".equals(methodName) || methodInvocation.arguments().size() != 0) //$NON-NLS-1$
+ return SEMANTIC_CHANGE_WARNING_STATUS;
+
+ return StatusInfo.OK_STATUS;
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopFixCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - copied and modified to create core class
+ *******************************************************************************/
+/**
+ *
+ **/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+
+public class ConvertLoopFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ public final static class ControlStatementFinder extends GenericVisitor {
+
+ private final List fResult;
+ private final Hashtable fUsedNames;
+ private final boolean fFindForLoopsToConvert;
+ private final boolean fConvertIterableForLoops;
+ private final boolean fMakeFinal;
+
+ public ControlStatementFinder(boolean findForLoopsToConvert, boolean convertIterableForLoops, boolean makeFinal, List resultingCollection) {
+ fFindForLoopsToConvert= findForLoopsToConvert;
+ fConvertIterableForLoops= convertIterableForLoops;
+ fMakeFinal= makeFinal;
+ fResult= resultingCollection;
+ fUsedNames= new Hashtable<>();
+ }
+
+ @Override
+ public boolean visit(ForStatement node) {
+ if (fFindForLoopsToConvert || fConvertIterableForLoops) {
+ ForStatement current= node;
+ ConvertLoopOperation operation= getConvertOperation(current);
+ ConvertLoopOperation oldOperation= null;
+ while (operation != null) {
+ if (oldOperation == null) {
+ fResult.add(operation);
+ } else {
+ oldOperation.setBodyConverter(operation);
+ }
+
+ if (current.getBody() instanceof ForStatement) {
+ current= (ForStatement)current.getBody();
+ oldOperation= operation;
+ operation= getConvertOperation(current);
+ } else {
+ operation= null;
+ }
+ }
+ current.getBody().accept(this);
+ return false;
+ }
+
+ return super.visit(node);
+ }
+
+ private ConvertLoopOperation getConvertOperation(ForStatement node) {
+
+ Collection usedNamesCollection= fUsedNames.values();
+ String[] usedNames= usedNamesCollection.toArray(new String[usedNamesCollection.size()]);
+ ConvertLoopOperation convertForLoopOperation= new ConvertForLoopOperation(node, usedNames, fMakeFinal);
+ if (convertForLoopOperation.satisfiesPreconditions().isOK()) {
+ if (fFindForLoopsToConvert) {
+ fUsedNames.put(node, convertForLoopOperation.getIntroducedVariableName());
+ return convertForLoopOperation;
+ }
+ } else if (fConvertIterableForLoops) {
+ ConvertLoopOperation iterableConverter= new ConvertIterableLoopOperation(node, usedNames, fMakeFinal);
+ if (iterableConverter.satisfiesPreconditions().isOK()) {
+ fUsedNames.put(node, iterableConverter.getIntroducedVariableName());
+ return iterableConverter;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void endVisit(ForStatement node) {
+ if (fFindForLoopsToConvert || fConvertIterableForLoops) {
+ fUsedNames.remove(node);
+ }
+ super.endVisit(node);
+ }
+
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, boolean convertForLoops, boolean convertIterableForLoops, boolean makeFinal) {
+ if (!JavaModelUtil.is50OrHigher(compilationUnit.getJavaElement().getJavaProject()))
+ return null;
+
+ if (!convertForLoops && !convertIterableForLoops)
+ return null;
+
+ List operations= new ArrayList<>();
+ ControlStatementFinder finder= new ControlStatementFinder(convertForLoops, convertIterableForLoops, makeFinal, operations);
+ compilationUnit.accept(finder);
+
+ if (operations.isEmpty())
+ return null;
+
+ CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] ops= operations.toArray(new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[operations.size()]);
+ return new ConvertLoopFixCore(FixMessages.ControlStatementsFix_change_name, compilationUnit, ops, null);
+ }
+
+ public static ConvertLoopFixCore createConvertForLoopToEnhancedFix(CompilationUnit compilationUnit, ForStatement loop) {
+ ConvertLoopOperation convertForLoopOperation= new ConvertForLoopOperation(loop);
+ if (!convertForLoopOperation.satisfiesPreconditions().isOK())
+ return null;
+
+ return new ConvertLoopFixCore(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description, compilationUnit, new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] {convertForLoopOperation}, null);
+ }
+
+ public static ConvertLoopFixCore createConvertIterableLoopToEnhancedFix(CompilationUnit compilationUnit, ForStatement loop) {
+ ConvertIterableLoopOperation loopConverter= new ConvertIterableLoopOperation(loop);
+ IStatus status= loopConverter.satisfiesPreconditions();
+ if (status.getSeverity() == IStatus.ERROR)
+ return null;
+
+ return new ConvertLoopFixCore(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description, compilationUnit, new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] {loopConverter}, status);
+ }
+
+ private final IStatus fStatus;
+
+ protected ConvertLoopFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] fixRewriteOperations, IStatus status) {
+ super(name, compilationUnit, fixRewriteOperations);
+ fStatus= status;
+ }
+
+ @Override
+ public IStatus getStatus() {
+ return fStatus;
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopOperation.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopOperation.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopOperation.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ConvertLoopOperation.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - refactored to jdt.core.manipulation
+ *******************************************************************************/
+/**
+ *
+ **/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+
+public abstract class ConvertLoopOperation extends CompilationUnitRewriteOperation {
+
+ protected static final String FOR_LOOP_ELEMENT_IDENTIFIER= "element"; //$NON-NLS-1$
+
+ protected static final IStatus ERROR_STATUS= new Status(IStatus.ERROR, JavaManipulationPlugin.getPluginId(), ""); //$NON-NLS-1$
+
+ private final ForStatement fStatement;
+ private ConvertLoopOperation fOperation;
+ private final String[] fUsedNames;
+
+ public ConvertLoopOperation(ForStatement statement, String[] usedNames) {
+ fStatement= statement;
+ fUsedNames= usedNames;
+ }
+
+ public void setBodyConverter(ConvertLoopOperation operation) {
+ fOperation= operation;
+ }
+
+ public abstract String getIntroducedVariableName();
+
+ public abstract IStatus satisfiesPreconditions();
+
+ protected abstract Statement convert(CompilationUnitRewrite cuRewrite, TextEditGroup group, LinkedProposalModelCore positionGroups) throws CoreException;
+
+ protected ForStatement getForStatement() {
+ return fStatement;
+ }
+
+ protected Statement getBody(CompilationUnitRewrite cuRewrite, TextEditGroup group, LinkedProposalModelCore positionGroups) throws CoreException {
+ if (fOperation != null) {
+ return fOperation.convert(cuRewrite, group, positionGroups);
+ } else {
+ return (Statement)cuRewrite.getASTRewrite().createMoveTarget(getForStatement().getBody());
+ }
+ }
+
+ protected String[] getUsedVariableNames() {
+ final List results= new ArrayList<>();
+
+ ForStatement forStatement= getForStatement();
+ CompilationUnit root= (CompilationUnit)forStatement.getRoot();
+
+ Collection variableNames= new ScopeAnalyzer(root).getUsedVariableNames(forStatement.getStartPosition(), forStatement.getLength());
+ results.addAll(variableNames);
+
+ forStatement.accept(new GenericVisitor() {
+ @Override
+ public boolean visit(SingleVariableDeclaration node) {
+ results.add(node.getName().getIdentifier());
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(VariableDeclarationFragment fragment) {
+ results.add(fragment.getName().getIdentifier());
+ return super.visit(fragment);
+ }
+ });
+
+ results.addAll(Arrays.asList(fUsedNames));
+
+ return results.toArray(new String[results.size()]);
+ }
+
+}
\ No newline at end of file
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java" 2020-02-26 15:31:57.000000000 +0000
@@ -96,6 +96,7 @@
public static String Java50Fix_SerialVersion_hash_description;
public static String Java50Fix_InitializeSerialVersionId_subtask_description;
public static String Java50Fix_SerialVersion_CalculateHierarchy_description;
+ public static String Java50Fix_RemoveUnnecessaryArrayCreation_description;
public static String StringFix_AddRemoveNonNls_description;
public static String StringFix_AddNonNls_description;
@@ -151,7 +152,7 @@
public static String TypeParametersFix_insert_inferred_type_arguments_name;
public static String TypeParametersFix_remove_redundant_type_arguments_description;
public static String TypeParametersFix_remove_redundant_type_arguments_name;
-
+
public static String TypeAnnotationFix_move;
public static String TypeAnnotationFix_remove;
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties" 2020-02-26 15:31:57.000000000 +0000
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2005, 2018 IBM Corporation and others.
+# Copyright (c) 2005, 2019 IBM Corporation and others.
#
# This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
@@ -62,6 +62,7 @@
Java50Fix_add_annotations_change_name=Add Annotations
Java50Fix_add_type_parameters_change_name=Add type arguments
Java50Fix_AddOverride_description=Add missing @Override annotation
+Java50Fix_RemoveUnnecessaryArrayCreation_description=Remove unnecessary array creation
StringFix_AddRemoveNonNls_description=Add/Remove '$NON-NLS$' tag
StringFix_AddNonNls_description=Add missing '$NON-NLS$' tag
StringFix_RemoveNonNls_description=Remove unnecessary '$NON-NLS$' tag
@@ -104,7 +105,7 @@
CleanUpPostSaveListener_name=Code Clean Up
CleanUpPostSaveListener_SaveAction_ChangeName=Save Actions
-CleanUpPostSaveListener_SlowCleanUpDialog_link=If this happens again we recommend to disabled the corresponding save actions on the 'Save Actions' preference page.
+CleanUpPostSaveListener_SlowCleanUpDialog_link=If this happens again we recommend to disable the corresponding save actions on the 'Save Actions' preference page.
CleanUpPostSaveListener_SlowCleanUpDialog_title=Slow Save Actions
CleanUpPostSaveListener_SlowCleanUpWarningDialog_explain=Some of the following save actions are very slow:
CleanUpPostSaveListener_unknown_profile_error_message=Clean Up save participant could not retrieve profile with id ''{0}''
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpCore.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -21,42 +21,46 @@
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.manipulation.CleanUpContextCore;
+import org.eclipse.jdt.core.manipulation.CleanUpOptionsCore;
+import org.eclipse.jdt.core.manipulation.CleanUpRequirementsCore;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
/**
* A clean up solves problems in a compilation unit.
*
- * The clean up is asked for its requirements through a call to {@link #getRequirements()}. The
+ * The clean up is asked for its requirements through a call to {@link #getRequirementsCore()}. The
* clean up can request an AST and define how to build this AST. It can base its requirements on the
- * options passed through {@link #setOptions(CleanUpOptions)}.
+ * options passed through {@link #setOptions(CleanUpOptionsCore)}.
*
*
* A context containing the information requested by the requirements are passed to
- * {@link #createFix(CleanUpContext)}. A fix capable of fixing the problems is returned by this
+ * {@link #createFixCore(CleanUpContextCore)}. A fix capable of fixing the problems is returned by this
* function if {@link #checkPreConditions(IJavaProject, ICompilationUnit[], IProgressMonitor)} has
* returned a non fatal error status.
*
*
* At the end {@link #checkPostConditions(IProgressMonitor)} is called.
*
- *
+ *
* @since 3.5
*/
public interface ICleanUpCore {
/**
* Sets the options that will be used.
- *
+ *
* @param options the options to use
*/
- void setOptions(CleanUpOptions options);
+ void setOptions(CleanUpOptionsCore options);
/**
* Human readable description for each step this clean up will execute.
*
* Note: This method must only be called after the options have been set.
*
- *
+ *
* @return descriptions an array of {@linkplain String strings} or null
*/
String[] getStepDescriptions();
@@ -66,16 +70,16 @@
*
* Note: This method must only be called after the options have been set.
*
- *
- * @return the requirements used for {@link #createFix(CleanUpContext)} to work
+ *
+ * @return the requirements used for {@link #createFixCore(CleanUpContextCore)} to work
*/
- CleanUpRequirements getRequirements();
+ CleanUpRequirementsCore getRequirementsCore();
/**
* After call to checkPreConditions clients will start creating fixes for
* compilationUnits
in project
unless the result of checkPreConditions
* contains a fatal error
- *
+ *
* @param project the project to clean up
* @param compilationUnits an array of compilation units to clean up, all member of project
* @param monitor the monitor to show progress
@@ -87,16 +91,16 @@
/**
* Create an ICleanUpFixCore
which fixes all problems in context
or
* null
if nothing to fix.
- *
- * @param context a context containing all information requested by {@link #getRequirements()}
+ *
+ * @param context a context containing all information requested by {@link #getRequirementsCore()}
* @return the fix for the problems or null
if nothing to fix
* @throws CoreException if an unexpected error occurred
*/
- ICleanUpFixCore createFix(CleanUpContext context) throws CoreException;
+ ICleanUpFixCore createFixCore(CleanUpContextCore context) throws CoreException;
/**
* Called when done cleaning up.
- *
+ *
* @param monitor the monitor to show progress
* @return the result of the postcondition check, not null
* @throws CoreException if an unexpected error occurred
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpFixCore.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ICleanUpFixCore.java" 1970-01-01 00:00:00.000000000 +0000
@@ -1,41 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2018 IBM Corporation and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- * Red Hat Inc. - copied and renamed in jdt.core.manipulation
- *******************************************************************************/
-package org.eclipse.jdt.internal.corext.fix;
-
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-
-import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
-
-
-/**
- * A clean up fix calculates a {@link CompilationUnitChange} which can be applied on a document to
- * fix one or more problems in a compilation unit.
- *
- * @since 1.10
- */
-public interface ICleanUpFixCore {
-
- /**
- * Calculates and returns a {@link CompilationUnitChange} which can be applied on a document to
- * fix one or more problems in a compilation unit.
- *
- * @param progressMonitor the progress monitor or null
if none
- * @return a compilation unit change change which should not be empty
- * @throws CoreException if something went wrong while calculating the change
- */
- public CompilationUnitChange createChange(IProgressMonitor progressMonitor) throws CoreException;
-
-}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ILinkedFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ILinkedFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ILinkedFixCore.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/ILinkedFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -14,6 +14,8 @@
*******************************************************************************/
package org.eclipse.jdt.internal.corext.fix;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
/**
* A fix which when executed can set up a linked mode model
* and put an editor into linked mode.
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/IProposableFix.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/IProposableFix.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/IProposableFix.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/IProposableFix.java" 2020-02-26 15:31:57.000000000 +0000
@@ -16,6 +16,8 @@
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
/**
* A ICleanUpFix
which can be used in a
* correction proposal environment. A proposal
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFixCore.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2019 IBM Corporation and others.
+ * Copyright (c) 2013, 2020 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -39,10 +39,13 @@
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
@@ -50,6 +53,7 @@
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
@@ -65,6 +69,8 @@
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+import org.eclipse.jdt.core.util.IModifierConstants;
import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
@@ -199,6 +205,109 @@
}
}
+ public static final class FinalFieldAccessInFieldDeclarationFinder extends HierarchicalASTVisitor {
+
+ private MethodDeclaration fMethodDeclaration;
+ private ASTNode fFieldDeclaration;
+ static boolean hasReference(MethodDeclaration node) {
+ try {
+ FinalFieldAccessInFieldDeclarationFinder finder= new FinalFieldAccessInFieldDeclarationFinder();
+ ClassInstanceCreation cic= (ClassInstanceCreation) node.getParent().getParent();
+ cic.getType().resolveBinding();
+ finder.fMethodDeclaration= node;
+ finder.fFieldDeclaration= finder.findFieldDeclaration(node);
+ if (finder.fFieldDeclaration == null) {
+ return false;
+ }
+ node.accept(finder);
+ } catch (AbortSearchException e) {
+ return true;
+ }
+ return false;
+ }
+
+ private ASTNode findFieldDeclaration(ASTNode node) {
+ while (node != null) {
+ if (node instanceof FieldDeclaration) {
+ return node;
+ }
+ if (node instanceof AbstractTypeDeclaration) {
+ return null;
+ }
+ node= node.getParent();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean visit(AnonymousClassDeclaration node) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(BodyDeclaration node) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(MethodDeclaration node) {
+ return node == fMethodDeclaration;
+ }
+
+ private void checkForUninitializedFinalReference(IBinding binding) {
+ if (binding instanceof IVariableBinding) {
+ int modifiers= ((IVariableBinding)binding).getModifiers();
+ if ((modifiers & IModifierConstants.ACC_FINAL) == IModifierConstants.ACC_FINAL) {
+ if (((IVariableBinding) binding).isField()) {
+ ASTNode decl= ((CompilationUnit)fMethodDeclaration.getRoot()).findDeclaringNode(binding);
+ if (decl instanceof VariableDeclaration && ((VariableDeclaration)decl).getInitializer() == null) {
+ throw new AbortSearchException();
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ IVariableBinding binding= node.resolveFieldBinding();
+ if (binding == null) {
+ return true;
+ }
+ IVariableBinding decl= binding.getVariableDeclaration();
+ checkForUninitializedFinalReference(decl);
+ return true;
+ }
+
+ @Override
+ public boolean visit(SimpleName node) {
+ node.getParent();
+ IBinding binding= node.resolveBinding();
+ checkForUninitializedFinalReference(binding);
+ return true;
+ }
+
+ @Override
+ public boolean visit(QualifiedName node) {
+ node.getParent();
+ IBinding binding= node.resolveBinding();
+ checkForUninitializedFinalReference(binding);
+ return true;
+ }
+
+ @Override
+ public boolean visit(FieldAccess node) {
+ IVariableBinding binding= node.resolveFieldBinding();
+ if (binding == null) {
+ return true;
+ }
+ IVariableBinding decl= binding.getVariableDeclaration();
+ checkForUninitializedFinalReference(decl);
+ return true;
+ }
+
+ }
+
public static final class SuperThisQualifier extends HierarchicalASTVisitor {
private ITypeBinding fQualifierTypeBinding;
@@ -696,6 +805,13 @@
return false;
}
+ // lambda cannot access this and so we should avoid lambda conversion
+ // when anonymous class is used to initialize field and refers to
+ // final fields that may or may not be initialized
+ if (FinalFieldAccessInFieldDeclarationFinder.hasReference(methodDecl)) {
+ return false;
+ }
+
if (ASTNodes.getTargetType(node) == null) {
return false;
}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LinkedProposalPositionGroupCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LinkedProposalPositionGroupCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LinkedProposalPositionGroupCore.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/LinkedProposalPositionGroupCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -110,7 +110,7 @@
/**
* A position that contains all of the given tracked node positions.
- *
+ *
* @since 3.7
*/
public static class TrackedNodesPosition extends PositionInformation {
@@ -119,7 +119,7 @@
/**
* A position that contains all of the given tracked node positions.
- *
+ *
* @param pos the positions
*/
public TrackedNodesPosition(Collection pos) {
@@ -154,7 +154,7 @@
/**
* A position for the start of the given tracked node position.
- *
+ *
* @since 3.7
*/
public static class StartPositionInformation extends PositionInformation {
@@ -163,7 +163,7 @@
/**
* A position for the start of the given tracked node position.
- *
+ *
* @param pos the position
*/
public StartPositionInformation(ITrackedNodePosition pos) {
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/PotentialProgrammingProblemsFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/PotentialProgrammingProblemsFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/PotentialProgrammingProblemsFixCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/PotentialProgrammingProblemsFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,458 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - created based on PotentialProgrammingProblemsFix
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NameQualifiedType;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore;
+import org.eclipse.jdt.internal.ui.text.correction.ProblemLocationCore;
+import org.eclipse.jdt.internal.ui.text.correction.SerialVersionHashOperationCore;
+
+
+public class PotentialProgrammingProblemsFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ /** Name of the serializable class */
+ private static final String SERIALIZABLE_NAME= "java.io.Serializable"; //$NON-NLS-1$
+
+ /** The name of the serial version field */
+ private static final String NAME_FIELD= "serialVersionUID"; //$NON-NLS-1$
+
+ public interface ISerialVersionFixContext {
+ public RefactoringStatus initialize(IProgressMonitor monitor) throws CoreException;
+ public Long getSerialVersionId(ITypeBinding binding);
+ }
+
+ public static class SerialVersionHashContext implements ISerialVersionFixContext {
+
+ private final IJavaProject fProject;
+ private final ICompilationUnit[] fCompilationUnits;
+ private final Hashtable fIdsTable;
+
+ public SerialVersionHashContext(IJavaProject project, ICompilationUnit[] compilationUnits) {
+ fProject= project;
+ fCompilationUnits= compilationUnits;
+ fIdsTable= new Hashtable<>();
+ }
+
+ @Override
+ public RefactoringStatus initialize(IProgressMonitor monitor) throws CoreException {
+ if (monitor == null)
+ monitor= new NullProgressMonitor();
+
+ RefactoringStatus result;
+ try {
+ monitor.beginTask("", 10); //$NON-NLS-1$
+
+ IType[] types= findTypesWithMissingUID(fProject, fCompilationUnits, new SubProgressMonitor(monitor, 1));
+ if (types.length == 0)
+ return new RefactoringStatus();
+
+ fProject.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new SubProgressMonitor(monitor, 60));
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+
+ result= new RefactoringStatus();
+ ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
+ parser.setProject(fProject);
+ IBinding[] bindings= parser.createBindings(types, new SubProgressMonitor(monitor, 1));
+ for (int i= 0; i < bindings.length; i++) {
+ IBinding curr= bindings[i];
+ if (curr instanceof ITypeBinding) {
+ ITypeBinding typeBinding= (ITypeBinding) curr;
+ try {
+ Long id= SerialVersionHashOperationCore.calculateSerialVersionId(typeBinding, new SubProgressMonitor(monitor, 1));
+ if (id != null) {
+ setSerialVersionId(typeBinding, id);
+ } else {
+ result.addWarning(Messages.format(FixMessages.PotentialProgrammingProblemsFix_calculatingUIDFailed_unknown, BasicElementLabels.getJavaElementName(typeBinding.getName())));
+ }
+ } catch (IOException e) {
+ result.addWarning(Messages.format(FixMessages.PotentialProgrammingProblemsFix_calculatingUIDFailed_exception, new String[] { BasicElementLabels.getJavaElementName(typeBinding.getName()), e.getLocalizedMessage()}), JavaStatusContext.create(types[i]));
+ } catch (CoreException e) {
+ result.addWarning(Messages.format(FixMessages.PotentialProgrammingProblemsFix_calculatingUIDFailed_exception, new String[] { BasicElementLabels.getJavaElementName(typeBinding.getName()), e.getLocalizedMessage()}), JavaStatusContext.create(types[i]));
+ }
+ }
+ }
+ } finally {
+ monitor.done();
+ }
+ return result;
+ }
+
+ @Override
+ public Long getSerialVersionId(ITypeBinding binding) {
+ return fIdsTable.get(binding.getKey());
+ }
+
+ protected void setSerialVersionId(ITypeBinding binding, Long id) {
+ fIdsTable.put(binding.getKey(), id);
+ }
+
+ private IType[] findTypesWithMissingUID(IJavaProject project, ICompilationUnit[] compilationUnits, IProgressMonitor monitor) throws CoreException {
+ try {
+ monitor.beginTask("", compilationUnits.length); //$NON-NLS-1$
+
+ IType serializable= project.findType(SERIALIZABLE_NAME);
+
+ List types= new ArrayList<>();
+
+ if (compilationUnits.length > 500) {
+ //500 is a guess. Building the type hierarchy on serializable is very expensive
+ //depending on how many subtypes exit in the project.
+
+ HashSet cus= new HashSet<>();
+ cus.addAll(Arrays.asList(compilationUnits));
+
+ monitor.subTask(Messages.format(FixMessages.Java50Fix_SerialVersion_CalculateHierarchy_description, SERIALIZABLE_NAME));
+ ITypeHierarchy hierarchy1= serializable.newTypeHierarchy(project, new SubProgressMonitor(monitor, compilationUnits.length));
+ IType[] allSubtypes1= hierarchy1.getAllSubtypes(serializable);
+ addTypes(allSubtypes1, cus, types);
+ } else {
+ monitor.subTask(FixMessages.Java50Fix_InitializeSerialVersionId_subtask_description);
+ for (int i= 0; i < compilationUnits.length; i++) {
+ collectChildrenWithMissingSerialVersionId(compilationUnits[i].getChildren(), serializable, types);
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ monitor.worked(1);
+ }
+ }
+
+ return types.toArray(new IType[types.size()]);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ private void addTypes(IType[] allSubtypes, HashSet cus, List types) throws JavaModelException {
+ for (int i= 0; i < allSubtypes.length; i++) {
+ IType type= allSubtypes[i];
+
+ IField field= type.getField(NAME_FIELD);
+ if (!field.exists()) {
+ if (type.isClass() && cus.contains(type.getCompilationUnit())){
+ types.add(type);
+ }
+ }
+ }
+ }
+
+ private void collectChildrenWithMissingSerialVersionId(IJavaElement[] children, IType serializable, List result) throws JavaModelException {
+ for (int i= 0; i < children.length; i++) {
+ IJavaElement child= children[i];
+ if (child instanceof IType) {
+ IType type= (IType)child;
+
+ if (type.isClass()) {
+ IField field= type.getField(NAME_FIELD);
+ if (!field.exists()) {
+ ITypeHierarchy hierarchy= type.newSupertypeHierarchy(new NullProgressMonitor());
+ IType[] interfaces= hierarchy.getAllSuperInterfaces(type);
+ for (int j= 0; j < interfaces.length; j++) {
+ if (interfaces[j].equals(serializable)) {
+ result.add(type);
+ break;
+ }
+ }
+ }
+ }
+
+ collectChildrenWithMissingSerialVersionId(type.getChildren(), serializable, result);
+ } else if (child instanceof IMethod) {
+ collectChildrenWithMissingSerialVersionId(((IMethod)child).getChildren(), serializable, result);
+ } else if (child instanceof IField) {
+ collectChildrenWithMissingSerialVersionId(((IField)child).getChildren(), serializable, result);
+ }
+ }
+ }
+ }
+
+ public static class SerialVersionHashBatchOperation extends AbstractSerialVersionOperationCore {
+
+ private final ISerialVersionFixContext fContext;
+
+ protected SerialVersionHashBatchOperation(ICompilationUnit unit, ASTNode[] node, ISerialVersionFixContext context) {
+ super(unit, node);
+ fContext= context;
+ }
+
+ @Override
+ protected boolean addInitializer(VariableDeclarationFragment fragment, ASTNode declarationNode) {
+ ITypeBinding typeBinding= getTypeBinding(declarationNode);
+ if (typeBinding == null)
+ return false;
+
+ Long id= fContext.getSerialVersionId(typeBinding);
+ if (id == null)
+ return false;
+
+ fragment.setInitializer(fragment.getAST().newNumberLiteral(id.toString() + LONG_SUFFIX));
+ return true;
+ }
+
+ @Override
+ protected void addLinkedPositions(ASTRewrite rewrite, VariableDeclarationFragment fragment, LinkedProposalModelCore positionGroups) {}
+
+ }
+
+ private static ISerialVersionFixContext fCurrentContext;
+
+ public static IProposableFix[] createMissingSerialVersionFixes(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ if (problem.getProblemId() != IProblem.MissingSerialVersion)
+ return null;
+
+ final ICompilationUnit unit= (ICompilationUnit)compilationUnit.getJavaElement();
+ if (unit == null)
+ return null;
+
+ final SimpleName simpleName= getSelectedName(compilationUnit, problem);
+ if (simpleName == null)
+ return null;
+
+ ASTNode declaringNode= getDeclarationNode(simpleName);
+ if (declaringNode == null)
+ return null;
+
+ SerialVersionDefaultOperationCore defop= new SerialVersionDefaultOperationCore(unit, new ASTNode[] {declaringNode});
+ IProposableFix fix1= new PotentialProgrammingProblemsFixCore(FixMessages.Java50Fix_SerialVersion_default_description, compilationUnit, new CompilationUnitRewriteOperation[] {defop});
+
+ SerialVersionHashOperationCore hashop= new SerialVersionHashOperationCore(unit, new ASTNode[] {declaringNode});
+ IProposableFix fix2= new PotentialProgrammingProblemsFixCore(FixMessages.Java50Fix_SerialVersion_hash_description, compilationUnit, new CompilationUnitRewriteOperation[] {hashop});
+
+ return new IProposableFix[] {fix1, fix2};
+ }
+
+ public static RefactoringStatus checkPreConditions(IJavaProject project, ICompilationUnit[] compilationUnits, IProgressMonitor monitor,
+ boolean calculatedId,
+ boolean defaultId,
+ boolean randomId) throws CoreException {
+
+ if (defaultId) {
+ fCurrentContext= new ISerialVersionFixContext() {
+ @Override
+ public Long getSerialVersionId(ITypeBinding binding) {
+ return Long.valueOf(1);
+ }
+ @Override
+ public RefactoringStatus initialize(IProgressMonitor pm) throws CoreException {
+ return new RefactoringStatus();
+ }
+ };
+ return fCurrentContext.initialize(monitor);
+ } else if (randomId) {
+ fCurrentContext= new ISerialVersionFixContext() {
+ private Random rng;
+ @Override
+ public Long getSerialVersionId(ITypeBinding binding) {
+ return Long.valueOf(rng.nextLong());
+ }
+ @Override
+ public RefactoringStatus initialize(IProgressMonitor pm) throws CoreException {
+ rng= new Random((new Date()).getTime());
+ return new RefactoringStatus();
+ }
+ };
+ return fCurrentContext.initialize(monitor);
+ } else if (calculatedId) {
+ fCurrentContext= new SerialVersionHashContext(project, compilationUnits);
+ return fCurrentContext.initialize(monitor);
+ } else {
+ return new RefactoringStatus();
+ }
+ }
+
+ public static RefactoringStatus checkPostConditions(IProgressMonitor monitor) {
+ if (monitor != null)
+ monitor.done();
+
+ fCurrentContext= null;
+ return new RefactoringStatus();
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, boolean addSerialVersionIds) {
+
+ IProblem[] problems= compilationUnit.getProblems();
+ IProblemLocationCore[] locations= new IProblemLocationCore[problems.length];
+ for (int i= 0; i < problems.length; i++) {
+ locations[i]= new ProblemLocationCore(problems[i]);
+ }
+ return createCleanUp(compilationUnit, locations, addSerialVersionIds);
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, IProblemLocationCore[] problems, boolean addSerialVersionIds) {
+ if (addSerialVersionIds) {
+
+ final ICompilationUnit unit= (ICompilationUnit)compilationUnit.getJavaElement();
+ if (unit == null)
+ return null;
+
+ List declarationNodes= new ArrayList<>();
+ for (int i= 0; i < problems.length; i++) {
+ if (problems[i].getProblemId() == IProblem.MissingSerialVersion) {
+ final SimpleName simpleName= getSelectedName(compilationUnit, problems[i]);
+ if (simpleName != null) {
+ ASTNode declarationNode= getDeclarationNode(simpleName);
+ if (declarationNode != null) {
+ declarationNodes.add(declarationNode);
+ }
+ }
+ }
+ }
+ if (declarationNodes.size() == 0)
+ return null;
+
+ for (Iterator iter= declarationNodes.iterator(); iter.hasNext();) {
+ ASTNode declarationNode= iter.next();
+ ITypeBinding binding= getTypeBinding(declarationNode);
+ if (fCurrentContext.getSerialVersionId(binding) != null) {
+ SerialVersionHashBatchOperation op= new SerialVersionHashBatchOperation(unit, declarationNodes.toArray(new ASTNode[declarationNodes.size()]), fCurrentContext);
+ return new PotentialProgrammingProblemsFixCore(FixMessages.PotentialProgrammingProblemsFix_add_id_change_name, compilationUnit, new CompilationUnitRewriteOperation[] {op});
+ }
+ }
+ }
+ return null;
+ }
+
+ public static SimpleName getSelectedName(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ final ASTNode selection= problem.getCoveredNode(compilationUnit);
+ if (selection == null)
+ return null;
+
+ Name name= null;
+ if (selection instanceof SimpleType) {
+ name= ((SimpleType) selection).getName();
+ } else if (selection instanceof NameQualifiedType) {
+ name= ((NameQualifiedType) selection).getName();
+ } else if (selection instanceof QualifiedType) {
+ name= ((QualifiedType) selection).getName();
+ } else if (selection instanceof ParameterizedType) {
+ final ParameterizedType type= (ParameterizedType) selection;
+ final Type raw= type.getType();
+ if (raw instanceof SimpleType)
+ name= ((SimpleType) raw).getName();
+ else if (raw instanceof NameQualifiedType)
+ name= ((NameQualifiedType) raw).getName();
+ else if (raw instanceof QualifiedType)
+ name= ((QualifiedType) raw).getName();
+ } else if (selection instanceof Name) {
+ name= (Name) selection;
+ }
+ if (name == null)
+ return null;
+
+ if (name.isSimpleName()) {
+ return (SimpleName)name;
+ } else {
+ return ((QualifiedName)name).getName();
+ }
+ }
+
+ /**
+ * Returns the declaration node for the originally selected node.
+ * @param name the name of the node
+ *
+ * @return the declaration node
+ */
+ public static ASTNode getDeclarationNode(SimpleName name) {
+ ASTNode parent= name.getParent();
+ if (!(parent instanceof AbstractTypeDeclaration)) {
+
+ parent= parent.getParent();
+ if (parent instanceof ParameterizedType || parent instanceof Type)
+ parent= parent.getParent();
+ if (parent instanceof ClassInstanceCreation) {
+
+ final ClassInstanceCreation creation= (ClassInstanceCreation) parent;
+ parent= creation.getAnonymousClassDeclaration();
+ }
+ }
+ return parent;
+ }
+
+ /**
+ * Returns the type binding of the class declaration node.
+ *
+ * @param parent the node to get the type for
+ * @return the type binding
+ */
+ public static ITypeBinding getTypeBinding(final ASTNode parent) {
+ if (parent instanceof AbstractTypeDeclaration) {
+ final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) parent;
+ return declaration.resolveBinding();
+ } else if (parent instanceof AnonymousClassDeclaration) {
+ final AnonymousClassDeclaration declaration= (AnonymousClassDeclaration) parent;
+ return declaration.resolveBinding();
+ } else if (parent instanceof ParameterizedType) {
+ final ParameterizedType type= (ParameterizedType) parent;
+ return type.resolveBinding();
+ }
+ return null;
+ }
+
+ protected PotentialProgrammingProblemsFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ super(name, compilationUnit, fixRewriteOperations);
+ }
+}
\ No newline at end of file
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SerialVersionDefaultOperationCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SerialVersionDefaultOperationCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SerialVersionDefaultOperationCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SerialVersionDefaultOperationCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - code modified from SerialVersionDefaultOperation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+
+/**
+ * Proposal for a default serial version id.
+ *
+ * @since 3.1
+ */
+public final class SerialVersionDefaultOperationCore extends AbstractSerialVersionOperationCore {
+
+ /** The initializer linked position group id */
+ private static final String GROUP_INITIALIZER= "initializer"; //$NON-NLS-1$
+
+ /**
+ * Creates a new serial version default proposal.
+ *
+ * @param unit
+ * the compilation unit
+ * @param nodes
+ * the originally selected nodes
+ */
+ public SerialVersionDefaultOperationCore(ICompilationUnit unit, ASTNode[] nodes) {
+ super(unit, nodes);
+ }
+
+
+ @Override
+ protected boolean addInitializer(final VariableDeclarationFragment fragment, final ASTNode declarationNode) {
+ Assert.isNotNull(fragment);
+
+ final Expression expression= fragment.getAST().newNumberLiteral(DEFAULT_EXPRESSION);
+ if (expression != null)
+ fragment.setInitializer(expression);
+ return true;
+ }
+
+ @Override
+ protected void addLinkedPositions(final ASTRewrite rewrite, final VariableDeclarationFragment fragment, final LinkedProposalModelCore positionGroups) {
+
+ Assert.isNotNull(rewrite);
+ Assert.isNotNull(fragment);
+
+ final Expression initializer= fragment.getInitializer();
+ if (initializer != null) {
+ LinkedProposalPositionGroupCore group= positionGroups.createPositionGroup(GROUP_INITIALIZER);
+ group.addPosition(rewrite.track(initializer), true);
+ positionGroups.addPositionGroup(group);
+ }
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/TypeParametersFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/TypeParametersFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/TypeParametersFixCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/TypeParametersFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - copied and modified for use jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.util.NoCommentSourceRangeComputer;
+
+import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore;
+import org.eclipse.jdt.internal.ui.text.correction.ProblemLocationCore;
+
+public class TypeParametersFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ public static final class InsertTypeArgumentsVisitor extends ASTVisitor {
+
+ private final ArrayList fNodes;
+
+ public InsertTypeArgumentsVisitor(ArrayList nodes) {
+ fNodes= nodes;
+ }
+
+ @Override
+ public boolean visit(ParameterizedType createdType) {
+ if (createdType == null || createdType.typeArguments().size() != 0) {
+ return true;
+ }
+
+ ITypeBinding binding= createdType.resolveBinding();
+ if (binding == null) {
+ return true;
+ }
+
+ ITypeBinding[] typeArguments= binding.getTypeArguments();
+ if (typeArguments.length == 0) {
+ return true;
+ }
+
+ fNodes.add(createdType);
+ return true;
+ }
+ }
+
+ public static class InsertTypeArgumentsOperation extends CompilationUnitRewriteOperation {
+
+ private final ParameterizedType[] fCreatedTypes;
+
+ public InsertTypeArgumentsOperation(ParameterizedType[] parameterizedTypes) {
+ fCreatedTypes= parameterizedTypes;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ TextEditGroup group= createTextEditGroup(FixMessages.TypeParametersFix_insert_inferred_type_arguments_description, cuRewrite);
+
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+
+ for (ParameterizedType createdType : fCreatedTypes) {
+ ITypeBinding[] typeArguments= createdType.resolveBinding().getTypeArguments();
+ ContextSensitiveImportRewriteContext importContext= new ContextSensitiveImportRewriteContext(cuRewrite.getRoot(), createdType.getStartPosition(), importRewrite);
+
+ ListRewrite argumentsRewrite= rewrite.getListRewrite(createdType, ParameterizedType.TYPE_ARGUMENTS_PROPERTY);
+ for (ITypeBinding typeArgument : typeArguments) {
+ Type argumentNode= importRewrite.addImport(typeArgument, ast, importContext, TypeLocation.TYPE_ARGUMENT);
+ argumentsRewrite.insertLast(argumentNode, group);
+ }
+ }
+ }
+ }
+
+ public static class RemoveTypeArgumentsOperation extends CompilationUnitRewriteOperation {
+
+ private final ParameterizedType fParameterizedType;
+
+ public RemoveTypeArgumentsOperation(ParameterizedType parameterizedType) {
+ fParameterizedType= parameterizedType;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ TextEditGroup group= createTextEditGroup(FixMessages.TypeParametersFix_remove_redundant_type_arguments_description, cuRewrite);
+
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ rewrite.setTargetSourceRangeComputer(new NoCommentSourceRangeComputer());
+
+ ListRewrite listRewrite= rewrite.getListRewrite(fParameterizedType, ParameterizedType.TYPE_ARGUMENTS_PROPERTY);
+
+ List typeArguments= fParameterizedType.typeArguments();
+ for (Type typeArgument : typeArguments) {
+ listRewrite.remove(typeArgument, group);
+ }
+ }
+ }
+
+ public static TypeParametersFixCore createInsertInferredTypeArgumentsFix(CompilationUnit compilationUnit, ParameterizedType node) {
+ if (node == null)
+ return null;
+
+ final ArrayList changedNodes= new ArrayList<>();
+ node.accept(new InsertTypeArgumentsVisitor(changedNodes));
+
+ if (changedNodes.isEmpty())
+ return null;
+
+ CompilationUnitRewriteOperation op= new InsertTypeArgumentsOperation(new ParameterizedType[] { node });
+ return new TypeParametersFixCore(FixMessages.TypeParametersFix_insert_inferred_type_arguments_name, compilationUnit, new CompilationUnitRewriteOperation[] { op });
+ }
+
+ public static TypeParametersFixCore createRemoveRedundantTypeArgumentsFix(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ int id= problem.getProblemId();
+ if (id == IProblem.RedundantSpecificationOfTypeArguments) {
+ ParameterizedType parameterizedType= getParameterizedType(compilationUnit, problem);
+ if (parameterizedType == null)
+ return null;
+ RemoveTypeArgumentsOperation operation= new RemoveTypeArgumentsOperation(parameterizedType);
+ return new TypeParametersFixCore(FixMessages.TypeParametersFix_remove_redundant_type_arguments_name, compilationUnit, new CompilationUnitRewriteOperation[] { operation });
+ }
+ return null;
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, boolean insertInferredTypeArguments, boolean removeRedundantTypeArguments) {
+
+ IProblem[] problems= compilationUnit.getProblems();
+ IProblemLocationCore[] locations= new IProblemLocationCore[problems.length];
+ for (int i= 0; i < problems.length; i++) {
+ locations[i]= new ProblemLocationCore(problems[i]);
+ }
+
+ return createCleanUp(compilationUnit, locations,
+ insertInferredTypeArguments,
+ removeRedundantTypeArguments);
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, IProblemLocationCore[] problems, boolean insertInferredTypeArguments, boolean removeRedundantTypeArguments) {
+
+ if (insertInferredTypeArguments) {
+ final ArrayList changedNodes= new ArrayList<>();
+ compilationUnit.accept(new InsertTypeArgumentsVisitor(changedNodes));
+
+ if (changedNodes.isEmpty())
+ return null;
+
+ CompilationUnitRewriteOperation op= new InsertTypeArgumentsOperation(changedNodes.toArray(new ParameterizedType[changedNodes.size()]));
+ return new TypeParametersFixCore(FixMessages.TypeParametersFix_insert_inferred_type_arguments_name, compilationUnit, new CompilationUnitRewriteOperation[] { op });
+
+ } else if (removeRedundantTypeArguments) {
+ List result= new ArrayList<>();
+ for (IProblemLocationCore problem : problems) {
+ int id= problem.getProblemId();
+
+ if (id == IProblem.RedundantSpecificationOfTypeArguments) {
+ ParameterizedType parameterizedType= getParameterizedType(compilationUnit, problem);
+ if (parameterizedType == null)
+ return null;
+ result.add(new RemoveTypeArgumentsOperation(parameterizedType));
+ }
+ }
+ if (!result.isEmpty()) {
+ return new TypeParametersFixCore(FixMessages.TypeParametersFix_remove_redundant_type_arguments_name, compilationUnit, result.toArray(new CompilationUnitRewriteOperation[result.size()]));
+ }
+ }
+ return null;
+ }
+
+ public static ParameterizedType getParameterizedType(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+ if (selectedNode == null)
+ return null;
+
+ while (!(selectedNode instanceof ParameterizedType) && !(selectedNode instanceof Statement)) {
+ selectedNode= selectedNode.getParent();
+ }
+ if (selectedNode instanceof ParameterizedType) {
+ return (ParameterizedType) selectedNode;
+ }
+ return null;
+ }
+
+ protected TypeParametersFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ super(name, compilationUnit, fixRewriteOperations);
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnimplementedCodeFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnimplementedCodeFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnimplementedCodeFixCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnimplementedCodeFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - copied and modified for use in jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages;
+import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore;
+
+public class UnimplementedCodeFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ public static final class MakeTypeAbstractOperation extends CompilationUnitRewriteOperation {
+
+ private final TypeDeclaration fTypeDeclaration;
+
+ public MakeTypeAbstractOperation(TypeDeclaration typeDeclaration) {
+ fTypeDeclaration= typeDeclaration;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore linkedProposalPositions) throws CoreException {
+ AST ast= cuRewrite.getAST();
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ Modifier newModifier= ast.newModifier(Modifier.ModifierKeyword.ABSTRACT_KEYWORD);
+ TextEditGroup textEditGroup= createTextEditGroup(CorrectionMessages.UnimplementedCodeFix_TextEditGroup_label, cuRewrite);
+ rewrite.getListRewrite(fTypeDeclaration, TypeDeclaration.MODIFIERS2_PROPERTY).insertLast(newModifier, textEditGroup);
+
+ LinkedProposalPositionGroupCore group= new LinkedProposalPositionGroupCore("modifier"); //$NON-NLS-1$
+ group.addPosition(rewrite.track(newModifier), !linkedProposalPositions.hasLinkedPositions());
+ linkedProposalPositions.addPositionGroup(group);
+ }
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit root, boolean addMissingMethod, boolean makeTypeAbstract, IProblemLocationCore[] problems) {
+ Assert.isLegal(!addMissingMethod || !makeTypeAbstract);
+ if (!addMissingMethod && !makeTypeAbstract)
+ return null;
+
+ if (problems.length == 0)
+ return null;
+
+ ArrayList operations= new ArrayList<>();
+
+ for (IProblemLocationCore problem : problems) {
+ if (addMissingMethod) {
+ ASTNode typeNode= getSelectedTypeNode(root, problem);
+ if (typeNode != null && !isTypeBindingNull(typeNode)) {
+ operations.add(new AddUnimplementedMethodsOperation(typeNode));
+ }
+ } else {
+ ASTNode typeNode= getSelectedTypeNode(root, problem);
+ if (typeNode instanceof TypeDeclaration) {
+ operations.add(new MakeTypeAbstractOperation((TypeDeclaration) typeNode));
+ }
+ }
+ }
+
+ if (operations.isEmpty())
+ return null;
+
+ String label;
+ if (addMissingMethod) {
+ label= CorrectionMessages.UnimplementedMethodsCorrectionProposal_description;
+ } else {
+ label= CorrectionMessages.UnimplementedCodeFix_MakeAbstractFix_label;
+ }
+ return new UnimplementedCodeFixCore(label, root, operations.toArray(new CompilationUnitRewriteOperation[operations.size()]));
+ }
+
+ public static IProposableFix createAddUnimplementedMethodsFix(final CompilationUnit root, IProblemLocationCore problem) {
+ ASTNode typeNode= getSelectedTypeNode(root, problem);
+ if (typeNode == null)
+ return null;
+
+ if (isTypeBindingNull(typeNode))
+ return null;
+
+ AddUnimplementedMethodsOperation operation= new AddUnimplementedMethodsOperation(typeNode);
+ if (operation.getMethodsToImplement().length > 0) {
+ return new UnimplementedCodeFixCore(CorrectionMessages.UnimplementedMethodsCorrectionProposal_description, root, new CompilationUnitRewriteOperation[] { operation });
+ }
+ return null;
+ }
+
+ public static UnimplementedCodeFixCore createMakeTypeAbstractFix(CompilationUnit root, IProblemLocationCore problem) {
+ ASTNode typeNode= getSelectedTypeNode(root, problem);
+ if (!(typeNode instanceof TypeDeclaration))
+ return null;
+
+ TypeDeclaration typeDeclaration= (TypeDeclaration) typeNode;
+ MakeTypeAbstractOperation operation= new MakeTypeAbstractOperation(typeDeclaration);
+
+ String label= Messages.format(CorrectionMessages.ModifierCorrectionSubProcessor_addabstract_description, BasicElementLabels.getJavaElementName(typeDeclaration.getName().getIdentifier()));
+ return new UnimplementedCodeFixCore(label, root, new CompilationUnitRewriteOperation[] { operation });
+ }
+
+ public static ASTNode getSelectedTypeNode(CompilationUnit root, IProblemLocationCore problem) {
+ ASTNode selectedNode= problem.getCoveringNode(root);
+ if (selectedNode == null)
+ return null;
+
+ if (selectedNode.getNodeType() == ASTNode.ANONYMOUS_CLASS_DECLARATION) { // bug 200016
+ selectedNode= selectedNode.getParent();
+ }
+
+ if (selectedNode.getLocationInParent() == EnumConstantDeclaration.NAME_PROPERTY) {
+ selectedNode= selectedNode.getParent();
+ }
+ if (selectedNode.getNodeType() == ASTNode.SIMPLE_NAME && selectedNode.getParent() instanceof AbstractTypeDeclaration) {
+ return selectedNode.getParent();
+ } else if (selectedNode.getNodeType() == ASTNode.CLASS_INSTANCE_CREATION) {
+ return ((ClassInstanceCreation) selectedNode).getAnonymousClassDeclaration();
+ } else if (selectedNode.getNodeType() == ASTNode.ENUM_CONSTANT_DECLARATION) {
+ EnumConstantDeclaration enumConst= (EnumConstantDeclaration) selectedNode;
+ if (enumConst.getAnonymousClassDeclaration() != null)
+ return enumConst.getAnonymousClassDeclaration();
+ return enumConst;
+ } else {
+ return null;
+ }
+ }
+
+ private static boolean isTypeBindingNull(ASTNode typeNode) {
+ if (typeNode instanceof AbstractTypeDeclaration) {
+ AbstractTypeDeclaration abstractTypeDeclaration= (AbstractTypeDeclaration) typeNode;
+ if (abstractTypeDeclaration.resolveBinding() == null)
+ return true;
+
+ return false;
+ } else if (typeNode instanceof AnonymousClassDeclaration) {
+ AnonymousClassDeclaration anonymousClassDeclaration= (AnonymousClassDeclaration) typeNode;
+ if (anonymousClassDeclaration.resolveBinding() == null)
+ return true;
+
+ return false;
+ } else if (typeNode instanceof EnumConstantDeclaration) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public UnimplementedCodeFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ super(name, compilationUnit, fixRewriteOperations);
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnusedCodeFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnusedCodeFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnusedCodeFixCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnusedCodeFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,957 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat Inc. - copied and modified for use in jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConditionalExpression;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.PostfixExpression;
+import org.eclipse.jdt.core.dom.PrefixExpression;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.core.manipulation.CleanUpOptionsCore;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.core.manipulation.dom.NecessaryParenthesesChecker;
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
+import org.eclipse.jdt.internal.corext.dom.StatementRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUpCore;
+import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore;
+import org.eclipse.jdt.internal.ui.text.correction.JavadocTagsSubProcessorCore;
+import org.eclipse.jdt.internal.ui.text.correction.ProblemLocationCore;
+
+/**
+ * Fix which removes unused code.
+ */
+public class UnusedCodeFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ public static class SideEffectFinder extends ASTVisitor {
+
+ private final ArrayList fSideEffectNodes;
+
+ public SideEffectFinder(ArrayList res) {
+ fSideEffectNodes= res;
+ }
+
+ @Override
+ public boolean visit(Assignment node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(PostfixExpression node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(PrefixExpression node) {
+ Object operator= node.getOperator();
+ if (operator == PrefixExpression.Operator.INCREMENT || operator == PrefixExpression.Operator.DECREMENT) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(ClassInstanceCreation node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+ }
+
+ public static class RemoveImportOperation extends CompilationUnitRewriteOperation {
+
+ private final ImportDeclaration fImportDeclaration;
+
+ public RemoveImportOperation(ImportDeclaration importDeclaration) {
+ fImportDeclaration= importDeclaration;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ ImportDeclaration node= fImportDeclaration;
+ TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveImport_description, cuRewrite);
+ cuRewrite.getASTRewrite().remove(node, group);
+ }
+
+ }
+
+ /**
+ * Removes the unused type parameter.
+ *
+ */
+ public static class RemoveUnusedTypeParameterOperation extends CompilationUnitRewriteOperation {
+ private final SimpleName fUnusedName;
+
+ public RemoveUnusedTypeParameterOperation(SimpleName unusedName) {
+ fUnusedName= unusedName;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ IBinding binding= fUnusedName.resolveBinding();
+ CompilationUnit root= (CompilationUnit) fUnusedName.getRoot();
+ String displayString= FixMessages.UnusedCodeFix_RemoveUnusedTypeParameter_description;
+ TextEditGroup group= createTextEditGroup(displayString, cuRewrite);
+
+ if (binding.getKind() == IBinding.TYPE) {
+ ITypeBinding decl= ((ITypeBinding) binding).getTypeDeclaration();
+ ASTNode declaration= root.findDeclaringNode(decl);
+ if (declaration.getParent() instanceof TypeDeclarationStatement) {
+ declaration= declaration.getParent();
+ }
+ rewrite.remove(declaration, group);
+ }
+ }
+ }
+
+ public static class RemoveUnusedMemberOperation extends CompilationUnitRewriteOperation {
+
+ private final SimpleName[] fUnusedNames;
+ private boolean fForceRemove;
+ private int fRemovedAssignmentsCount;
+ private int fAlteredAssignmentsCount;
+
+ public RemoveUnusedMemberOperation(SimpleName[] unusedNames, boolean removeAllAsignements) {
+ fUnusedNames= unusedNames;
+ fForceRemove= removeAllAsignements;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ for (SimpleName unusedName : fUnusedNames) {
+ removeUnusedName(cuRewrite, unusedName);
+ }
+ }
+
+ private void removeUnusedName(CompilationUnitRewrite cuRewrite, SimpleName simpleName) {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ CompilationUnit completeRoot= cuRewrite.getRoot();
+
+ IBinding binding= simpleName.resolveBinding();
+ CompilationUnit root= (CompilationUnit) simpleName.getRoot();
+ String displayString= getDisplayString(binding);
+ TextEditGroup group= createTextEditGroup(displayString, cuRewrite);
+ if (binding.getKind() == IBinding.METHOD) {
+ IMethodBinding decl= ((IMethodBinding) binding).getMethodDeclaration();
+ ASTNode declaration= root.findDeclaringNode(decl);
+ rewrite.remove(declaration, group);
+ } else if (binding.getKind() == IBinding.TYPE) {
+ ITypeBinding decl= ((ITypeBinding) binding).getTypeDeclaration();
+ ASTNode declaration= root.findDeclaringNode(decl);
+ if (declaration.getParent() instanceof TypeDeclarationStatement) {
+ declaration= declaration.getParent();
+ }
+ rewrite.remove(declaration, group);
+ } else if (binding.getKind() == IBinding.VARIABLE) {
+ SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
+ SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
+ for (int i= 0; i < references.length; i++) {
+ removeVariableReferences(rewrite, references[i], group);
+ }
+
+ IVariableBinding bindingDecl= ((IVariableBinding) nameNode.resolveBinding()).getVariableDeclaration();
+ ASTNode declaringNode= completeRoot.findDeclaringNode(bindingDecl);
+ if (declaringNode instanceof SingleVariableDeclaration) {
+ removeParamTag(rewrite, (SingleVariableDeclaration) declaringNode, group);
+ }
+ } else {
+ // unexpected
+ }
+ }
+
+ private String getDisplayString(IBinding binding) {
+ switch (binding.getKind()) {
+ case IBinding.TYPE:
+ return FixMessages.UnusedCodeFix_RemoveUnusedType_description;
+ case IBinding.METHOD:
+ if (((IMethodBinding) binding).isConstructor()) {
+ return FixMessages.UnusedCodeFix_RemoveUnusedConstructor_description;
+ } else {
+ return FixMessages.UnusedCodeFix_RemoveUnusedPrivateMethod_description;
+ }
+ case IBinding.VARIABLE:
+ if (((IVariableBinding) binding).isField()) {
+ return FixMessages.UnusedCodeFix_RemoveUnusedField_description;
+ } else {
+ return FixMessages.UnusedCodeFix_RemoveUnusedVariabl_description;
+ }
+ default:
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ private void removeParamTag(ASTRewrite rewrite, SingleVariableDeclaration varDecl, TextEditGroup group) {
+ if (varDecl.getParent() instanceof MethodDeclaration) {
+ Javadoc javadoc= ((MethodDeclaration) varDecl.getParent()).getJavadoc();
+ if (javadoc != null) {
+ TagElement tagElement= JavadocTagsSubProcessorCore.findParamTag(javadoc, varDecl.getName().getIdentifier());
+ if (tagElement != null) {
+ rewrite.remove(tagElement, group);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the field or variable declaration including the initializer.
+ * @param rewrite the AST rewriter to use
+ * @param reference a reference to the variable to remove
+ * @param group the text edit group to use
+ */
+ private void removeVariableReferences(ASTRewrite rewrite, SimpleName reference, TextEditGroup group) {
+ ASTNode parent= reference.getParent();
+ while (parent instanceof QualifiedName) {
+ parent= parent.getParent();
+ }
+ if (parent instanceof FieldAccess) {
+ parent= parent.getParent();
+ }
+
+ int nameParentType= parent.getNodeType();
+ if (nameParentType == ASTNode.ASSIGNMENT) {
+ Assignment assignment= (Assignment) parent;
+ Expression rightHand= assignment.getRightHandSide();
+
+ ASTNode assignParent= assignment.getParent();
+ if (assignParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT && rightHand.getNodeType() != ASTNode.ASSIGNMENT) {
+ removeVariableWithInitializer(rewrite, rightHand, assignParent, group);
+ } else {
+ rewrite.replace(assignment, rewrite.createCopyTarget(rightHand), group);
+ }
+ } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
+ rewrite.remove(parent, group);
+ } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
+ VariableDeclarationFragment frag= (VariableDeclarationFragment) parent;
+ ASTNode varDecl= frag.getParent();
+ List fragments;
+ if (varDecl instanceof VariableDeclarationExpression) {
+ fragments= ((VariableDeclarationExpression) varDecl).fragments();
+ } else if (varDecl instanceof FieldDeclaration) {
+ fragments= ((FieldDeclaration) varDecl).fragments();
+ } else {
+ fragments= ((VariableDeclarationStatement) varDecl).fragments();
+ }
+ Expression initializer= frag.getInitializer();
+ ArrayList sideEffects= new ArrayList<>();
+ if (initializer != null) {
+ initializer.accept(new SideEffectFinder(sideEffects));
+ }
+
+ /*
+ * Special case for when the variable initializer is a conditional expression.
+ * Certain actions must be taken depending on where in the conditional the side effect expressions are located.
+ */
+ if (initializer instanceof ConditionalExpression && varDecl instanceof VariableDeclarationStatement) {
+ AST ast= rewrite.getAST();
+ ConditionalExpression ce= (ConditionalExpression) initializer;
+
+ // check if side effects and both expressions are to be removed then we remove whole statement
+ if (fForceRemove || (!checkSideEffects(sideEffects) &&
+ !checkCondtionalExpression(ce.getThenExpression()) &&
+ !checkCondtionalExpression(ce.getElseExpression()))) {
+ rewrite.remove(varDecl, group);
+ return;
+ }
+
+ IfStatement ifStatement= ast.newIfStatement();
+ ifStatement.setExpression((Expression) rewrite.createCopyTarget(getExpressionWithoutParenthezis(ce.getExpression())));
+
+ Block thenBlock= ast.newBlock();
+ // check if 'then' block contains code to keep
+ if (checkCondtionalExpression(ce.getThenExpression())) {
+ ASTNode thenExpression= rewrite.createCopyTarget(getExpressionWithoutParenthezis(ce.getThenExpression()));
+ thenBlock.statements().add(ast.newExpressionStatement((Expression) thenExpression));
+ }
+ ifStatement.setThenStatement(thenBlock);
+
+ // check if 'else' block contains code to keep
+ if (checkCondtionalExpression(ce.getElseExpression())) {
+ Block elseBlock= ast.newBlock();
+ ASTNode elseExpression= rewrite.createCopyTarget(getExpressionWithoutParenthezis(ce.getElseExpression()));
+ elseBlock.statements().add(ast.newExpressionStatement((Expression) elseExpression));
+ ifStatement.setElseStatement(elseBlock);
+ }
+
+ rewrite.replace(varDecl, ifStatement, group);
+ return;
+ }
+
+ boolean sideEffectInitializer= sideEffects.size() > 0;
+ if (fragments.size() == fUnusedNames.length) {
+ if (fForceRemove) {
+ rewrite.remove(varDecl, group);
+ return;
+ }
+ if (parent.getParent() instanceof FieldDeclaration) {
+ rewrite.remove(varDecl, group);
+ return;
+ }
+ if (sideEffectInitializer) {
+ Statement[] wrapped= new Statement[sideEffects.size()];
+ for (int i= 0; i < wrapped.length; i++) {
+ Expression sideEffect= sideEffects.get(i);
+ Expression movedInit= (Expression) rewrite.createMoveTarget(sideEffect);
+ wrapped[i]= rewrite.getAST().newExpressionStatement(movedInit);
+ }
+ StatementRewrite statementRewrite= new StatementRewrite(rewrite, new ASTNode[] { varDecl });
+ statementRewrite.replace(wrapped, group);
+ } else {
+ rewrite.remove(varDecl, group);
+ }
+ } else {
+ if (fForceRemove) {
+ rewrite.remove(frag, group);
+ return;
+ }
+ //multiple declarations in one line
+ ASTNode declaration = parent.getParent();
+ if (declaration instanceof FieldDeclaration) {
+ rewrite.remove(frag, group);
+ return;
+ }
+ if (declaration instanceof VariableDeclarationStatement) {
+ splitUpDeclarations(rewrite, group, frag, (VariableDeclarationStatement) declaration, sideEffects);
+ rewrite.remove(frag, group);
+ return;
+ }
+ if (declaration instanceof VariableDeclarationExpression) {
+ //keep constructors and method invocations
+ if (!sideEffectInitializer){
+ rewrite.remove(frag, group);
+ }
+ }
+ }
+ } else if (nameParentType == ASTNode.POSTFIX_EXPRESSION || nameParentType == ASTNode.PREFIX_EXPRESSION) {
+ Expression expression= (Expression)parent;
+ ASTNode expressionParent= expression.getParent();
+ if (expressionParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
+ removeStatement(rewrite, expressionParent, group);
+ } else {
+ rewrite.remove(expression, group);
+ }
+ }
+ }
+
+ private static Expression getExpressionWithoutParenthezis(Expression expression) {
+ Expression currExpression= expression;
+ while (currExpression instanceof ParenthesizedExpression) {
+ currExpression= ((ParenthesizedExpression) currExpression).getExpression();
+ }
+ return currExpression;
+ }
+
+ /*
+ * Return TRUE if the expression node type is a method, pre/posfix or assignment
+ */
+ private static boolean checkCondtionalExpression(Expression expression) {
+ int nodeType= getExpressionWithoutParenthezis(expression).getNodeType();
+ if (nodeType == ASTNode.METHOD_INVOCATION ||
+ nodeType == ASTNode.POSTFIX_EXPRESSION ||
+ nodeType == ASTNode.PREFIX_EXPRESSION ||
+ nodeType == ASTNode.ASSIGNMENT) {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Return TRUE if any of the sideEffects expression node type is a method, pre/posfix or assignment
+ */
+ private static boolean checkSideEffects(List sideEffects) {
+ if (sideEffects.isEmpty()) {
+ return false;
+ }
+ for (Expression expression : sideEffects) {
+ if (checkCondtionalExpression(expression)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void splitUpDeclarations(ASTRewrite rewrite, TextEditGroup group, VariableDeclarationFragment frag, VariableDeclarationStatement originalStatement, List sideEffects) {
+ if (sideEffects.size() > 0) {
+ ListRewrite statementRewrite= rewrite.getListRewrite(originalStatement.getParent(), (ChildListPropertyDescriptor) originalStatement.getLocationInParent());
+
+ Statement previousStatement= originalStatement;
+ for (Expression sideEffect : sideEffects) {
+ Expression movedInit= (Expression) rewrite.createMoveTarget(sideEffect);
+ ExpressionStatement wrapped= rewrite.getAST().newExpressionStatement(movedInit);
+ statementRewrite.insertAfter(wrapped, previousStatement, group);
+ previousStatement= wrapped;
+ }
+
+ VariableDeclarationStatement newDeclaration= null;
+ List fragments= originalStatement.fragments();
+ int fragIndex= fragments.indexOf(frag);
+ ListIterator fragmentIterator= fragments.listIterator(fragIndex+1);
+ while (fragmentIterator.hasNext()) {
+ VariableDeclarationFragment currentFragment= fragmentIterator.next();
+ VariableDeclarationFragment movedFragment= (VariableDeclarationFragment) rewrite.createMoveTarget(currentFragment);
+ if (newDeclaration == null) {
+ newDeclaration= rewrite.getAST().newVariableDeclarationStatement(movedFragment);
+ Type copiedType= (Type) rewrite.createCopyTarget(originalStatement.getType());
+ newDeclaration.setType(copiedType);
+ } else {
+ newDeclaration.fragments().add(movedFragment);
+ }
+ }
+ if (newDeclaration != null){
+ statementRewrite.insertAfter(newDeclaration, previousStatement, group);
+ if (originalStatement.fragments().size() == newDeclaration.fragments().size() + 1){
+ rewrite.remove(originalStatement, group);
+ }
+ }
+ }
+ }
+
+ private void removeVariableWithInitializer(ASTRewrite rewrite, ASTNode initializerNode, ASTNode statementNode, TextEditGroup group) {
+ boolean performRemove= fForceRemove;
+ if (!performRemove) {
+ ArrayList sideEffectNodes= new ArrayList<>();
+ initializerNode.accept(new SideEffectFinder(sideEffectNodes));
+ performRemove= sideEffectNodes.isEmpty();
+ }
+ if (performRemove) {
+ removeStatement(rewrite, statementNode, group);
+ fRemovedAssignmentsCount++;
+ } else {
+ ASTNode initNode = rewrite.createMoveTarget(initializerNode);
+ ExpressionStatement statement = rewrite.getAST().newExpressionStatement((Expression) initNode);
+ rewrite.replace(statementNode, statement, null);
+ fAlteredAssignmentsCount++;
+ }
+ }
+
+ private void removeStatement(ASTRewrite rewrite, ASTNode statementNode, TextEditGroup group) {
+ if (ASTNodes.isControlStatementBody(statementNode.getLocationInParent())) {
+ rewrite.replace(statementNode, rewrite.getAST().newBlock(), group);
+ } else {
+ rewrite.remove(statementNode, group);
+ }
+ }
+
+ @Override
+ public String getAdditionalInfo() {
+ StringBuilder sb=new StringBuilder();
+ if (fRemovedAssignmentsCount == 1) {
+ sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_singular);
+ } else if (fRemovedAssignmentsCount > 1) {
+ sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_plural, String.valueOf(fRemovedAssignmentsCount)));
+ }
+ if (fAlteredAssignmentsCount == 1) {
+ sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_singular);
+ } else if (fAlteredAssignmentsCount > 1) {
+ sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_plural, String.valueOf(fAlteredAssignmentsCount)));
+ }
+ if (sb.length()>0) {
+ return sb.toString();
+ } else
+ return null;
+ }
+ }
+
+ public static class RemoveCastOperation extends CompilationUnitRewriteOperation {
+
+ private final CastExpression fCast;
+
+ public RemoveCastOperation(CastExpression cast) {
+ fCast= cast;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+
+ TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
+
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+
+ CastExpression cast= fCast;
+ Expression expression= cast.getExpression();
+ if (expression instanceof ParenthesizedExpression) {
+ Expression childExpression= ((ParenthesizedExpression) expression).getExpression();
+ if (NecessaryParenthesesChecker.needsParentheses(childExpression, cast, CastExpression.EXPRESSION_PROPERTY)) {
+ expression= childExpression;
+ }
+ }
+
+ replaceCast(cast, expression, rewrite, group);
+ }
+ }
+
+ public static class RemoveAllCastOperation extends CompilationUnitRewriteOperation {
+
+ private final LinkedHashSet fUnnecessaryCasts;
+
+ public RemoveAllCastOperation(LinkedHashSet unnecessaryCasts) {
+ fUnnecessaryCasts= unnecessaryCasts;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+
+ TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
+
+ while (fUnnecessaryCasts.size() > 0) {
+ CastExpression castExpression= fUnnecessaryCasts.iterator().next();
+ fUnnecessaryCasts.remove(castExpression);
+
+ /*
+ * ASTRewrite doesn't allow replacing (deleting) of moved nodes. To solve problems
+ * with nested casts, we need to replace all casts at once.
+ *
+ * The loop proceeds downwards to find the innermost expression that stays in the result (downChild)
+ * and it also skips necessary parentheses.
+ */
+ CastExpression down= castExpression;
+ Expression downChild= down.getExpression();
+ while (true) {
+ if (fUnnecessaryCasts.contains(downChild)) {
+ down= (CastExpression) downChild;
+ fUnnecessaryCasts.remove(down);
+ downChild= down.getExpression();
+ } else if (downChild instanceof ParenthesizedExpression) {
+ Expression downChildExpression= ((ParenthesizedExpression) downChild).getExpression();
+ // is it justified that downChild is a ParenthesizedExpression?
+ if (NecessaryParenthesesChecker.needsParentheses(downChildExpression, down, CastExpression.EXPRESSION_PROPERTY)) {
+ // yes => continue walking down
+ downChild= downChildExpression;
+ } else {
+ // no => stop walking
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // downChild is the innermost CastExpression's expression, stripped of a necessary surrounding ParenthesizedExpression
+ // Move either downChild (if it doesn't need parentheses), or a parenthesized version if necessary
+
+ replaceCast(castExpression, downChild, rewrite, group);
+ }
+ }
+ }
+
+ public static UnusedCodeFixCore createRemoveUnusedImportFix(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ if (isUnusedImport(problem)) {
+ ImportDeclaration node= getImportDeclaration(problem, compilationUnit);
+ if (node != null) {
+ String label= FixMessages.UnusedCodeFix_RemoveImport_description;
+ RemoveImportOperation operation= new RemoveImportOperation(node);
+ Map options= new Hashtable<>();
+ options.put(CleanUpConstants.REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptionsCore.TRUE);
+ return new UnusedCodeFixCore(label, compilationUnit, new CompilationUnitRewriteOperation[] {operation}, options);
+ }
+ }
+ return null;
+ }
+
+ public static boolean isUnusedImport(IProblemLocationCore problem) {
+ int id= problem.getProblemId();
+ return id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport || id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound;
+ }
+
+ public static UnusedCodeFixCore createUnusedMemberFix(CompilationUnit compilationUnit, IProblemLocationCore problem, boolean removeAllAssignements) {
+ if (isUnusedMember(problem)) {
+ SimpleName name= getUnusedName(compilationUnit, problem);
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+ if (binding != null) {
+ if (isFormalParameterInEnhancedForStatement(name))
+ return null;
+
+ String label= getDisplayString(name, binding, removeAllAssignements);
+ RemoveUnusedMemberOperation operation= new RemoveUnusedMemberOperation(new SimpleName[] { name }, removeAllAssignements);
+ return new UnusedCodeFixCore(label, compilationUnit, new CompilationUnitRewriteOperation[] { operation }, getCleanUpOptions(binding, removeAllAssignements));
+ }
+ }
+ }
+ return null;
+ }
+
+ public static UnusedCodeFixCore createUnusedTypeParameterFix(CompilationUnit compilationUnit, IProblemLocationCore problemLoc) {
+ if (problemLoc.getProblemId() == IProblem.UnusedTypeParameter) {
+ SimpleName name= getUnusedName(compilationUnit, problemLoc);
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+ if (binding != null) {
+ String label= FixMessages.UnusedCodeFix_RemoveUnusedTypeParameter_description;
+ RemoveUnusedTypeParameterOperation operation= new RemoveUnusedTypeParameterOperation(name);
+ return new UnusedCodeFixCore(label, compilationUnit, new CompilationUnitRewriteOperation[] { operation }, getCleanUpOptions(binding, false));
+ }
+ }
+ }
+ return null;
+ }
+
+ public static boolean isUnusedMember(IProblemLocationCore problem) {
+ int id= problem.getProblemId();
+ return id == IProblem.UnusedPrivateMethod || id == IProblem.UnusedPrivateConstructor || id == IProblem.UnusedPrivateField || id == IProblem.UnusedPrivateType
+ || id == IProblem.LocalVariableIsNeverUsed || id == IProblem.ArgumentIsNeverUsed;
+ }
+
+ public static UnusedCodeFixCore createRemoveUnusedCastFix(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ if (problem.getProblemId() != IProblem.UnnecessaryCast)
+ return null;
+
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+
+ ASTNode curr= selectedNode;
+ while (curr instanceof ParenthesizedExpression) {
+ curr= ((ParenthesizedExpression) curr).getExpression();
+ }
+
+ if (!(curr instanceof CastExpression))
+ return null;
+
+ return new UnusedCodeFixCore(FixMessages.UnusedCodeFix_RemoveCast_description, compilationUnit, new CompilationUnitRewriteOperation[] {new RemoveCastOperation((CastExpression)curr)});
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit,
+ boolean removeUnusedPrivateMethods,
+ boolean removeUnusedPrivateConstructors,
+ boolean removeUnusedPrivateFields,
+ boolean removeUnusedPrivateTypes,
+ boolean removeUnusedLocalVariables,
+ boolean removeUnusedImports,
+ boolean removeUnusedCast) {
+
+ IProblem[] problems= compilationUnit.getProblems();
+ IProblemLocationCore[] locations= new IProblemLocationCore[problems.length];
+ for (int i= 0; i < problems.length; i++) {
+ locations[i]= new ProblemLocationCore(problems[i]);
+ }
+
+ return createCleanUp(compilationUnit, locations,
+ removeUnusedPrivateMethods,
+ removeUnusedPrivateConstructors,
+ removeUnusedPrivateFields,
+ removeUnusedPrivateTypes,
+ removeUnusedLocalVariables,
+ removeUnusedImports,
+ removeUnusedCast);
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit, IProblemLocationCore[] problems,
+ boolean removeUnusedPrivateMethods,
+ boolean removeUnusedPrivateConstructors,
+ boolean removeUnusedPrivateFields,
+ boolean removeUnusedPrivateTypes,
+ boolean removeUnusedLocalVariables,
+ boolean removeUnusedImports,
+ boolean removeUnusedCast) {
+
+ List result= new ArrayList<>();
+ Hashtable> variableDeclarations= new Hashtable<>();
+ LinkedHashSet unnecessaryCasts= new LinkedHashSet<>();
+ for (IProblemLocationCore problem : problems) {
+ int id= problem.getProblemId();
+
+ if (removeUnusedImports && (id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport ||
+ id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound))
+ {
+ ImportDeclaration node= UnusedCodeFixCore.getImportDeclaration(problem, compilationUnit);
+ if (node != null) {
+ result.add(new RemoveImportOperation(node));
+ }
+ }
+
+ if ((removeUnusedPrivateMethods && id == IProblem.UnusedPrivateMethod) || (removeUnusedPrivateConstructors && id == IProblem.UnusedPrivateConstructor) ||
+ (removeUnusedPrivateTypes && id == IProblem.UnusedPrivateType)) {
+
+ SimpleName name= getUnusedName(compilationUnit, problem);
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+ if (binding != null) {
+ result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
+ }
+ }
+ }
+
+ if ((removeUnusedLocalVariables && id == IProblem.LocalVariableIsNeverUsed) || (removeUnusedPrivateFields && id == IProblem.UnusedPrivateField)) {
+ SimpleName name= getUnusedName(compilationUnit, problem);
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+ if (binding instanceof IVariableBinding && !isFormalParameterInEnhancedForStatement(name) && (!((IVariableBinding) binding).isField() || isSideEffectFree(name, compilationUnit))) {
+ VariableDeclarationFragment parent= ASTNodes.getParent(name, VariableDeclarationFragment.class);
+ if (parent != null) {
+ ASTNode varDecl= parent.getParent();
+ if (!variableDeclarations.containsKey(varDecl)) {
+ variableDeclarations.put(varDecl, new ArrayList());
+ }
+ variableDeclarations.get(varDecl).add(name);
+ } else {
+ result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
+ }
+ }
+ }
+ }
+
+ if (removeUnusedCast && id == IProblem.UnnecessaryCast) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+
+ ASTNode curr= selectedNode;
+ while (curr instanceof ParenthesizedExpression) {
+ curr= ((ParenthesizedExpression) curr).getExpression();
+ }
+
+ if (curr instanceof CastExpression) {
+ unnecessaryCasts.add((CastExpression) curr);
+ }
+ }
+ }
+ for (Map.Entry> entry : variableDeclarations.entrySet()) {
+ List names= entry.getValue();
+ result.add(new RemoveUnusedMemberOperation(names.toArray(new SimpleName[0]), false));
+ }
+ if (unnecessaryCasts.size() > 0)
+ result.add(new RemoveAllCastOperation(unnecessaryCasts));
+
+ if (result.isEmpty())
+ return null;
+
+ return new UnusedCodeFixCore(FixMessages.UnusedCodeFix_change_name, compilationUnit, result.toArray(new CompilationUnitRewriteOperation[result.size()]));
+ }
+
+ public static boolean isFormalParameterInEnhancedForStatement(SimpleName name) {
+ return name.getParent() instanceof SingleVariableDeclaration && name.getParent().getLocationInParent() == EnhancedForStatement.PARAMETER_PROPERTY;
+ }
+
+ public static boolean isSideEffectFree(SimpleName simpleName, CompilationUnit completeRoot) {
+ SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
+ SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
+ for (SimpleName reference : references) {
+ if (hasSideEffect(reference))
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean hasSideEffect(SimpleName reference) {
+ ASTNode parent= reference.getParent();
+ while (parent instanceof QualifiedName) {
+ parent= parent.getParent();
+ }
+ if (parent instanceof FieldAccess) {
+ parent= parent.getParent();
+ }
+
+ ASTNode node= null;
+ int nameParentType= parent.getNodeType();
+ if (nameParentType == ASTNode.ASSIGNMENT) {
+ Assignment assignment= (Assignment) parent;
+ node= assignment.getRightHandSide();
+ } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
+ SingleVariableDeclaration decl= (SingleVariableDeclaration)parent;
+ node= decl.getInitializer();
+ if (node == null)
+ return false;
+ } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
+ node= parent;
+ } else {
+ return false;
+ }
+
+ ArrayList sideEffects= new ArrayList<>();
+ node.accept(new SideEffectFinder(sideEffects));
+ return sideEffects.size() > 0;
+ }
+
+ public static SimpleName getUnusedName(CompilationUnit compilationUnit, IProblemLocationCore problem) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+
+ if (selectedNode instanceof MethodDeclaration) {
+ return ((MethodDeclaration) selectedNode).getName();
+ } else if (selectedNode instanceof SimpleName) {
+ return (SimpleName) selectedNode;
+ }
+
+ return null;
+ }
+
+ public static String getDisplayString(SimpleName simpleName, IBinding binding, boolean removeAllAssignements) {
+ String name= BasicElementLabels.getJavaElementName(simpleName.getIdentifier());
+ switch (binding.getKind()) {
+ case IBinding.TYPE:
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveType_description, name);
+ case IBinding.METHOD:
+ if (((IMethodBinding) binding).isConstructor()) {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveConstructor_description, name);
+ } else {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveMethod_description, name);
+ }
+ case IBinding.VARIABLE:
+ if (removeAllAssignements) {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocalWithInitializer_description, name);
+ } else {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_description, name);
+ }
+ default:
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ public static Map getCleanUpOptions(IBinding binding, boolean removeAll) {
+ Map result= new Hashtable<>();
+
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpOptionsCore.TRUE);
+ switch (binding.getKind()) {
+ case IBinding.TYPE:
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_TYPES, CleanUpOptionsCore.TRUE);
+ break;
+ case IBinding.METHOD:
+ if (((IMethodBinding) binding).isConstructor()) {
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_CONSTRUCTORS, CleanUpOptionsCore.TRUE);
+ } else {
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_METHODS, CleanUpOptionsCore.TRUE);
+ }
+ break;
+ case IBinding.VARIABLE:
+ if (removeAll)
+ return null;
+
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_FELDS, CleanUpOptionsCore.TRUE);
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_LOCAL_VARIABLES, CleanUpOptionsCore.TRUE);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ public static ImportDeclaration getImportDeclaration(IProblemLocationCore problem, CompilationUnit compilationUnit) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+ if (selectedNode != null) {
+ ASTNode node= ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
+ if (node instanceof ImportDeclaration) {
+ return (ImportDeclaration)node;
+ }
+ }
+ return null;
+ }
+
+ private static void replaceCast(CastExpression castExpression, Expression replacement, ASTRewrite rewrite, TextEditGroup group) {
+ boolean castEnclosedInNecessaryParentheses= castExpression.getParent() instanceof ParenthesizedExpression
+ && NecessaryParenthesesChecker.needsParentheses(castExpression, castExpression.getParent().getParent(), castExpression.getParent().getLocationInParent());
+
+ ASTNode toReplace= castEnclosedInNecessaryParentheses ? castExpression.getParent() : castExpression;
+ ASTNode move;
+ if (NecessaryParenthesesChecker.needsParentheses(replacement, toReplace.getParent(), toReplace.getLocationInParent())) {
+ if (replacement.getParent() instanceof ParenthesizedExpression) {
+ move= rewrite.createMoveTarget(replacement.getParent());
+ } else if (castEnclosedInNecessaryParentheses) {
+ toReplace= castExpression;
+ move= rewrite.createMoveTarget(replacement);
+ } else {
+ ParenthesizedExpression parentheses= replacement.getAST().newParenthesizedExpression();
+ parentheses.setExpression((Expression) rewrite.createMoveTarget(replacement));
+ move= parentheses;
+ }
+ } else {
+ move= rewrite.createMoveTarget(replacement);
+ }
+ rewrite.replace(toReplace, move, group);
+ }
+
+ private final Map fCleanUpOptions;
+
+ private UnusedCodeFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ this(name, compilationUnit, fixRewriteOperations, null);
+ }
+
+ private UnusedCodeFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations, Map options) {
+ super(name, compilationUnit, fixRewriteOperations);
+ fCleanUpOptions= options;
+ }
+
+ public UnusedCodeCleanUpCore getCleanUp() {
+ if (fCleanUpOptions == null)
+ return null;
+
+ return new UnusedCodeCleanUpCore(fCleanUpOptions);
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnwrapNewArrayOperation.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnwrapNewArrayOperation.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnwrapNewArrayOperation.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/UnwrapNewArrayOperation.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+
+/**
+ * Unwrap a new array with initializer used as input for varargs and replace with initializer elements
+ *
+ */
+public class UnwrapNewArrayOperation extends CompilationUnitRewriteOperation {
+ private final ArrayCreation node;
+
+ private final ASTNode call;
+
+ public UnwrapNewArrayOperation(ArrayCreation node, MethodInvocation method) {
+ this.node= node;
+ this.call= method;
+ }
+
+ public UnwrapNewArrayOperation(ArrayCreation node, SuperMethodInvocation superMethod) {
+ this.node= node;
+ this.call= superMethod;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore linkedModel) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getRoot().getAST();
+ if (call instanceof MethodInvocation) {
+ MethodInvocation method= (MethodInvocation)call;
+ MethodInvocation newMethod= ast.newMethodInvocation();
+ newMethod.setSourceRange(method.getStartPosition(), method.getLength());
+ newMethod.setName(ast.newSimpleName(method.getName().getFullyQualifiedName()));
+ newMethod.setExpression((Expression) ASTNode.copySubtree(ast, method.getExpression()));
+ if (method.typeArguments() != null) {
+ newMethod.typeArguments().addAll(ASTNode.copySubtrees(ast, method.typeArguments()));
+ }
+ for (int i= 0; i < method.arguments().size() - 1; ++i) {
+ newMethod.arguments().add(ASTNode.copySubtree(ast, (Expression)method.arguments().get(i)));
+ }
+ ArrayInitializer initializer= node.getInitializer();
+ if (initializer != null && initializer.expressions() != null) {
+ for (Object exp : initializer.expressions()) {
+ newMethod.arguments().add(ASTNode.copySubtree(ast, (Expression)exp));
+ }
+ }
+ rewrite.replace(method, newMethod, null);
+ } else if (call instanceof SuperMethodInvocation) {
+ SuperMethodInvocation method= (SuperMethodInvocation)call;
+ SuperMethodInvocation newSuperMethod= ast.newSuperMethodInvocation();
+ newSuperMethod.setSourceRange(method.getStartPosition(), method.getLength());
+ newSuperMethod.setName(ast.newSimpleName(method.getName().getFullyQualifiedName()));
+ if (method.typeArguments() != null) {
+ newSuperMethod.typeArguments().addAll(ASTNode.copySubtrees(ast, method.typeArguments()));
+ }
+ for (int i= 0; i < method.arguments().size() - 1; ++i) {
+ newSuperMethod.arguments().add(ASTNode.copySubtree(ast, (Expression)method.arguments().get(i)));
+ }
+ ArrayInitializer initializer= node.getInitializer();
+ if (initializer != null && initializer.expressions() != null) {
+ for (Object exp : initializer.expressions()) {
+ newSuperMethod.arguments().add(ASTNode.copySubtree(ast, (Expression)exp));
+ }
+ }
+ rewrite.replace(method, newSuperMethod, null);
+
+ }
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/VariableDeclarationFixCore.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/VariableDeclarationFixCore.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/VariableDeclarationFixCore.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/VariableDeclarationFixCore.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,575 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Chris West (Faux) - [clean up] "Use modifier 'final' where possible" can introduce compile errors - https://bugs.eclipse.org/bugs/show_bug.cgi?id=272532
+ * Red Hat Inc. - created VariableDeclarationFixCore from VariableDeclarationFix
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+import org.eclipse.jdt.internal.corext.dom.VariableDeclarationRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+
+public class VariableDeclarationFixCore extends CompilationUnitRewriteOperationsFixCore {
+
+ public static class WrittenNamesFinder extends GenericVisitor {
+
+ private final HashMap> fResult;
+
+ public WrittenNamesFinder(HashMap> result) {
+ fResult= result;
+ }
+
+ @Override
+ public boolean visit(SimpleName node) {
+ if (node.getParent() instanceof VariableDeclarationFragment)
+ return super.visit(node);
+ if (node.getParent() instanceof SingleVariableDeclaration)
+ return super.visit(node);
+
+ IBinding binding= node.resolveBinding();
+ if (!(binding instanceof IVariableBinding))
+ return super.visit(node);
+
+ binding= ((IVariableBinding)binding).getVariableDeclaration();
+ if (ASTResolving.isWriteAccess(node)) {
+ List list;
+ if (fResult.containsKey(binding)) {
+ list= fResult.get(binding);
+ } else {
+ list= new ArrayList<>();
+ }
+ list.add(node);
+ fResult.put(binding, list);
+ }
+
+ return super.visit(node);
+ }
+ }
+
+ public static class ReturnFinder extends ASTVisitor {
+ boolean foundOne;
+ @Override
+ public boolean visit(ReturnStatement node) {
+ foundOne= true;
+ return super.visit(node);
+ }
+ }
+
+ public static class VariableDeclarationFinder extends GenericVisitor {
+
+ private final List fResult;
+ private final HashMap> fWrittenVariables;
+ private final boolean fAddFinalFields;
+ private final boolean fAddFinalParameters;
+ private final boolean fAddFinalLocals;
+
+ public VariableDeclarationFinder(boolean addFinalFields,
+ boolean addFinalParameters,
+ boolean addFinalLocals,
+ final List result, final HashMap> writtenNames) {
+
+ super();
+ fAddFinalFields= addFinalFields;
+ fAddFinalParameters= addFinalParameters;
+ fAddFinalLocals= addFinalLocals;
+ fResult= result;
+ fWrittenVariables= writtenNames;
+ }
+
+ @Override
+ public boolean visit(FieldDeclaration node) {
+ if (fAddFinalFields)
+ handleFragments(node.fragments(), node);
+
+ List fragments= node.fragments();
+ for (Iterator iterator= fragments.iterator(); iterator.hasNext();) {
+ VariableDeclarationFragment fragment= iterator.next();
+ Expression initializer= fragment.getInitializer();
+ if (initializer != null) {
+ initializer.accept(this);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(VariableDeclarationStatement node) {
+ if (fAddFinalLocals)
+ handleFragments(node.fragments(), node);
+
+ List fragments= node.fragments();
+ for (Iterator iterator= fragments.iterator(); iterator.hasNext();) {
+ VariableDeclarationFragment fragment= iterator.next();
+ Expression initializer= fragment.getInitializer();
+ if (initializer != null) {
+ initializer.accept(this);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(VariableDeclarationExpression node) {
+ if (fAddFinalLocals && node.fragments().size() == 1) {
+ SimpleName name= ((VariableDeclarationFragment)node.fragments().get(0)).getName();
+
+ IBinding binding= name.resolveBinding();
+ if (binding == null)
+ return false;
+
+ if (fWrittenVariables.containsKey(binding))
+ return false;
+
+ ModifierChangeOperation op= createAddFinalOperation(name, node);
+ if (op == null)
+ return false;
+
+ fResult.add(op);
+ return false;
+ }
+ return false;
+ }
+
+ private boolean handleFragments(List list, ASTNode declaration) {
+ List toChange= new ArrayList<>();
+
+ for (Iterator iter= list.iterator(); iter.hasNext();) {
+ VariableDeclarationFragment fragment= iter.next();
+ SimpleName name= fragment.getName();
+ IBinding resolveBinding= name.resolveBinding();
+ if (canAddFinal(resolveBinding, declaration)) {
+ IVariableBinding varbinding= (IVariableBinding)resolveBinding;
+ if (varbinding.isField()) {
+ if (fieldCanBeFinal(fragment, varbinding))
+ toChange.add(fragment);
+ } else {
+ if (!fWrittenVariables.containsKey(resolveBinding))
+ toChange.add(fragment);
+ }
+ }
+ }
+
+ if (toChange.size() == 0)
+ return false;
+
+ ModifierChangeOperation op= new ModifierChangeOperation(declaration, toChange, Modifier.FINAL, Modifier.NONE);
+ fResult.add(op);
+ return false;
+ }
+
+ private boolean fieldCanBeFinal(VariableDeclarationFragment fragment, IVariableBinding binding) {
+ if (Modifier.isStatic(((FieldDeclaration)fragment.getParent()).getModifiers()))
+ return false;
+
+ if (!fWrittenVariables.containsKey(binding)) {
+ //variable is not written
+ if (fragment.getInitializer() == null) {//variable is not initialized
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ if (fragment.getInitializer() != null)//variable is initialized and written
+ return false;
+
+ ITypeBinding declaringClass= binding.getDeclaringClass();
+ if (declaringClass == null)
+ return false;
+
+ List writes= fWrittenVariables.get(binding);
+ if (!isWrittenInTypeConstructors(writes, declaringClass))
+ return false;
+
+ HashSet writingConstructorBindings= new HashSet<>();
+ ArrayList writingConstructors= new ArrayList<>();
+ for (int i= 0; i < writes.size(); i++) {
+ SimpleName name= writes.get(i);
+ MethodDeclaration constructor= getWritingConstructor(name);
+ if (writingConstructors.contains(constructor))//variable is written twice or more in constructor
+ return false;
+
+ if (canReturn(constructor))
+ return false;
+
+ writingConstructors.add(constructor);
+ IMethodBinding constructorBinding= constructor.resolveBinding();
+ if (constructorBinding == null)
+ return false;
+
+ writingConstructorBindings.add(constructorBinding);
+ }
+
+ for (int i= 0; i < writingConstructors.size(); i++) {
+ MethodDeclaration constructor= writingConstructors.get(i);
+ if (callsWritingConstructor(constructor, writingConstructorBindings))//writing constructor calls other writing constructor
+ return false;
+ }
+
+ MethodDeclaration constructor= writingConstructors.get(0);
+ TypeDeclaration typeDecl= ASTNodes.getParent(constructor, TypeDeclaration.class);
+ if (typeDecl == null)
+ return false;
+
+ MethodDeclaration[] methods= typeDecl.getMethods();
+ for (int i= 0; i < methods.length; i++) {
+ if (methods[i].isConstructor()) {
+ IMethodBinding methodBinding= methods[i].resolveBinding();
+ if (methodBinding == null)
+ return false;
+
+ if (!writingConstructorBindings.contains(methodBinding)) {
+ if (!callsWritingConstructor(methods[i], writingConstructorBindings))//non writing constructor does not call a writing constructor
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private boolean canReturn(MethodDeclaration constructor) {
+ final ReturnFinder retFinder= new ReturnFinder();
+ constructor.accept(retFinder);
+ return retFinder.foundOne;
+ }
+
+ private boolean callsWritingConstructor(MethodDeclaration methodDeclaration, HashSet writingConstructorBindings) {
+ HashSet visitedMethodDeclarations= new HashSet<>();
+ visitedMethodDeclarations.add(methodDeclaration);
+ return callsWritingConstructor(methodDeclaration, writingConstructorBindings, visitedMethodDeclarations);
+ }
+
+ private boolean callsWritingConstructor(MethodDeclaration methodDeclaration, HashSet writingConstructorBindings, Set visitedMethodDeclarations) {
+ Block body= methodDeclaration.getBody();
+ if (body == null)
+ return false;
+
+ List statements= body.statements();
+ if (statements.size() == 0)
+ return false;
+
+ Statement statement= statements.get(0);
+ if (!(statement instanceof ConstructorInvocation))
+ return false;
+
+ ConstructorInvocation invocation= (ConstructorInvocation)statement;
+ IMethodBinding constructorBinding= invocation.resolveConstructorBinding();
+ if (constructorBinding == null)
+ return false;
+
+ if (writingConstructorBindings.contains(constructorBinding)) {
+ return true;
+ } else {
+ ASTNode declaration= ASTNodes.findDeclaration(constructorBinding, methodDeclaration.getParent());
+ if (!(declaration instanceof MethodDeclaration))
+ return false;
+
+ if (visitedMethodDeclarations.contains(declaration)) {
+ return false;
+ }
+ visitedMethodDeclarations.add(methodDeclaration);
+ return callsWritingConstructor((MethodDeclaration)declaration, writingConstructorBindings, visitedMethodDeclarations);
+ }
+ }
+
+ private boolean isWrittenInTypeConstructors(List writes, ITypeBinding declaringClass) {
+
+ for (int i= 0; i < writes.size(); i++) {
+ SimpleName name= writes.get(i);
+
+ MethodDeclaration methodDeclaration= getWritingConstructor(name);
+ if (methodDeclaration == null)
+ return false;
+
+ if (!methodDeclaration.isConstructor())
+ return false;
+
+ IMethodBinding constructor= methodDeclaration.resolveBinding();
+ if (constructor == null)
+ return false;
+
+ ITypeBinding declaringClass2= constructor.getDeclaringClass();
+ if (!declaringClass.equals(declaringClass2))
+ return false;
+ }
+
+ return true;
+ }
+
+ private MethodDeclaration getWritingConstructor(SimpleName name) {
+ Assignment assignement= ASTNodes.getParent(name, Assignment.class);
+ if (assignement == null)
+ return null;
+
+ ASTNode expression= assignement.getParent();
+ if (!(expression instanceof ExpressionStatement))
+ return null;
+
+ ASTNode block= expression.getParent();
+ if (!(block instanceof Block))
+ return null;
+
+ ASTNode methodDeclaration= block.getParent();
+ if (!(methodDeclaration instanceof MethodDeclaration))
+ return null;
+
+ return (MethodDeclaration)methodDeclaration;
+ }
+
+ @Override
+ public boolean visit(VariableDeclarationFragment node) {
+ SimpleName name= node.getName();
+
+ IBinding binding= name.resolveBinding();
+ if (binding == null)
+ return true;
+
+ if (fWrittenVariables.containsKey(binding))
+ return true;
+
+ ModifierChangeOperation op= createAddFinalOperation(name, node);
+ if (op == null)
+ return true;
+
+ fResult.add(op);
+ return true;
+ }
+
+ @Override
+ public boolean visit(SingleVariableDeclaration node) {
+ SimpleName name= node.getName();
+
+ IBinding binding= name.resolveBinding();
+ if (!(binding instanceof IVariableBinding))
+ return false;
+
+ IVariableBinding varBinding= (IVariableBinding)binding;
+ if (fWrittenVariables.containsKey(varBinding))
+ return false;
+
+ if (fAddFinalParameters && fAddFinalLocals) {
+
+ ModifierChangeOperation op= createAddFinalOperation(name, node);
+ if (op == null)
+ return false;
+
+ fResult.add(op);
+ return false;
+ } else if (fAddFinalParameters) {
+ if (!varBinding.isParameter())
+ return false;
+
+ ModifierChangeOperation op= createAddFinalOperation(name, node);
+ if (op == null)
+ return false;
+
+ fResult.add(op);
+ return false;
+ } else if (fAddFinalLocals) {
+ if (varBinding.isParameter())
+ return false;
+
+ ModifierChangeOperation op= createAddFinalOperation(name, node);
+ if (op == null)
+ return false;
+
+ fResult.add(op);
+ return false;
+ }
+ return false;
+ }
+ }
+
+ public static class ModifierChangeOperation extends CompilationUnitRewriteOperation {
+
+ private final ASTNode fDeclaration;
+ private final List fToChange;
+ private final int fIncludedModifiers;
+ private final int fExcludedModifiers;
+
+ public ModifierChangeOperation(ASTNode declaration, List toChange, int includedModifiers, int excludedModifiers) {
+ fDeclaration= declaration;
+ fToChange= toChange;
+ fIncludedModifiers= includedModifiers;
+ fExcludedModifiers= excludedModifiers;
+ }
+
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore model) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+
+ TextEditGroup group= createTextEditGroup(FixMessages.VariableDeclarationFix_changeModifierOfUnknownToFinal_description, cuRewrite);
+
+ if (fDeclaration instanceof VariableDeclarationStatement) {
+ VariableDeclarationFragment[] toChange= fToChange.toArray(new VariableDeclarationFragment[fToChange.size()]);
+ VariableDeclarationRewrite.rewriteModifiers((VariableDeclarationStatement)fDeclaration, toChange, fIncludedModifiers, fExcludedModifiers, rewrite, group);
+ } else if (fDeclaration instanceof FieldDeclaration) {
+ VariableDeclarationFragment[] toChange= fToChange.toArray(new VariableDeclarationFragment[fToChange.size()]);
+ VariableDeclarationRewrite.rewriteModifiers((FieldDeclaration)fDeclaration, toChange, fIncludedModifiers, fExcludedModifiers, rewrite, group);
+ } else if (fDeclaration instanceof SingleVariableDeclaration) {
+ VariableDeclarationRewrite.rewriteModifiers((SingleVariableDeclaration)fDeclaration, fIncludedModifiers, fExcludedModifiers, rewrite, group);
+ } else if (fDeclaration instanceof VariableDeclarationExpression) {
+ VariableDeclarationRewrite.rewriteModifiers((VariableDeclarationExpression)fDeclaration, fIncludedModifiers, fExcludedModifiers, rewrite, group);
+ }
+ }
+ }
+
+ public static VariableDeclarationFixCore createChangeModifierToFinalFix(final CompilationUnit compilationUnit, ASTNode[] selectedNodes) {
+ HashMap> writtenNames= new HashMap<>();
+ WrittenNamesFinder finder= new WrittenNamesFinder(writtenNames);
+ compilationUnit.accept(finder);
+ List ops= new ArrayList<>();
+ VariableDeclarationFinder visitor= new VariableDeclarationFinder(true, true, true, ops, writtenNames);
+ if (selectedNodes.length == 1) {
+ selectedNodes[0].accept(visitor);
+ } else {
+ for (int i= 0; i < selectedNodes.length; i++) {
+ ASTNode selectedNode= selectedNodes[i];
+ selectedNode.accept(visitor);
+ }
+ }
+ if (ops.size() == 0)
+ return null;
+
+ CompilationUnitRewriteOperation[] result= ops.toArray(new CompilationUnitRewriteOperation[ops.size()]);
+ String label;
+ if (result.length == 1) {
+ label= FixMessages.VariableDeclarationFix_changeModifierOfUnknownToFinal_description;
+ } else {
+ label= FixMessages.VariableDeclarationFix_ChangeMidifiersToFinalWherPossible_description;
+ }
+ return new VariableDeclarationFixCore(label, compilationUnit, result);
+ }
+
+ public static ICleanUpFixCore createCleanUp(CompilationUnit compilationUnit,
+ boolean addFinalFields, boolean addFinalParameters, boolean addFinalLocals) {
+
+ if (!addFinalFields && !addFinalParameters && !addFinalLocals)
+ return null;
+
+ HashMap> writtenNames= new HashMap<>();
+ WrittenNamesFinder finder= new WrittenNamesFinder(writtenNames);
+ compilationUnit.accept(finder);
+
+ List operations= new ArrayList<>();
+ VariableDeclarationFinder visitor= new VariableDeclarationFinder(addFinalFields, addFinalParameters, addFinalLocals, operations, writtenNames);
+ compilationUnit.accept(visitor);
+
+ if (operations.isEmpty())
+ return null;
+
+ return new VariableDeclarationFixCore(FixMessages.VariableDeclarationFix_add_final_change_name, compilationUnit, operations.toArray(new CompilationUnitRewriteOperation[operations.size()]));
+ }
+
+ private static ModifierChangeOperation createAddFinalOperation(SimpleName name, ASTNode decl) {
+ if (decl == null)
+ return null;
+
+ IBinding binding= name.resolveBinding();
+ if (!canAddFinal(binding, decl))
+ return null;
+
+ if (decl instanceof SingleVariableDeclaration) {
+ return new ModifierChangeOperation(decl, new ArrayList(), Modifier.FINAL, Modifier.NONE);
+ } else if (decl instanceof VariableDeclarationExpression) {
+ return new ModifierChangeOperation(decl, new ArrayList(), Modifier.FINAL, Modifier.NONE);
+ } else if (decl instanceof VariableDeclarationFragment){
+ VariableDeclarationFragment frag= (VariableDeclarationFragment)decl;
+ decl= decl.getParent();
+ if (decl instanceof FieldDeclaration || decl instanceof VariableDeclarationStatement) {
+ List list= new ArrayList<>();
+ list.add(frag);
+ return new ModifierChangeOperation(decl, list, Modifier.FINAL, Modifier.NONE);
+ } else if (decl instanceof VariableDeclarationExpression) {
+ return new ModifierChangeOperation(decl, new ArrayList(), Modifier.FINAL, Modifier.NONE);
+ }
+ }
+
+ return null;
+ }
+
+ public static boolean canAddFinal(IBinding binding, ASTNode declNode) {
+ if (!(binding instanceof IVariableBinding))
+ return false;
+
+ IVariableBinding varbinding= (IVariableBinding)binding;
+ int modifiers= varbinding.getModifiers();
+ if (Modifier.isFinal(modifiers) || Modifier.isVolatile(modifiers) || Modifier.isTransient(modifiers))
+ return false;
+
+ VariableDeclarationExpression parent= ASTNodes.getParent(declNode, VariableDeclarationExpression.class);
+ if (parent != null && parent.fragments().size() > 1)
+ return false;
+
+ if (varbinding.isField() && !Modifier.isPrivate(modifiers))
+ return false;
+
+ if (varbinding.isParameter()) {
+ ASTNode varDecl= declNode.getParent();
+ if (varDecl instanceof MethodDeclaration) {
+ MethodDeclaration declaration= (MethodDeclaration)varDecl;
+ if (declaration.getBody() == null)
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected VariableDeclarationFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ super(name, compilationUnit, fixRewriteOperations);
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/JavaStringStatusContext.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/JavaStringStatusContext.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/JavaStringStatusContext.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/JavaStringStatusContext.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.base;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
+
+import org.eclipse.jdt.core.ISourceRange;
+
+/**
+ * A Java string context can be used to annotate a
RefactoringStatusEntry
+ * with detailed information about an error detected in Java source code represented
+ * by a string.
+ */
+public class JavaStringStatusContext extends RefactoringStatusContext {
+
+ private String fSource;
+ private ISourceRange fSourceRange;
+
+ /**
+ * Creates a new JavaStringStatusContext
.
+ *
+ * @param source the source code containing the error
+ * @param range a source range inside source
or
+ * null
if no special source range is known.
+ */
+ public JavaStringStatusContext(String source, ISourceRange range){
+ Assert.isNotNull(source);
+ fSource= source;
+ fSourceRange= range;
+ }
+
+ public String getSource() {
+ return fSource;
+ }
+
+ public ISourceRange getSourceRange() {
+ return fSourceRange;
+ }
+
+ @Override
+ public Object getCorrespondingElement() {
+ return null;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/ReferencesInBinaryContext.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/ReferencesInBinaryContext.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/ReferencesInBinaryContext.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/base/ReferencesInBinaryContext.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.base;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
+
+import org.eclipse.jdt.core.search.SearchMatch;
+
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+
+public class ReferencesInBinaryContext extends RefactoringStatusContext {
+
+ private List fMatches= new ArrayList<>();
+
+ private final String fDescription;
+
+ public ReferencesInBinaryContext(String description) {
+ fDescription= description;
+ }
+
+ public String getDescription() {
+ return fDescription;
+ }
+
+
+ public void add(SearchMatch match) {
+ fMatches.add(match);
+ }
+
+ public List getMatches() {
+ return fMatches;
+ }
+
+ /*
+ * @see org.eclipse.ltk.core.refactoring.RefactoringStatusContext#getCorrespondingElement()
+ */
+ @Override
+ public Object getCorrespondingElement() {
+ return null;
+ }
+
+ public void addErrorIfNecessary(RefactoringStatus status) {
+ if (getMatches().size() != 0) {
+ status.addError(RefactoringCoreMessages.ReferencesInBinaryContext_binaryRefsNotUpdated, this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return fDescription + " (" + fMatches.size() + " matches)"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CompilationUnitChange.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CompilationUnitChange.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CompilationUnitChange.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CompilationUnitChange.java" 2020-02-26 15:31:57.000000000 +0000
@@ -56,9 +56,9 @@
setKeepPreviewEdits(change.getKeepPreviewEdits());
setSaveMode(change.getSaveMode());
setTextType(change.getTextType());
- TextEditBasedChangeGroup[] groups= change.getChangeGroups();
- for (int i= 0; i < groups.length; i++)
- addChangeGroup(groups[i]);
+ for (TextEditBasedChangeGroup group : change.getChangeGroups()) {
+ addChangeGroup(group);
+ }
}
}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CreateCompilationUnitChange.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CreateCompilationUnitChange.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CreateCompilationUnitChange.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/CreateCompilationUnitChange.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.changes;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.nls.changes.CreateTextFileChange;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+
+public final class CreateCompilationUnitChange extends CreateTextFileChange {
+
+ private final ICompilationUnit fUnit;
+
+ public CreateCompilationUnitChange(ICompilationUnit unit, String source, String encoding) {
+ super(unit.getResource().getFullPath(), source, encoding, "java"); //$NON-NLS-1$
+ fUnit= unit;
+ }
+
+ @Override
+ public String getName() {
+ String cuName= BasicElementLabels.getFileName(fUnit);
+ String cuContainerName= BasicElementLabels.getPathLabel(fUnit.getParent().getPath(), false);
+ return Messages.format(RefactoringCoreMessages.CompilationUnitChange_label, new String[] { cuName, cuContainerName });
+ }
+
+ public ICompilationUnit getCu() {
+ return fUnit;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationRefactoringChange.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationRefactoringChange.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationRefactoringChange.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationRefactoringChange.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.changes;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
+
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+
+/**
+ * Dynamic validation state change with support for refactoring descriptors.
+ *
+ * @since 3.2
+ */
+public final class DynamicValidationRefactoringChange extends DynamicValidationStateChange {
+
+ /** The refactoring descriptor */
+ private final RefactoringDescriptor fDescriptor;
+
+ /**
+ * Creates a new dynamic validation refactoring change.
+ *
+ * @param descriptor
+ * the refactoring descriptor
+ * @param name
+ * the name of the change
+ */
+ public DynamicValidationRefactoringChange(final JavaRefactoringDescriptor descriptor, final String name) {
+ super(name);
+ Assert.isNotNull(descriptor);
+ fDescriptor= descriptor;
+ }
+
+ /**
+ * Creates a new dynamic validation refactoring change.
+ *
+ * @param descriptor
+ * the refactoring descriptor
+ * @param name
+ * the name of the change
+ * @param changes
+ * the changes
+ */
+ public DynamicValidationRefactoringChange(final JavaRefactoringDescriptor descriptor, final String name, final Change[] changes) {
+ super(name, changes);
+ Assert.isNotNull(descriptor);
+ Assert.isTrue(!descriptor.validateDescriptor().hasFatalError(), RefactoringCoreMessages.DynamicValidationRefactoringChange_fatal_error);
+ fDescriptor= descriptor;
+ }
+
+ @Override
+ public ChangeDescriptor getDescriptor() {
+ return new RefactoringChangeDescriptor(fDescriptor);
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationStateChange.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationStateChange.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationStateChange.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/DynamicValidationStateChange.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.changes;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.JavaCore;
+
+import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+
+//import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+
+public class DynamicValidationStateChange extends CompositeChange implements WorkspaceTracker.Listener {
+
+ private boolean fListenerRegistered= false;
+ private RefactoringStatus fValidationState= null;
+ private long fTimeStamp= DO_NOT_EXPIRE;
+ private ISchedulingRule fSchedulingRule;
+
+ private static final long DO_NOT_EXPIRE= -1;
+
+ // 30 minutes
+ private static final long LIFE_TIME= 30 * 60 * 1000;
+
+ public DynamicValidationStateChange(Change change) {
+ super(change.getName());
+ add(change);
+ markAsSynthetic();
+ fSchedulingRule= ResourcesPlugin.getWorkspace().getRoot();
+ }
+
+ public DynamicValidationStateChange(String name) {
+ super(name);
+ markAsSynthetic();
+ fSchedulingRule= ResourcesPlugin.getWorkspace().getRoot();
+ }
+
+ public DynamicValidationStateChange(String name, Change[] changes) {
+ super(name, changes);
+ markAsSynthetic();
+ fSchedulingRule= ResourcesPlugin.getWorkspace().getRoot();
+ }
+
+ private DynamicValidationStateChange(String name, boolean expire) {
+ this(name);
+ if (expire) {
+ fTimeStamp= 0;
+ }
+ }
+
+ @Override
+ public void initializeValidationData(IProgressMonitor pm) {
+ super.initializeValidationData(pm);
+ if (fTimeStamp != DO_NOT_EXPIRE) {
+ WorkspaceTracker.INSTANCE.addListener(this);
+ fListenerRegistered= true;
+ fTimeStamp= System.currentTimeMillis();
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if (fListenerRegistered) {
+ WorkspaceTracker.INSTANCE.removeListener(this);
+ fListenerRegistered= false;
+ }
+ super.dispose();
+ }
+
+ @Override
+ public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException {
+ if (fValidationState == null) {
+ return super.isValid(pm);
+ }
+ return fValidationState;
+ }
+
+ @Override
+ public Change perform(IProgressMonitor pm) throws CoreException {
+ final Change[] result= new Change[1];
+ IWorkspaceRunnable runnable= new IWorkspaceRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor) throws CoreException {
+ result[0]= DynamicValidationStateChange.super.perform(monitor);
+ }
+ };
+ JavaCore.run(runnable, fSchedulingRule, pm);
+ return result[0];
+ }
+
+ @Override
+ protected Change createUndoChange(Change[] childUndos) {
+ DynamicValidationStateChange result= new DynamicValidationStateChange(getName(), true);
+ for (int i= 0; i < childUndos.length; i++) {
+ result.add(childUndos[i]);
+ }
+ return result;
+ }
+
+ @Override
+ public void workspaceChanged() {
+ long currentTime= System.currentTimeMillis();
+ if (currentTime - fTimeStamp < LIFE_TIME)
+ return;
+ fValidationState= RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.DynamicValidationStateChange_workspace_changed);
+ // remove listener from workspace tracker
+ WorkspaceTracker.INSTANCE.removeListener(this);
+ fListenerRegistered= false;
+ // clear up the children to not hang onto too much memory
+ Change[] children= clear();
+ for (int i= 0; i < children.length; i++) {
+ final Change change= children[i];
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ change.dispose();
+ }
+ @Override
+ public void handleException(Throwable exception) {
+ JavaManipulationPlugin.log(exception);
+ }
+ });
+ }
+ }
+
+ public void setSchedulingRule(ISchedulingRule schedulingRule) {
+ fSchedulingRule= schedulingRule;
+ }
+
+ public ISchedulingRule getSchedulingRule() {
+ return fSchedulingRule;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/TextChangeCompatibility.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/TextChangeCompatibility.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/TextChangeCompatibility.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/TextChangeCompatibility.java" 2020-02-26 15:31:57.000000000 +0000
@@ -68,8 +68,7 @@
}
TextEdit[] children= parent.getChildren();
// First dive down to find the right parent.
- for (int i= 0; i < children.length; i++) {
- TextEdit child= children[i];
+ for (TextEdit child : children) {
if (covers(child, edit)) {
insert(child, edit);
return;
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/WorkspaceTracker.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/WorkspaceTracker.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/WorkspaceTracker.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/changes/WorkspaceTracker.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.changes;
+
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+
+public class WorkspaceTracker {
+
+ public final static WorkspaceTracker INSTANCE= new WorkspaceTracker();
+
+ public interface Listener {
+ public void workspaceChanged();
+ }
+
+ private ListenerList fListeners;
+ private ResourceListener fResourceListener;
+
+ private WorkspaceTracker() {
+ fListeners= new ListenerList<>();
+ }
+
+ private class ResourceListener implements IResourceChangeListener {
+ @Override
+ public void resourceChanged(IResourceChangeEvent event) {
+ workspaceChanged();
+ }
+ }
+
+ private void workspaceChanged() {
+ for (Listener listener : fListeners) {
+ listener.workspaceChanged();
+ }
+ }
+
+ public void addListener(Listener l) {
+ fListeners.add(l);
+ if (fResourceListener == null) {
+ fResourceListener= new ResourceListener();
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(fResourceListener);
+ }
+ }
+
+ public void removeListener(Listener l) {
+ if (fListeners.size() == 0)
+ return;
+ fListeners.remove(l);
+ if (fListeners.size() == 0) {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(fResourceListener);
+ fResourceListener= null;
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/Checks.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/Checks.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/Checks.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/Checks.java" 2020-02-26 15:31:57.000000000 +0000
@@ -48,7 +48,6 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
-import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
@@ -177,7 +176,7 @@
*/
public static RefactoringStatus checkTypeName(String name, IJavaElement context) {
//fix for: 1GF5Z0Z: ITPJUI:WINNT - assertion failed after renameType refactoring
- if (name.indexOf(".") != -1) //$NON-NLS-1$
+ if (name.contains(".")) //$NON-NLS-1$
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.Checks_no_dot);
else
return checkName(name, JavaConventionsUtil.validateJavaTypeName(name, context));
@@ -210,7 +209,7 @@
/**
* Returns OK status if the new name is OK, i.e. when no file with that name exists.
* The name of the given CU is not OK.
- *
+ *
* @param cu CU to rename
* @param newBareName the new name of the CU (without extension)
* @return the status: FATAL if the CU already exists, OK if OK
@@ -225,13 +224,15 @@
}
public static boolean startsWithLowerCase(String s){
- if (s == null)
+ if (null == s)
return false;
- else if ("".equals(s)) //$NON-NLS-1$
+ else switch (s) {
+ case "": //$NON-NLS-1$
return false;
- else
+ default:
//workaround for JDK bug (see 26529)
return s.charAt(0) == Character.toLowerCase(s.charAt(0));
+ }
}
public static boolean resourceExists(IPath resourcePath){
@@ -270,8 +271,9 @@
public static RefactoringStatus checkForMainAndNativeMethods(IType[] types) throws JavaModelException {
RefactoringStatus result= new RefactoringStatus();
- for (int i= 0; i < types.length; i++)
- result.merge(checkForMainAndNativeMethods(types[i]));
+ for (IType type : types) {
+ result.merge(checkForMainAndNativeMethods(type));
+ }
return result;
}
@@ -284,18 +286,17 @@
private static RefactoringStatus checkForMainAndNativeMethods(IMethod[] methods) throws JavaModelException {
RefactoringStatus result= new RefactoringStatus();
- for (int i= 0; i < methods.length; i++) {
- if (JdtFlags.isNative(methods[i])){
- String typeName= JavaElementLabelsCore.getElementLabel(methods[i].getDeclaringType(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED);
- String methodName= JavaElementLabelsCore.getElementLabel(methods[i], JavaElementLabelsCore.ALL_DEFAULT);
+ for (IMethod method : methods) {
+ if (JdtFlags.isNative(method)) {
+ String typeName= JavaElementLabelsCore.getElementLabel(method.getDeclaringType(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED);
+ String methodName= JavaElementLabelsCore.getElementLabel(method, JavaElementLabelsCore.ALL_DEFAULT);
String msg= Messages.format(RefactoringCoreMessages.Checks_method_native,
- new String[]{typeName, methodName, "UnsatisfiedLinkError"});//$NON-NLS-1$
- result.addEntry(RefactoringStatus.ERROR, msg, JavaStatusContext.create(methods[i]), JavaManipulation.getPreferenceNodeId(), RefactoringStatusCodes.NATIVE_METHOD);
+ new String[]{typeName, methodName, "UnsatisfiedLinkError"});//$NON-NLS-1$
+ result.addEntry(RefactoringStatus.ERROR, msg, JavaStatusContext.create(method), JavaManipulation.getPreferenceNodeId(), RefactoringStatusCodes.NATIVE_METHOD);
}
- if (methods[i].isMainMethod()) {
- String msg= Messages.format(RefactoringCoreMessages.Checks_has_main,
- JavaElementLabelsCore.getElementLabel(methods[i].getDeclaringType(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED));
- result.addEntry(RefactoringStatus.WARNING, msg, JavaStatusContext.create(methods[i]), JavaManipulation.getPreferenceNodeId(), RefactoringStatusCodes.MAIN_METHOD);
+ if (method.isMainMethod()) {
+ String msg= Messages.format(RefactoringCoreMessages.Checks_has_main, JavaElementLabelsCore.getElementLabel(method.getDeclaringType(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED));
+ result.addEntry(RefactoringStatus.WARNING, msg, JavaStatusContext.create(method), JavaManipulation.getPreferenceNodeId(), RefactoringStatusCodes.MAIN_METHOD);
}
}
return result;
@@ -387,7 +388,7 @@
public static boolean isEnumCase(ASTNode node) {
if (node instanceof SwitchCase) {
final SwitchCase caze= (SwitchCase) node;
- if (node.getAST().apiLevel() >= AST.JLS12) {
+ if (node.getAST().isPreviewEnabled()) {
List expressions= caze.expressions();
boolean isEnumConst= true;
for (Expression expression : expressions) {
@@ -541,8 +542,7 @@
*/
public static IMethod findSimilarMethod(IMethod method, IMethod[] methods) throws JavaModelException {
boolean isConstructor= method.isConstructor();
- for (int i= 0; i < methods.length; i++) {
- IMethod otherMethod= methods[i];
+ for (IMethod otherMethod : methods) {
if (otherMethod.isConstructor() == isConstructor && method.isSimilar(otherMethod))
return otherMethod;
}
@@ -592,8 +592,8 @@
public static SearchResultGroup[] excludeCompilationUnits(SearchResultGroup[] grouped, RefactoringStatus status) throws JavaModelException{
List result= new ArrayList<>();
boolean wasEmpty= grouped.length == 0;
- for (int i= 0; i < grouped.length; i++){
- IResource resource= grouped[i].getResource();
+ for (SearchResultGroup g : grouped) {
+ IResource resource= g.getResource();
IJavaElement element= JavaCore.create(resource);
if (! (element instanceof ICompilationUnit))
continue;
@@ -603,7 +603,7 @@
status.addError(Messages.format(RefactoringCoreMessages.Checks_cannot_be_parsed, BasicElementLabels.getPathLabel(cu.getPath(), false)));
continue; //removed, go to the next one
}
- result.add(grouped[i]);
+ result.add(g);
}
if ((!wasEmpty) && result.isEmpty())
@@ -614,8 +614,9 @@
public static RefactoringStatus checkCompileErrorsInAffectedFiles(SearchResultGroup[] grouped) throws JavaModelException {
RefactoringStatus result= new RefactoringStatus();
- for (int i= 0; i < grouped.length; i++)
- checkCompileErrorsInAffectedFile(result, grouped[i].getResource());
+ for (SearchResultGroup g : grouped) {
+ checkCompileErrorsInAffectedFile(result, g.getResource());
+ }
return result;
}
@@ -626,8 +627,8 @@
public static RefactoringStatus checkCompileErrorsInAffectedFiles(SearchResultGroup[] references, IResource declaring) throws JavaModelException {
RefactoringStatus result= new RefactoringStatus();
- for (int i= 0; i < references.length; i++){
- IResource resource= references[i].getResource();
+ for (SearchResultGroup reference : references) {
+ IResource resource= reference.getResource();
if (resource.equals(declaring))
declaring= null;
checkCompileErrorsInAffectedFile(result, resource);
@@ -639,10 +640,10 @@
private static boolean hasCompileErrors(IResource resource) throws JavaModelException {
try {
- IMarker[] problemMarkers= resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
- for (int i= 0; i < problemMarkers.length; i++) {
- if (problemMarkers[i].getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR)
+ for (IMarker problemMarker : resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE)) {
+ if (problemMarker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR) {
return true;
+ }
}
return false;
} catch (JavaModelException e){
@@ -677,10 +678,10 @@
IContainer container= (IContainer)res;
try {
- IResource[] children= container.members();
- for (int i= 0; i < children.length; i++) {
- if (isReadOnly(children[i]))
+ for (IResource child : container.members()) {
+ if (isReadOnly(child)) {
return true;
+ }
}
return false;
} catch (JavaModelException e){
@@ -723,8 +724,8 @@
ResourceChangeChecker checker= context.getChecker(ResourceChangeChecker.class);
IResourceChangeDescriptionFactory deltaFactory= checker.getDeltaFactory();
- for (int i= 0; i < filesToModify.length; i++) {
- deltaFactory.change(filesToModify[i]);
+ for (IFile file : filesToModify) {
+ deltaFactory.change(file);
}
}
@@ -849,9 +850,10 @@
if (! iType.isClass())
return false;
IType[] superTypes= iType.newSupertypeHierarchy(pm).getAllSupertypes(iType);
- for (int i= 0; i < superTypes.length; i++) {
- if ("java.lang.Throwable".equals(superTypes[i].getFullyQualifiedName())) //$NON-NLS-1$
+ for (IType superType : superTypes) {
+ if ("java.lang.Throwable".equals(superType.getFullyQualifiedName())) { //$NON-NLS-1$
return true;
+ }
}
return false;
} finally{
@@ -875,7 +877,7 @@
}
if (e instanceof Annotation)
return NOT_RVALUE_MISC;
-
+
ITypeBinding tb= e.resolveTypeBinding();
boolean guessingRequired= false;
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+
+import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
+
+public class CallContext {
+
+ public ASTNode invocation;
+ public Expression[] arguments;
+ public String receiver;
+ public boolean receiverIsStatic;
+ public CodeScopeBuilder.Scope scope;
+ public int callMode;
+ public ImportRewrite importer;
+ public ICompilationUnit compilationUnit;
+
+ public CallContext(ASTNode inv, CodeScopeBuilder.Scope s, int cm, ImportRewrite i) {
+ super();
+ invocation= inv;
+ scope= s;
+ callMode= cm;
+ importer= i;
+ }
+
+ public ITypeBinding getReceiverType() {
+ Expression expression= Invocations.getExpression(invocation);
+ if (expression != null) {
+ return expression.resolveTypeBinding();
+ }
+ IMethodBinding method= Invocations.resolveBinding(invocation);
+ if (method != null) {
+ return method.getDeclaringClass();
+ }
+ return null;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,850 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
+ * o bug "inline method - doesn't handle implicit cast" (see
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
+ * o bug inline method: compile error (array related)
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38471)
+ * o inline call that is used in a field initializer
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
+ * o inline call a field initializer: could detect self reference
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=44417)
+ * o Allow 'this' constructor to be inlined
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
+ * Nikolay Metchev - Anonymous class using final parameter breaks method inlining - https://bugs.eclipse.org/269401
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ * Pierre-Yves B. - [inline] Inlining a local variable leads to ambiguity with overloaded methods - https://bugs.eclipse.org/434747
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jface.text.BadLocationException;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.LabeledStatement;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.WhileStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.core.manipulation.dom.NecessaryParenthesesChecker;
+import org.eclipse.jdt.internal.corext.CorextCore;
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
+import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
+import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
+import org.eclipse.jdt.internal.corext.dom.Selection;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.InputFlowAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TypeEnvironment;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
+import org.eclipse.jdt.internal.corext.refactoring.util.NoCommentSourceRangeComputer;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringFileBuffers;
+
+public class CallInliner {
+
+ private ICompilationUnit fCUnit;
+ private ASTRewrite fRewrite;
+ private ImportRewrite fImportRewrite;
+ private ITextFileBuffer fBuffer;
+ private SourceProvider fSourceProvider;
+ private TypeEnvironment fTypeEnvironment;
+
+ private BodyDeclaration fBodyDeclaration;
+ private CodeScopeBuilder.Scope fRootScope;
+ private int fNumberOfLocals;
+
+ private ASTNode fInvocation;
+
+ private int fInsertionIndex;
+ private ListRewrite fListRewrite;
+
+ private boolean fNeedsStatement;
+ private ASTNode fTargetNode;
+ private FlowContext fFlowContext;
+ private FlowInfo fFlowInfo;
+ private CodeScopeBuilder.Scope fInvocationScope;
+ private boolean fFieldInitializer;
+ private List fLocals;
+ private CallContext fContext;
+
+ private class InlineEvaluator extends HierarchicalASTVisitor {
+ private ParameterData fFormalArgument;
+ private boolean fResult;
+ public InlineEvaluator(ParameterData argument) {
+ fFormalArgument= argument;
+ }
+ public boolean getResult() {
+ return fResult;
+ }
+ private boolean setResult(boolean result) {
+ fResult= result;
+ return false;
+ }
+ @Override
+ public boolean visit(Expression node) {
+ int accessMode= fFormalArgument.getSimplifiedAccessMode();
+ if (accessMode == FlowInfo.WRITE)
+ return setResult(false);
+ if (accessMode == FlowInfo.UNUSED)
+ return setResult(true);
+ if (ASTNodes.isLiteral(node))
+ return setResult(true);
+ return setResult(fFormalArgument.getNumberOfAccesses() <= 1);
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof IVariableBinding) {
+ int accessMode = fFormalArgument.getSimplifiedAccessMode();
+ if (fFormalArgument.isFinal() && !Modifier.isFinal(binding.getModifiers())) {
+ return setResult(false);
+ }
+ if (accessMode == FlowInfo.READ || accessMode == FlowInfo.UNUSED)
+ return setResult(true);
+ // from now on we only have write accesses.
+ IVariableBinding vb= (IVariableBinding)binding;
+ if (vb.isField())
+ return setResult(false);
+ return setResult(fFlowInfo.hasAccessMode(fFlowContext, vb, FlowInfo.UNUSED | FlowInfo.WRITE));
+ }
+ return setResult(false);
+ }
+ @Override
+ public boolean visit(FieldAccess node) {
+ return visit(node.getName());
+ }
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ return visit(node.getName());
+ }
+ @Override
+ public boolean visit(ThisExpression node) {
+ int accessMode= fFormalArgument.getSimplifiedAccessMode();
+ if (accessMode == FlowInfo.READ || accessMode == FlowInfo.UNUSED)
+ return setResult(true);
+ return setResult(false);
+ }
+ }
+
+ public CallInliner(ICompilationUnit unit, CompilationUnit targetAstRoot, SourceProvider provider) throws CoreException {
+ super();
+ fCUnit= unit;
+ fBuffer= RefactoringFileBuffers.acquire(fCUnit);
+ fSourceProvider= provider;
+ fImportRewrite= StubUtility.createImportRewrite(targetAstRoot, true);
+ fLocals= new ArrayList<>(3);
+ fRewrite= ASTRewrite.create(targetAstRoot.getAST());
+ fRewrite.setTargetSourceRangeComputer(new NoCommentSourceRangeComputer());
+ fTypeEnvironment= new TypeEnvironment();
+ }
+
+ public void dispose() {
+ try {
+ RefactoringFileBuffers.release(fCUnit);
+ } catch (CoreException exception) {
+ JavaManipulationPlugin.log(exception);
+ }
+ }
+
+
+ public ImportRewrite getImportEdit() {
+ return fImportRewrite;
+ }
+
+ public ASTNode getTargetNode() {
+ return fTargetNode;
+ }
+
+ public void initialize(BodyDeclaration declaration) {
+ fBodyDeclaration= declaration;
+ fRootScope= CodeScopeBuilder.perform(declaration, fSourceProvider.getDeclaration().resolveBinding());
+ fNumberOfLocals= 0;
+ switch (declaration.getNodeType()) {
+ case ASTNode.METHOD_DECLARATION:
+ case ASTNode.INITIALIZER:
+ fNumberOfLocals= LocalVariableIndex.perform(declaration);
+ break;
+ }
+ }
+
+ public RefactoringStatus initialize(ASTNode invocation, int severity) {
+ RefactoringStatus result= new RefactoringStatus();
+ fInvocation= invocation;
+ fLocals= new ArrayList<>(3);
+
+ checkMethodDeclaration(result, severity);
+ if (result.getSeverity() >= severity)
+ return result;
+
+ initializeRewriteState();
+ initializeTargetNode();
+ flowAnalysis();
+
+ fContext= new CallContext(fInvocation, fInvocationScope, fTargetNode.getNodeType(), fImportRewrite);
+
+ try {
+ computeRealArguments();
+ computeReceiver();
+ } catch (BadLocationException exception) {
+ JavaManipulationPlugin.log(exception);
+ }
+ checkInvocationContext(result, severity);
+
+ return result;
+ }
+
+ private void initializeRewriteState() {
+ fFieldInitializer= false;
+ ASTNode parent= fInvocation.getParent();
+ do {
+ if (parent instanceof FieldDeclaration) {
+ fFieldInitializer= true;
+ return;
+ } else if (parent instanceof Block) {
+ return;
+ }
+ parent= parent.getParent();
+ } while (parent != null);
+ }
+
+ private void initializeTargetNode() {
+ ASTNode parent= fInvocation.getParent();
+ int nodeType= parent.getNodeType();
+ if (nodeType == ASTNode.EXPRESSION_STATEMENT || nodeType == ASTNode.RETURN_STATEMENT) {
+ fTargetNode= parent;
+ } else {
+ fTargetNode= fInvocation;
+ }
+ }
+
+ // the checks depend on invocation context and therefore can't be done in SourceAnalyzer
+ private void checkMethodDeclaration(RefactoringStatus result, int severity) {
+ MethodDeclaration methodDeclaration= fSourceProvider.getDeclaration();
+ // it is not allowed to inline constructor invocation only if it is used for class instance creation
+ // if constructor is invoked from another constructor then we can inline such invocation
+ if (fInvocation.getNodeType() != ASTNode.CONSTRUCTOR_INVOCATION && methodDeclaration.isConstructor()) {
+ result.addEntry(new RefactoringStatusEntry(
+ severity,
+ RefactoringCoreMessages.CallInliner_constructors,
+ JavaStatusContext.create(fCUnit, fInvocation)));
+ }
+ if (fSourceProvider.hasSuperMethodInvocation() && fInvocation.getNodeType() == ASTNode.METHOD_INVOCATION) {
+ Expression receiver= ((MethodInvocation)fInvocation).getExpression();
+ if (receiver instanceof ThisExpression) {
+ result.addEntry(new RefactoringStatusEntry(
+ severity,
+ RefactoringCoreMessages.CallInliner_super_into_this_expression,
+ JavaStatusContext.create(fCUnit, fInvocation)));
+ }
+ }
+ }
+
+ private void checkInvocationContext(RefactoringStatus result, int severity) {
+ if (fInvocation.getNodeType() == ASTNode.METHOD_INVOCATION) {
+ if (((MethodInvocation)fInvocation).resolveTypeBinding() == null) {
+ addEntry(result, RefactoringCoreMessages.CallInliner_receiver_type,
+ RefactoringStatusCodes.INLINE_METHOD_NULL_BINDING, severity);
+ return;
+ }
+ }
+ int nodeType= fTargetNode.getNodeType();
+ if (nodeType == ASTNode.EXPRESSION_STATEMENT) {
+ if (fSourceProvider.isExecutionFlowInterrupted()) {
+ addEntry(result, RefactoringCoreMessages.CallInliner_execution_flow,
+ RefactoringStatusCodes.INLINE_METHOD_EXECUTION_FLOW, severity);
+ return;
+ }
+ } else if (nodeType == ASTNode.METHOD_INVOCATION) {
+ ASTNode parent= fTargetNode.getParent();
+ if (isReturnStatement(parent)) {
+ //support inlining even if the execution flow is interrupted
+ return;
+ }
+ if (fSourceProvider.isExecutionFlowInterrupted()) {
+ addEntry(result, RefactoringCoreMessages.CallInliner_execution_flow,
+ RefactoringStatusCodes.INLINE_METHOD_EXECUTION_FLOW, severity);
+ return;
+ }
+ if (isAssignment(parent) || isSingleDeclaration(parent)) {
+ // we support inlining expression in assigment and initializers as
+ // long as the execution flow isn't interrupted.
+ return;
+ } else {
+ boolean isFieldDeclaration= ASTNodes.getParent(fInvocation, FieldDeclaration.class) != null;
+ if (!fSourceProvider.isSimpleFunction()) {
+ if (isMultiDeclarationFragment(parent)) {
+ addEntry(result, RefactoringCoreMessages.CallInliner_multiDeclaration,
+ RefactoringStatusCodes.INLINE_METHOD_INITIALIZER_IN_FRAGEMENT, severity);
+ } else if (isFieldDeclaration) {
+ addEntry(result,
+ RefactoringCoreMessages.CallInliner_field_initializer_simple,
+ RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
+ } else {
+ addEntry(result, RefactoringCoreMessages.CallInliner_simple_functions,
+ RefactoringStatusCodes.INLINE_METHOD_ONLY_SIMPLE_FUNCTIONS, severity);
+ }
+ return;
+ }
+ if (isFieldDeclaration) {
+ int argumentsCount= fContext.arguments.length;
+ for (int i= 0; i < argumentsCount; i++) {
+ ParameterData parameter= fSourceProvider.getParameterData(i);
+ if(parameter.isWrite()) {
+ addEntry(result,
+ RefactoringCoreMessages.CallInliner_field_initialize_write_parameter,
+ RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
+ return;
+ }
+ }
+ if(fLocals.size() > 0) {
+ addEntry(result,
+ RefactoringCoreMessages.CallInliner_field_initialize_new_local,
+ RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
+ return;
+ }
+ // verify that the field is not referenced by the initializer method
+ VariableDeclarationFragment variable= (VariableDeclarationFragment)ASTNodes.getParent(fInvocation, ASTNode.VARIABLE_DECLARATION_FRAGMENT);
+ if(fSourceProvider.isVariableReferenced(variable.resolveBinding())) {
+ addEntry(result,
+ RefactoringCoreMessages.CallInliner_field_initialize_self_reference,
+ RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean isAssignment(ASTNode node) {
+ return node instanceof Assignment;
+ }
+
+ private static boolean isReturnStatement(ASTNode node) {
+ return node instanceof ReturnStatement;
+ }
+
+ private static boolean isSingleDeclaration(ASTNode node) {
+ int type= node.getNodeType();
+ if (type == ASTNode.SINGLE_VARIABLE_DECLARATION)
+ return true;
+ if (type == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
+ node= node.getParent();
+ if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) {
+ VariableDeclarationStatement vs= (VariableDeclarationStatement)node;
+ return vs.fragments().size() == 1;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isMultiDeclarationFragment(ASTNode node) {
+ int nodeType= node.getNodeType();
+ if (nodeType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
+ node= node.getParent();
+ if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) {
+ VariableDeclarationStatement vs= (VariableDeclarationStatement)node;
+ return vs.fragments().size() > 1;
+ }
+ }
+ return false;
+ }
+
+ private void addEntry(RefactoringStatus result, String message, int code, int severity) {
+ result.addEntry(new RefactoringStatusEntry(
+ severity, message,
+ JavaStatusContext.create(fCUnit, fInvocation),
+ CorextCore.getPluginId(),
+ code, null));
+ }
+
+ private void flowAnalysis() {
+ fInvocationScope= fRootScope.findScope(fTargetNode.getStartPosition(), fTargetNode.getLength());
+ fInvocationScope.setCursor(fTargetNode.getStartPosition());
+ fFlowContext= new FlowContext(0, fNumberOfLocals + 1);
+ fFlowContext.setConsiderAccessMode(true);
+ fFlowContext.setComputeMode(FlowContext.ARGUMENTS);
+ Selection selection= Selection.createFromStartLength(fInvocation.getStartPosition(), fInvocation.getLength());
+ switch (fBodyDeclaration.getNodeType()) {
+ case ASTNode.INITIALIZER:
+ case ASTNode.FIELD_DECLARATION:
+ case ASTNode.METHOD_DECLARATION:
+ case ASTNode.ENUM_CONSTANT_DECLARATION:
+ fFlowInfo= new InputFlowAnalyzer(fFlowContext, selection, true).perform(fBodyDeclaration);
+ break;
+ default:
+ Assert.isTrue(false, "Should not happen"); //$NON-NLS-1$
+ }
+ }
+
+ public RefactoringStatus perform(TextEditGroup textEditGroup) throws CoreException {
+ RefactoringStatus result= new RefactoringStatus();
+ String[] blocks= fSourceProvider.getCodeBlocks(fContext, fImportRewrite);
+ if(!fFieldInitializer) {
+ initializeInsertionPoint(fSourceProvider.getNumberOfStatements() + fLocals.size());
+ }
+
+ addNewLocals(textEditGroup);
+ replaceCall(result, blocks, textEditGroup);
+ return result;
+ }
+
+ public TextEdit getModifications() {
+ return fRewrite.rewriteAST(fBuffer.getDocument(), fCUnit.getJavaProject().getOptions(true));
+ }
+
+ private void computeRealArguments() {
+ List arguments= Invocations.getArguments(fInvocation);
+ Set canNotInline= crossCheckArguments(arguments);
+ boolean needsVarargBoxing= needsVarargBoxing(arguments);
+ int varargIndex= fSourceProvider.getVarargIndex();
+ AST ast= fInvocation.getAST();
+ Expression[] realArguments= new Expression[needsVarargBoxing ? varargIndex + 1 : arguments.size()];
+ for (int i= 0; i < (needsVarargBoxing ? varargIndex : arguments.size()); i++) {
+ Expression expression= arguments.get(i);
+ ParameterData parameter= fSourceProvider.getParameterData(i);
+ if (canInline(expression, parameter) && !canNotInline.contains(expression)) {
+ realArguments[i]= expression;
+ } else {
+ String name= fInvocationScope.createName(parameter.getName(), true);
+ realArguments[i]= ast.newSimpleName(name);
+ VariableDeclarationStatement local= createLocalDeclaration(parameter.getTypeBinding(), name, (Expression) fRewrite.createCopyTarget(expression));
+ if (parameter.isFinal()) {
+ local.modifiers().add(fInvocation.getAST().newModifier(ModifierKeyword.FINAL_KEYWORD));
+ }
+ fLocals.add(local);
+ }
+ }
+ if (needsVarargBoxing) {
+ ParameterData parameter= fSourceProvider.getParameterData(varargIndex);
+ String name= fInvocationScope.createName(parameter.getName(), true);
+ realArguments[varargIndex]= ast.newSimpleName(name);
+ Type type= fImportRewrite.addImport(parameter.getTypeBinding(), ast);
+ VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
+ fragment.setName(ast.newSimpleName(name));
+ ArrayInitializer initializer= ast.newArrayInitializer();
+ for (int i= varargIndex; i < arguments.size(); i++) {
+ initializer.expressions().add(fRewrite.createCopyTarget(arguments.get(i)));
+ }
+ fragment.setInitializer(initializer);
+ VariableDeclarationStatement decl= ast.newVariableDeclarationStatement(fragment);
+ decl.setType(type);
+ fLocals.add(decl);
+ }
+ fContext.compilationUnit= fCUnit;
+ fContext.arguments= realArguments;
+ }
+
+ private boolean needsVarargBoxing(List arguments) {
+ if (!fSourceProvider.isVarargs())
+ return false;
+ /*
+ if (!fSourceProvider.hasArrayAccess())
+ return false;
+ */
+ int index= fSourceProvider.getVarargIndex();
+ // we have varags but the call doesn't pass any arguments
+ if (index >= arguments.size())
+ return true;
+ // parameter is array type
+ // one arg
+ if (index == arguments.size() - 1) {
+ ITypeBinding argument= arguments.get(index).resolveTypeBinding();
+ if (argument == null)
+ return false;
+ ITypeBinding parameter= fSourceProvider.getParameterData(index).getTypeBinding();
+ return !fTypeEnvironment.create(argument).canAssignTo(fTypeEnvironment.create(parameter));
+ }
+ return true;
+ }
+
+ private void computeReceiver() throws BadLocationException {
+ Expression receiver= Invocations.getExpression(fInvocation);
+ if (receiver == null)
+ return;
+ final boolean isName= receiver instanceof Name;
+ if (isName)
+ fContext.receiverIsStatic= ((Name)receiver).resolveBinding() instanceof ITypeBinding;
+ if (ASTNodes.isLiteral(receiver) || isName || receiver instanceof ThisExpression) {
+ fContext.receiver= fBuffer.getDocument().get(receiver.getStartPosition(), receiver.getLength());
+ return;
+ }
+ switch(fSourceProvider.getReceiversToBeUpdated()) {
+ case 0:
+ // Make sure we evaluate the current receiver. Best is to assign to
+ // local.
+ fLocals.add(createLocalDeclaration(
+ receiver.resolveTypeBinding(),
+ fInvocationScope.createName("r", true), //$NON-NLS-1$
+ (Expression)fRewrite.createCopyTarget(receiver)));
+ return;
+ case 1:
+ fContext.receiver= fBuffer.getDocument().get(receiver.getStartPosition(), receiver.getLength());
+ return;
+ default:
+ String local= fInvocationScope.createName("r", true); //$NON-NLS-1$
+ fLocals.add(createLocalDeclaration(
+ receiver.resolveTypeBinding(),
+ local,
+ (Expression)fRewrite.createCopyTarget(receiver)));
+ fContext.receiver= local;
+ return;
+ }
+ }
+
+ private void addNewLocals(TextEditGroup textEditGroup) {
+ if (fLocals.isEmpty())
+ return;
+ for (Iterator iter= fLocals.iterator(); iter.hasNext();) {
+ ASTNode element= iter.next();
+ fListRewrite.insertAt(element, fInsertionIndex++, textEditGroup);
+ }
+ }
+
+ private void replaceCall(RefactoringStatus status, String[] blocks, TextEditGroup textEditGroup) {
+ // Inline empty body
+ if (blocks.length == 0 && fTargetNode != null) {
+ if (fNeedsStatement) {
+ fRewrite.replace(fTargetNode, fTargetNode.getAST().newEmptyStatement(), textEditGroup);
+ } else {
+ fRewrite.remove(fTargetNode, textEditGroup);
+ }
+ } else {
+ ASTNode node= null;
+ for (int i= 0; i < blocks.length - 1; i++) {
+ node= fRewrite.createStringPlaceholder(blocks[i], ASTNode.RETURN_STATEMENT);
+ fListRewrite.insertAt(node, fInsertionIndex++, textEditGroup);
+ }
+ String block= blocks[blocks.length - 1];
+ // We can inline a call where the declaration is a function and the call itself
+ // is a statement. In this case we have to create a temporary variable if the
+ // returned expression must be evaluated.
+ if (fContext.callMode == ASTNode.EXPRESSION_STATEMENT && fSourceProvider.hasReturnValue()) {
+ if (fSourceProvider.mustEvaluateReturnedExpression()) {
+ if (fSourceProvider.returnValueNeedsLocalVariable()) {
+ IMethodBinding invocation= Invocations.resolveBinding(fInvocation);
+ node= createLocalDeclaration(
+ invocation.getReturnType(),
+ fInvocationScope.createName(fSourceProvider.getMethodName(), true),
+ (Expression)fRewrite.createStringPlaceholder(block, ASTNode.METHOD_INVOCATION));
+ } else {
+ node= fRewrite.getAST().newExpressionStatement(
+ (Expression)fRewrite.createStringPlaceholder(block, ASTNode.METHOD_INVOCATION));
+ }
+ } else {
+ node= null;
+ }
+ } else if (fTargetNode instanceof Expression) {
+ node= fRewrite.createStringPlaceholder(block, ASTNode.METHOD_INVOCATION);
+
+ // fixes bug #24941
+ if (needsExplicitCast(status)) {
+ AST ast= node.getAST();
+ CastExpression castExpression= ast.newCastExpression();
+ Type returnType= fImportRewrite.addImport(fSourceProvider.getReturnType(), ast);
+ castExpression.setType(returnType);
+
+ if (NecessaryParenthesesChecker.needsParentheses(fSourceProvider.getReturnExpressions().get(0), castExpression, CastExpression.EXPRESSION_PROPERTY)) {
+ ParenthesizedExpression parenthesized= ast.newParenthesizedExpression();
+ parenthesized.setExpression((Expression)node);
+ node= parenthesized;
+ }
+
+ castExpression.setExpression((Expression)node);
+ node= castExpression;
+
+ if (NecessaryParenthesesChecker.needsParentheses(castExpression, fTargetNode.getParent(), fTargetNode.getLocationInParent())) {
+ ParenthesizedExpression parenthesized= ast.newParenthesizedExpression();
+ parenthesized.setExpression((Expression)node);
+ node= parenthesized;
+ }
+ } else if (fSourceProvider.needsReturnedExpressionParenthesis(fTargetNode.getParent(), fTargetNode.getLocationInParent())) {
+ ParenthesizedExpression pExp= fTargetNode.getAST().newParenthesizedExpression();
+ pExp.setExpression((Expression)node);
+ node= pExp;
+ }
+ } else {
+ node= fRewrite.createStringPlaceholder(block, ASTNode.RETURN_STATEMENT);
+ }
+
+ // Now replace the target node with the source node
+ if (node != null) {
+ if (fTargetNode == null) {
+ fListRewrite.insertAt(node, fInsertionIndex++, textEditGroup);
+ } else {
+ fRewrite.replace(fTargetNode, node, textEditGroup);
+ }
+ } else {
+ if (fTargetNode != null) {
+ fRewrite.remove(fTargetNode, textEditGroup);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param status the status
+ * @return true
if explicit cast is needed otherwise false
+ */
+ private boolean needsExplicitCast(RefactoringStatus status) {
+ // if the return type of the method is the same as the type of the
+ // returned expression then we don't need an explicit cast.
+ if (fSourceProvider.returnTypeMatchesReturnExpressions())
+ return false;
+
+ List returnExprs= fSourceProvider.getReturnExpressions();
+ // it is inferred that only methods consisting of a single
+ // return statement can be inlined as parameters in other
+ // method invocations
+ if (returnExprs.size() != 1)
+ return false;
+
+ if (fTargetNode.getLocationInParent() == MethodInvocation.ARGUMENTS_PROPERTY) {
+ MethodInvocation methodInvocation= (MethodInvocation)fTargetNode.getParent();
+ if(methodInvocation.getExpression() == fTargetNode)
+ return false;
+ IMethodBinding method= methodInvocation.resolveMethodBinding();
+ if (method == null) {
+ status.addError(RefactoringCoreMessages.CallInliner_cast_analysis_error,
+ JavaStatusContext.create(fCUnit, methodInvocation));
+ return false;
+ }
+
+ ITypeBinding parameterType= returnExprs.get(0).resolveTypeBinding();
+ return ASTNodes.isTargetAmbiguous((Expression) fTargetNode, parameterType);
+ } else {
+ ITypeBinding explicitCast= ASTNodes.getExplicitCast(returnExprs.get(0), (Expression)fTargetNode);
+ return explicitCast != null;
+ }
+ }
+
+ private VariableDeclarationStatement createLocalDeclaration(ITypeBinding type, String name, Expression initializer) {
+ ImportRewriteContext context= new ContextSensitiveImportRewriteContext(fTargetNode, fImportRewrite);
+ String typeName= fImportRewrite.addImport(type, context);
+ VariableDeclarationStatement decl= (VariableDeclarationStatement)ASTNodeFactory.newStatement(
+ fInvocation.getAST(), typeName + " " + name + ";"); //$NON-NLS-1$ //$NON-NLS-2$
+ ((VariableDeclarationFragment)decl.fragments().get(0)).setInitializer(initializer);
+ return decl;
+ }
+
+ /**
+ * Checks whether arguments are passed to the method which do some assignments
+ * inside the expression. If so these arguments can't be inlined into the
+ * calling method since the assignments might be reorder. An example is:
+ *
+ * add((field=args).length,field.hashCode());
+ *
+ * Field might not be initialized when the arguments are reorder in the called
+ * method.
+ * @param arguments the arguments
+ * @return all arguments that cannot be inlined
+ */
+ private Set crossCheckArguments(List arguments) {
+ final Set assigned= new HashSet<>();
+ final Set result= new HashSet<>();
+ for (Iterator iter= arguments.iterator(); iter.hasNext();) {
+ final Expression expression= iter.next();
+ expression.accept(new ASTVisitor() {
+ @Override
+ public boolean visit(Assignment node) {
+ Expression lhs= node.getLeftHandSide();
+ if (lhs instanceof Name) {
+ IBinding binding= ((Name)lhs).resolveBinding();
+ if (binding instanceof IVariableBinding) {
+ assigned.add(binding);
+ result.add(expression);
+ }
+ }
+ return true;
+ }
+ });
+ }
+ for (Iterator iter= arguments.iterator(); iter.hasNext();) {
+ final Expression expression= iter.next();
+ if (!result.contains(expression)) {
+ expression.accept(new HierarchicalASTVisitor() {
+ @Override
+ public boolean visit(Name node) {
+ IBinding binding= node.resolveBinding();
+ if (binding != null && assigned.contains(binding))
+ result.add(expression);
+ return false;
+ }
+ });
+ }
+ }
+ return result;
+ }
+
+ private boolean canInline(Expression actualParameter, ParameterData formalParameter) {
+ InlineEvaluator evaluator= new InlineEvaluator(formalParameter);
+ actualParameter.accept(evaluator);
+ return evaluator.getResult();
+ }
+
+ private void initializeInsertionPoint(int nos) {
+ fInsertionIndex= -1;
+ fNeedsStatement= false;
+ // if we have a constructor invocation the invocation itself is already a statement
+ ASTNode parentStatement= fInvocation instanceof Statement
+ ? fInvocation
+ : ASTNodes.getParent(fInvocation, Statement.class);
+ if (parentStatement == null)
+ return;
+
+ ASTNode container= parentStatement.getParent();
+ int type= container.getNodeType();
+ if (type == ASTNode.BLOCK) {
+ Block block= (Block)container;
+ fListRewrite= fRewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
+ fInsertionIndex= fListRewrite.getRewrittenList().indexOf(parentStatement);
+ } else if (type == ASTNode.SWITCH_STATEMENT) {
+ SwitchStatement switchStatement= (SwitchStatement)container;
+ fListRewrite= fRewrite.getListRewrite(switchStatement, SwitchStatement.STATEMENTS_PROPERTY);
+ fInsertionIndex= fListRewrite.getRewrittenList().indexOf(parentStatement);
+ } else if (isControlStatement(container) || type == ASTNode.LABELED_STATEMENT) {
+ fNeedsStatement= true;
+ if (nos > 1 || needsBlockAroundDanglingIf()) {
+ Block block= fInvocation.getAST().newBlock();
+ fInsertionIndex= 0;
+ Statement currentStatement= null;
+ switch(type) {
+ case ASTNode.LABELED_STATEMENT:
+ currentStatement= ((LabeledStatement)container).getBody();
+ break;
+ case ASTNode.FOR_STATEMENT:
+ currentStatement= ((ForStatement)container).getBody();
+ break;
+ case ASTNode.ENHANCED_FOR_STATEMENT:
+ currentStatement= ((EnhancedForStatement)container).getBody();
+ break;
+ case ASTNode.WHILE_STATEMENT:
+ currentStatement= ((WhileStatement)container).getBody();
+ break;
+ case ASTNode.DO_STATEMENT:
+ currentStatement= ((DoStatement)container).getBody();
+ break;
+ case ASTNode.IF_STATEMENT:
+ IfStatement node= (IfStatement)container;
+ Statement thenPart= node.getThenStatement();
+ if (fTargetNode == thenPart || ASTNodes.isParent(fTargetNode, thenPart)) {
+ currentStatement= thenPart;
+ } else {
+ currentStatement= node.getElseStatement();
+ }
+ break;
+ }
+ Assert.isNotNull(currentStatement);
+ fRewrite.replace(currentStatement, block, null);
+ fListRewrite= fRewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
+ // The method to be inlined is not the body of the control statement.
+ if (currentStatement != fTargetNode) {
+ fListRewrite.insertLast(fRewrite.createCopyTarget(currentStatement), null);
+ } else {
+ // We can't replace a copy with something else. So we
+ // have to insert all statements to be inlined.
+ fTargetNode= null;
+ }
+ }
+ }
+ // We only insert one new statement or we delete the existing call.
+ // So there is no need to have an insertion index.
+ }
+
+ private boolean needsBlockAroundDanglingIf() {
+ /* see https://bugs.eclipse.org/bugs/show_bug.cgi?id=169331
+ *
+ * Situation:
+ * boolean a, b;
+ * void toInline() {
+ * if (a)
+ * hashCode();
+ * }
+ * void m() {
+ * if (b)
+ * toInline();
+ * else
+ * toString();
+ * }
+ * => needs block around inlined "if (a)..." to avoid attaching else to wrong if.
+ */
+ return fTargetNode.getLocationInParent() == IfStatement.THEN_STATEMENT_PROPERTY
+ && fTargetNode.getParent().getStructuralProperty(IfStatement.ELSE_STATEMENT_PROPERTY) != null
+ && fSourceProvider.isDangligIf();
+ }
+
+ private boolean isControlStatement(ASTNode node) {
+ int type= node.getNodeType();
+ return type == ASTNode.IF_STATEMENT || type == ASTNode.FOR_STATEMENT || type == ASTNode.ENHANCED_FOR_STATEMENT ||
+ type == ASTNode.WHILE_STATEMENT || type == ASTNode.DO_STATEMENT;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConstantChecks.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConstantChecks.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConstantChecks.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConstantChecks.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.ThisExpression;
+
+import org.eclipse.jdt.internal.corext.dom.fragments.ASTFragmentFactory;
+import org.eclipse.jdt.internal.corext.dom.fragments.IExpressionFragment;
+
+public class ConstantChecks {
+ private static abstract class ExpressionChecker extends ASTVisitor {
+
+ private final IExpressionFragment fExpression;
+ protected boolean fResult= true;
+
+ public ExpressionChecker(IExpressionFragment ex) {
+ fExpression= ex;
+ }
+ public boolean check() {
+ fResult= true;
+ fExpression.getAssociatedNode().accept(this);
+ return fResult;
+ }
+ }
+
+ private static class LoadTimeConstantChecker extends ExpressionChecker {
+ public LoadTimeConstantChecker(IExpressionFragment ex) {
+ super(ex);
+ }
+
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ fResult= false;
+ return false;
+ }
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ fResult= false;
+ return false;
+ }
+ @Override
+ public boolean visit(ThisExpression node) {
+ fResult= false;
+ return false;
+ }
+ @Override
+ public boolean visit(FieldAccess node) {
+ fResult&= new LoadTimeConstantChecker((IExpressionFragment) ASTFragmentFactory.createFragmentForFullSubtree(node.getExpression())).check();
+ return false;
+ }
+ @Override
+ public boolean visit(MethodInvocation node) {
+ if(node.getExpression() == null) {
+ visitName(node.getName());
+ } else {
+ fResult&= new LoadTimeConstantChecker((IExpressionFragment) ASTFragmentFactory.createFragmentForFullSubtree(node.getExpression())).check();
+ }
+
+ return false;
+ }
+ @Override
+ public boolean visit(QualifiedName node) {
+ return visitName(node);
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ return visitName(node);
+ }
+
+ private boolean visitName(Name name) {
+ fResult&= checkName(name);
+ return false; //Do not descend further
+ }
+
+ private boolean checkName(Name name) {
+ IBinding binding= name.resolveBinding();
+ if (binding == null)
+ return true; /* If the binding is null because of compile errors etc.,
+ scenarios which may have been deemed unacceptable in
+ the presence of semantic information will be admitted. */
+
+ // If name represents a member:
+ if (binding instanceof IVariableBinding || binding instanceof IMethodBinding)
+ return isMemberReferenceValidInClassInitialization(name);
+ else if (binding instanceof ITypeBinding)
+ return ! ((ITypeBinding) binding).isTypeVariable();
+ else {
+ return true; // e.g. a NameQualifiedType's qualifier, which can be a package binding
+ }
+ }
+
+ private boolean isMemberReferenceValidInClassInitialization(Name name) {
+ IBinding binding= name.resolveBinding();
+ Assert.isTrue(binding instanceof IVariableBinding || binding instanceof IMethodBinding);
+
+ if(name instanceof SimpleName)
+ return Modifier.isStatic(binding.getModifiers());
+ else {
+ Assert.isTrue(name instanceof QualifiedName);
+ return checkName(((QualifiedName) name).getQualifier());
+ }
+ }
+ }
+
+ private static class StaticFinalConstantChecker extends ExpressionChecker {
+ public StaticFinalConstantChecker(IExpressionFragment ex) {
+ super(ex);
+ }
+
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ fResult= false;
+ return false;
+ }
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ fResult= false;
+ return false;
+ }
+ @Override
+ public boolean visit(ThisExpression node) {
+ fResult= false;
+ return false;
+ }
+
+ @Override
+ public boolean visit(QualifiedName node) {
+ return visitName(node);
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ return visitName(node);
+ }
+ private boolean visitName(Name name) {
+ IBinding binding= name.resolveBinding();
+ if(binding == null) {
+ /* If the binding is null because of compile errors etc.,
+ scenarios which may have been deemed unacceptable in
+ the presence of semantic information will be admitted.
+ Descend deeper.
+ */
+ return true;
+ }
+
+ int modifiers= binding.getModifiers();
+ if(binding instanceof IVariableBinding) {
+ if (!(Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers))) {
+ fResult= false;
+ return false;
+ }
+ } else if(binding instanceof IMethodBinding) {
+ if (!Modifier.isStatic(modifiers)) {
+ fResult= false;
+ return false;
+ }
+ } else if(binding instanceof ITypeBinding) {
+ return false; // It's o.k. Don't descend deeper.
+
+ } else {
+ return false; // e.g. a NameQualifiedType's qualifier, which can be a package binding
+ }
+
+ //Descend deeper:
+ return true;
+ }
+ }
+
+ public static boolean isStaticFinalConstant(IExpressionFragment ex) {
+ return new StaticFinalConstantChecker(ex).check();
+ }
+
+ public static boolean isLoadTimeConstant(IExpressionFragment ex) {
+ return new LoadTimeConstantChecker(ex).check();
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConvertAnonymousToNestedRefactoring.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConvertAnonymousToNestedRefactoring.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConvertAnonymousToNestedRefactoring.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ConvertAnonymousToNestedRefactoring.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,1212 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * NikolayMetchev@gmail.com - contributed fixes for
+ * - convert anonymous to nested should sometimes declare class as static [refactoring]
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=43360)
+ * - Convert anonymous to nested: should show error if field form outer anonymous type is references [refactoring]
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=48282)
+ * - [refactoring][convert anonymous] gets confused with generic methods
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=124978)
+ * - [convert anonymous] Convert Anonymous to nested generates wrong code
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=159917)
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.NamingConventions;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.Initializer;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.TypeParameter;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.manipulation.CodeGeneration;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
+import org.eclipse.jdt.core.refactoring.descriptors.ConvertAnonymousDescriptor;
+import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
+
+import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalModelCore;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroupCore;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
+import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.text.correction.ModifierCorrectionSubProcessorCore;
+
+import org.eclipse.jdt.internal.core.manipulation.BindingLabelProviderCore;
+import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+
+public class ConvertAnonymousToNestedRefactoring extends Refactoring {
+
+ private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_FINAL= "final"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_STATIC= "static"; //$NON-NLS-1$
+
+ private static final String KEY_TYPE_NAME= "type_name"; //$NON-NLS-1$
+ private static final String KEY_PARAM_NAME_EXT= "param_name_ext"; //$NON-NLS-1$
+ private static final String KEY_PARAM_NAME_CONST= "param_name_const"; //$NON-NLS-1$
+ private static final String KEY_FIELD_NAME_EXT= "field_name_ext"; //$NON-NLS-1$
+
+ public static class TypeVariableFinder extends ASTVisitor {
+
+ private final Map fBindings= new HashMap<>();
+ private final List fFound= new ArrayList<>();
+
+ @Override
+ public final boolean visit(final SimpleName node) {
+ Assert.isNotNull(node);
+ final ITypeBinding binding= node.resolveTypeBinding();
+ if (binding != null && binding.isTypeVariable() && !fBindings.containsKey(binding.getKey())) {
+ fBindings.put(binding.getKey(), binding);
+ fFound.add(binding);
+ }
+ return true;
+ }
+
+ @Override
+ public final boolean visit(TypeParameter parameter) {
+ ITypeBinding binding= parameter.resolveBinding();
+ if (binding != null) {
+ // don't collect type parameters declared inside the anonymous
+ fBindings.put(binding.getKey(), binding);
+ }
+ return false;
+ }
+
+ public final ITypeBinding[] getResult() {
+ final ITypeBinding[] result= new ITypeBinding[fFound.size()];
+ fFound.toArray(result);
+ return result;
+ }
+ }
+
+ private int fSelectionStart;
+ private int fSelectionLength;
+ private ICompilationUnit fCu;
+
+ private int fVisibility; /* see Modifier */
+ private boolean fDeclareFinal= true;
+ private boolean fDeclareStatic;
+ private String fClassName= ""; //$NON-NLS-1$
+
+ private CompilationUnit fCompilationUnitNode;
+ private AnonymousClassDeclaration fAnonymousInnerClassNode;
+ private Set fClassNamesUsed;
+ private boolean fSelfInitializing= false;
+
+ private LinkedProposalModelCore fLinkedProposalModelCore;
+
+ /**
+ * Creates a new convert anonymous to nested refactoring.
+ *
+ * @param unit the compilation unit, or null
if invoked by scripting
+ * @param selectionStart start
+ * @param selectionLength length
+ */
+ public ConvertAnonymousToNestedRefactoring(ICompilationUnit unit, int selectionStart, int selectionLength) {
+ Assert.isTrue(selectionStart >= 0);
+ Assert.isTrue(selectionLength >= 0);
+ Assert.isTrue(unit == null || unit.exists());
+ fSelectionStart= selectionStart;
+ fSelectionLength= selectionLength;
+ fCu= unit;
+ fAnonymousInnerClassNode= null;
+ fCompilationUnitNode= null;
+ }
+
+ public ConvertAnonymousToNestedRefactoring(AnonymousClassDeclaration declaration) {
+ Assert.isTrue(declaration != null);
+
+ ASTNode astRoot= declaration.getRoot();
+ Assert.isTrue(astRoot instanceof CompilationUnit);
+ fCompilationUnitNode= (CompilationUnit) astRoot;
+
+ IJavaElement javaElement= fCompilationUnitNode.getJavaElement();
+ Assert.isTrue(javaElement instanceof ICompilationUnit);
+
+ fCu= (ICompilationUnit) javaElement;
+ fSelectionStart= declaration.getStartPosition();
+ fSelectionLength= declaration.getLength();
+ }
+
+ public ConvertAnonymousToNestedRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
+ this(null, 0, 0);
+ RefactoringStatus initializeStatus= initialize(arguments);
+ status.merge(initializeStatus);
+ }
+
+ public void setLinkedProposalModel(LinkedProposalModelCore linkedProposalModel) {
+ fLinkedProposalModelCore= linkedProposalModel;
+ }
+
+ public int[] getAvailableVisibilities() {
+ if (isLocalInnerType()) {
+ return new int[] { Modifier.NONE };
+ } else {
+ return new int[] { Modifier.PUBLIC, Modifier.PROTECTED, Modifier.NONE, Modifier.PRIVATE };
+ }
+ }
+
+ public boolean isLocalInnerType() {
+ return ASTNodes.getParent(ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class), ASTNode.ANONYMOUS_CLASS_DECLARATION) != null;
+ }
+
+ public int getVisibility() {
+ return fVisibility;
+ }
+
+ public void setVisibility(int visibility) {
+ Assert.isTrue(visibility == Modifier.PRIVATE || visibility == Modifier.NONE || visibility == Modifier.PROTECTED || visibility == Modifier.PUBLIC);
+ fVisibility= visibility;
+ }
+
+ public void setClassName(String className) {
+ Assert.isNotNull(className);
+ fClassName= className;
+ }
+
+ public boolean canEnableSettingFinal() {
+ return true;
+ }
+
+ public boolean getDeclareFinal() {
+ return fDeclareFinal;
+ }
+
+ public boolean getDeclareStatic() {
+ return fDeclareStatic;
+ }
+
+ public void setDeclareFinal(boolean declareFinal) {
+ fDeclareFinal= declareFinal;
+ }
+
+ public void setDeclareStatic(boolean declareStatic) {
+ fDeclareStatic= declareStatic;
+ }
+
+ @Override
+ public String getName() {
+ return RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name;
+ }
+
+ private boolean useThisForFieldAccess() {
+ return StubUtility.useThisForFieldAccess(fCu.getJavaProject());
+ }
+
+ private boolean doAddComments() {
+ return StubUtility.doAddComments(fCu.getJavaProject());
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
+ RefactoringStatus result= Checks.validateModifiesFiles(
+ ResourceUtil.getFiles(new ICompilationUnit[]{fCu}),
+ getValidationContext());
+ if (result.hasFatalError())
+ return result;
+
+ initAST(pm);
+
+ if (fAnonymousInnerClassNode == null)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_place_caret);
+ if (!fSelfInitializing)
+ initializeDefaults();
+ if (getSuperConstructorBinding() == null)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_compile_errors);
+ if (getSuperTypeBinding().isLocal())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_extends_local_class);
+ return new RefactoringStatus();
+ }
+
+ private void initializeDefaults() {
+ fVisibility= isLocalInnerType() ? Modifier.NONE : Modifier.PRIVATE;
+ fDeclareStatic = mustInnerClassBeStatic();
+ }
+
+ private void initAST(IProgressMonitor pm) {
+ if (fCompilationUnitNode == null) {
+ fCompilationUnitNode= RefactoringASTParser.parseWithASTProvider(fCu, true, pm);
+ }
+ if (fAnonymousInnerClassNode == null) {
+ fAnonymousInnerClassNode= getAnonymousInnerClass(NodeFinder.perform(fCompilationUnitNode, fSelectionStart, fSelectionLength));
+ }
+ if (fAnonymousInnerClassNode != null) {
+ final AbstractTypeDeclaration declaration= ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class);
+ if (declaration instanceof TypeDeclaration) {
+ final AbstractTypeDeclaration[] nested= ((TypeDeclaration) declaration).getTypes();
+ fClassNamesUsed= new HashSet<>(nested.length);
+ for (int index= 0; index < nested.length; index++)
+ fClassNamesUsed.add(nested[index].getName().getIdentifier());
+ } else
+ fClassNamesUsed= Collections.emptySet();
+ }
+ }
+
+ private static AnonymousClassDeclaration getAnonymousInnerClass(ASTNode node) {
+ if (node == null)
+ return null;
+ if (node instanceof AnonymousClassDeclaration)
+ return (AnonymousClassDeclaration)node;
+ if (node instanceof ClassInstanceCreation) {
+ AnonymousClassDeclaration anon= ((ClassInstanceCreation)node).getAnonymousClassDeclaration();
+ if (anon != null)
+ return anon;
+ }
+ node= ASTNodes.getNormalizedNode(node);
+ if (node.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) {
+ AnonymousClassDeclaration anon= ((ClassInstanceCreation)node.getParent()).getAnonymousClassDeclaration();
+ if (anon != null)
+ return anon;
+ }
+ return ASTNodes.getParent(node, AnonymousClassDeclaration.class);
+ }
+
+ public RefactoringStatus validateInput() {
+ RefactoringStatus result= Checks.checkTypeName(fClassName, fCu);
+ if (result.hasFatalError())
+ return result;
+
+ if (fClassNamesUsed.contains(fClassName))
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_type_exists);
+ IMethodBinding superConstructorBinding = getSuperConstructorBinding();
+ if (superConstructorBinding == null)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_compile_errors);
+ if (fClassName.equals(superConstructorBinding.getDeclaringClass().getName()))
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_another_name);
+ if (classNameHidesEnclosingType())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name_hides);
+ return result;
+ }
+
+ private boolean accessesAnonymousFields() {
+ List anonymousInnerFieldTypes = getAllEnclosingAnonymousTypesField();
+ List accessedField = getAllAccessedFields();
+ final Iterator it = anonymousInnerFieldTypes.iterator();
+ while(it.hasNext()) {
+ final IVariableBinding variableBinding = it.next();
+ final Iterator it2 = accessedField.iterator();
+ while (it2.hasNext()) {
+ IVariableBinding variableBinding2 = (IVariableBinding) it2.next();
+ if(Bindings.equals(variableBinding, variableBinding2)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private List getAllAccessedFields() {
+ final List accessedFields= new ArrayList<>();
+
+ ASTVisitor visitor= new ASTVisitor() {
+
+ @Override
+ public boolean visit(FieldAccess node) {
+ final IVariableBinding binding= node.resolveFieldBinding();
+ if (binding != null && !binding.isEnumConstant())
+ accessedFields.add(binding);
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(QualifiedName node) {
+ final IBinding binding= node.resolveBinding();
+ if (binding != null && binding instanceof IVariableBinding) {
+ IVariableBinding variable= (IVariableBinding) binding;
+ if (!variable.isEnumConstant() && variable.isField())
+ accessedFields.add(binding);
+ }
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(SimpleName node) {
+ final IBinding binding= node.resolveBinding();
+ if (binding != null && binding instanceof IVariableBinding) {
+ IVariableBinding variable= (IVariableBinding) binding;
+ if (!variable.isEnumConstant() && variable.isField())
+ accessedFields.add(binding);
+ }
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ final IVariableBinding binding= node.resolveFieldBinding();
+ if (binding != null && !binding.isEnumConstant())
+ accessedFields.add(binding);
+ return super.visit(node);
+ }
+ };
+ fAnonymousInnerClassNode.accept(visitor);
+
+ return accessedFields;
+ }
+
+ private List getAllEnclosingAnonymousTypesField() {
+ final List ans= new ArrayList<>();
+ final AbstractTypeDeclaration declaration= ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class);
+ AnonymousClassDeclaration anonymous= (AnonymousClassDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, ASTNode.ANONYMOUS_CLASS_DECLARATION);
+ while (anonymous != null) {
+ if (ASTNodes.isParent(anonymous, declaration)) {
+ ITypeBinding binding= anonymous.resolveBinding();
+ if (binding != null) {
+ ans.addAll(Arrays.asList(binding.getDeclaredFields()));
+ }
+ } else {
+ break;
+ }
+ anonymous= (AnonymousClassDeclaration) ASTNodes.getParent(anonymous, ASTNode.ANONYMOUS_CLASS_DECLARATION);
+ }
+ return ans;
+ }
+
+ private boolean classNameHidesEnclosingType() {
+ ITypeBinding type= ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class).resolveBinding();
+ while (type != null) {
+ if (fClassName.equals(type.getName()))
+ return true;
+ type= type.getDeclaringClass();
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
+ try {
+ RefactoringStatus status= validateInput();
+ if (accessesAnonymousFields())
+ status.merge(RefactoringStatus.createErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_anonymous_field_access));
+ return status;
+ } finally {
+ pm.done();
+ }
+ }
+
+ public CompilationUnitChange createCompilationUnitChange(IProgressMonitor pm) throws CoreException {
+ final CompilationUnitRewrite rewrite= new CompilationUnitRewrite(fCu, fCompilationUnitNode);
+ final ITypeBinding[] typeParameters= getTypeParameters();
+ addNestedClass(rewrite, typeParameters);
+ modifyConstructorCall(rewrite, typeParameters);
+ return rewrite.createChange(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name, false, pm);
+ }
+
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException {
+ final CompilationUnitChange result= createCompilationUnitChange(pm);
+ result.setDescriptor(createRefactoringDescriptor());
+ return result;
+ }
+
+ private ITypeBinding[] getTypeParameters() {
+ final List parameters= new ArrayList<>(4);
+ final ClassInstanceCreation creation= (ClassInstanceCreation) fAnonymousInnerClassNode.getParent();
+ if (fDeclareStatic) {
+ final TypeVariableFinder finder= new TypeVariableFinder();
+ creation.accept(finder);
+ return finder.getResult();
+ } else {
+ final MethodDeclaration declaration= getEnclosingMethodDeclaration(creation);
+ if (declaration != null) {
+ ITypeBinding binding= null;
+ TypeParameter parameter= null;
+ for (final Iterator iterator= declaration.typeParameters().iterator(); iterator.hasNext();) {
+ parameter= iterator.next();
+ binding= parameter.resolveBinding();
+ if (binding != null)
+ parameters.add(binding);
+ }
+ }
+ }
+ final TypeVariableFinder finder= new TypeVariableFinder();
+ creation.accept(finder);
+ final ITypeBinding[] variables= finder.getResult();
+ final List remove= new ArrayList<>(4);
+ boolean match= false;
+ ITypeBinding binding= null;
+ ITypeBinding variable= null;
+ for (final Iterator iterator= parameters.iterator(); iterator.hasNext();) {
+ match= false;
+ binding= iterator.next();
+ for (int index= 0; index < variables.length; index++) {
+ variable= variables[index];
+ if (variable.equals(binding))
+ match= true;
+ }
+ if (!match)
+ remove.add(binding);
+ }
+ parameters.removeAll(remove);
+ final ITypeBinding[] result= new ITypeBinding[parameters.size()];
+ parameters.toArray(result);
+ return result;
+ }
+
+ private MethodDeclaration getEnclosingMethodDeclaration(ASTNode node) {
+ ASTNode parent= node.getParent();
+ if (parent != null) {
+ if (parent instanceof AbstractTypeDeclaration)
+ return null;
+ else if (parent instanceof MethodDeclaration)
+ return (MethodDeclaration) parent;
+ return getEnclosingMethodDeclaration(parent);
+ }
+ return null;
+ }
+
+ private RefactoringChangeDescriptor createRefactoringDescriptor() {
+ final ITypeBinding binding= fAnonymousInnerClassNode.resolveBinding();
+ final String[] labels= new String[] { BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED), BindingLabelProviderCore.getBindingLabel(binding.getDeclaringMethod(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED)};
+ final Map arguments= new HashMap<>();
+ final String projectName= fCu.getJavaProject().getElementName();
+ final int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
+ final String description= RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_descriptor_description_short;
+ final String header= Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_descriptor_description, labels);
+ final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(projectName, this, header);
+ comment.addSetting(Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_original_pattern, BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)));
+ comment.addSetting(Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_class_name_pattern, BasicElementLabels.getJavaElementName(fClassName)));
+ String visibility= JdtFlags.getVisibilityString(fVisibility);
+ if (visibility.length() == 0)
+ visibility= RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_default_visibility;
+ comment.addSetting(Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_visibility_pattern, visibility));
+ if (fDeclareFinal && fDeclareStatic)
+ comment.addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_final_static);
+ else if (fDeclareFinal)
+ comment.addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_final);
+ else if (fDeclareStatic)
+ comment.addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_static);
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(projectName, fCu));
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fClassName);
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + ' ' + Integer.valueOf(fSelectionLength).toString());
+ arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fDeclareFinal).toString());
+ arguments.put(ATTRIBUTE_STATIC, Boolean.valueOf(fDeclareStatic).toString());
+ arguments.put(ATTRIBUTE_VISIBILITY, Integer.valueOf(fVisibility).toString());
+
+ ConvertAnonymousDescriptor descriptor= RefactoringSignatureDescriptorFactory.createConvertAnonymousDescriptor(projectName, description, comment.asString(), arguments, flags);
+ return new RefactoringChangeDescriptor(descriptor);
+ }
+
+ private void modifyConstructorCall(CompilationUnitRewrite rewrite, ITypeBinding[] parameters) {
+ rewrite.getASTRewrite().replace(fAnonymousInnerClassNode.getParent(), createNewClassInstanceCreation(rewrite, parameters), null);
+ }
+
+ private ASTNode createNewClassInstanceCreation(CompilationUnitRewrite rewrite, ITypeBinding[] parameters) {
+ AST ast= fAnonymousInnerClassNode.getAST();
+ ClassInstanceCreation newClassCreation= ast.newClassInstanceCreation();
+ newClassCreation.setAnonymousClassDeclaration(null);
+ Type type= null;
+ SimpleName newNameNode= ast.newSimpleName(fClassName);
+ if (parameters.length > 0) {
+ final ParameterizedType parameterized= ast.newParameterizedType(ast.newSimpleType(newNameNode));
+ for (int index= 0; index < parameters.length; index++)
+ parameterized.typeArguments().add(ast.newSimpleType(ast.newSimpleName(parameters[index].getName())));
+ type= parameterized;
+ } else
+ type= ast.newSimpleType(newNameNode);
+ newClassCreation.setType(type);
+ copyArguments(rewrite, newClassCreation);
+ addArgumentsForLocalsUsedInInnerClass(newClassCreation);
+
+ addLinkedPosition(KEY_TYPE_NAME, newNameNode, rewrite.getASTRewrite(), true);
+
+ ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) fAnonymousInnerClassNode.getParent();
+ if (classInstanceCreation.getExpression() instanceof SimpleName) {
+ String name= ((SimpleName) classInstanceCreation.getExpression()).getIdentifier();
+ if (name != null && !name.isEmpty()) {
+ newClassCreation.setExpression(ast.newSimpleName(name));
+ }
+ }
+ return newClassCreation;
+ }
+
+ private void addArgumentsForLocalsUsedInInnerClass(ClassInstanceCreation newClassCreation) {
+ IVariableBinding[] usedLocals= getUsedLocalVariables();
+ for (int i= 0; i < usedLocals.length; i++) {
+ final AST ast= fAnonymousInnerClassNode.getAST();
+ final IVariableBinding binding= usedLocals[i];
+ Name name= null;
+ if (binding.isEnumConstant())
+ name= ast.newQualifiedName(ast.newSimpleName(binding.getDeclaringClass().getName()), ast.newSimpleName(binding.getName()));
+ else
+ name= ast.newSimpleName(binding.getName());
+ newClassCreation.arguments().add(name);
+ }
+ }
+
+ private void copyArguments(CompilationUnitRewrite rewrite, ClassInstanceCreation newClassCreation) {
+ Iterator iter= ((ClassInstanceCreation) fAnonymousInnerClassNode.getParent()).arguments().iterator();
+ if (!iter.hasNext())
+ return;
+
+ IMethodBinding superConstructorBinding= getSuperConstructorBinding();
+ ITypeBinding[] parameterTypes= superConstructorBinding.getParameterTypes();
+
+ List arguments= newClassCreation.arguments();
+ ASTRewrite astRewrite= rewrite.getASTRewrite();
+ int last= parameterTypes.length - 1;
+
+ for (int i= 0; i < last; i++) {
+ arguments.add((Expression) astRewrite.createCopyTarget(iter.next()));
+ }
+ if (superConstructorBinding.isVarargs()) {
+ AST ast= astRewrite.getAST();
+ ArrayCreation arrayCreation= ast.newArrayCreation();
+ arrayCreation.setType((ArrayType) rewrite.getImportRewrite().addImport(parameterTypes[last], ast));
+ ArrayInitializer initializer= ast.newArrayInitializer();
+ arrayCreation.setInitializer(initializer);
+ arguments.add(arrayCreation);
+ arguments= initializer.expressions();
+ }
+ while (iter.hasNext()) {
+ arguments.add((Expression) astRewrite.createCopyTarget(iter.next()));
+ }
+ }
+
+ private void addNestedClass(CompilationUnitRewrite rewrite, ITypeBinding[] typeParameters) throws CoreException {
+ final AbstractTypeDeclaration declarations= ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class);
+ int index= findIndexOfFistNestedClass(declarations.bodyDeclarations());
+ if (index == -1)
+ index= 0;
+ rewrite.getASTRewrite().getListRewrite(declarations, declarations.getBodyDeclarationsProperty()).insertAt(createNewNestedClass(rewrite, typeParameters), index, null);
+ }
+
+ private static int findIndexOfFistNestedClass(List list) {
+ for (int i= 0, n= list.size(); i < n; i++) {
+ BodyDeclaration each= list.get(i);
+ if (isNestedType(each))
+ return i;
+ }
+ return -1;
+ }
+
+ private static boolean isNestedType(BodyDeclaration each) {
+ if (!(each instanceof AbstractTypeDeclaration))
+ return false;
+ return (each.getParent() instanceof AbstractTypeDeclaration);
+ }
+
+ private AbstractTypeDeclaration createNewNestedClass(CompilationUnitRewrite rewrite, ITypeBinding[] typeParameters) throws CoreException {
+ final AST ast= fAnonymousInnerClassNode.getAST();
+
+ final TypeDeclaration newDeclaration= ast.newTypeDeclaration();
+ newDeclaration.setInterface(false);
+ newDeclaration.setJavadoc(null);
+ newDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(ast, createModifiersForNestedClass()));
+ newDeclaration.setName(ast.newSimpleName(fClassName));
+
+ TypeParameter parameter= null;
+ for (int index= 0; index < typeParameters.length; index++) {
+ parameter= ast.newTypeParameter();
+ parameter.setName(ast.newSimpleName(typeParameters[index].getName()));
+ newDeclaration.typeParameters().add(parameter);
+ }
+ setSuperType(newDeclaration);
+
+ IJavaProject project= fCu.getJavaProject();
+
+ IVariableBinding[] bindings= getUsedLocalVariables();
+ ArrayList fieldNames= new ArrayList<>();
+ for (int i= 0; i < bindings.length; i++) {
+ String name= StubUtility.getBaseName(bindings[i], project);
+ String[] fieldNameProposals= StubUtility.getVariableNameSuggestions(NamingConventions.VK_INSTANCE_FIELD, project, name, 0, fieldNames, true);
+ fieldNames.add(fieldNameProposals[0]);
+
+
+ if (fLinkedProposalModelCore != null) {
+ LinkedProposalPositionGroupCore positionGroup= fLinkedProposalModelCore.getPositionGroup(KEY_FIELD_NAME_EXT + i, true);
+ for (int k= 0; k < fieldNameProposals.length; k++) {
+ positionGroup.addProposal(fieldNameProposals[k], fieldNameProposals.length - k);
+ }
+ }
+ }
+ String[] allFieldNames= fieldNames.toArray(new String[fieldNames.size()]);
+
+ List newBodyDeclarations= newDeclaration.bodyDeclarations();
+
+ createFieldsForAccessedLocals(rewrite, bindings, allFieldNames, newBodyDeclarations);
+
+ MethodDeclaration newConstructorDecl= createNewConstructor(rewrite, bindings, allFieldNames);
+ if (newConstructorDecl != null) {
+ newBodyDeclarations.add(newConstructorDecl);
+ }
+
+ updateAndMoveBodyDeclarations(rewrite, bindings, allFieldNames, newBodyDeclarations, newConstructorDecl);
+
+ if (doAddComments()) {
+ String[] parameterNames= new String[typeParameters.length];
+ for (int index= 0; index < parameterNames.length; index++) {
+ parameterNames[index]= typeParameters[index].getName();
+ }
+ String string= CodeGeneration.getTypeComment(rewrite.getCu(), fClassName, parameterNames, StubUtility.getLineDelimiterUsed(fCu));
+ if (string != null) {
+ Javadoc javadoc= (Javadoc) rewrite.getASTRewrite().createStringPlaceholder(string, ASTNode.JAVADOC);
+ newDeclaration.setJavadoc(javadoc);
+ }
+ }
+ if (fLinkedProposalModelCore != null) {
+ addLinkedPosition(KEY_TYPE_NAME, newDeclaration.getName(), rewrite.getASTRewrite(), false);
+ ModifierCorrectionSubProcessorCore.installLinkedVisibilityProposals(fLinkedProposalModelCore, rewrite.getASTRewrite(), newDeclaration.modifiers(), false);
+ }
+
+ return newDeclaration;
+ }
+
+ private void updateAndMoveBodyDeclarations(CompilationUnitRewrite rewriter, IVariableBinding[] bindings, String[] fieldNames, List newBodyDeclarations, MethodDeclaration newConstructorDecl) {
+ final ASTRewrite astRewrite= rewriter.getASTRewrite();
+ final AST ast= astRewrite.getAST();
+
+ final boolean useThisAccess= useThisForFieldAccess();
+
+ int fieldInsertIndex= newConstructorDecl != null ? newBodyDeclarations.lastIndexOf(newConstructorDecl) : newBodyDeclarations.size();
+
+ for (Iterator iterator= fAnonymousInnerClassNode.bodyDeclarations().iterator(); iterator.hasNext();) {
+ BodyDeclaration body= iterator.next();
+
+ for (int i= 0; i < bindings.length; i++) {
+ SimpleName[] names= LinkedNodeFinder.findByBinding(body, bindings[i]);
+ String fieldName= fieldNames[i];
+ for (int k= 0; k < names.length; k++) {
+ SimpleName newNode= ast.newSimpleName(fieldName);
+ if (useThisAccess) {
+ FieldAccess access= ast.newFieldAccess();
+ access.setExpression(ast.newThisExpression());
+ access.setName(newNode);
+ astRewrite.replace(names[k], access, null);
+ } else {
+ astRewrite.replace(names[k], newNode, null);
+ }
+ addLinkedPosition(KEY_FIELD_NAME_EXT + i, newNode, astRewrite, false);
+ }
+ }
+ if (body instanceof Initializer || body instanceof FieldDeclaration) {
+ newBodyDeclarations.add(fieldInsertIndex++, (BodyDeclaration) astRewrite.createMoveTarget(body));
+ } else {
+ newBodyDeclarations.add((BodyDeclaration) astRewrite.createMoveTarget(body));
+ }
+ }
+
+ if (newConstructorDecl != null) {
+ // move initialization of existing fields to constructor if an outer is referenced
+ List bodyStatements= newConstructorDecl.getBody().statements();
+
+ List fieldsToInitializeInConstructor= getFieldsToInitializeInConstructor();
+ for (Iterator iter= fieldsToInitializeInConstructor.iterator(); iter.hasNext();) {
+ VariableDeclarationFragment fragment= iter.next();
+ Expression initializer= fragment.getInitializer();
+ Expression replacement= (Expression) astRewrite.get(fragment, VariableDeclarationFragment.INITIALIZER_PROPERTY);
+ if (replacement == initializer) {
+ replacement= (Expression) astRewrite.createMoveTarget(initializer);
+ }
+ astRewrite.remove(initializer, null);
+ SimpleName fieldNameNode= ast.newSimpleName(fragment.getName().getIdentifier());
+ bodyStatements.add(newFieldAssignment(ast, fieldNameNode, replacement, useThisAccess));
+ }
+ }
+ }
+
+ private void createFieldsForAccessedLocals(CompilationUnitRewrite rewrite, IVariableBinding[] varBindings, String[] fieldNames, List newBodyDeclarations) throws CoreException {
+ final ImportRewrite importRewrite= rewrite.getImportRewrite();
+ final ASTRewrite astRewrite= rewrite.getASTRewrite();
+ final AST ast= astRewrite.getAST();
+
+ for (int i= 0; i < varBindings.length; i++) {
+ VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
+ fragment.setInitializer(null);
+ fragment.setName(ast.newSimpleName(fieldNames[i]));
+ FieldDeclaration field= ast.newFieldDeclaration(fragment);
+ ITypeBinding varType= varBindings[i].getType();
+ field.setType(importRewrite.addImport(varType, ast));
+ field.modifiers().addAll(ASTNodeFactory.newModifiers(ast, Modifier.PRIVATE | Modifier.FINAL));
+ if (doAddComments()) {
+ String string= CodeGeneration.getFieldComment(rewrite.getCu(), varType.getName(), fieldNames[i], StubUtility.getLineDelimiterUsed(fCu));
+ if (string != null) {
+ Javadoc javadoc= (Javadoc) astRewrite.createStringPlaceholder(string, ASTNode.JAVADOC);
+ field.setJavadoc(javadoc);
+ }
+ }
+
+ newBodyDeclarations.add(field);
+
+ addLinkedPosition(KEY_FIELD_NAME_EXT + i, fragment.getName(), astRewrite, false);
+ }
+ }
+
+ private void addLinkedPosition(String key, ASTNode nodeToTrack, ASTRewrite rewrite, boolean isFirst) {
+ if (fLinkedProposalModelCore != null) {
+ fLinkedProposalModelCore.getPositionGroup(key, true).addPosition(rewrite.track(nodeToTrack), isFirst);
+ }
+ }
+
+
+ private IVariableBinding[] getUsedLocalVariables() {
+ final Set result= new HashSet<>(0);
+ collectRefrencedVariables(fAnonymousInnerClassNode, result);
+ ArrayList usedLocals= new ArrayList<>();
+ for (Iterator iterator= result.iterator(); iterator.hasNext();) {
+ IVariableBinding next= (IVariableBinding) iterator.next();
+ if (isBindingToTemp(next)) {
+ usedLocals.add(next);
+ }
+ }
+ return usedLocals.toArray(new IVariableBinding[usedLocals.size()]);
+ }
+
+ private void collectRefrencedVariables(ASTNode root, final Set result) {
+ root.accept(new ASTVisitor() {
+ @Override
+ public boolean visit(SimpleName node) {
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof IVariableBinding)
+ result.add(binding);
+ return true;
+ }
+ });
+ }
+
+ private boolean isBindingToTemp(IVariableBinding variable) {
+ if (variable.isField())
+ return false;
+ if (!Modifier.isFinal(variable.getModifiers()) && !JavaModelUtil.is18OrHigher(fCu.getJavaProject()))
+ return false;
+ ASTNode declaringNode= fCompilationUnitNode.findDeclaringNode(variable);
+ if (declaringNode == null)
+ return false;
+ if (ASTNodes.isParent(declaringNode, fAnonymousInnerClassNode))
+ return false;
+ return true;
+ }
+
+ private MethodDeclaration createNewConstructor(CompilationUnitRewrite rewrite, IVariableBinding[] bindings, String[] fieldNames) throws JavaModelException {
+ ClassInstanceCreation instanceCreation= (ClassInstanceCreation) fAnonymousInnerClassNode.getParent();
+
+ if (instanceCreation.arguments().isEmpty() && bindings.length == 0)
+ return null;
+
+ IJavaProject project= fCu.getJavaProject();
+ AST ast= rewrite.getAST();
+ ImportRewrite importRewrite= rewrite.getImportRewrite();
+ ASTRewrite astRewrite= rewrite.getASTRewrite();
+
+ MethodDeclaration newConstructor= ast.newMethodDeclaration();
+ newConstructor.setConstructor(true);
+ newConstructor.setJavadoc(null);
+ newConstructor.modifiers().addAll(ASTNodeFactory.newModifiers(ast, fVisibility));
+ newConstructor.setName(ast.newSimpleName(fClassName));
+ addLinkedPosition(KEY_TYPE_NAME, newConstructor.getName(), astRewrite, false);
+
+ newConstructor.setBody(ast.newBlock());
+
+ List newStatements= newConstructor.getBody().statements();
+
+ List newParameters= newConstructor.parameters();
+ List newParameterNames= new ArrayList<>();
+
+ // add parameters for elements passed with the instance creation
+ if (!instanceCreation.arguments().isEmpty()) {
+ IMethodBinding constructorBinding= getSuperConstructorBinding();
+ if (constructorBinding != null) {
+ SuperConstructorInvocation superConstructorInvocation= ast.newSuperConstructorInvocation();
+ ITypeBinding[] parameterTypes= constructorBinding.getParameterTypes();
+ String[][] parameterNames= StubUtility.suggestArgumentNamesWithProposals(project, constructorBinding);
+ for (int i= 0; i < parameterNames.length; i++) {
+ String[] nameProposals= parameterNames[i];
+ String paramName= nameProposals[0];
+
+ SingleVariableDeclaration param= newParameterDeclaration(ast, importRewrite, paramName, parameterTypes[i]);
+ newParameters.add(param);
+ newParameterNames.add(paramName);
+
+ SimpleName newSIArgument= ast.newSimpleName(paramName);
+ superConstructorInvocation.arguments().add(newSIArgument);
+
+ if (fLinkedProposalModelCore != null) {
+ LinkedProposalPositionGroupCore positionGroup= fLinkedProposalModelCore.getPositionGroup(KEY_PARAM_NAME_CONST+ String.valueOf(i), true);
+ positionGroup.addPosition(astRewrite.track(param.getName()), false);
+ positionGroup.addPosition(astRewrite.track(newSIArgument), false);
+ for (int k= 0; k < nameProposals.length; k++) {
+ positionGroup.addProposal(nameProposals[k], nameProposals.length - k);
+ }
+ }
+ }
+ newStatements.add(superConstructorInvocation);
+ }
+ }
+ // add parameters for all outer variables used
+ boolean useThisAccess= useThisForFieldAccess();
+ for (int i= 0; i < bindings.length; i++) {
+ String baseName= StubUtility.getBaseName(bindings[i], project);
+ String[] paramNameProposals= StubUtility.getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, baseName, 0, newParameterNames, true);
+ String paramName= paramNameProposals[0];
+
+ SingleVariableDeclaration param= newParameterDeclaration(ast, importRewrite, paramName, bindings[i].getType());
+ newParameters.add(param);
+ newParameterNames.add(paramName);
+
+ String fieldName= fieldNames[i];
+ SimpleName fieldNameNode= ast.newSimpleName(fieldName);
+ SimpleName paramNameNode= ast.newSimpleName(paramName);
+ newStatements.add(newFieldAssignment(ast, fieldNameNode, paramNameNode, useThisAccess || newParameterNames.contains(fieldName)));
+
+ if (fLinkedProposalModelCore != null) {
+ LinkedProposalPositionGroupCore positionGroup= fLinkedProposalModelCore.getPositionGroup(KEY_PARAM_NAME_EXT+ String.valueOf(i), true);
+ positionGroup.addPosition(astRewrite.track(param.getName()), false);
+ positionGroup.addPosition(astRewrite.track(paramNameNode), false);
+ for (int k= 0; k < paramNameProposals.length; k++) {
+ positionGroup.addProposal(paramNameProposals[k], paramNameProposals.length - k);
+ }
+
+ fLinkedProposalModelCore.getPositionGroup(KEY_FIELD_NAME_EXT + i, true).addPosition(astRewrite.track(fieldNameNode), false);
+ }
+ }
+
+ addExceptionsToNewConstructor(newConstructor, importRewrite);
+
+ if (doAddComments()) {
+ try {
+ String[] allParamNames= newParameterNames.toArray(new String[newParameterNames.size()]);
+ String string= CodeGeneration.getMethodComment(fCu, fClassName, fClassName, allParamNames, new String[0], null, new String[0], null, StubUtility.getLineDelimiterUsed(fCu));
+ if (string != null) {
+ Javadoc javadoc= (Javadoc) astRewrite.createStringPlaceholder(string, ASTNode.JAVADOC);
+ newConstructor.setJavadoc(javadoc);
+ }
+ } catch (CoreException exception) {
+ throw new JavaModelException(exception);
+ }
+ }
+ return newConstructor;
+ }
+
+ private Statement newFieldAssignment(AST ast, SimpleName fieldNameNode, Expression initializer, boolean useThisAccess) {
+ Assignment assignment= ast.newAssignment();
+ if (useThisAccess) {
+ FieldAccess access= ast.newFieldAccess();
+ access.setExpression(ast.newThisExpression());
+ access.setName(fieldNameNode);
+ assignment.setLeftHandSide(access);
+ } else {
+ assignment.setLeftHandSide(fieldNameNode);
+ }
+ assignment.setOperator(Assignment.Operator.ASSIGN);
+ assignment.setRightHandSide(initializer);
+
+ return ast.newExpressionStatement(assignment);
+ }
+
+
+ // live List of VariableDeclarationFragments
+ private List getFieldsToInitializeInConstructor() {
+ List result= new ArrayList<>(0);
+ for (Iterator iter= fAnonymousInnerClassNode.bodyDeclarations().iterator(); iter.hasNext(); ) {
+ Object element= iter.next();
+ if (element instanceof FieldDeclaration) {
+ List fragments= ((FieldDeclaration) element).fragments();
+ for (Iterator fragmentIter= fragments.iterator(); fragmentIter.hasNext(); ) {
+ VariableDeclarationFragment fragment= fragmentIter.next();
+ if (isToBeInitializerInConstructor(fragment, result))
+ result.add(fragment);
+ }
+ }
+ }
+ return result;
+ }
+
+ private boolean isToBeInitializerInConstructor(VariableDeclarationFragment fragment, List fieldsToInitialize) {
+ return fragment.getInitializer() != null && areLocalsUsedIn(fragment.getInitializer(), fieldsToInitialize);
+ }
+
+ private boolean areLocalsUsedIn(Expression fieldInitializer, List fieldsToInitialize) {
+ Set localsUsed= new HashSet<>(0);
+ collectRefrencedVariables(fieldInitializer, localsUsed);
+
+ ITypeBinding anonType= fAnonymousInnerClassNode.resolveBinding();
+
+ for (Iterator iterator= localsUsed.iterator(); iterator.hasNext();) {
+ IVariableBinding curr= (IVariableBinding) iterator.next();
+ if (isBindingToTemp(curr)) { // reference a local from outside
+ return true;
+ } else if (curr.isField() && (curr.getDeclaringClass() == anonType) && fieldsToInitialize.contains(fCompilationUnitNode.findDeclaringNode(curr))) {
+ return true; // references a field that references a local from outside
+ }
+ }
+ return false;
+ }
+
+ private IMethodBinding getSuperConstructorBinding() {
+ //workaround for missing java core functionality - finding a
+ // super constructor for an anonymous class creation
+ IMethodBinding anonConstr= ((ClassInstanceCreation) fAnonymousInnerClassNode.getParent()).resolveConstructorBinding();
+ if (anonConstr == null)
+ return null;
+ ITypeBinding superClass= anonConstr.getDeclaringClass().getSuperclass();
+ IMethodBinding[] superMethods= superClass.getDeclaredMethods();
+ for (int i= 0; i < superMethods.length; i++) {
+ IMethodBinding superMethod= superMethods[i];
+ if (superMethod.isConstructor() && parameterTypesMatch(superMethod, anonConstr))
+ return superMethod;
+ }
+ Assert.isTrue(false);//there's no way - it must be there
+ return null;
+ }
+
+ private static boolean parameterTypesMatch(IMethodBinding m1, IMethodBinding m2) {
+ ITypeBinding[] m1Params= m1.getParameterTypes();
+ ITypeBinding[] m2Params= m2.getParameterTypes();
+ if (m1Params.length != m2Params.length)
+ return false;
+ for (int i= 0; i < m2Params.length; i++) {
+ if (!m1Params[i].equals(m2Params[i]))
+ return false;
+ }
+ return true;
+ }
+
+ private void addExceptionsToNewConstructor(MethodDeclaration newConstructor, ImportRewrite importRewrite) {
+ IMethodBinding constructorBinding= getSuperConstructorBinding();
+ if (constructorBinding == null)
+ return;
+ ITypeBinding[] exceptions= constructorBinding.getExceptionTypes();
+ for (int i= 0; i < exceptions.length; i++) {
+ Type exceptionType= importRewrite.addImport(exceptions[i], fAnonymousInnerClassNode.getAST());
+ newConstructor.thrownExceptionTypes().add(exceptionType);
+ }
+ }
+
+ private SingleVariableDeclaration newParameterDeclaration(AST ast, ImportRewrite importRewrite, String paramName, ITypeBinding paramType) {
+ SingleVariableDeclaration param= ast.newSingleVariableDeclaration();
+ param.setType(importRewrite.addImport(paramType, ast));
+ param.setName(ast.newSimpleName(paramName));
+ return param;
+ }
+
+ private void setSuperType(TypeDeclaration declaration) {
+ ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) fAnonymousInnerClassNode.getParent();
+ ITypeBinding binding= classInstanceCreation.resolveTypeBinding();
+ if (binding == null)
+ return;
+ Type newType= (Type) ASTNode.copySubtree(fAnonymousInnerClassNode.getAST(), classInstanceCreation.getType());
+ if (binding.getSuperclass().getQualifiedName().equals("java.lang.Object")) { //$NON-NLS-1$
+ Assert.isTrue(binding.getInterfaces().length <= 1);
+ if (binding.getInterfaces().length == 0)
+ return;
+ declaration.superInterfaceTypes().add(0, newType);
+ } else {
+ declaration.setSuperclassType(newType);
+ }
+ }
+
+ private ITypeBinding getSuperTypeBinding() {
+ ITypeBinding types= fAnonymousInnerClassNode.resolveBinding();
+ ITypeBinding[] interfaces= types.getInterfaces();
+ if (interfaces.length > 0)
+ return interfaces[0];
+ else
+ return types.getSuperclass();
+ }
+
+ private int createModifiersForNestedClass() {
+ int flags= fVisibility;
+ if (fDeclareFinal)
+ flags|= Modifier.FINAL;
+ if (mustInnerClassBeStatic() || fDeclareStatic)
+ flags|= Modifier.STATIC;
+ return flags;
+ }
+
+ public boolean mustInnerClassBeStatic() {
+ ITypeBinding typeBinding = ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class).resolveBinding();
+ ASTNode current = fAnonymousInnerClassNode.getParent();
+ boolean ans = false;
+ while(current != null) {
+ switch(current.getNodeType()) {
+ case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
+ case ASTNode.CONSTRUCTOR_INVOCATION:
+ return true;
+ case ASTNode.ANONYMOUS_CLASS_DECLARATION:
+ {
+ AnonymousClassDeclaration enclosingAnonymousClassDeclaration= (AnonymousClassDeclaration)current;
+ ITypeBinding binding= enclosingAnonymousClassDeclaration.resolveBinding();
+ if (binding != null && Bindings.isSuperType(typeBinding, binding.getSuperclass())) {
+ return false;
+ }
+ break;
+ }
+ case ASTNode.FIELD_DECLARATION:
+ {
+ FieldDeclaration enclosingFieldDeclaration= (FieldDeclaration)current;
+ if (Modifier.isStatic(enclosingFieldDeclaration.getModifiers())) {
+ ans = true;
+ }
+ break;
+ }
+ case ASTNode.METHOD_DECLARATION:
+ {
+ MethodDeclaration enclosingMethodDeclaration = (MethodDeclaration)current;
+ if (Modifier.isStatic(enclosingMethodDeclaration.getModifiers())) {
+ ans = true;
+ }
+ break;
+ }
+ case ASTNode.TYPE_DECLARATION:
+ {
+ return ans;
+ }
+ case ASTNode.CLASS_INSTANCE_CREATION:
+ {
+ if(((ClassInstanceCreation)current).getExpression() != null) {
+ // class creation is part of an expression, then we don't want static
+ return false;
+ }
+ }
+ }
+ current = current.getParent();
+ }
+ return ans;
+ }
+
+ private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
+ fSelfInitializing= true;
+ final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
+ if (handle != null) {
+ final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
+ if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
+ return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.CONVERT_ANONYMOUS);
+ else {
+ fCu= (ICompilationUnit) element;
+ }
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
+ final String name= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
+ if (name != null && !"".equals(name)) //$NON-NLS-1$
+ fClassName= name;
+ else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
+ final String visibility= arguments.getAttribute(ATTRIBUTE_VISIBILITY);
+ if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$
+ int flag= 0;
+ try {
+ flag= Integer.parseInt(visibility);
+ } catch (NumberFormatException exception) {
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY));
+ }
+ fVisibility= flag;
+ }
+ final String selection= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
+ if (selection != null) {
+ int offset= -1;
+ int length= -1;
+ final StringTokenizer tokenizer= new StringTokenizer(selection);
+ if (tokenizer.hasMoreTokens())
+ offset= Integer.valueOf(tokenizer.nextToken()).intValue();
+ if (tokenizer.hasMoreTokens())
+ length= Integer.valueOf(tokenizer.nextToken()).intValue();
+ if (offset >= 0 && length >= 0) {
+ fSelectionStart= offset;
+ fSelectionLength= length;
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION}));
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
+ final String declareStatic= arguments.getAttribute(ATTRIBUTE_STATIC);
+ if (declareStatic != null) {
+ fDeclareStatic= Boolean.valueOf(declareStatic).booleanValue();
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_STATIC));
+ final String declareFinal= arguments.getAttribute(ATTRIBUTE_FINAL);
+ if (declareFinal != null) {
+ fDeclareFinal= Boolean.valueOf(declareFinal).booleanValue();
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FINAL));
+ return new RefactoringStatus();
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BlockFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BlockFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BlockFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BlockFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class BlockFlowInfo extends GenericSequentialFlowInfo {
+
+ public BlockFlowInfo() {
+ }
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BranchFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BranchFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BranchFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/BranchFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import java.util.HashSet;
+
+import org.eclipse.jdt.core.dom.SimpleName;
+
+class BranchFlowInfo extends FlowInfo {
+
+ public BranchFlowInfo(SimpleName label, FlowContext context) {
+ super(NO_RETURN);
+ fBranches= new HashSet<>(2);
+ fBranches.add(makeString(label));
+ }
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ConditionalFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ConditionalFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ConditionalFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ConditionalFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+
+
+class ConditionalFlowInfo extends FlowInfo {
+
+ public ConditionalFlowInfo() {
+ super(NO_RETURN);
+ }
+
+ public void mergeCondition(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void merge(FlowInfo truePart, FlowInfo falsePart, FlowContext context) {
+ if (truePart == null && falsePart == null)
+ return;
+
+ GenericConditionalFlowInfo cond= new GenericConditionalFlowInfo();
+ if (truePart != null)
+ cond.mergeAccessMode(truePart, context);
+
+ if (falsePart != null)
+ cond.mergeAccessMode(falsePart, context);
+
+ if (truePart == null || falsePart == null)
+ cond.mergeEmptyCondition(context);
+
+ mergeAccessModeSequential(cond, context);
+ }
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class DoWhileFlowInfo extends FlowInfo {
+
+ private boolean fActionBranches;
+
+ public void mergeAction(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ fActionBranches= info.branches();
+
+ assign(info);
+
+ if (fActionBranches && fReturnKind == VALUE_RETURN) {
+ fReturnKind= PARTIAL_RETURN;
+ }
+
+ }
+
+ public void mergeCondition(FlowInfo info, FlowContext context) {
+ if (fActionBranches || fReturnKind == VALUE_RETURN || fReturnKind == VOID_RETURN || info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/EnhancedForFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/EnhancedForFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/EnhancedForFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/EnhancedForFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class EnhancedForFlowInfo extends FlowInfo {
+
+ public void mergeParameter(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void mergeExpression(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void mergeAction(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ info.mergeEmptyCondition(context);
+
+ mergeSequential(info, context);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,1107 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayAccess;
+import org.eclipse.jdt.core.dom.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.AssertStatement;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.eclipse.jdt.core.dom.BreakStatement;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.CharacterLiteral;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConditionalExpression;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.ContinueStatement;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EmptyStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.Initializer;
+import org.eclipse.jdt.core.dom.InstanceofExpression;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.LabeledStatement;
+import org.eclipse.jdt.core.dom.LambdaExpression;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.MemberValuePair;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NameQualifiedType;
+import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.NullLiteral;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.PackageDeclaration;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.PostfixExpression;
+import org.eclipse.jdt.core.dom.PrefixExpression;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.SwitchCase;
+import org.eclipse.jdt.core.dom.SwitchExpression;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.SynchronizedStatement;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.ThrowStatement;
+import org.eclipse.jdt.core.dom.TryStatement;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jdt.core.dom.TypeParameter;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.WhileStatement;
+import org.eclipse.jdt.core.dom.WildcardType;
+
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+
+/**
+ * Special flow analyzer to determine the return value of the extracted method
+ * and the variables which have to be passed to the method.
+ *
+ * Note: This analyzer doesn't do a full flow analysis. For example it doesn't
+ * do dead code analysis or variable initialization analysis. It analyses the
+ * the first access to a variable (read or write) and if all execution paths
+ * return a value.
+ */
+abstract class FlowAnalyzer extends GenericVisitor {
+
+ static protected class SwitchData {
+ private boolean fHasDefaultCase;
+ private List fRanges= new ArrayList<>(4);
+ private List fInfos= new ArrayList<>(4);
+ public void setHasDefaultCase() {
+ fHasDefaultCase= true;
+ }
+ public boolean hasDefaultCase() {
+ return fHasDefaultCase;
+ }
+ public void add(IRegion range, FlowInfo info) {
+ fRanges.add(range);
+ fInfos.add(info);
+ }
+ public IRegion[] getRanges() {
+ return fRanges.toArray(new IRegion[fRanges.size()]);
+ }
+ public FlowInfo[] getInfos() {
+ return fInfos.toArray(new FlowInfo[fInfos.size()]);
+ }
+ public FlowInfo getInfo(int index) {
+ return fInfos.get(index);
+ }
+ }
+
+ private HashMap fData = new HashMap<>(100);
+ /* package */ FlowContext fFlowContext= null;
+
+ public FlowAnalyzer(FlowContext context) {
+ fFlowContext= context;
+ }
+
+ protected abstract boolean createReturnFlowInfo(ReturnStatement node);
+
+ protected abstract boolean traverseNode(ASTNode node);
+
+ protected boolean skipNode(ASTNode node) {
+ return !traverseNode(node);
+ }
+
+ @Override
+ protected final boolean visitNode(ASTNode node) {
+ return traverseNode(node);
+ }
+
+ //---- Hooks to create Flow info objects. User may introduce their own infos.
+
+ protected ReturnFlowInfo createReturn(ReturnStatement statement) {
+ return new ReturnFlowInfo(statement);
+ }
+
+ protected ThrowFlowInfo createThrow() {
+ return new ThrowFlowInfo();
+ }
+
+ protected BranchFlowInfo createBranch(SimpleName label) {
+ return new BranchFlowInfo(label, fFlowContext);
+ }
+
+ protected GenericSequentialFlowInfo createSequential() {
+ return new GenericSequentialFlowInfo();
+ }
+
+ protected ConditionalFlowInfo createConditional() {
+ return new ConditionalFlowInfo();
+ }
+
+ protected EnhancedForFlowInfo createEnhancedFor() {
+ return new EnhancedForFlowInfo();
+ }
+
+ protected ForFlowInfo createFor() {
+ return new ForFlowInfo();
+ }
+
+ protected TryFlowInfo createTry() {
+ return new TryFlowInfo();
+ }
+
+ protected WhileFlowInfo createWhile() {
+ return new WhileFlowInfo();
+ }
+
+ protected IfFlowInfo createIf() {
+ return new IfFlowInfo();
+ }
+
+ protected DoWhileFlowInfo createDoWhile() {
+ return new DoWhileFlowInfo();
+ }
+
+ protected SwitchFlowInfo createSwitch() {
+ return new SwitchFlowInfo();
+ }
+
+ protected BlockFlowInfo createBlock() {
+ return new BlockFlowInfo();
+ }
+
+ protected MessageSendFlowInfo createMessageSendFlowInfo() {
+ return new MessageSendFlowInfo();
+ }
+
+ protected FlowContext getFlowContext() {
+ return fFlowContext;
+ }
+
+ //---- Helpers to access flow analysis objects ----------------------------------------
+
+ protected FlowInfo getFlowInfo(ASTNode node) {
+ return fData.remove(node);
+ }
+
+ protected void setFlowInfo(ASTNode node, FlowInfo info) {
+ fData.put(node, info);
+ }
+
+ protected FlowInfo assignFlowInfo(ASTNode target, ASTNode source) {
+ FlowInfo result= getFlowInfo(source);
+ setFlowInfo(target, result);
+ return result;
+ }
+
+ protected FlowInfo accessFlowInfo(ASTNode node) {
+ return fData.get(node);
+ }
+
+ //---- Helpers to process sequential flow infos -------------------------------------
+
+ protected GenericSequentialFlowInfo processSequential(ASTNode parent, List extends ASTNode> nodes) {
+ GenericSequentialFlowInfo result= createSequential(parent);
+ process(result, nodes);
+ return result;
+ }
+
+ protected GenericSequentialFlowInfo processSequential(ASTNode parent, ASTNode node1) {
+ GenericSequentialFlowInfo result= createSequential(parent);
+ if (node1 != null)
+ result.merge(getFlowInfo(node1), fFlowContext);
+ return result;
+ }
+
+ protected GenericSequentialFlowInfo processSequential(ASTNode parent, ASTNode node1, ASTNode node2) {
+ GenericSequentialFlowInfo result= createSequential(parent);
+ if (node1 != null)
+ result.merge(getFlowInfo(node1), fFlowContext);
+ if (node2 != null)
+ result.merge(getFlowInfo(node2), fFlowContext);
+ return result;
+ }
+
+ protected GenericSequentialFlowInfo createSequential(ASTNode parent) {
+ GenericSequentialFlowInfo result= createSequential();
+ setFlowInfo(parent, result);
+ return result;
+ }
+
+ protected GenericSequentialFlowInfo createSequential(List extends ASTNode> nodes) {
+ GenericSequentialFlowInfo result= createSequential();
+ process(result, nodes);
+ return result;
+ }
+
+ //---- Generic merge methods --------------------------------------------------------
+
+ protected void process(GenericSequentialFlowInfo info, List extends ASTNode> nodes) {
+ if (nodes == null)
+ return;
+ for (Iterator extends ASTNode> iter= nodes.iterator(); iter.hasNext();) {
+ info.merge(getFlowInfo(iter.next()), fFlowContext);
+ }
+ }
+
+ protected void process(GenericSequentialFlowInfo info, ASTNode node) {
+ if (node != null)
+ info.merge(getFlowInfo(node), fFlowContext);
+ }
+
+ protected void process(GenericSequentialFlowInfo info, ASTNode node1, ASTNode node2) {
+ if (node1 != null)
+ info.merge(getFlowInfo(node1), fFlowContext);
+ if (node2 != null)
+ info.merge(getFlowInfo(node2), fFlowContext);
+ }
+
+ //---- special visit methods -------------------------------------------------------
+
+ @Override
+ public boolean visit(EmptyStatement node) {
+ // Empty statements aren't of any interest.
+ return false;
+ }
+
+ @Override
+ public boolean visit(TryStatement node) {
+ if (traverseNode(node)) {
+ fFlowContext.pushExcptions(node);
+ for (Iterator iterator= node.resources().iterator(); iterator.hasNext();) {
+ iterator.next().accept(this);
+ }
+ node.getBody().accept(this);
+ fFlowContext.popExceptions();
+ List catchClauses= node.catchClauses();
+ for (Iterator iter= catchClauses.iterator(); iter.hasNext();) {
+ iter.next().accept(this);
+ }
+ Block finallyBlock= node.getFinally();
+ if (finallyBlock != null) {
+ finallyBlock.accept(this);
+ }
+ }
+ return false;
+ }
+
+ //---- Helper to process switch statement ----------------------------------------
+
+ protected SwitchData createSwitchData(SwitchStatement node) {
+ return createSwitchData(node.statements());
+ }
+
+ protected SwitchData createSwitchData(SwitchExpression node) {
+ return createSwitchData(node.statements());
+ }
+
+ protected SwitchData createSwitchData(List statements) {
+ SwitchData result= new SwitchData();
+ if (statements == null || statements.isEmpty())
+ return result;
+
+ int start= -1, end= -1;
+ GenericSequentialFlowInfo info= null;
+
+ for (Iterator iter= statements.iterator(); iter.hasNext();) {
+ Statement statement= iter.next();
+ if (statement instanceof SwitchCase) {
+ SwitchCase switchCase= (SwitchCase) statement;
+ if (switchCase.isDefault()) {
+ result.setHasDefaultCase();
+ }
+ if (info == null) {
+ info= createSequential();
+ start= statement.getStartPosition();
+ } else {
+ if (info.isReturn() || info.isPartialReturn() || info.branches()) {
+ result.add(new Region(start, end - start + 1), info);
+ info= createSequential();
+ start= statement.getStartPosition();
+ }
+ }
+ } else {
+ info.merge(getFlowInfo(statement), fFlowContext);
+ }
+ end= statement.getStartPosition() + statement.getLength() - 1;
+ }
+ result.add(new Region(start, end - start + 1), info);
+ return result;
+ }
+
+ protected void endVisit(SwitchStatement node, SwitchData data) {
+ preEndVisit(node, node.getExpression(), data);
+ }
+
+ protected void endVisit(SwitchExpression node, SwitchData data) {
+ preEndVisit(node, node.getExpression(), data);
+ }
+
+ private void preEndVisit(ASTNode node, Expression expression, SwitchData data) {
+ SwitchFlowInfo switchFlowInfo= createSwitch();
+ setFlowInfo(node, switchFlowInfo);
+ switchFlowInfo.mergeTest(getFlowInfo(expression), fFlowContext);
+ FlowInfo[] cases= data.getInfos();
+ for (int i= 0; i < cases.length; i++)
+ switchFlowInfo.mergeCase(cases[i], fFlowContext);
+ switchFlowInfo.mergeDefault(data.hasDefaultCase(), fFlowContext);
+ switchFlowInfo.removeLabel(null);
+ }
+
+ //---- concret endVisit methods ---------------------------------------------------
+
+ @Override
+ public void endVisit(AnnotationTypeDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(AnnotationTypeMemberDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType(), node.getDefault());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(AnonymousClassDeclaration node) {
+ if (skipNode(node))
+ return;
+ FlowInfo info= processSequential(node, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(ArrayAccess node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getArray(), node.getIndex());
+ }
+
+ @Override
+ public void endVisit(ArrayCreation node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.dimensions());
+ process(info, node.getInitializer());
+ }
+
+ @Override
+ public void endVisit(ArrayInitializer node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.expressions());
+ }
+
+ @Override
+ public void endVisit(ArrayType node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getElementType());
+ }
+
+ @Override
+ public void endVisit(AssertStatement node) {
+ if (skipNode(node))
+ return;
+ IfFlowInfo info= new IfFlowInfo();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.merge(getFlowInfo(node.getMessage()), null, fFlowContext);
+ }
+
+ @Override
+ public void endVisit(Assignment node) {
+ if (skipNode(node))
+ return;
+ FlowInfo lhs= getFlowInfo(node.getLeftHandSide());
+ FlowInfo rhs= getFlowInfo(node.getRightHandSide());
+ if (lhs instanceof LocalFlowInfo) {
+ LocalFlowInfo llhs= (LocalFlowInfo)lhs;
+ llhs.setWriteAccess(fFlowContext);
+ if (node.getOperator() != Assignment.Operator.ASSIGN) {
+ GenericSequentialFlowInfo tmp= createSequential();
+ tmp.merge(new LocalFlowInfo(llhs, FlowInfo.READ, fFlowContext), fFlowContext);
+ tmp.merge(rhs, fFlowContext);
+ rhs= tmp;
+ }
+ }
+ GenericSequentialFlowInfo info= createSequential(node);
+ // first process right and side and then left hand side.
+ info.merge(rhs, fFlowContext);
+ info.merge(lhs, fFlowContext);
+ }
+
+ @Override
+ public void endVisit(Block node) {
+ if (skipNode(node))
+ return;
+ BlockFlowInfo info= createBlock();
+ setFlowInfo(node, info);
+ process(info, node.statements());
+ }
+
+ @Override
+ public void endVisit(BooleanLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(BreakStatement node) {
+ if (skipNode(node))
+ return;
+ setFlowInfo(node, createBranch(node.getLabel()));
+ }
+
+ @Override
+ public void endVisit(CastExpression node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getType(), node.getExpression());
+ }
+
+ @Override
+ public void endVisit(CatchClause node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getException(), node.getBody());
+ }
+
+ @Override
+ public void endVisit(CharacterLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(ClassInstanceCreation node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getExpression());
+ process(info, node.getType());
+ process(info, node.arguments());
+ process(info, node.getAnonymousClassDeclaration());
+ }
+
+ @Override
+ public void endVisit(CompilationUnit node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.imports());
+ process(info, node.types());
+ }
+
+ @Override
+ public void endVisit(ConditionalExpression node) {
+ if (skipNode(node))
+ return;
+ ConditionalFlowInfo info= createConditional();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.merge(
+ getFlowInfo(node.getThenExpression()),
+ getFlowInfo(node.getElseExpression()),
+ fFlowContext);
+ }
+
+ @Override
+ public void endVisit(ConstructorInvocation node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.arguments());
+ }
+
+ @Override
+ public void endVisit(ContinueStatement node) {
+ if (skipNode(node))
+ return;
+ setFlowInfo(node, createBranch(node.getLabel()));
+ }
+
+ @Override
+ public void endVisit(DoStatement node) {
+ if (skipNode(node))
+ return;
+ DoWhileFlowInfo info= createDoWhile();
+ setFlowInfo(node, info);
+ info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(EmptyStatement node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(EnhancedForStatement node) {
+ if (skipNode(node))
+ return;
+ EnhancedForFlowInfo forInfo= createEnhancedFor();
+ setFlowInfo(node, forInfo);
+ forInfo.mergeParameter(getFlowInfo(node.getParameter()), fFlowContext);
+ forInfo.mergeExpression(getFlowInfo(node.getExpression()), fFlowContext);
+ forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ forInfo.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(EnumConstantDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.arguments());
+ process(info, node.getAnonymousClassDeclaration());
+ }
+
+ @Override
+ public void endVisit(EnumDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.superInterfaceTypes());
+ process(info, node.enumConstants());
+ process(info, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(ExpressionStatement node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getExpression());
+ }
+
+ @Override
+ public void endVisit(FieldAccess node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getExpression(), node.getName());
+ }
+
+ @Override
+ public void endVisit(FieldDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.fragments());
+ }
+
+ @Override
+ public void endVisit(ForStatement node) {
+ if (skipNode(node))
+ return;
+ ForFlowInfo forInfo= createFor();
+ setFlowInfo(node, forInfo);
+ forInfo.mergeInitializer(createSequential(node.initializers()), fFlowContext);
+ forInfo.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ // Increments are executed after the action.
+ forInfo.mergeIncrement(createSequential(node.updaters()), fFlowContext);
+ forInfo.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(IfStatement node) {
+ if (skipNode(node))
+ return;
+ IfFlowInfo info= createIf();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.merge(getFlowInfo(node.getThenStatement()), getFlowInfo(node.getElseStatement()), fFlowContext);
+ }
+
+ @Override
+ public void endVisit(ImportDeclaration node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getName());
+ }
+
+ @Override
+ public void endVisit(InfixExpression node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getLeftOperand(), node.getRightOperand());
+ process(info, node.extendedOperands());
+ }
+
+ @Override
+ public void endVisit(InstanceofExpression node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getLeftOperand(), node.getRightOperand());
+ }
+
+ @Override
+ public void endVisit(Initializer node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getBody());
+ }
+
+ @Override
+ public void endVisit(Javadoc node) {
+ // no influence on flow analysis
+ }
+
+ @Override
+ public void endVisit(LabeledStatement node) {
+ if (skipNode(node))
+ return;
+ FlowInfo info= assignFlowInfo(node, node.getBody());
+ if (info != null)
+ info.removeLabel(node.getLabel());
+ }
+
+ @Override
+ public void endVisit(LambdaExpression node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= createSequential(node);
+ process(info, node.parameters());
+ process(info, node.getBody());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(MarkerAnnotation node) {
+ // nothing to do for marker annotations;
+ }
+
+ @Override
+ public void endVisit(MemberValuePair node) {
+ if (skipNode(node))
+ return;
+
+ FlowInfo name= getFlowInfo(node.getName());
+ FlowInfo value= getFlowInfo(node.getValue());
+ if (name instanceof LocalFlowInfo) {
+ LocalFlowInfo llhs= (LocalFlowInfo)name;
+ llhs.setWriteAccess(fFlowContext);
+ }
+ GenericSequentialFlowInfo info= createSequential(node);
+ // first process value and then name.
+ info.merge(value, fFlowContext);
+ info.merge(name, fFlowContext);
+
+ }
+
+ @Override
+ public void endVisit(MethodDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getReturnType2());
+ process(info, node.parameters());
+ process(info, node.thrownExceptionTypes());
+ process(info, node.getBody());
+ }
+
+ @Override
+ public void endVisit(MethodInvocation node) {
+ endVisitMethodInvocation(node, node.getExpression(), node.arguments(), getMethodBinding(node.getName()));
+ }
+
+ @Override
+ public void endVisit(NameQualifiedType node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getQualifier(), node.getName());
+ }
+
+ @Override
+ public void endVisit(NormalAnnotation node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getTypeName());
+ process(info, node.values());
+ }
+
+ @Override
+ public void endVisit(NullLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(NumberLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(PackageDeclaration node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getName());
+ }
+
+ @Override
+ public void endVisit(ParameterizedType node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.typeArguments());
+ }
+
+ @Override
+ public void endVisit(ParenthesizedExpression node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getExpression());
+ }
+
+ @Override
+ public void endVisit(PostfixExpression node) {
+ endVisitIncDecOperation(node, node.getOperand());
+ }
+
+ @Override
+ public void endVisit(PrefixExpression node) {
+ PrefixExpression.Operator op= node.getOperator();
+ if (PrefixExpression.Operator.INCREMENT.equals(op) || PrefixExpression.Operator.DECREMENT.equals(op)) {
+ endVisitIncDecOperation(node, node.getOperand());
+ } else {
+ assignFlowInfo(node, node.getOperand());
+ }
+ }
+
+ @Override
+ public void endVisit(PrimitiveType node) {
+ // Leaf node
+ }
+
+ @Override
+ public void endVisit(QualifiedName node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getQualifier(), node.getName());
+ }
+
+ @Override
+ public void endVisit(QualifiedType node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getQualifier(), node.getName());
+ }
+
+ @Override
+ public void endVisit(ReturnStatement node) {
+ if (skipNode(node))
+ return;
+
+ if (createReturnFlowInfo(node)) {
+ ReturnFlowInfo info= createReturn(node);
+ setFlowInfo(node, info);
+ info.merge(getFlowInfo(node.getExpression()), fFlowContext);
+ } else {
+ assignFlowInfo(node, node.getExpression());
+ }
+ }
+
+ @Override
+ public void endVisit(SimpleName node) {
+ if (skipNode(node) || node.isDeclaration())
+ return;
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof IVariableBinding) {
+ IVariableBinding variable= (IVariableBinding)binding;
+ if (!variable.isField()) {
+ setFlowInfo(node, new LocalFlowInfo(
+ variable,
+ FlowInfo.READ,
+ fFlowContext));
+ }
+ } else if (binding instanceof ITypeBinding) {
+ ITypeBinding type= (ITypeBinding)binding;
+ if (type.isTypeVariable()) {
+ setFlowInfo(node, new TypeVariableFlowInfo(type, fFlowContext));
+ }
+ }
+ }
+
+ @Override
+ public void endVisit(SimpleType node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getName());
+ }
+
+ @Override
+ public void endVisit(SingleMemberAnnotation node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getValue());
+ }
+
+ @Override
+ public void endVisit(SingleVariableDeclaration node) {
+ if (skipNode(node))
+ return;
+
+ IVariableBinding binding= node.resolveBinding();
+ LocalFlowInfo nameInfo= null;
+ Expression initializer= node.getInitializer();
+ if (binding != null && !binding.isField() && initializer != null) {
+ nameInfo= new LocalFlowInfo(binding, FlowInfo.WRITE, fFlowContext);
+ }
+ GenericSequentialFlowInfo info= processSequential(node, node.getType(), initializer);
+ info.merge(nameInfo, fFlowContext);
+ }
+
+ @Override
+ public void endVisit(StringLiteral node) {
+ // Leaf node
+ }
+
+ @Override
+ public void endVisit(SuperConstructorInvocation node) {
+ endVisitMethodInvocation(node, node.getExpression(), node.arguments(), node.resolveConstructorBinding());
+ }
+
+ @Override
+ public void endVisit(SuperFieldAccess node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getQualifier(), node.getName());
+ }
+
+ @Override
+ public void endVisit(SuperMethodInvocation node) {
+ endVisitMethodInvocation(node, node.getQualifier(), node.arguments(), getMethodBinding(node.getName()));
+ }
+
+ @Override
+ public void endVisit(SwitchCase node) {
+ endVisitNode(node);
+ }
+
+ @Override
+ public void endVisit(SwitchStatement node) {
+ if (skipNode(node))
+ return;
+ endVisit(node, createSwitchData(node));
+ }
+
+ @Override
+ public void endVisit(SwitchExpression node) {
+ if (skipNode(node))
+ return;
+ endVisit(node, createSwitchData(node));
+ }
+
+ @Override
+ public void endVisit(SynchronizedStatement node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getExpression());
+ process(info, node.getBody());
+ }
+
+ @Override
+ public void endVisit(ThisExpression node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getQualifier());
+ }
+
+ @Override
+ public void endVisit(ThrowStatement node) {
+ if (skipNode(node))
+ return;
+ ThrowFlowInfo info= createThrow();
+ setFlowInfo(node, info);
+ Expression expression= node.getExpression();
+ info.merge(getFlowInfo(expression), fFlowContext);
+ }
+
+ @Override
+ public void endVisit(TryStatement node) {
+ if (skipNode(node))
+ return;
+ TryFlowInfo info= createTry();
+ setFlowInfo(node, info);
+ for (Iterator iterator= node.resources().iterator(); iterator.hasNext();) {
+ info.mergeResources(getFlowInfo(iterator.next()), fFlowContext);
+ }
+ info.mergeTry(getFlowInfo(node.getBody()), fFlowContext);
+ for (Iterator iter= node.catchClauses().iterator(); iter.hasNext();) {
+ CatchClause element= iter.next();
+ info.mergeCatch(getFlowInfo(element), fFlowContext);
+ }
+ info.mergeFinally(getFlowInfo(node.getFinally()), fFlowContext);
+ }
+
+ // TODO account for enums and annotations
+
+ @Override
+ public void endVisit(TypeDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getSuperclassType());
+ process(info, node.superInterfaceTypes());
+ process(info, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(TypeDeclarationStatement node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getDeclaration());
+ }
+
+ @Override
+ public void endVisit(TypeLiteral node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getType());
+ }
+
+ @Override
+ public void endVisit(TypeParameter node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getName());
+ process(info, node.typeBounds());
+ }
+
+ @Override
+ public void endVisit(VariableDeclarationExpression node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.fragments());
+ }
+
+ @Override
+ public void endVisit(VariableDeclarationStatement node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.fragments());
+ }
+
+ @Override
+ public void endVisit(VariableDeclarationFragment node) {
+ if (skipNode(node))
+ return;
+
+ IVariableBinding binding= node.resolveBinding();
+ LocalFlowInfo nameInfo= null;
+ Expression initializer= node.getInitializer();
+ if (binding != null && !binding.isField() && initializer != null) {
+ nameInfo= new LocalFlowInfo(binding, FlowInfo.WRITE, fFlowContext);
+ }
+ GenericSequentialFlowInfo info= processSequential(node, initializer);
+ info.merge(nameInfo, fFlowContext);
+ }
+
+ @Override
+ public void endVisit(WhileStatement node) {
+ if (skipNode(node))
+ return;
+ WhileFlowInfo info= createWhile();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ info.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(WildcardType node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getBound());
+ }
+
+ private void endVisitMethodInvocation(ASTNode node, ASTNode receiver, List arguments, IMethodBinding binding) {
+ if (skipNode(node))
+ return;
+ MessageSendFlowInfo info= createMessageSendFlowInfo();
+ setFlowInfo(node, info);
+ for (Iterator iter= arguments.iterator(); iter.hasNext();) {
+ Expression arg= iter.next();
+ info.mergeArgument(getFlowInfo(arg), fFlowContext);
+ }
+ info.mergeReceiver(getFlowInfo(receiver), fFlowContext);
+ }
+
+ private void endVisitIncDecOperation(Expression node, Expression operand) {
+ if (skipNode(node))
+ return;
+ FlowInfo info= getFlowInfo(operand);
+ if (info instanceof LocalFlowInfo) {
+ // Normally we should do this in the parent node since the write access take place later.
+ // But I couldn't come up with a case where this influences the flow analysis. So I kept
+ // it here to simplify the code.
+ GenericSequentialFlowInfo result= createSequential(node);
+ result.merge(info, fFlowContext);
+ result.merge(
+ new LocalFlowInfo((LocalFlowInfo)info, FlowInfo.WRITE, fFlowContext),
+ fFlowContext);
+ } else {
+ setFlowInfo(node, info);
+ }
+ }
+
+ private IMethodBinding getMethodBinding(Name name) {
+ if (name == null)
+ return null;
+ IBinding binding= name.resolveBinding();
+ if (binding instanceof IMethodBinding)
+ return (IMethodBinding)binding;
+ return null;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowContext.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowContext.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowContext.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowContext.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TryStatement;
+
+
+public class FlowContext {
+
+ private static class Enum {
+ }
+
+ public static final Enum MERGE= new Enum();
+ public static final Enum ARGUMENTS= new Enum();
+ public static final Enum RETURN_VALUES= new Enum();
+
+ private int fStart;
+ private int fLength;
+ private boolean fConsiderAccessMode;
+ private boolean fLoopReentranceMode;
+ private Enum fComputeMode;
+ private IVariableBinding[] fLocals;
+ private List> fExceptionStack;
+
+ private static final List EMPTY_CATCH_CLAUSE= new ArrayList<>(0);
+
+ public FlowContext(int start, int length) {
+ fStart= start;
+ fLength= length;
+ fExceptionStack= new ArrayList<>(3);
+ }
+
+ public void setConsiderAccessMode(boolean b) {
+ fConsiderAccessMode= b;
+ }
+
+ public void setComputeMode(Enum mode) {
+ fComputeMode= mode;
+ }
+
+ void setLoopReentranceMode(boolean b) {
+ fLoopReentranceMode= b;
+ }
+
+ int getArrayLength() {
+ return fLength;
+ }
+
+ int getStartingIndex() {
+ return fStart;
+ }
+
+ boolean considerAccessMode() {
+ return fConsiderAccessMode;
+ }
+
+ boolean isLoopReentranceMode() {
+ return fLoopReentranceMode;
+ }
+
+ boolean computeMerge() {
+ return fComputeMode == MERGE;
+ }
+
+ boolean computeArguments() {
+ return fComputeMode == ARGUMENTS;
+ }
+
+ boolean computeReturnValues() {
+ return fComputeMode == RETURN_VALUES;
+ }
+
+ public IVariableBinding getLocalFromId(int id) {
+ return getLocalFromIndex(id - fStart);
+ }
+
+ public IVariableBinding getLocalFromIndex(int index) {
+ if (fLocals == null || index > fLocals.length)
+ return null;
+ return fLocals[index];
+ }
+
+ public int getIndexFromLocal(IVariableBinding local) {
+ if (fLocals == null)
+ return -1;
+ for (int i= 0; i < fLocals.length; i++) {
+ if (fLocals[i] == local)
+ return i;
+ }
+ return -1;
+ }
+
+ void manageLocal(IVariableBinding local) {
+ if (fLocals == null)
+ fLocals= new IVariableBinding[fLength];
+ fLocals[local.getVariableId() - fStart]= local;
+ }
+
+ //---- Exception handling --------------------------------------------------------
+
+ void pushExcptions(TryStatement node) {
+ List catchClauses= node.catchClauses();
+ if (catchClauses == null)
+ catchClauses= EMPTY_CATCH_CLAUSE;
+ fExceptionStack.add(catchClauses);
+ }
+
+ void popExceptions() {
+ Assert.isTrue(fExceptionStack.size() > 0);
+ fExceptionStack.remove(fExceptionStack.size() - 1);
+ }
+
+ boolean isExceptionCaught(ITypeBinding excpetionType) {
+ for (Iterator> exceptions= fExceptionStack.iterator(); exceptions.hasNext(); ) {
+ for (Iterator catchClauses= exceptions.next().iterator(); catchClauses.hasNext(); ) {
+ SingleVariableDeclaration caughtException= catchClauses.next().getException();
+ IVariableBinding binding= caughtException.resolveBinding();
+ if (binding == null)
+ continue;
+ ITypeBinding caughtype= binding.getType();
+ while (caughtype != null) {
+ if (caughtype == excpetionType)
+ return true;
+ caughtype= caughtype.getSuperclass();
+ }
+ }
+ }
+ return false;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,465 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Benjamin Muskalla - [extract method] missing return type when code can throw exception - https://bugs.eclipse.org/bugs/show_bug.cgi?id=97413
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.SimpleName;
+
+public abstract class FlowInfo {
+
+ // Return statement handling.
+ protected static final int NOT_POSSIBLE= 0;
+ protected static final int UNDEFINED= 1;
+ protected static final int NO_RETURN= 2;
+ protected static final int PARTIAL_RETURN= 3;
+ protected static final int VOID_RETURN= 4;
+ protected static final int VALUE_RETURN= 5;
+ protected static final int THROW= 6;
+
+ // Local access handling.
+ public static final int UNUSED= 1 << 0;
+ public static final int READ= 1 << 1;
+ public static final int READ_POTENTIAL= 1 << 2;
+ public static final int WRITE= 1 << 3;
+ public static final int WRITE_POTENTIAL= 1 << 4;
+ public static final int UNKNOWN= 1 << 5;
+
+ // Table to merge access modes for condition statements (e.g branch[x] || branch[y]).
+ private static final int[][] ACCESS_MODE_CONDITIONAL_TABLE= {
+ /* UNUSED READ READ_POTENTIAL WRTIE WRITE_POTENTIAL UNKNOWN */
+ /* UNUSED */ { UNUSED, READ_POTENTIAL, READ_POTENTIAL, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN },
+ /* READ */ { READ_POTENTIAL, READ, READ_POTENTIAL, UNKNOWN, UNKNOWN, UNKNOWN },
+ /* READ_POTENTIAL */ { READ_POTENTIAL, READ_POTENTIAL, READ_POTENTIAL, UNKNOWN, UNKNOWN, UNKNOWN },
+ /* WRITE */ { WRITE_POTENTIAL, UNKNOWN, UNKNOWN, WRITE, WRITE_POTENTIAL, UNKNOWN },
+ /* WRITE_POTENTIAL */ { WRITE_POTENTIAL, UNKNOWN, UNKNOWN, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN },
+ /* UNKNOWN */ { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN }
+ };
+
+ // Table to change access mode if there is an open branch statement
+ private static final int[] ACCESS_MODE_OPEN_BRANCH_TABLE= {
+ /* UNUSED READ READ_POTENTIAL WRTIE WRITE_POTENTIAL UNKNOWN */
+ UNUSED, READ_POTENTIAL, READ_POTENTIAL, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN
+ };
+
+ // Table to merge return modes for condition statements (y: fReturnKind, x: other.fReturnKind)
+ private static final int[][] RETURN_KIND_CONDITIONAL_TABLE = {
+ /* NOT_POSSIBLE UNDEFINED NO_RETURN PARTIAL_RETURN VOID_RETURN VALUE_RETURN THROW */
+ /* NOT_POSSIBLE */ { NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE },
+ /* UNDEFINED */ { NOT_POSSIBLE, UNDEFINED, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
+ /* NO_RETURN */ { NOT_POSSIBLE, NO_RETURN, NO_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, NO_RETURN },
+ /* PARTIAL_RETURN */ { NOT_POSSIBLE, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN },
+ /* VOID_RETURN */ { NOT_POSSIBLE, VOID_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, VOID_RETURN, NOT_POSSIBLE, VOID_RETURN },
+ /* VALUE_RETURN */ { NOT_POSSIBLE, VALUE_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, NOT_POSSIBLE, VALUE_RETURN, VALUE_RETURN },
+ /* THROW */ { NOT_POSSIBLE, THROW, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW }
+ };
+
+ // Table to merge return modes for sequential statements (y: fReturnKind, x: other.fReturnKind)
+ private static final int[][] RETURN_KIND_SEQUENTIAL_TABLE = {
+ /* NOT_POSSIBLE UNDEFINED NO_RETURN PARTIAL_RETURN VOID_RETURN VALUE_RETURN THROW */
+ /* NOT_POSSIBLE */ { NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE },
+ /* UNDEFINED */ { NOT_POSSIBLE, UNDEFINED, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
+ /* NO_RETURN */ { NOT_POSSIBLE, NO_RETURN, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
+ /* PARTIAL_RETURN */ { NOT_POSSIBLE, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, VALUE_RETURN },
+ /* VOID_RETURN */ { NOT_POSSIBLE, VOID_RETURN, VOID_RETURN, PARTIAL_RETURN, VOID_RETURN, NOT_POSSIBLE, NOT_POSSIBLE },
+ /* VALUE_RETURN */ { NOT_POSSIBLE, VALUE_RETURN, VALUE_RETURN, PARTIAL_RETURN, NOT_POSSIBLE, VALUE_RETURN, NOT_POSSIBLE },
+ /* THROW */ { NOT_POSSIBLE, THROW, THROW, VALUE_RETURN, VOID_RETURN, VALUE_RETURN, THROW }
+ };
+
+ protected static final String UNLABELED = "@unlabeled"; //$NON-NLS-1$
+ protected static final IVariableBinding[] EMPTY_ARRAY= new IVariableBinding[0];
+
+ protected int fReturnKind;
+ protected int[] fAccessModes;
+ protected Set fBranches;
+ //protected Set fExceptions;
+ protected Set fTypeVariables;
+
+ protected FlowInfo() {
+ this(UNDEFINED);
+ }
+
+ protected FlowInfo(int returnKind) {
+ fReturnKind= returnKind;
+ }
+
+ //---- General Helpers ----------------------------------------------------------
+
+ protected void assignExecutionFlow(FlowInfo right) {
+ fReturnKind= right.fReturnKind;
+ fBranches= right.fBranches;
+ }
+
+ protected void assignAccessMode(FlowInfo right) {
+ fAccessModes= right.fAccessModes;
+ }
+
+ protected void assign(FlowInfo right) {
+ assignExecutionFlow(right);
+ assignAccessMode(right);
+ }
+
+ protected void mergeConditional(FlowInfo info, FlowContext context) {
+ mergeAccessModeConditional(info, context);
+ mergeExecutionFlowConditional(info);
+ mergeTypeVariablesConditional(info);
+ }
+
+ protected void mergeSequential(FlowInfo info, FlowContext context) {
+ mergeAccessModeSequential(info, context);
+ mergeExecutionFlowSequential(info);
+ mergeTypeVariablesSequential(info);
+ }
+
+ //---- Return Kind ------------------------------------------------------------------
+
+ public void setNoReturn() {
+ fReturnKind= NO_RETURN;
+ }
+
+ public boolean isUndefined() {
+ return fReturnKind == UNDEFINED;
+ }
+
+ public boolean isNoReturn() {
+ return fReturnKind == NO_RETURN;
+ }
+
+ public boolean isPartialReturn() {
+ return fReturnKind == PARTIAL_RETURN;
+ }
+
+ public boolean isVoidReturn() {
+ return fReturnKind == VOID_RETURN;
+ }
+
+ public boolean isValueReturn() {
+ return fReturnKind == VALUE_RETURN;
+ }
+
+ public boolean isThrow() {
+ return fReturnKind == THROW;
+ }
+
+ public boolean isReturn() {
+ return fReturnKind == VOID_RETURN || fReturnKind == VALUE_RETURN;
+ }
+
+ //---- Branches -------------------------------------------------------------------------
+
+ public boolean branches() {
+ return fBranches != null && !fBranches.isEmpty();
+ }
+
+ protected Set getBranches() {
+ return fBranches;
+ }
+
+ protected void removeLabel(SimpleName label) {
+ if (fBranches != null) {
+ fBranches.remove(makeString(label));
+ if (fBranches.isEmpty())
+ fBranches= null;
+ }
+ }
+
+ protected static String makeString(SimpleName label) {
+ if (label == null)
+ return UNLABELED;
+ else
+ return label.getIdentifier();
+ }
+
+ //---- Type parameters -----------------------------------------------------------------
+
+ public ITypeBinding[] getTypeVariables() {
+ if (fTypeVariables == null)
+ return new ITypeBinding[0];
+ return fTypeVariables.toArray(new ITypeBinding[fTypeVariables.size()]);
+ }
+
+ protected void addTypeVariable(ITypeBinding typeParameter) {
+ if (fTypeVariables == null)
+ fTypeVariables= new HashSet<>();
+ fTypeVariables.add(typeParameter);
+ }
+
+ private void mergeTypeVariablesSequential(FlowInfo otherInfo) {
+ fTypeVariables= mergeSets(fTypeVariables, otherInfo.fTypeVariables);
+ }
+
+ private void mergeTypeVariablesConditional(FlowInfo otherInfo) {
+ fTypeVariables= mergeSets(fTypeVariables, otherInfo.fTypeVariables);
+ }
+
+ //---- Execution flow -------------------------------------------------------------------
+
+ private void mergeExecutionFlowSequential(FlowInfo otherInfo) {
+ int other= otherInfo.fReturnKind;
+ if (branches() && other == VALUE_RETURN)
+ other= PARTIAL_RETURN;
+ fReturnKind= RETURN_KIND_SEQUENTIAL_TABLE[fReturnKind][other];
+ mergeBranches(otherInfo);
+ }
+
+ private void mergeExecutionFlowConditional(FlowInfo otherInfo) {
+ fReturnKind= RETURN_KIND_CONDITIONAL_TABLE[fReturnKind][otherInfo.fReturnKind];
+ mergeBranches(otherInfo);
+ }
+
+ private void mergeBranches(FlowInfo otherInfo) {
+ fBranches= mergeSets(fBranches, otherInfo.fBranches);
+ }
+
+ private static Set mergeSets(Set thisSet, Set otherSet) {
+ if (otherSet != null) {
+ if (thisSet == null) {
+ thisSet= otherSet;
+ } else {
+ Iterator iter= otherSet.iterator();
+ while (iter.hasNext()) {
+ thisSet.add(iter.next());
+ }
+ }
+ }
+ return thisSet;
+ }
+
+ //---- Local access handling --------------------------------------------------
+
+ /**
+ * Returns an array of IVariableBinding
that conform to the given
+ * access mode mode
.
+ *
+ * @param context the flow context object used to compute this flow info
+ * @param mode the access type. Valid values are READ
, WRITE
,
+ * UNKNOWN
and any combination of them.
+ * @return an array of local variable bindings conforming to the given type.
+ */
+ public IVariableBinding[] get(FlowContext context, int mode) {
+ List result= new ArrayList<>();
+ int[] locals= getAccessModes();
+ if (locals == null)
+ return EMPTY_ARRAY;
+ for (int i= 0; i < locals.length; i++) {
+ int accessMode= locals[i];
+ if ((accessMode & mode) != 0)
+ result.add(context.getLocalFromIndex(i));
+ }
+ return result.toArray(new IVariableBinding[result.size()]);
+ }
+
+ /**
+ * Checks whether the given local variable binding has the given access
+ * mode.
+ *
+ * @param context the flow context used during flow analysis
+ * @param local local variable of interest
+ * @param mode the access mode of the local variable
+ *
+ * @return true
if the binding has the given access mode.
+ * False
otherwise
+ */
+ public boolean hasAccessMode(FlowContext context, IVariableBinding local, int mode) {
+ boolean unusedMode= (mode & UNUSED) != 0;
+ if (fAccessModes == null && unusedMode)
+ return true;
+ int index= context.getIndexFromLocal(local);
+ if (index == -1)
+ return unusedMode;
+ return (fAccessModes[index] & mode) != 0;
+ }
+
+ /**
+ * Returns the access mode of the local variable identified by the given binding.
+ *
+ * @param context the flow context used during flow analysis
+ * @param local the local variable of interest
+ * @return the access mode of the local variable
+ */
+ public int getAccessMode(FlowContext context, IVariableBinding local) {
+ if (fAccessModes == null)
+ return UNUSED;
+ int index= context.getIndexFromLocal(local);
+ if (index == -1)
+ return UNUSED;
+ return fAccessModes[index];
+ }
+
+ protected int[] getAccessModes() {
+ return fAccessModes;
+ }
+
+ protected void clearAccessMode(IVariableBinding binding, FlowContext context) {
+ if (fAccessModes == null) // all are unused
+ return;
+ fAccessModes[binding.getVariableId() - context.getStartingIndex()]= UNUSED;
+ }
+
+ protected void mergeAccessModeSequential(FlowInfo otherInfo, FlowContext context) {
+ if (!context.considerAccessMode())
+ return;
+
+ int[] others= otherInfo.fAccessModes;
+ if (others == null) // others are all unused. So nothing to do
+ return;
+
+ // Must not consider return kind since a return statement can't control execution flow
+ // inside a method. It always leaves the method.
+ if (branches()) {
+ for (int i= 0; i < others.length; i++)
+ others[i]= ACCESS_MODE_OPEN_BRANCH_TABLE[getIndex(others[i])];
+ }
+
+ if (fAccessModes == null) { // all current variables are unused
+ fAccessModes= others;
+ return;
+ }
+
+ if (context.computeArguments()) {
+ handleComputeArguments(others);
+ } else if (context.computeReturnValues()) {
+ handleComputeReturnValues(others);
+ } else if (context.computeMerge()) {
+ handleMergeValues(others);
+ }
+ }
+
+ private void handleComputeReturnValues(int[] others) {
+ for (int i= 0; i < fAccessModes.length; i++) {
+ int accessmode= fAccessModes[i];
+ int othermode= others[i];
+ if (accessmode == WRITE)
+ continue;
+ if (accessmode == WRITE_POTENTIAL) {
+ if (othermode == WRITE)
+ fAccessModes[i]= WRITE;
+ continue;
+ }
+
+ if (others[i] != UNUSED)
+ fAccessModes[i]= othermode;
+ }
+ }
+
+ private void handleComputeArguments(int[] others) {
+ for (int i= 0; i < fAccessModes.length; i++) {
+ int accessMode= fAccessModes[i];
+ int otherMode= others[i];
+ if (accessMode == UNUSED) {
+ fAccessModes[i]= otherMode;
+ } else if (accessMode == WRITE_POTENTIAL && (otherMode == READ || otherMode == READ_POTENTIAL)) {
+ // Read always supersedes a potential write even if the read is potential as well
+ // (we have to consider the potential read as an argument then).
+ fAccessModes[i]= otherMode;
+ } else if (accessMode == WRITE_POTENTIAL && otherMode == WRITE) {
+ fAccessModes[i]= WRITE;
+ }
+ }
+ }
+
+ private void handleMergeValues(int[] others) {
+ for (int i= 0; i < fAccessModes.length; i++) {
+ fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
+ [getIndex(fAccessModes[i])]
+ [getIndex(others[i])];
+ }
+ }
+
+ protected void createAccessModeArray(FlowContext context) {
+ fAccessModes= new int[context.getArrayLength()];
+ for (int i= 0; i < fAccessModes.length; i++) {
+ fAccessModes[i]= UNUSED;
+ }
+ }
+
+ protected void mergeAccessModeConditional(FlowInfo otherInfo, FlowContext context) {
+ if (!context.considerAccessMode())
+ return;
+
+ int[] others= otherInfo.fAccessModes;
+ // first access
+ if (fAccessModes == null) {
+ if (others != null)
+ fAccessModes= others;
+ else
+ createAccessModeArray(context);
+ return;
+ } else {
+ if (others == null) {
+ for (int i= 0; i < fAccessModes.length; i++) {
+ int unused_index= getIndex(UNUSED);
+ fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
+ [getIndex(fAccessModes[i])]
+ [unused_index];
+ }
+ } else {
+ for (int i= 0; i < fAccessModes.length; i++) {
+ fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
+ [getIndex(fAccessModes[i])]
+ [getIndex(others[i])];
+ }
+ }
+ }
+ }
+
+ protected void mergeEmptyCondition(FlowContext context) {
+ if (fReturnKind == VALUE_RETURN || fReturnKind == VOID_RETURN)
+ fReturnKind= PARTIAL_RETURN;
+
+ if (!context.considerAccessMode())
+ return;
+
+ if (fAccessModes == null) {
+ createAccessModeArray(context);
+ return;
+ }
+
+ int unused_index= getIndex(UNUSED);
+ for (int i= 0; i < fAccessModes.length; i++) {
+ fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE
+ [getIndex(fAccessModes[i])]
+ [unused_index];
+ }
+ }
+
+ private static int getIndex(int accessMode) {
+ // Fast log function
+ switch (accessMode) {
+ case UNUSED:
+ return 0;
+ case READ:
+ return 1;
+ case READ_POTENTIAL:
+ return 2;
+ case WRITE:
+ return 3;
+ case WRITE_POTENTIAL:
+ return 4;
+ case UNKNOWN:
+ return 5;
+ }
+ return -1;
+ }
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class ForFlowInfo extends FlowInfo {
+
+ public void mergeInitializer(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void mergeCondition(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void mergeIncrement(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ info.mergeEmptyCondition(context);
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void mergeAction(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ info.mergeEmptyCondition(context);
+
+ mergeSequential(info, context);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericConditionalFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericConditionalFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericConditionalFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericConditionalFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+
+
+class GenericConditionalFlowInfo extends FlowInfo {
+
+ public GenericConditionalFlowInfo() {
+ super(UNDEFINED);
+ }
+
+ public void merge(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ mergeConditional(info, context);
+ }
+
+ public void mergeAccessMode(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ mergeAccessModeConditional(info, context);
+ }
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericSequentialFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericSequentialFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericSequentialFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/GenericSequentialFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+
+
+class GenericSequentialFlowInfo extends FlowInfo {
+
+ public GenericSequentialFlowInfo() {
+ super(NO_RETURN);
+ }
+
+ public void merge(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeSequential(info, context);
+ }
+
+ public void mergeAccessMode(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class IfFlowInfo extends FlowInfo {
+
+ public void mergeCondition(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void merge(FlowInfo thenPart, FlowInfo elsePart, FlowContext context) {
+ if (thenPart == null && elsePart == null)
+ return;
+
+ GenericConditionalFlowInfo cond= new GenericConditionalFlowInfo();
+ if (thenPart != null)
+ cond.merge(thenPart, context);
+
+ if (elsePart != null)
+ cond.merge(elsePart, context);
+
+ if (thenPart == null || elsePart == null)
+ cond.mergeEmptyCondition(context);
+
+ mergeSequential(cond, context);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+
+public class InOutFlowAnalyzer extends FlowAnalyzer {
+
+ public InOutFlowAnalyzer(FlowContext context) {
+ super(context);
+ }
+
+ public FlowInfo perform(ASTNode[] selectedNodes) {
+ FlowContext context= getFlowContext();
+ GenericSequentialFlowInfo result= createSequential();
+ for (int i= 0; i < selectedNodes.length; i++) {
+ ASTNode node= selectedNodes[i];
+ node.accept(this);
+ result.merge(getFlowInfo(node), context);
+ }
+ return result;
+ }
+
+ @Override
+ protected boolean traverseNode(ASTNode node) {
+ // we are only traversing the selected nodes.
+ return true;
+ }
+
+ @Override
+ protected boolean createReturnFlowInfo(ReturnStatement node) {
+ // we are only traversing selected nodes.
+ return true;
+ }
+
+ @Override
+ public void endVisit(Block node) {
+ super.endVisit(node);
+ clearAccessMode(accessFlowInfo(node), node.statements());
+ }
+
+ @Override
+ public void endVisit(CatchClause node) {
+ super.endVisit(node);
+ clearAccessMode(accessFlowInfo(node), node.getException());
+ }
+
+ @Override
+ public void endVisit(EnhancedForStatement node) {
+ super.endVisit(node);
+ clearAccessMode(accessFlowInfo(node), node.getParameter());
+ }
+
+ @Override
+ public void endVisit(ForStatement node) {
+ super.endVisit(node);
+ clearAccessMode(accessFlowInfo(node), node.initializers());
+ }
+
+ @Override
+ public void endVisit(MethodDeclaration node) {
+ super.endVisit(node);
+ FlowInfo info= accessFlowInfo(node);
+ for (Iterator iter= node.parameters().iterator(); iter.hasNext();) {
+ clearAccessMode(info, iter.next());
+ }
+ }
+
+ private void clearAccessMode(FlowInfo info, SingleVariableDeclaration decl) {
+ IVariableBinding binding= decl.resolveBinding();
+ if (binding != null && !binding.isField())
+ info.clearAccessMode(binding, fFlowContext);
+ }
+
+ private void clearAccessMode(FlowInfo info, List extends ASTNode> nodes) {
+ if (nodes== null || nodes.isEmpty() || info == null)
+ return;
+ for (Iterator extends ASTNode> iter= nodes.iterator(); iter.hasNext(); ) {
+ Object node= iter.next();
+ Iterator fragments= null;
+ if (node instanceof VariableDeclarationStatement) {
+ fragments= ((VariableDeclarationStatement)node).fragments().iterator();
+ } else if (node instanceof VariableDeclarationExpression) {
+ fragments= ((VariableDeclarationExpression)node).fragments().iterator();
+ }
+ if (fragments != null) {
+ while (fragments.hasNext()) {
+ clearAccessMode(info, fragments.next());
+ }
+ }
+ }
+ }
+
+ private void clearAccessMode(FlowInfo info, VariableDeclarationFragment fragment) {
+ IVariableBinding binding= fragment.resolveBinding();
+ if (binding != null && !binding.isField())
+ info.clearAccessMode(binding, fFlowContext);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
+ * o inline call that is used in a field initializer
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
+ * Benjamin Muskalla - [extract method] Missing return value, while extracting code out of a loop - https://bugs.eclipse.org/bugs/show_bug.cgi?id=213519
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jface.text.IRegion;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.ConditionalExpression;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SwitchExpression;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.WhileStatement;
+
+import org.eclipse.jdt.internal.corext.dom.Selection;
+
+public class InputFlowAnalyzer extends FlowAnalyzer {
+
+ private static class LoopReentranceVisitor extends FlowAnalyzer {
+ private Selection fSelection;
+ private ASTNode fLoopNode;
+ public LoopReentranceVisitor(FlowContext context, Selection selection, ASTNode loopNode) {
+ super(context);
+ fSelection= selection;
+ fLoopNode= loopNode;
+ }
+ @Override
+ protected boolean traverseNode(ASTNode node) {
+ return true; // end <= fSelection.end || fSelection.enclosedBy(start, end);
+ }
+ @Override
+ protected boolean createReturnFlowInfo(ReturnStatement node) {
+ // Make sure that the whole return statement is selected or located before the selection.
+ return node.getStartPosition() + node.getLength() <= fSelection.getExclusiveEnd();
+ }
+ public void process(ASTNode node) {
+ try {
+ fFlowContext.setLoopReentranceMode(true);
+ node.accept(this);
+ } finally {
+ fFlowContext.setLoopReentranceMode(false);
+ }
+ }
+ @Override
+ public void endVisit(DoStatement node) {
+ if (skipNode(node))
+ return;
+ DoWhileFlowInfo info= createDoWhile();
+ setFlowInfo(node, info);
+ info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ // No need to merge the condition. It was already considered by the InputFlowAnalyzer.
+ info.removeLabel(null);
+ }
+ @Override
+ public void endVisit(EnhancedForStatement node) {
+ if (skipNode(node))
+ return;
+ FlowInfo paramInfo= getFlowInfo(node.getParameter());
+ FlowInfo expressionInfo= getFlowInfo(node.getExpression());
+ FlowInfo actionInfo= getFlowInfo(node.getBody());
+ EnhancedForFlowInfo forInfo= createEnhancedFor();
+ setFlowInfo(node, forInfo);
+ // If the for statement is the outermost loop then we only have to consider
+ // the action. The parameter and expression are only evaluated once.
+ if (node == fLoopNode) {
+ forInfo.mergeAction(actionInfo, fFlowContext);
+ } else {
+ // Inner for loops are evaluated in the sequence expression, parameter,
+ // action.
+ forInfo.mergeExpression(expressionInfo, fFlowContext);
+ forInfo.mergeParameter(paramInfo, fFlowContext);
+ forInfo.mergeAction(actionInfo, fFlowContext);
+ }
+ forInfo.removeLabel(null);
+ }
+ @Override
+ public void endVisit(ForStatement node) {
+ if (skipNode(node))
+ return;
+ FlowInfo initInfo= createSequential(node.initializers());
+ FlowInfo conditionInfo= getFlowInfo(node.getExpression());
+ FlowInfo incrementInfo= createSequential(node.updaters());
+ FlowInfo actionInfo= getFlowInfo(node.getBody());
+ ForFlowInfo forInfo= createFor();
+ setFlowInfo(node, forInfo);
+ // the for statement is the outermost loop. In this case we only have
+ // to consider the increment, condition and action.
+ if (node == fLoopNode) {
+ forInfo.mergeIncrement(incrementInfo, fFlowContext);
+ forInfo.mergeCondition(conditionInfo, fFlowContext);
+ forInfo.mergeAction(actionInfo, fFlowContext);
+ } else {
+ // we have to merge two different cases. One if we reenter the for statement
+ // immediatelly (that means we have to consider increments, condition and action)
+ // and the other case if we reenter the for in the next loop of
+ // the outer loop. Then we have to consider initializations, condtion and action.
+ // For a conditional flow info that means:
+ // (initializations | increments) & condition & action.
+ GenericConditionalFlowInfo initIncr= new GenericConditionalFlowInfo();
+ initIncr.merge(initInfo, fFlowContext);
+ initIncr.merge(incrementInfo, fFlowContext);
+ forInfo.mergeAccessModeSequential(initIncr, fFlowContext);
+ forInfo.mergeCondition(conditionInfo, fFlowContext);
+ forInfo.mergeAction(actionInfo, fFlowContext);
+ }
+ forInfo.removeLabel(null);
+ }
+ }
+
+ private Selection fSelection;
+ private boolean fDoLoopReentrance;
+
+ public InputFlowAnalyzer(FlowContext context, Selection selection, boolean doLoopReentrance) {
+ super(context);
+ fSelection= selection;
+ Assert.isNotNull(fSelection);
+ fDoLoopReentrance= doLoopReentrance;
+ }
+
+ public FlowInfo perform(BodyDeclaration node) {
+ Assert.isTrue(!(node instanceof AbstractTypeDeclaration));
+ node.accept(this);
+ return getFlowInfo(node);
+ }
+
+ @Override
+ protected boolean traverseNode(ASTNode node) {
+ return node.getStartPosition() + node.getLength() > fSelection.getInclusiveEnd();
+ }
+
+ @Override
+ protected boolean createReturnFlowInfo(ReturnStatement node) {
+ // Make sure that the whole return statement is located after the selection. There can be cases like
+ // return i + [x + 10] * 10; In this case we must not create a return info node.
+ return node.getStartPosition() >= fSelection.getInclusiveEnd();
+ }
+
+ @Override
+ public void endVisit(ConditionalExpression node) {
+ if (skipNode(node))
+ return;
+ Expression thenPart= node.getThenExpression();
+ Expression elsePart= node.getElseExpression();
+ if ((thenPart != null && fSelection.coveredBy(thenPart)) ||
+ (elsePart != null && fSelection.coveredBy(elsePart))) {
+ GenericSequentialFlowInfo info= createSequential();
+ setFlowInfo(node, info);
+ endVisitConditional(info, node.getExpression(), new ASTNode[] {thenPart, elsePart});
+ } else {
+ super.endVisit(node);
+ }
+ }
+
+ @Override
+ public void endVisit(DoStatement node) {
+ super.endVisit(node);
+ handleLoopReentrance(node);
+ }
+
+ @Override
+ public void endVisit(IfStatement node) {
+ if (skipNode(node))
+ return;
+ Statement thenPart= node.getThenStatement();
+ Statement elsePart= node.getElseStatement();
+ if ((thenPart != null && fSelection.coveredBy(thenPart)) ||
+ (elsePart != null && fSelection.coveredBy(elsePart))) {
+ GenericSequentialFlowInfo info= createSequential();
+ setFlowInfo(node, info);
+ endVisitConditional(info, node.getExpression(), new ASTNode[] {thenPart, elsePart});
+ } else {
+ super.endVisit(node);
+ }
+ }
+
+ @Override
+ public void endVisit(EnhancedForStatement node) {
+ super.endVisit(node);
+ handleLoopReentrance(node);
+ }
+
+ @Override
+ public void endVisit(ForStatement node) {
+ super.endVisit(node);
+ handleLoopReentrance(node);
+ }
+
+ @Override
+ public void endVisit(SwitchStatement node) {
+ if (skipNode(node))
+ return;
+ SwitchData data= preEndVisit(node, node.statements(), node.getExpression());
+ if (data == null) {
+ return;
+ }
+ super.endVisit(node, data);
+ }
+
+ @Override
+ public void endVisit(SwitchExpression node) {
+ if (skipNode(node))
+ return;
+ SwitchData data= preEndVisit(node, node.statements(), node.getExpression());
+ if (data == null) {
+ return;
+ }
+ super.endVisit(node, data);
+ }
+
+ public SwitchData preEndVisit(ASTNode node, List statements, Expression expression) {
+ SwitchData data= createSwitchData(statements);
+ IRegion[] ranges= data.getRanges();
+ for (int i= 0; i < ranges.length; i++) {
+ IRegion range= ranges[i];
+ if (fSelection.coveredBy(range)) {
+ GenericSequentialFlowInfo info= createSequential();
+ setFlowInfo(node, info);
+ info.merge(getFlowInfo(expression), fFlowContext);
+ info.merge(data.getInfo(i), fFlowContext);
+ info.removeLabel(null);
+ return null;
+ }
+ }
+ return data;
+ }
+
+ @Override
+ public void endVisit(WhileStatement node) {
+ super.endVisit(node);
+ handleLoopReentrance(node);
+ }
+
+ private void endVisitConditional(GenericSequentialFlowInfo info, ASTNode condition, ASTNode[] branches) {
+ info.merge(getFlowInfo(condition), fFlowContext);
+ for (int i= 0; i < branches.length; i++) {
+ ASTNode branch= branches[i];
+ if (branch != null && fSelection.coveredBy(branch)) {
+ info.merge(getFlowInfo(branch), fFlowContext);
+ break;
+ }
+ }
+ }
+
+ private void handleLoopReentrance(ASTNode node) {
+ if (fDoLoopReentrance && fSelection.coveredBy(node) && !fSelection.covers(node)) {
+ LoopReentranceVisitor loopReentranceVisitor= new LoopReentranceVisitor(fFlowContext, fSelection, node);
+ loopReentranceVisitor.process(node);
+ GenericSequentialFlowInfo info= createSequential();
+ info.merge(getFlowInfo(node), fFlowContext);
+ info.merge(loopReentranceVisitor.getFlowInfo(node), fFlowContext);
+ setFlowInfo(node, info);
+ }
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/LocalFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/LocalFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/LocalFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/LocalFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import org.eclipse.jdt.core.dom.IVariableBinding;
+
+class LocalFlowInfo extends FlowInfo {
+
+ private int fVariableId;
+
+ public LocalFlowInfo(IVariableBinding binding, int localAccessMode, FlowContext context) {
+ super(NO_RETURN);
+ fVariableId= binding.getVariableId();
+ if (context.considerAccessMode()) {
+ createAccessModeArray(context);
+ fAccessModes[fVariableId - context.getStartingIndex()]= localAccessMode;
+ context.manageLocal(binding);
+ }
+ }
+
+ public LocalFlowInfo(LocalFlowInfo info, int localAccessMode, FlowContext context) {
+ super(NO_RETURN);
+ fVariableId= info.fVariableId;
+ if (context.considerAccessMode()) {
+ createAccessModeArray(context);
+ fAccessModes[fVariableId - context.getStartingIndex()]= localAccessMode;
+ }
+ }
+
+ public void setWriteAccess(FlowContext context) {
+ if (context.considerAccessMode()) {
+ fAccessModes[fVariableId - context.getStartingIndex()]= FlowInfo.WRITE;
+ }
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/MessageSendFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/MessageSendFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/MessageSendFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/MessageSendFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+
+class MessageSendFlowInfo extends FlowInfo {
+
+ public MessageSendFlowInfo() {
+ super(NO_RETURN);
+ }
+
+ public void mergeArgument(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeSequential(info, context);
+ }
+
+ public void mergeReceiver(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeSequential(info, context);
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+
+class ReturnFlowInfo extends FlowInfo {
+
+ public ReturnFlowInfo(ReturnStatement node) {
+ super(getReturnFlag(node));
+ }
+
+ public void merge(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ assignAccessMode(info);
+ }
+
+ private static int getReturnFlag(ReturnStatement node) {
+ Expression expression= node.getExpression();
+ if (expression == null || expression.resolveTypeBinding() == node.getAST().resolveWellKnownType("void")) //$NON-NLS-1$
+ return VOID_RETURN;
+ return VALUE_RETURN;
+ }
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class SwitchFlowInfo extends FlowInfo {
+ private GenericConditionalFlowInfo fCases;
+ private boolean fHasNullCaseInfo;
+
+ public SwitchFlowInfo() {
+ fCases= new GenericConditionalFlowInfo();
+ }
+
+ public void mergeTest(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+ mergeSequential(info, context);
+ }
+
+ public void mergeCase(FlowInfo info, FlowContext context) {
+ if (info == null) {
+ fHasNullCaseInfo= true;
+ return;
+ }
+ fCases.mergeConditional(info, context);
+ }
+
+ public void mergeDefault(boolean defaultCaseExists, FlowContext context) {
+ if (!defaultCaseExists || fHasNullCaseInfo)
+ fCases.mergeEmptyCondition(context);
+ mergeSequential(fCases, context);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ThrowFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ThrowFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ThrowFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ThrowFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+
+class ThrowFlowInfo extends FlowInfo {
+
+ public ThrowFlowInfo() {
+ super(THROW);
+ }
+
+ public void merge(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ assignAccessMode(info);
+ }
+
+}
+
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TryFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TryFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TryFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TryFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class TryFlowInfo extends FlowInfo {
+
+ public TryFlowInfo() {
+ super();
+ }
+
+ public void mergeResources(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ mergeSequential(info, context);
+ }
+
+ public void mergeTry(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ mergeSequential(info, context);
+ }
+
+ public void mergeCatch(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ mergeConditional(info, context);
+ }
+
+ public void mergeFinally(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ mergeSequential(info, context);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TypeVariableFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TypeVariableFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TypeVariableFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/TypeVariableFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import org.eclipse.jdt.core.dom.ITypeBinding;
+
+class TypeVariableFlowInfo extends FlowInfo {
+
+ public TypeVariableFlowInfo(ITypeBinding binding, FlowContext context) {
+ super(NO_RETURN);
+ addTypeVariable(binding);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+class WhileFlowInfo extends FlowInfo {
+
+ public void mergeCondition(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ mergeAccessModeSequential(info, context);
+ }
+
+ public void mergeAction(FlowInfo info, FlowContext context) {
+ if (info == null)
+ return;
+
+ info.mergeEmptyCondition(context);
+
+ mergeSequential(info, context);
+ }
+}
+
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineConstantRefactoring.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineConstantRefactoring.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineConstantRefactoring.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineConstantRefactoring.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,1060 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Nikolay Metchev - [inline] problem with fields from generic types - https://bugs.eclipse.org/218431
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.RangeMarker;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.TextUtilities;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionMethodReference;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
+import org.eclipse.jdt.core.dom.SuperMethodReference;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+import org.eclipse.jdt.core.dom.TypeMethodReference;
+import org.eclipse.jdt.core.dom.VariableDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.core.manipulation.ImportReferencesCollector;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
+import org.eclipse.jdt.core.refactoring.descriptors.InlineConstantDescriptor;
+import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchPattern;
+
+import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
+import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+import org.eclipse.jdt.internal.core.manipulation.dom.NecessaryParenthesesChecker;
+import org.eclipse.jdt.internal.core.manipulation.util.Strings;
+import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
+import org.eclipse.jdt.internal.corext.CorextCore;
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
+import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
+import org.eclipse.jdt.internal.corext.dom.fragments.ASTFragmentFactory;
+import org.eclipse.jdt.internal.corext.dom.fragments.IExpressionFragment;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.IRefactoringSearchRequestor;
+import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine2;
+import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
+import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes;
+import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
+import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+public class InlineConstantRefactoring extends Refactoring {
+
+ private static final String ATTRIBUTE_REPLACE= "replace"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_REMOVE= "remove"; //$NON-NLS-1$
+
+ private static class InlineTargetCompilationUnit {
+
+ private static class InitializerTraversal extends HierarchicalASTVisitor {
+
+ private static boolean areInSameType(ASTNode one, ASTNode other) {
+ ASTNode onesContainer= getContainingTypeDeclaration(one);
+ ASTNode othersContainer= getContainingTypeDeclaration(other);
+
+ if (onesContainer == null || othersContainer == null)
+ return false;
+
+ ITypeBinding onesContainerBinding= getTypeBindingForTypeDeclaration(onesContainer);
+ ITypeBinding othersContainerBinding= getTypeBindingForTypeDeclaration(othersContainer);
+
+ Assert.isNotNull(onesContainerBinding);
+ Assert.isNotNull(othersContainerBinding);
+
+ String onesKey= onesContainerBinding.getKey();
+ String othersKey= othersContainerBinding.getKey();
+
+ if (onesKey == null || othersKey == null)
+ return false;
+
+ return onesKey.equals(othersKey);
+ }
+
+ private static boolean isStaticAccess(SimpleName memberName) {
+ IBinding binding= memberName.resolveBinding();
+ Assert.isTrue(binding instanceof IVariableBinding || binding instanceof IMethodBinding || binding instanceof ITypeBinding);
+
+ if (binding instanceof ITypeBinding)
+ return true;
+
+ if (binding instanceof IVariableBinding)
+ return ((IVariableBinding) binding).isField();
+
+ int modifiers= binding.getModifiers();
+ return Modifier.isStatic(modifiers);
+ }
+
+ private static ASTNode getContainingTypeDeclaration(ASTNode node) {
+ while (node != null && !(node instanceof AbstractTypeDeclaration) && !(node instanceof AnonymousClassDeclaration)) {
+ node= node.getParent();
+ }
+ return node;
+ }
+
+ private static ITypeBinding getTypeBindingForTypeDeclaration(ASTNode declaration) {
+ if (declaration instanceof AnonymousClassDeclaration)
+ return ((AnonymousClassDeclaration) declaration).resolveBinding();
+
+ if (declaration instanceof AbstractTypeDeclaration)
+ return ((AbstractTypeDeclaration) declaration).resolveBinding();
+
+ Assert.isTrue(false);
+ return null;
+ }
+
+ private final Expression fInitializer;
+ private ASTRewrite fInitializerRewrite;
+ private final HashSet fStaticImportsInInitializer2;
+
+ // cache:
+ private Set fNamesDeclaredLocallyAtNewLocation;
+
+ private final Expression fNewLocation;
+ private final HashSet fStaticImportsInReference;
+ private final CompilationUnitRewrite fNewLocationCuRewrite;
+ private final ImportRewriteContext fNewLocationContext;
+
+ public InitializerTraversal(Expression initializer, HashSet staticImportsInInitializer, Expression newLocation, HashSet staticImportsInReference, CompilationUnitRewrite newLocationCuRewrite) {
+ fInitializer= initializer;
+ fInitializerRewrite= ASTRewrite.create(initializer.getAST());
+ fStaticImportsInInitializer2= staticImportsInInitializer;
+
+ fNewLocation= newLocation;
+ fStaticImportsInReference= staticImportsInReference;
+ fNewLocationCuRewrite= newLocationCuRewrite;
+ fNewLocationContext= new ContextSensitiveImportRewriteContext(fNewLocation, fNewLocationCuRewrite.getImportRewrite());
+
+ perform(initializer);
+ }
+
+ /**
+ * @param scope not a TypeDeclaration
+ * @return Set containing Strings representing simple names
+ */
+ private Set getLocallyDeclaredNames(BodyDeclaration scope) {
+ Assert.isTrue(!(scope instanceof AbstractTypeDeclaration));
+
+ final Set result= new HashSet<>();
+
+ if (scope instanceof FieldDeclaration)
+ return result;
+
+ scope.accept(new HierarchicalASTVisitor() {
+
+ @Override
+ public boolean visit(AbstractTypeDeclaration node) {
+ Assert.isTrue(node.getParent() instanceof TypeDeclarationStatement);
+
+ result.add(node.getName().getIdentifier());
+ return false;
+ }
+
+ @Override
+ public boolean visit(AnonymousClassDeclaration anonDecl) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(VariableDeclaration varDecl) {
+ result.add(varDecl.getName().getIdentifier());
+ return false;
+ }
+ });
+ return result;
+ }
+
+ public ASTRewrite getInitializerRewrite() {
+ return fInitializerRewrite;
+ }
+
+ private void perform(Expression initializer) {
+ initializer.accept(this);
+ addExplicitTypeArgumentsIfNecessary(initializer);
+ }
+
+ private void addExplicitTypeArgumentsIfNecessary(Expression invocation) {
+ if (Invocations.isResolvedTypeInferredFromExpectedType(invocation)) {
+ ASTNode referenceContext= fNewLocation.getParent();
+ if (! (referenceContext instanceof VariableDeclarationFragment
+ || referenceContext instanceof SingleVariableDeclaration
+ || referenceContext instanceof Assignment)) {
+ ITypeBinding[] typeArguments= Invocations.getInferredTypeArguments(invocation);
+ ListRewrite typeArgsRewrite= Invocations.getInferredTypeArgumentsRewrite(fInitializerRewrite, invocation);
+ for (int i= 0; i < typeArguments.length; i++) {
+ Type typeArgument= fNewLocationCuRewrite.getImportRewrite().addImport(typeArguments[i], fNewLocationCuRewrite.getAST(), fNewLocationContext, TypeLocation.TYPE_ARGUMENT);
+ fNewLocationCuRewrite.getImportRemover().registerAddedImports(typeArgument);
+ typeArgsRewrite.insertLast(typeArgument, null);
+ }
+
+ if (invocation instanceof MethodInvocation) {
+ MethodInvocation methodInvocation= (MethodInvocation) invocation;
+ Expression expression= methodInvocation.getExpression();
+ if (expression == null) {
+ IMethodBinding methodBinding= methodInvocation.resolveMethodBinding();
+ if (methodBinding != null) {
+ expression= fNewLocationCuRewrite.getAST().newName(fNewLocationCuRewrite.getImportRewrite().addImport(methodBinding.getDeclaringClass().getTypeDeclaration(), fNewLocationContext));
+ fInitializerRewrite.set(invocation, MethodInvocation.EXPRESSION_PROPERTY, expression, null);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean visit(FieldAccess fieldAccess) {
+ fieldAccess.getExpression().accept(this);
+ return false;
+ }
+
+ @Override
+ public boolean visit(MethodInvocation invocation) {
+ if (invocation.getExpression() == null)
+ qualifyUnqualifiedMemberNameIfNecessary(invocation.getName());
+ else
+ invocation.getExpression().accept(this);
+
+ for (Iterator it= invocation.arguments().iterator(); it.hasNext();)
+ it.next().accept(this);
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(Name name) {
+ StructuralPropertyDescriptor locationInParent= name.getLocationInParent();
+ if (locationInParent == ExpressionMethodReference.NAME_PROPERTY
+ || locationInParent == TypeMethodReference.NAME_PROPERTY
+ || locationInParent == SuperMethodReference.NAME_PROPERTY) {
+ return false;
+ }
+
+ SimpleName leftmost= getLeftmost(name);
+
+ IBinding leftmostBinding= leftmost.resolveBinding();
+ if (leftmostBinding instanceof IVariableBinding || leftmostBinding instanceof IMethodBinding || leftmostBinding instanceof ITypeBinding) {
+ if (shouldUnqualify(leftmost))
+ unqualifyMemberName(leftmost);
+ else
+ qualifyUnqualifiedMemberNameIfNecessary(leftmost);
+ }
+
+ if (leftmostBinding instanceof ITypeBinding) {
+ String addedImport= fNewLocationCuRewrite.getImportRewrite().addImport((ITypeBinding)leftmostBinding, fNewLocationContext);
+ fNewLocationCuRewrite.getImportRemover().registerAddedImport(addedImport);
+ }
+
+ return false;
+ }
+
+ private void qualifyUnqualifiedMemberNameIfNecessary(SimpleName memberName) {
+ if (shouldQualify(memberName))
+ qualifyMemberName(memberName);
+ }
+
+ private boolean shouldUnqualify(SimpleName memberName) {
+ if (areInSameType(memberName, fNewLocation))
+ return ! mayBeShadowedByLocalDeclaration(memberName);
+
+ return false;
+ }
+
+ private void unqualifyMemberName(SimpleName memberName) {
+ if (doesParentQualify(memberName))
+ fInitializerRewrite.replace(memberName.getParent(), memberName, null);
+ }
+
+ private boolean shouldQualify(SimpleName memberName) {
+ if (! areInSameType(fInitializer, fNewLocation))
+ return true;
+
+ return mayBeShadowedByLocalDeclaration(memberName);
+ }
+
+ private boolean mayBeShadowedByLocalDeclaration(SimpleName memberName) {
+ return getNamesDeclaredLocallyAtNewLocation().contains(memberName.getIdentifier());
+ }
+
+ private Set getNamesDeclaredLocallyAtNewLocation() {
+ if (fNamesDeclaredLocallyAtNewLocation != null)
+ return fNamesDeclaredLocallyAtNewLocation;
+
+ BodyDeclaration enclosingBodyDecl= ASTNodes.getParent(fNewLocation, BodyDeclaration.class);
+ Assert.isTrue(!(enclosingBodyDecl instanceof AbstractTypeDeclaration));
+
+ return fNamesDeclaredLocallyAtNewLocation= getLocallyDeclaredNames(enclosingBodyDecl);
+ }
+
+ private void qualifyMemberName(SimpleName memberName) {
+ if (isStaticAccess(memberName)) {
+ IBinding memberBinding= memberName.resolveBinding();
+
+ if (memberBinding instanceof IVariableBinding || memberBinding instanceof IMethodBinding) {
+ if (fStaticImportsInReference.contains(fNewLocation)) { // use static import if reference location used static import
+ importStatically(memberName, memberBinding);
+ return;
+ } else if (fStaticImportsInInitializer2.contains(memberName)) { // use static import if already imported statically in initializer
+ importStatically(memberName, memberBinding);
+ return;
+ }
+ }
+ qualifyToTopLevelClass(memberName); //otherwise: qualify and import non-static
+ }
+ }
+
+ private void importStatically(SimpleName toImport, IBinding binding) {
+ String newName= fNewLocationCuRewrite.getImportRewrite().addStaticImport(binding);
+ fNewLocationCuRewrite.getImportRemover().registerAddedStaticImport(binding);
+
+ Name newReference= ASTNodeFactory.newName(fInitializerRewrite.getAST(), newName);
+ fInitializerRewrite.replace(toImport, newReference, null);
+ }
+
+ private void qualifyToTopLevelClass(SimpleName toQualify) {
+ ITypeBinding declaringClass= getDeclaringClassBinding(toQualify);
+ if (declaringClass == null)
+ return;
+
+ Type newQualification= fNewLocationCuRewrite.getImportRewrite().addImport(declaringClass.getErasure(), fInitializerRewrite.getAST(), fNewLocationContext);
+ fNewLocationCuRewrite.getImportRemover().registerAddedImports(newQualification);
+
+ SimpleName newToQualify= (SimpleName) fInitializerRewrite.createMoveTarget(toQualify);
+ Type newType= fInitializerRewrite.getAST().newQualifiedType(newQualification, newToQualify);
+ fInitializerRewrite.replace(toQualify, newType, null);
+ }
+
+ private static ITypeBinding getDeclaringClassBinding(SimpleName memberName) {
+
+ IBinding binding= memberName.resolveBinding();
+ if (binding instanceof IMethodBinding)
+ return ((IMethodBinding) binding).getDeclaringClass();
+
+ if (binding instanceof IVariableBinding)
+ return ((IVariableBinding) binding).getDeclaringClass();
+
+ if (binding instanceof ITypeBinding)
+ return ((ITypeBinding) binding).getDeclaringClass();
+
+ Assert.isTrue(false);
+ return null;
+
+ }
+
+ }
+
+ private final Expression fInitializer;
+ private final ICompilationUnit fInitializerUnit;
+ private final VariableDeclarationFragment fOriginalDeclaration;
+
+ /** The references in this compilation unit, represented as AST Nodes in the parsed representation of the compilation unit */
+ private final Expression[] fReferences;
+ private final VariableDeclarationFragment fDeclarationToRemove;
+ private final CompilationUnitRewrite fCuRewrite;
+ private final TightSourceRangeComputer fSourceRangeComputer;
+ private final HashSet fStaticImportsInInitializer;
+ private final boolean fIs15;
+
+ private InlineTargetCompilationUnit(CompilationUnitRewrite cuRewrite, Name[] references, InlineConstantRefactoring refactoring, HashSet staticImportsInInitializer) {
+ fInitializer= refactoring.getInitializer();
+ fInitializerUnit= refactoring.getDeclaringCompilationUnit();
+
+ fCuRewrite= cuRewrite;
+ fSourceRangeComputer= new TightSourceRangeComputer();
+ fCuRewrite.getASTRewrite().setTargetSourceRangeComputer(fSourceRangeComputer);
+ if (refactoring.getRemoveDeclaration() && refactoring.getReplaceAllReferences() && cuRewrite.getCu().equals(fInitializerUnit))
+ fDeclarationToRemove= refactoring.getDeclaration();
+ else
+ fDeclarationToRemove= null;
+
+ fOriginalDeclaration= refactoring.getDeclaration();
+
+ fReferences= new Expression[references.length];
+ for (int i= 0; i < references.length; i++)
+ fReferences[i]= getQualifiedReference(references[i]);
+
+ fIs15= JavaModelUtil.is50OrHigher(cuRewrite.getCu().getJavaProject());
+ fStaticImportsInInitializer= fIs15 ? staticImportsInInitializer : new HashSet<>(0);
+ }
+
+ private static Expression getQualifiedReference(Name fieldName) {
+ if (doesParentQualify(fieldName))
+ return (Expression) fieldName.getParent();
+
+ return fieldName;
+ }
+
+ private static boolean doesParentQualify(Name fieldName) {
+ ASTNode parent= fieldName.getParent();
+ Assert.isNotNull(parent);
+
+ if (parent instanceof FieldAccess && ((FieldAccess) parent).getName() == fieldName)
+ return true;
+
+ if (parent instanceof QualifiedName && ((QualifiedName) parent).getName() == fieldName)
+ return true;
+
+ if (parent instanceof MethodInvocation && ((MethodInvocation) parent).getName() == fieldName)
+ return true;
+
+ return false;
+ }
+
+ public CompilationUnitChange getChange() throws CoreException {
+ for (int i= 0; i < fReferences.length; i++)
+ inlineReference(fReferences[i]);
+
+ removeConstantDeclarationIfNecessary();
+
+ return fCuRewrite.createChange(true);
+ }
+
+ private void inlineReference(Expression reference) throws CoreException {
+ ImportDeclaration importDecl= ASTNodes.getParent(reference, ImportDeclaration.class);
+ if (importDecl != null) {
+ fCuRewrite.getImportRemover().registerInlinedStaticImport(importDecl);
+ return;
+ }
+
+ String modifiedInitializer= prepareInitializerForLocation(reference);
+ if (modifiedInitializer == null)
+ return;
+
+ TextEditGroup msg= fCuRewrite.createGroupDescription(RefactoringCoreMessages.InlineConstantRefactoring_Inline);
+
+ Expression newReference;
+ boolean isStringPlaceholder= false;
+
+ AST ast= fCuRewrite.getAST();
+ ITypeBinding explicitCast= ASTNodes.getExplicitCast(fInitializer, reference);
+ if (explicitCast != null) {
+ CastExpression cast= ast.newCastExpression();
+ Expression modifiedInitializerExpr= (Expression) fCuRewrite.getASTRewrite().createStringPlaceholder(modifiedInitializer, reference.getNodeType());
+ if (NecessaryParenthesesChecker.needsParentheses(fInitializer, cast, CastExpression.EXPRESSION_PROPERTY)) {
+ ParenthesizedExpression parenthesized= ast.newParenthesizedExpression();
+ parenthesized.setExpression(modifiedInitializerExpr);
+ modifiedInitializerExpr= parenthesized;
+ }
+ cast.setExpression(modifiedInitializerExpr);
+ ImportRewriteContext context= new ContextSensitiveImportRewriteContext(reference, fCuRewrite.getImportRewrite());
+ cast.setType(fCuRewrite.getImportRewrite().addImport(explicitCast, ast, context, TypeLocation.CAST));
+ newReference= cast;
+
+ } else if (fInitializer instanceof ArrayInitializer) {
+ ArrayCreation arrayCreation= ast.newArrayCreation();
+ ArrayType arrayType= (ArrayType) ASTNodeFactory.newType(ast, fOriginalDeclaration);
+ arrayCreation.setType(arrayType);
+
+ ArrayInitializer newArrayInitializer= (ArrayInitializer) fCuRewrite.getASTRewrite().createStringPlaceholder(modifiedInitializer,
+ ASTNode.ARRAY_INITIALIZER);
+ arrayCreation.setInitializer(newArrayInitializer);
+ newReference= arrayCreation;
+
+ ITypeBinding typeToAddToImport= ASTNodes.getType(fOriginalDeclaration).resolveBinding();
+ ImportRewriteContext context= new ContextSensitiveImportRewriteContext(reference, fCuRewrite.getImportRewrite());
+ fCuRewrite.getImportRewrite().addImport(typeToAddToImport, context);
+ fCuRewrite.getImportRemover().registerAddedImport(typeToAddToImport.getName());
+
+ } else {
+ newReference= (Expression) fCuRewrite.getASTRewrite().createStringPlaceholder(modifiedInitializer, reference.getNodeType());
+ isStringPlaceholder= true;
+ }
+
+ if (NecessaryParenthesesChecker.needsParentheses((isStringPlaceholder ? fInitializer : newReference), reference.getParent(), reference.getLocationInParent())) {
+ ParenthesizedExpression parenthesized= ast.newParenthesizedExpression();
+ parenthesized.setExpression(newReference);
+ newReference= parenthesized;
+ }
+ fCuRewrite.getASTRewrite().replace(reference, newReference, msg);
+ fSourceRangeComputer.addTightSourceNode(reference);
+ fCuRewrite.getImportRemover().registerRemovedNode(reference);
+ }
+
+ private String prepareInitializerForLocation(Expression location) throws CoreException {
+ HashSet staticImportsInReference= new HashSet<>();
+ final IJavaProject project= fCuRewrite.getCu().getJavaProject();
+ if (fIs15)
+ ImportReferencesCollector.collect(location, project, null, new ArrayList(), staticImportsInReference);
+
+ InitializerTraversal traversal= new InitializerTraversal(fInitializer, fStaticImportsInInitializer, location, staticImportsInReference, fCuRewrite);
+ ASTRewrite initializerRewrite= traversal.getInitializerRewrite();
+ IDocument document= new Document(fInitializerUnit.getBuffer().getContents()); // could reuse document when generating and applying undo edits
+
+ final RangeMarker marker= new RangeMarker(fInitializer.getStartPosition(), fInitializer.getLength());
+ TextEdit[] rewriteEdits= initializerRewrite.rewriteAST(document, fInitializerUnit.getJavaProject().getOptions(true)).removeChildren();
+ marker.addChildren(rewriteEdits);
+ try {
+ marker.apply(document, TextEdit.UPDATE_REGIONS);
+ String rewrittenInitializer= document.get(marker.getOffset(), marker.getLength());
+ IRegion region= document.getLineInformation(document.getLineOfOffset(marker.getOffset()));
+ int oldIndent= Strings.computeIndentUnits(document.get(region.getOffset(), region.getLength()), project);
+ return Strings.changeIndent(rewrittenInitializer, oldIndent, project, "", TextUtilities.getDefaultLineDelimiter(document)); //$NON-NLS-1$
+ } catch (MalformedTreeException e) {
+ JavaManipulationPlugin.log(e);
+ } catch (BadLocationException e) {
+ JavaManipulationPlugin.log(e);
+ }
+ return fInitializerUnit.getBuffer().getText(fInitializer.getStartPosition(), fInitializer.getLength());
+ }
+
+ private void removeConstantDeclarationIfNecessary() {
+ if (fDeclarationToRemove == null)
+ return;
+
+ FieldDeclaration parentDeclaration= (FieldDeclaration) fDeclarationToRemove.getParent();
+ ASTNode toRemove;
+ if (parentDeclaration.fragments().size() == 1)
+ toRemove= parentDeclaration;
+ else
+ toRemove= fDeclarationToRemove;
+
+ TextEditGroup msg= fCuRewrite.createGroupDescription(RefactoringCoreMessages.InlineConstantRefactoring_remove_declaration);
+ fCuRewrite.getASTRewrite().remove(toRemove, msg);
+ fCuRewrite.getImportRemover().registerRemovedNode(toRemove);
+ }
+ }
+
+ // ---- End InlineTargetCompilationUnit ----------------------------------------------------------------------------------------------
+
+ private static SimpleName getLeftmost(Name name) {
+ if (name instanceof SimpleName)
+ return (SimpleName) name;
+
+ return getLeftmost(((QualifiedName) name).getQualifier());
+ }
+
+ private int fSelectionStart;
+ private int fSelectionLength;
+
+ private ICompilationUnit fSelectionCu;
+ private CompilationUnitRewrite fSelectionCuRewrite;
+ private Name fSelectedConstantName;
+
+ private IField fField;
+ private CompilationUnitRewrite fDeclarationCuRewrite;
+ private VariableDeclarationFragment fDeclaration;
+ private boolean fDeclarationSelected;
+ private boolean fDeclarationSelectedChecked= false;
+ private boolean fInitializerAllStaticFinal;
+ private boolean fInitializerChecked= false;
+
+ private boolean fRemoveDeclaration= false;
+ private boolean fReplaceAllReferences= true;
+
+ private CompilationUnitChange[] fChanges;
+
+ /**
+ * Creates a new inline constant refactoring.
+ *
+ * This constructor is only used by DelegateCreator
.
+ *
+ *
+ * @param field the field to inline
+ */
+ public InlineConstantRefactoring(IField field) {
+ Assert.isNotNull(field);
+ Assert.isTrue(!field.isBinary());
+ fField= field;
+ }
+
+ /**
+ * Creates a new inline constant refactoring.
+ *
+ * @param unit the compilation unit, or null
if invoked by scripting
+ * @param node the compilation unit node, or null
if invoked by scripting
+ * @param selectionStart the start of the selection
+ * @param selectionLength the length of the selection
+ */
+ public InlineConstantRefactoring(ICompilationUnit unit, CompilationUnit node, int selectionStart, int selectionLength) {
+ Assert.isTrue(selectionStart >= 0);
+ Assert.isTrue(selectionLength >= 0);
+ fSelectionCu= unit;
+ fSelectionStart= selectionStart;
+ fSelectionLength= selectionLength;
+ if (unit != null)
+ initialize(unit, node);
+ }
+
+ public InlineConstantRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
+ this(null, null, 0, 0);
+ RefactoringStatus initializeStatus= initialize(arguments);
+ status.merge(initializeStatus);
+ }
+
+ private void initialize(ICompilationUnit cu, CompilationUnit node) {
+ fSelectionCuRewrite= new CompilationUnitRewrite(cu, node);
+ fSelectedConstantName= findConstantNameNode();
+ }
+
+ private Name findConstantNameNode() {
+ ASTNode node= NodeFinder.perform(fSelectionCuRewrite.getRoot(), fSelectionStart, fSelectionLength);
+ if (node == null)
+ return null;
+ if (node instanceof FieldAccess)
+ node= ((FieldAccess) node).getName();
+ if (!(node instanceof Name))
+ return null;
+ Name name= (Name) node;
+ IBinding binding= name.resolveBinding();
+ if (!(binding instanceof IVariableBinding))
+ return null;
+ IVariableBinding variableBinding= (IVariableBinding) binding;
+ if (!variableBinding.isField() || variableBinding.isEnumConstant())
+ return null;
+ int modifiers= binding.getModifiers();
+ if (! (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)))
+ return null;
+
+ return name;
+ }
+
+ public RefactoringStatus checkStaticFinalConstantNameSelected() {
+ if (fSelectedConstantName == null)
+ return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_static_final_field, null, CorextCore.getPluginId(), RefactoringStatusCodes.NOT_STATIC_FINAL_SELECTED, null);
+
+ return new RefactoringStatus();
+ }
+
+ @Override
+ public String getName() {
+ return RefactoringCoreMessages.InlineConstantRefactoring_name;
+ }
+
+ /**
+ * Returns the field to inline, or null if the field could not be found or
+ * {@link #checkInitialConditions(IProgressMonitor)} has not been called yet.
+ *
+ * @return the field, or null
+ */
+ public IJavaElement getField() {
+ return fField;
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
+ try {
+ pm.beginTask("", 3); //$NON-NLS-1$
+
+ if (!fSelectionCu.isStructureKnown())
+ return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_syntax_errors, null, CorextCore.getPluginId(), RefactoringStatusCodes.SYNTAX_ERRORS, null);
+
+ RefactoringStatus result= checkStaticFinalConstantNameSelected();
+ if (result.hasFatalError())
+ return result;
+
+ result.merge(findField());
+ if (result.hasFatalError())
+ return result;
+ pm.worked(1);
+
+ result.merge(findDeclaration());
+ if (result.hasFatalError())
+ return result;
+ pm.worked(1);
+
+ result.merge(checkInitializer());
+ if (result.hasFatalError())
+ return result;
+ pm.worked(1);
+
+ return result;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ private RefactoringStatus findField() {
+ fField= (IField) ((IVariableBinding) fSelectedConstantName.resolveBinding()).getJavaElement();
+ if (fField != null && ! fField.exists())
+ return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_local_anonymous_unsupported, null, CorextCore.getPluginId(), RefactoringStatusCodes.LOCAL_AND_ANONYMOUS_NOT_SUPPORTED, null);
+
+ return null;
+ }
+ private RefactoringStatus findDeclaration() throws JavaModelException {
+ fDeclarationSelectedChecked= true;
+ fDeclarationSelected= false;
+ ASTNode parent= fSelectedConstantName.getParent();
+ if (parent instanceof VariableDeclarationFragment) {
+ VariableDeclarationFragment parentDeclaration= (VariableDeclarationFragment) parent;
+ if (parentDeclaration.getName() == fSelectedConstantName) {
+ fDeclarationSelected= true;
+ fDeclarationCuRewrite= fSelectionCuRewrite;
+ fDeclaration= (VariableDeclarationFragment) fSelectedConstantName.getParent();
+ return null;
+ }
+ }
+
+ VariableDeclarationFragment declaration= (VariableDeclarationFragment) fSelectionCuRewrite.getRoot().findDeclaringNode(fSelectedConstantName.resolveBinding());
+ if (declaration != null) {
+ fDeclarationCuRewrite= fSelectionCuRewrite;
+ fDeclaration= declaration;
+ return null;
+ }
+
+ if (fField.getCompilationUnit() == null)
+ return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_binary_file, null, CorextCore.getPluginId(), RefactoringStatusCodes.DECLARED_IN_CLASSFILE, null);
+
+ fDeclarationCuRewrite= new CompilationUnitRewrite(fField.getCompilationUnit());
+ fDeclaration= ASTNodeSearchUtil.getFieldDeclarationFragmentNode(fField, fDeclarationCuRewrite.getRoot());
+ return null;
+ }
+
+ private RefactoringStatus checkInitializer() {
+ Expression initializer= getInitializer();
+ if (initializer == null)
+ return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_blank_finals, null, CorextCore.getPluginId(), RefactoringStatusCodes.CANNOT_INLINE_BLANK_FINAL, null);
+
+ fInitializerAllStaticFinal= ConstantChecks.isStaticFinalConstant((IExpressionFragment) ASTFragmentFactory.createFragmentForFullSubtree(initializer));
+ fInitializerChecked= true;
+ return new RefactoringStatus();
+ }
+
+ private VariableDeclarationFragment getDeclaration() {
+ return fDeclaration;
+ }
+
+ private Expression getInitializer() {
+ return fDeclaration.getInitializer();
+ }
+
+ private ICompilationUnit getDeclaringCompilationUnit() {
+ return fField.getCompilationUnit();
+ }
+
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
+ RefactoringStatus result= new RefactoringStatus();
+ pm.beginTask("", 3); //$NON-NLS-1$
+
+ try {
+ fSelectionCuRewrite.clearASTAndImportRewrites();
+ fDeclarationCuRewrite.clearASTAndImportRewrites();
+ Listchanges= new ArrayList<>();
+ HashSet staticImportsInInitializer= new HashSet<>();
+ ImportReferencesCollector.collect(getInitializer(), fField.getJavaProject(), null, new ArrayList(), staticImportsInInitializer);
+
+ if (getReplaceAllReferences()) {
+ SearchResultGroup[] searchResultGroups= findReferences(pm, result);
+ for (int i= 0; i < searchResultGroups.length; i++) {
+ if (pm.isCanceled())
+ throw new OperationCanceledException();
+ SearchResultGroup group= searchResultGroups[i];
+ ICompilationUnit cu= group.getCompilationUnit();
+
+ CompilationUnitRewrite cuRewrite= getCuRewrite(cu);
+ Name[] references= extractReferenceNodes(group.getSearchResults(), cuRewrite.getRoot());
+ InlineTargetCompilationUnit targetCompilationUnit= new InlineTargetCompilationUnit(
+ cuRewrite, references, this, staticImportsInInitializer);
+ CompilationUnitChange change= targetCompilationUnit.getChange();
+ if (change != null)
+ changes.add(change);
+ }
+
+ } else {
+ Assert.isTrue(! isDeclarationSelected());
+ InlineTargetCompilationUnit targetForOnlySelectedReference= new InlineTargetCompilationUnit(
+ fSelectionCuRewrite, new Name[] { fSelectedConstantName }, this, staticImportsInInitializer);
+ CompilationUnitChange change= targetForOnlySelectedReference.getChange();
+ if (change != null)
+ changes.add(change);
+ }
+
+ if (result.hasFatalError())
+ return result;
+
+ if (getRemoveDeclaration() && getReplaceAllReferences()) {
+ boolean declarationRemoved= false;
+ for (Iterator iter= changes.iterator(); iter.hasNext();) {
+ CompilationUnitChange change= iter.next();
+ if (change.getCompilationUnit().equals(fDeclarationCuRewrite.getCu())) {
+ declarationRemoved= true;
+ break;
+ }
+ }
+ if (! declarationRemoved) {
+ InlineTargetCompilationUnit targetForDeclaration= new InlineTargetCompilationUnit(fDeclarationCuRewrite, new Name[0], this, staticImportsInInitializer);
+ CompilationUnitChange change= targetForDeclaration.getChange();
+ if (change != null)
+ changes.add(change);
+ }
+ }
+
+ ICompilationUnit[] cus= new ICompilationUnit[changes.size()];
+ for (int i= 0; i < changes.size(); i++) {
+ CompilationUnitChange change= changes.get(i);
+ cus[i]= change.getCompilationUnit();
+ }
+ result.merge(Checks.validateModifiesFiles(ResourceUtil.getFiles(cus), getValidationContext()));
+
+ pm.worked(1);
+
+ fChanges= changes.toArray(new CompilationUnitChange[changes.size()]);
+
+ return result;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ private Name[] extractReferenceNodes(SearchMatch[] searchResults, CompilationUnit cuNode) {
+ Name[] references= new Name[searchResults.length];
+ for (int i= 0; i < searchResults.length; i++)
+ references[i]= (Name) NodeFinder.perform(cuNode, searchResults[i].getOffset(), searchResults[i].getLength());
+ return references;
+ }
+
+ private CompilationUnitRewrite getCuRewrite(ICompilationUnit cu) {
+ CompilationUnitRewrite cuRewrite;
+ if (cu.equals(fSelectionCu)) {
+ cuRewrite= fSelectionCuRewrite;
+ } else if (cu.equals(fField.getCompilationUnit())) {
+ cuRewrite= fDeclarationCuRewrite;
+ } else {
+ cuRewrite= new CompilationUnitRewrite(cu);
+ }
+ return cuRewrite;
+ }
+
+ private SearchResultGroup[] findReferences(IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
+ final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(fField, IJavaSearchConstants.REFERENCES));
+ engine.setFiltering(true, true);
+ engine.setScope(RefactoringScopeFactory.create(fField));
+ engine.setStatus(status);
+ engine.setRequestor(new IRefactoringSearchRequestor() {
+ @Override
+ public SearchMatch acceptSearchMatch(SearchMatch match) {
+ return match.isInsideDocComment() ? null : match;
+ }
+ });
+ engine.searchPattern(new SubProgressMonitor(pm, 1));
+ return (SearchResultGroup[]) engine.getResults();
+ }
+
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException {
+ try {
+ pm.beginTask(RefactoringCoreMessages.InlineConstantRefactoring_preview, 2);
+ final Map arguments= new HashMap<>();
+ String project= null;
+ IJavaProject javaProject= fSelectionCu.getJavaProject();
+ if (javaProject != null)
+ project= javaProject.getElementName();
+ int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
+ try {
+ if (!Flags.isPrivate(fField.getFlags()))
+ flags|= RefactoringDescriptor.MULTI_CHANGE;
+ } catch (JavaModelException exception) {
+ JavaManipulationPlugin.log(exception);
+ }
+ final String description= Messages.format(RefactoringCoreMessages.InlineConstantRefactoring_descriptor_description_short, JavaElementLabelsCore.getElementLabel(fField, JavaElementLabelsCore.ALL_DEFAULT));
+ final String header= Messages.format(RefactoringCoreMessages.InlineConstantRefactoring_descriptor_description, new String[] { JavaElementLabelsCore.getElementLabel(fField, JavaElementLabelsCore.ALL_FULLY_QUALIFIED), JavaElementLabelsCore.getElementLabel(fField.getParent(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED)});
+ final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
+ comment.addSetting(Messages.format(RefactoringCoreMessages.InlineConstantRefactoring_original_pattern, JavaElementLabelsCore.getElementLabel(fField, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)));
+ if (fRemoveDeclaration)
+ comment.addSetting(RefactoringCoreMessages.InlineConstantRefactoring_remove_declaration);
+ if (fReplaceAllReferences)
+ comment.addSetting(RefactoringCoreMessages.InlineConstantRefactoring_replace_references);
+ final InlineConstantDescriptor descriptor= RefactoringSignatureDescriptorFactory.createInlineConstantDescriptor(project, description, comment.asString(), arguments, flags);
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fSelectionCu));
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + " " + Integer.valueOf(fSelectionLength).toString()); //$NON-NLS-1$
+ arguments.put(ATTRIBUTE_REMOVE, Boolean.valueOf(fRemoveDeclaration).toString());
+ arguments.put(ATTRIBUTE_REPLACE, Boolean.valueOf(fReplaceAllReferences).toString());
+ return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.InlineConstantRefactoring_inline, fChanges);
+ } finally {
+ pm.done();
+ fChanges= null;
+ }
+ }
+
+ private void checkInvariant() {
+ if (isDeclarationSelected())
+ Assert.isTrue(fReplaceAllReferences);
+ }
+
+ public boolean getRemoveDeclaration() {
+ return fRemoveDeclaration;
+ }
+
+ public boolean getReplaceAllReferences() {
+ checkInvariant();
+ return fReplaceAllReferences;
+ }
+
+ public boolean isDeclarationSelected() {
+ Assert.isTrue(fDeclarationSelectedChecked);
+ return fDeclarationSelected;
+ }
+
+ public boolean isInitializerAllStaticFinal() {
+ Assert.isTrue(fInitializerChecked);
+ return fInitializerAllStaticFinal;
+ }
+
+ public void setRemoveDeclaration(boolean removeDeclaration) {
+ fRemoveDeclaration= removeDeclaration;
+ }
+
+ public void setReplaceAllReferences(boolean replaceAllReferences) {
+ fReplaceAllReferences= replaceAllReferences;
+ checkInvariant();
+ }
+
+ private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
+ final String selection= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
+ if (selection != null) {
+ int offset= -1;
+ int length= -1;
+ final StringTokenizer tokenizer= new StringTokenizer(selection);
+ if (tokenizer.hasMoreTokens())
+ offset= Integer.valueOf(tokenizer.nextToken()).intValue();
+ if (tokenizer.hasMoreTokens())
+ length= Integer.valueOf(tokenizer.nextToken()).intValue();
+ if (offset >= 0 && length >= 0) {
+ fSelectionStart= offset;
+ fSelectionLength= length;
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION}));
+ }
+ final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
+ if (handle != null) {
+ final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
+ if (element == null || !element.exists())
+ return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.INLINE_CONSTANT);
+ else {
+ if (element instanceof ICompilationUnit) {
+ fSelectionCu= (ICompilationUnit) element;
+ if (selection == null)
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
+ } else if (element instanceof IField) {
+ final IField field= (IField) element;
+ try {
+ final ISourceRange range= field.getNameRange();
+ if (range != null) {
+ fSelectionStart= range.getOffset();
+ fSelectionLength= range.getLength();
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, IJavaRefactorings.INLINE_CONSTANT));
+ } catch (JavaModelException exception) {
+ return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.INLINE_CONSTANT);
+ }
+ fSelectionCu= field.getCompilationUnit();
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { handle, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT}));
+ final ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
+ parser.setResolveBindings(true);
+ parser.setSource(fSelectionCu);
+ final CompilationUnit unit= (CompilationUnit) parser.createAST(null);
+ initialize(fSelectionCu, unit);
+ if (checkStaticFinalConstantNameSelected().hasFatalError())
+ return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.INLINE_CONSTANT);
+ }
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
+ final String replace= arguments.getAttribute(ATTRIBUTE_REPLACE);
+ if (replace != null) {
+ fReplaceAllReferences= Boolean.valueOf(replace).booleanValue();
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REPLACE));
+ final String remove= arguments.getAttribute(ATTRIBUTE_REMOVE);
+ if (remove != null)
+ fRemoveDeclaration= Boolean.valueOf(remove).booleanValue();
+ else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REMOVE));
+ return new RefactoringStatus();
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,546 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
+ * o inline call that is used in a field initializer
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
+ * o Allow 'this' constructor to be inlined
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextChange;
+import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
+
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.IClassFile;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+import org.eclipse.jdt.core.refactoring.descriptors.InlineMethodDescriptor;
+import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
+
+import org.eclipse.jdt.internal.core.manipulation.BindingLabelProviderCore;
+import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
+import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTesterCore;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext;
+import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
+import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
+import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+/*
+ * Open items:
+ * - generate import statements for newly generated local variable declarations.
+ * - forbid cases like foo(foo(10)) when inlining foo().
+ * - case ref.foo(); and we want to inline foo. Inline a method in a different context;
+ * - optimize code when the method to be inlined returns an argument and that one is
+ * assigned to a parameter again. No need for a separate local (important to be able
+ * to reverse extract method correctly).
+ */
+public class InlineMethodRefactoring extends Refactoring {
+
+ private static final String ATTRIBUTE_MODE= "mode"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_DELETE= "delete"; //$NON-NLS-1$
+
+ public static class Mode {
+ private Mode() {
+ }
+ public static final Mode INLINE_ALL= new Mode();
+ public static final Mode INLINE_SINGLE= new Mode();
+ }
+
+ private ITypeRoot fInitialTypeRoot;
+ private ASTNode fInitialNode;
+ private TextChangeManager fChangeManager;
+ private SourceProvider fSourceProvider;
+ private TargetProvider fTargetProvider;
+ /**
+ * must never be true if fInitialUnit instanceof IClassFile
+ */
+ private boolean fDeleteSource;
+ private Mode fCurrentMode;
+ private Mode fInitialMode;
+ private int fSelectionStart;
+ private int fSelectionLength;
+
+ private InlineMethodRefactoring(ITypeRoot typeRoot, ASTNode node, int offset, int length) {
+ Assert.isNotNull(typeRoot);
+ Assert.isTrue(JavaElementUtil.isSourceAvailable(typeRoot));
+ Assert.isNotNull(node);
+ fInitialTypeRoot= typeRoot;
+ fInitialNode= node;
+ fSelectionStart= offset;
+ fSelectionLength= length;
+ }
+
+ private InlineMethodRefactoring(ICompilationUnit unit, MethodInvocation node, int offset, int length) {
+ this(unit, (ASTNode)node, offset, length);
+ fTargetProvider= TargetProvider.create(unit, node);
+ fInitialMode= fCurrentMode= Mode.INLINE_SINGLE;
+ fDeleteSource= false;
+ }
+
+ private InlineMethodRefactoring(ICompilationUnit unit, SuperMethodInvocation node, int offset, int length) {
+ this(unit, (ASTNode)node, offset, length);
+ fTargetProvider= TargetProvider.create(unit, node);
+ fInitialMode= fCurrentMode= Mode.INLINE_SINGLE;
+ fDeleteSource= false;
+ }
+
+ private InlineMethodRefactoring(ICompilationUnit unit, ConstructorInvocation node, int offset, int length) {
+ this(unit, (ASTNode)node, offset, length);
+ fTargetProvider= TargetProvider.create(unit, node);
+ fInitialMode= fCurrentMode= Mode.INLINE_SINGLE;
+ fDeleteSource= false;
+ }
+
+ private InlineMethodRefactoring(ITypeRoot typeRoot, MethodDeclaration node, int offset, int length) {
+ this(typeRoot, (ASTNode)node, offset, length);
+ fSourceProvider= new SourceProvider(typeRoot, node);
+ fTargetProvider= TargetProvider.create(node);
+ fInitialMode= fCurrentMode= Mode.INLINE_ALL;
+ fDeleteSource= canEnableDeleteSource();
+ }
+
+ /**
+ * Creates a new inline method refactoring
+ * @param unit the compilation unit or class file
+ * @param node the compilation unit node
+ * @param selectionStart start
+ * @param selectionLength length
+ * @return returns the refactoring
+ */
+ public static InlineMethodRefactoring create(ITypeRoot unit, CompilationUnit node, int selectionStart, int selectionLength) {
+ ASTNode target= RefactoringAvailabilityTesterCore.getInlineableMethodNode(unit, node, selectionStart, selectionLength);
+ if (target == null)
+ return null;
+ if (target.getNodeType() == ASTNode.METHOD_DECLARATION) {
+
+ return new InlineMethodRefactoring(unit, (MethodDeclaration)target, selectionStart, selectionLength);
+ } else {
+ ICompilationUnit cu= (ICompilationUnit) unit;
+ if (target.getNodeType() == ASTNode.METHOD_INVOCATION) {
+ return new InlineMethodRefactoring(cu, (MethodInvocation)target, selectionStart, selectionLength);
+ } else if (target.getNodeType() == ASTNode.SUPER_METHOD_INVOCATION) {
+ return new InlineMethodRefactoring(cu, (SuperMethodInvocation)target, selectionStart, selectionLength);
+ } else if (target.getNodeType() == ASTNode.CONSTRUCTOR_INVOCATION) {
+ return new InlineMethodRefactoring(cu, (ConstructorInvocation)target, selectionStart, selectionLength);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return RefactoringCoreMessages.InlineMethodRefactoring_name;
+ }
+
+ /**
+ * Returns the method to inline, or null if the method could not be found or
+ * {@link #checkInitialConditions(IProgressMonitor)} has not been called yet.
+ *
+ * @return the method, or null
+ */
+ public IMethod getMethod() {
+ if (fSourceProvider == null)
+ return null;
+ IMethodBinding binding= fSourceProvider.getDeclaration().resolveBinding();
+ if (binding == null)
+ return null;
+ return (IMethod) binding.getJavaElement();
+ }
+
+ public boolean canEnableDeleteSource() {
+ return ! (fSourceProvider.getTypeRoot() instanceof IClassFile);
+ }
+
+ public boolean getDeleteSource() {
+ return fDeleteSource;
+ }
+
+ public void setDeleteSource(boolean remove) {
+ if (remove)
+ Assert.isTrue(canEnableDeleteSource());
+ fDeleteSource= remove;
+ }
+
+ public Mode getInitialMode() {
+ return fInitialMode;
+ }
+
+ public RefactoringStatus setCurrentMode(Mode mode) throws JavaModelException {
+ if (fCurrentMode == mode)
+ return new RefactoringStatus();
+ Assert.isTrue(getInitialMode() == Mode.INLINE_SINGLE);
+ fCurrentMode= mode;
+ if (mode == Mode.INLINE_SINGLE) {
+ if (fInitialNode instanceof MethodInvocation)
+ fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (MethodInvocation)fInitialNode);
+ else if (fInitialNode instanceof SuperMethodInvocation)
+ fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (SuperMethodInvocation)fInitialNode);
+ else if (fInitialNode instanceof ConstructorInvocation)
+ fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (ConstructorInvocation)fInitialNode);
+ else
+ throw new IllegalStateException(String.valueOf(fInitialNode));
+ } else {
+ fTargetProvider= TargetProvider.create(fSourceProvider.getDeclaration());
+ }
+ return fTargetProvider.checkActivation();
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
+ RefactoringStatus result= new RefactoringStatus();
+ if (fSourceProvider == null && Invocations.isInvocation(fInitialNode)) {
+ fSourceProvider= resolveSourceProvider(result, fInitialTypeRoot, fInitialNode);
+ if (result.hasFatalError())
+ return result;
+ }
+ result.merge(fSourceProvider.checkActivation());
+ result.merge(fTargetProvider.checkActivation());
+ return result;
+ }
+
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
+ pm.beginTask("", 20); //$NON-NLS-1$
+ fChangeManager= new TextChangeManager();
+ RefactoringStatus result= new RefactoringStatus();
+ fSourceProvider.initialize();
+ fTargetProvider.initialize();
+
+ pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_searching);
+ RefactoringStatus searchStatus= new RefactoringStatus();
+ String binaryRefsDescription= Messages.format(RefactoringCoreMessages.ReferencesInBinaryContext_ref_in_binaries_description , BasicElementLabels.getJavaElementName(fSourceProvider.getMethodName()));
+ ReferencesInBinaryContext binaryRefs= new ReferencesInBinaryContext(binaryRefsDescription);
+ ICompilationUnit[] units= fTargetProvider.getAffectedCompilationUnits(searchStatus, binaryRefs, new SubProgressMonitor(pm, 1));
+ binaryRefs.addErrorIfNecessary(searchStatus);
+ if (searchStatus.hasFatalError()) {
+ result.merge(searchStatus);
+ return result;
+ }
+
+ IFile[] filesToBeModified= getFilesToBeModified(units);
+ result.merge(Checks.validateModifiesFiles(filesToBeModified, getValidationContext()));
+ if (result.hasFatalError())
+ return result;
+ result.merge(ResourceChangeChecker.checkFilesToBeChanged(filesToBeModified, new SubProgressMonitor(pm, 1)));
+ checkOverridden(result, new SubProgressMonitor(pm, 4));
+ IProgressMonitor sub= new SubProgressMonitor(pm, 15);
+ sub.beginTask("", units.length * 3); //$NON-NLS-1$
+ for (int c= 0; c < units.length; c++) {
+ ICompilationUnit unit= units[c];
+ sub.subTask(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_processing, BasicElementLabels.getFileName(unit)));
+ CallInliner inliner= null;
+ try {
+ boolean added= false;
+ MultiTextEdit root= new MultiTextEdit();
+ CompilationUnitChange change= (CompilationUnitChange)fChangeManager.get(unit);
+ change.setEdit(root);
+ BodyDeclaration[] bodies= fTargetProvider.getAffectedBodyDeclarations(unit, new SubProgressMonitor(pm, 1));
+ if (bodies.length == 0)
+ continue;
+ inliner= new CallInliner(unit, (CompilationUnit) bodies[0].getRoot(), fSourceProvider);
+ for (int b= 0; b < bodies.length; b++) {
+ BodyDeclaration body= bodies[b];
+ inliner.initialize(body);
+ RefactoringStatus nestedInvocations= new RefactoringStatus();
+ ASTNode[] invocations= removeNestedCalls(nestedInvocations, unit,
+ fTargetProvider.getInvocations(body, new SubProgressMonitor(sub, 2)));
+ for (int i= 0; i < invocations.length; i++) {
+ ASTNode invocation= invocations[i];
+ result.merge(inliner.initialize(invocation, fTargetProvider.getStatusSeverity()));
+ if (result.hasFatalError())
+ break;
+ if (result.getSeverity() < fTargetProvider.getStatusSeverity()) {
+ added= true;
+ TextEditGroup group= new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_inline);
+ change.addTextEditGroup(group);
+ result.merge(inliner.perform(group));
+ } else {
+ fDeleteSource= false;
+ }
+ }
+ // do this after we have inlined the method calls. We still want
+ // to generate the modifications.
+ if (!nestedInvocations.isOK()) {
+ result.merge(nestedInvocations);
+ fDeleteSource= false;
+ }
+ }
+ if (!added) {
+ fChangeManager.remove(unit);
+ } else {
+ root.addChild(inliner.getModifications());
+ ImportRewrite rewrite= inliner.getImportEdit();
+ if (rewrite.hasRecordedChanges()) {
+ TextEdit edit= rewrite.rewriteImports(null);
+ if (edit instanceof MultiTextEdit ? ((MultiTextEdit)edit).getChildrenSize() > 0 : true) {
+ root.addChild(edit);
+ change.addTextEditGroup(
+ new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_import, new TextEdit[] {edit}));
+ }
+ }
+ }
+ } finally {
+ if (inliner != null)
+ inliner.dispose();
+ }
+ sub.worked(1);
+ if (sub.isCanceled())
+ throw new OperationCanceledException();
+ }
+ result.merge(searchStatus);
+ sub.done();
+ pm.done();
+ return result;
+ }
+
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException {
+ if (fDeleteSource && fCurrentMode == Mode.INLINE_ALL) {
+ TextChange change= fChangeManager.get((ICompilationUnit) fSourceProvider.getTypeRoot());
+ TextEdit delete= fSourceProvider.getDeleteEdit();
+ TextEditGroup description= new TextEditGroup(
+ RefactoringCoreMessages.InlineMethodRefactoring_edit_delete, new TextEdit[] { delete });
+ TextEdit root= change.getEdit();
+ if (root != null) {
+ // TODO instead of finding the right insert position the call inliner should
+ // reuse the AST & rewriter of the source provide and we should rewrite the
+ // whole AST at the end. However, since recursive calls aren't allowed there
+ // shouldn't be a text edit overlap.
+ // root.addChild(delete);
+ TextChangeCompatibility.insert(root, delete);
+ } else {
+ change.setEdit(delete);
+ }
+ change.addTextEditGroup(description);
+ }
+ final Map arguments= new HashMap<>();
+ String project= null;
+ IJavaProject javaProject= fInitialTypeRoot.getJavaProject();
+ if (javaProject != null)
+ project= javaProject.getElementName();
+ int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
+ final IMethodBinding binding= fSourceProvider.getDeclaration().resolveBinding();
+ final ITypeBinding declaring= binding.getDeclaringClass();
+ if (!Modifier.isPrivate(binding.getModifiers()))
+ flags|= RefactoringDescriptor.MULTI_CHANGE;
+ final String description= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(binding.getName()));
+ final String header= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description, new String[] { BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED), BindingLabelProviderCore.getBindingLabel(declaring, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)});
+ final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
+ comment.addSetting(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_original_pattern, BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)));
+ if (fDeleteSource)
+ comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_remove_method);
+ if (fCurrentMode == Mode.INLINE_ALL)
+ comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_replace_references);
+ final InlineMethodDescriptor descriptor= RefactoringSignatureDescriptorFactory.createInlineMethodDescriptor(project, description, comment.asString(), arguments, flags);
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fInitialTypeRoot));
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + " " + Integer.valueOf(fSelectionLength).toString()); //$NON-NLS-1$
+ arguments.put(ATTRIBUTE_DELETE, Boolean.valueOf(fDeleteSource).toString());
+ arguments.put(ATTRIBUTE_MODE, Integer.valueOf(fCurrentMode == Mode.INLINE_ALL ? 1 : 0).toString());
+ return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.InlineMethodRefactoring_edit_inlineCall, fChangeManager.getAllChanges());
+ }
+
+ private static SourceProvider resolveSourceProvider(RefactoringStatus status, ITypeRoot typeRoot, ASTNode invocation) {
+ CompilationUnit root= (CompilationUnit)invocation.getRoot();
+ IMethodBinding methodBinding= Invocations.resolveBinding(invocation);
+ if (methodBinding == null) {
+ status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration);
+ return null;
+ }
+ MethodDeclaration declaration= (MethodDeclaration)root.findDeclaringNode(methodBinding);
+ if (declaration != null) {
+ return new SourceProvider(typeRoot, declaration);
+ }
+ IMethod method= (IMethod)methodBinding.getJavaElement();
+ if (method != null) {
+ CompilationUnit methodDeclarationAstRoot;
+ ICompilationUnit methodCu= method.getCompilationUnit();
+ if (methodCu != null) {
+ methodDeclarationAstRoot= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL).parse(methodCu, true);
+ } else {
+ IClassFile classFile= method.getClassFile();
+ if (! JavaElementUtil.isSourceAvailable(classFile)) {
+ String methodLabel= JavaElementLabelsCore.getTextLabel(method, JavaElementLabelsCore.M_FULLY_QUALIFIED | JavaElementLabelsCore.M_PARAMETER_TYPES);
+ status.addFatalError(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_error_classFile, methodLabel));
+ return null;
+ }
+ methodDeclarationAstRoot= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL).parse(classFile, true);
+ }
+ ASTNode node= methodDeclarationAstRoot.findDeclaringNode(methodBinding.getMethodDeclaration().getKey());
+ if (node instanceof MethodDeclaration) {
+ return new SourceProvider(methodDeclarationAstRoot.getTypeRoot(), (MethodDeclaration) node);
+ }
+ }
+ status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration);
+ return null;
+ }
+
+ private IFile[] getFilesToBeModified(ICompilationUnit[] units) {
+ List result= new ArrayList<>(units.length + 1);
+ IFile file;
+ for (int i= 0; i < units.length; i++) {
+ file= getFile(units[i]);
+ if (file != null)
+ result.add(file);
+ }
+ if (fDeleteSource) {
+ file= getFile((ICompilationUnit) fSourceProvider.getTypeRoot());
+ if (file != null && !result.contains(file))
+ result.add(file);
+ }
+ return result.toArray(new IFile[result.size()]);
+ }
+
+ private IFile getFile(ICompilationUnit unit) {
+ unit= unit.getPrimary();
+ IResource resource= unit.getResource();
+ if (resource != null && resource.getType() == IResource.FILE)
+ return (IFile)resource;
+ return null;
+ }
+
+ private void checkOverridden(RefactoringStatus status, IProgressMonitor pm) throws JavaModelException {
+ pm.beginTask("", 9); //$NON-NLS-1$
+ pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden);
+ MethodDeclaration decl= fSourceProvider.getDeclaration();
+ IMethod method= (IMethod) decl.resolveBinding().getJavaElement();
+ if (method == null || Flags.isPrivate(method.getFlags())) {
+ pm.worked(8);
+ return;
+ }
+ IType type= method.getDeclaringType();
+ ITypeHierarchy hierarchy= type.newTypeHierarchy(new SubProgressMonitor(pm, 6));
+ checkSubTypes(status, method, hierarchy.getAllSubtypes(type), new SubProgressMonitor(pm, 1));
+ checkSuperClasses(status, method, hierarchy.getAllSuperclasses(type), new SubProgressMonitor(pm, 1));
+ checkSuperInterfaces(status, method, hierarchy.getAllSuperInterfaces(type), new SubProgressMonitor(pm, 1));
+ pm.setTaskName(""); //$NON-NLS-1$
+ }
+
+ private void checkSubTypes(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) {
+ checkTypes(
+ result, method, types,
+ RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden_error,
+ pm);
+ }
+
+ private void checkSuperClasses(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) {
+ checkTypes(
+ result, method, types,
+ RefactoringCoreMessages.InlineMethodRefactoring_checking_overrides_error,
+ pm);
+ }
+
+ private void checkSuperInterfaces(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) {
+ checkTypes(
+ result, method, types,
+ RefactoringCoreMessages.InlineMethodRefactoring_checking_implements_error,
+ pm);
+ }
+ private void checkTypes(RefactoringStatus result, IMethod method, IType[] types, String key, IProgressMonitor pm) {
+ pm.beginTask("", types.length); //$NON-NLS-1$
+ for (int i= 0; i < types.length; i++) {
+ pm.worked(1);
+ IMethod[] overridden= types[i].findMethods(method);
+ if (overridden != null && overridden.length > 0) {
+ result.addError(
+ Messages.format(key, JavaElementLabelsCore.getElementLabel(types[i], JavaElementLabelsCore.ALL_DEFAULT)),
+ JavaStatusContext.create(overridden[0]));
+ }
+ }
+ }
+
+ private ASTNode[] removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] invocations) {
+ if (invocations.length <= 1)
+ return invocations;
+ ASTNode[] parents= new ASTNode[invocations.length];
+ for (int i= 0; i < invocations.length; i++) {
+ parents[i]= invocations[i].getParent();
+ }
+ for (int i= 0; i < invocations.length; i++) {
+ removeNestedCalls(status, unit, parents, invocations, i);
+ }
+ List result= new ArrayList<>();
+ for (int i= 0; i < invocations.length; i++) {
+ if (invocations[i] != null)
+ result.add(invocations[i]);
+ }
+ return result.toArray(new ASTNode[result.size()]);
+ }
+
+ private void removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] parents, ASTNode[] invocations, int index) {
+ ASTNode invocation= invocations[index];
+ for (int i= 0; i < parents.length; i++) {
+ ASTNode parent= parents[i];
+ while (parent != null) {
+ if (parent == invocation) {
+ status.addError(RefactoringCoreMessages.InlineMethodRefactoring_nestedInvocation,
+ JavaStatusContext.create(unit, parent));
+ invocations[index]= null;
+ }
+ parent= parent.getParent();
+ }
+ }
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineTempRefactoring.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineTempRefactoring.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineTempRefactoring.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/InlineTempRefactoring.java" 2020-02-26 15:31:57.000000000 +0000
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,8 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pierre-Yves B. - [inline] Allow inlining of local variable initialized to null. - https://bugs.eclipse.org/93850
+ * Microsoft Corporation - copied to jdt.core.manipulation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.code;
@@ -97,6 +99,7 @@
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.rename.RefactoringAnalyzeUtil;
import org.eclipse.jdt.internal.corext.refactoring.rename.TempDeclarationFinder;
import org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
@@ -116,6 +119,8 @@
private VariableDeclaration fVariableDeclaration;
private SimpleName[] fReferences;
private CompilationUnit fASTRoot;
+ private boolean fCheckResultForCompileProblems;
+ private CompilationUnitChange fChange;
/**
* Creates a new inline constant refactoring.
@@ -133,6 +138,8 @@
fASTRoot= node;
fVariableDeclaration= null;
+ fCheckResultForCompileProblems= true;
+ fChange= null;
}
/**
@@ -155,6 +162,8 @@
fSelectionStart= decl.getStartPosition();
fSelectionLength= decl.getLength();
fCu= (ICompilationUnit) fASTRoot.getJavaElement();
+ fCheckResultForCompileProblems= true;
+ fChange= null;
}
public InlineTempRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
@@ -163,6 +172,9 @@
status.merge(initializeStatus);
}
+ public void setCheckResultForCompileProblems(boolean checkResultForCompileProblems) {
+ fCheckResultForCompileProblems = checkResultForCompileProblems;
+ }
public RefactoringStatus checkIfTempSelected() {
VariableDeclaration decl= getVariableDeclaration();
@@ -211,22 +223,13 @@
VariableDeclaration declaration= getVariableDeclaration();
result.merge(checkSelection(selected, declaration));
- if (result.hasFatalError())
- return result;
- result.merge(checkInitializer(declaration));
return result;
} finally {
pm.done();
}
}
- private RefactoringStatus checkInitializer(VariableDeclaration decl) {
- if (decl.getInitializer().getNodeType() == ASTNode.NULL_LITERAL)
- return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InlineTemRefactoring_error_message_nulLiteralsCannotBeInlined);
- return null;
- }
-
private RefactoringStatus checkSelection(ASTNode selectedNode, VariableDeclaration decl) {
ASTNode parent= decl.getParent();
if (parent instanceof MethodDeclaration) {
@@ -283,7 +286,14 @@
public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
try {
pm.beginTask("", 1); //$NON-NLS-1$
- return new RefactoringStatus();
+ CompilationUnitRewrite cuRewrite= new CompilationUnitRewrite(fCu, fASTRoot);
+
+ inlineTemp(cuRewrite);
+ removeTemp(cuRewrite);
+
+ fChange= cuRewrite.createChange(RefactoringCoreMessages.InlineTempRefactoring_inline, false, new SubProgressMonitor(pm, 1));
+
+ return fCheckResultForCompileProblems ? RefactoringAnalyzeUtil.checkNewSource(fChange, fCu, fASTRoot, pm) : new RefactoringStatus();
} finally {
pm.done();
}
@@ -295,48 +305,44 @@
public Change createChange(IProgressMonitor pm) throws CoreException {
try {
pm.beginTask(RefactoringCoreMessages.InlineTempRefactoring_preview, 2);
- final Map arguments= new HashMap<>();
- String project= null;
- IJavaProject javaProject= fCu.getJavaProject();
- if (javaProject != null)
- project= javaProject.getElementName();
-
- final IVariableBinding binding= getVariableDeclaration().resolveBinding();
- String text= null;
- final IMethodBinding method= binding.getDeclaringMethod();
- if (method != null)
- text= BindingLabelProviderCore.getBindingLabel(method, JavaElementLabelsCore.ALL_FULLY_QUALIFIED);
- else
- text= BasicElementLabels.getJavaElementName('{' + JavaElementLabelsCore.ELLIPSIS_STRING + '}');
- final String description= Messages.format(RefactoringCoreMessages.InlineTempRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(binding.getName()));
- final String header= Messages.format(RefactoringCoreMessages.InlineTempRefactoring_descriptor_description, new String[] { BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED), text});
- final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
- comment.addSetting(Messages.format(RefactoringCoreMessages.InlineTempRefactoring_original_pattern, BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)));
- final InlineLocalVariableDescriptor descriptor= RefactoringSignatureDescriptorFactory.createInlineLocalVariableDescriptor(project, description, comment.asString(), arguments, RefactoringDescriptor.NONE);
- arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fCu));
- arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, String.valueOf(fSelectionStart) + ' ' + String.valueOf(fSelectionLength));
-
- CompilationUnitRewrite cuRewrite= new CompilationUnitRewrite(fCu, fASTRoot);
- inlineTemp(cuRewrite);
- removeTemp(cuRewrite);
-
- final CompilationUnitChange result= cuRewrite.createChange(RefactoringCoreMessages.InlineTempRefactoring_inline, false, new SubProgressMonitor(pm, 1));
- result.setDescriptor(new RefactoringChangeDescriptor(descriptor));
- return result;
+ final InlineLocalVariableDescriptor descriptor= createRefactoringDescriptor();
+ fChange.setDescriptor(new RefactoringChangeDescriptor(descriptor));
+ return fChange;
} finally {
pm.done();
}
}
- private void inlineTemp(CompilationUnitRewrite cuRewrite) throws JavaModelException {
- SimpleName[] references= getReferences();
+ private InlineLocalVariableDescriptor createRefactoringDescriptor() {
+ final Map arguments= new HashMap<>();
+ String project= null;
+ IJavaProject javaProject= fCu.getJavaProject();
+ if (javaProject != null)
+ project= javaProject.getElementName();
+
+ final IVariableBinding binding= getVariableDeclaration().resolveBinding();
+ String text= null;
+ final IMethodBinding method= binding.getDeclaringMethod();
+ if (method != null)
+ text= BindingLabelProviderCore.getBindingLabel(method, JavaElementLabelsCore.ALL_FULLY_QUALIFIED);
+ else
+ text= BasicElementLabels.getJavaElementName('{' + JavaElementLabelsCore.ELLIPSIS_STRING + '}');
+ final String description= Messages.format(RefactoringCoreMessages.InlineTempRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(binding.getName()));
+ final String header= Messages.format(RefactoringCoreMessages.InlineTempRefactoring_descriptor_description, new String[] { BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED), text});
+ final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
+ comment.addSetting(Messages.format(RefactoringCoreMessages.InlineTempRefactoring_original_pattern, BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)));
+ final InlineLocalVariableDescriptor descriptor= RefactoringSignatureDescriptorFactory.createInlineLocalVariableDescriptor(project, description, comment.asString(), arguments, RefactoringDescriptor.NONE);
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fCu));
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, String.valueOf(fSelectionStart) + ' ' + String.valueOf(fSelectionLength));
+ return descriptor;
+ }
+ private void inlineTemp(CompilationUnitRewrite cuRewrite) throws JavaModelException {
TextEditGroup groupDesc= cuRewrite.createGroupDescription(RefactoringCoreMessages.InlineTempRefactoring_inline_edit_name);
ASTRewrite rewrite= cuRewrite.getASTRewrite();
- for (int i= 0; i < references.length; i++){
- SimpleName curr= references[i];
+ for (SimpleName curr : getReferences()) {
ASTNode initializerCopy= getInitializerSource(cuRewrite, curr);
rewrite.replace(curr, initializerCopy, groupDesc);
}
@@ -384,7 +390,7 @@
}
}
}
-
+
Expression copy= (Expression) rewrite.getASTRewrite().createCopyTarget(initializer);
AST ast= rewrite.getAST();
if (NecessaryParenthesesChecker.needsParentheses(initializer, reference.getParent(), reference.getLocationInParent())) {
@@ -392,7 +398,7 @@
parenthesized.setExpression(copy);
copy= parenthesized;
}
-
+
ITypeBinding explicitCast= ASTNodes.getExplicitCast(initializer, reference);
if (explicitCast != null) {
CastExpression cast= ast.newCastExpression();
@@ -405,7 +411,7 @@
ImportRewriteContext context= new ContextSensitiveImportRewriteContext(reference, rewrite.getImportRewrite());
cast.setType(rewrite.getImportRewrite().addImport(explicitCast, ast, context, TypeLocation.CAST));
copy= cast;
-
+
} else if (initializer instanceof ArrayInitializer && ASTNodes.getDimensions(varDecl) > 0) {
ArrayType newType= (ArrayType) ASTNodeFactory.newType(ast, varDecl);
@@ -420,12 +426,12 @@
private String createParameterizedInvocation(Expression invocation, ITypeBinding[] typeArguments, CompilationUnitRewrite cuRewrite) throws JavaModelException {
ASTRewrite rewrite= ASTRewrite.create(invocation.getAST());
ListRewrite typeArgsRewrite= Invocations.getInferredTypeArgumentsRewrite(rewrite, invocation);
-
- for (int i= 0; i < typeArguments.length; i++) {
- Type typeArgumentNode= cuRewrite.getImportRewrite().addImport(typeArguments[i], cuRewrite.getAST());
+
+ for (ITypeBinding typeArgument : typeArguments) {
+ Type typeArgumentNode = cuRewrite.getImportRewrite().addImport(typeArgument, cuRewrite.getAST());
typeArgsRewrite.insertLast(typeArgumentNode, null);
}
-
+
if (invocation instanceof MethodInvocation) {
MethodInvocation methodInvocation= (MethodInvocation) invocation;
Expression expression= methodInvocation.getExpression();
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/Invocations.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/Invocations.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/Invocations.java" 2019-06-01 21:39:59.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/Invocations.java" 2020-02-26 15:31:57.000000000 +0000
@@ -42,17 +42,17 @@
return ((MethodInvocation)invocation).arguments();
case ASTNode.SUPER_METHOD_INVOCATION:
return ((SuperMethodInvocation)invocation).arguments();
-
+
case ASTNode.CONSTRUCTOR_INVOCATION:
return ((ConstructorInvocation)invocation).arguments();
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
return ((SuperConstructorInvocation)invocation).arguments();
-
+
case ASTNode.CLASS_INSTANCE_CREATION:
return ((ClassInstanceCreation)invocation).arguments();
case ASTNode.ENUM_CONSTANT_DECLARATION:
return ((EnumConstantDeclaration)invocation).arguments();
-
+
default:
throw new IllegalArgumentException(invocation.toString());
}
@@ -64,39 +64,39 @@
return MethodInvocation.ARGUMENTS_PROPERTY;
case ASTNode.SUPER_METHOD_INVOCATION:
return SuperMethodInvocation.ARGUMENTS_PROPERTY;
-
+
case ASTNode.CONSTRUCTOR_INVOCATION:
return ConstructorInvocation.ARGUMENTS_PROPERTY;
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
return SuperConstructorInvocation.ARGUMENTS_PROPERTY;
-
+
case ASTNode.CLASS_INSTANCE_CREATION:
return ClassInstanceCreation.ARGUMENTS_PROPERTY;
case ASTNode.ENUM_CONSTANT_DECLARATION:
return EnumConstantDeclaration.ARGUMENTS_PROPERTY;
-
+
default:
throw new IllegalArgumentException(invocation.toString());
}
}
-
+
public static Expression getExpression(ASTNode invocation) {
switch (invocation.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
return ((MethodInvocation)invocation).getExpression();
case ASTNode.SUPER_METHOD_INVOCATION:
return null;
-
+
case ASTNode.CONSTRUCTOR_INVOCATION:
return null;
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
return ((SuperConstructorInvocation)invocation).getExpression();
-
+
case ASTNode.CLASS_INSTANCE_CREATION:
return ((ClassInstanceCreation)invocation).getExpression();
case ASTNode.ENUM_CONSTANT_DECLARATION:
return null;
-
+
default:
throw new IllegalArgumentException(invocation.toString());
}
@@ -107,19 +107,19 @@
return type == ASTNode.METHOD_INVOCATION || type == ASTNode.SUPER_METHOD_INVOCATION ||
type == ASTNode.CONSTRUCTOR_INVOCATION;
}
-
+
public static boolean isInvocationWithArguments(ASTNode node) {
switch (node.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
case ASTNode.SUPER_METHOD_INVOCATION:
-
+
case ASTNode.CONSTRUCTOR_INVOCATION:
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
-
+
case ASTNode.CLASS_INSTANCE_CREATION:
case ASTNode.ENUM_CONSTANT_DECLARATION:
return true;
-
+
default:
return false;
}
@@ -131,17 +131,17 @@
return ((MethodInvocation)invocation).resolveMethodBinding();
case ASTNode.SUPER_METHOD_INVOCATION:
return ((SuperMethodInvocation)invocation).resolveMethodBinding();
-
+
case ASTNode.CONSTRUCTOR_INVOCATION:
return ((ConstructorInvocation)invocation).resolveConstructorBinding();
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
return ((SuperConstructorInvocation)invocation).resolveConstructorBinding();
-
+
case ASTNode.CLASS_INSTANCE_CREATION:
return ((ClassInstanceCreation)invocation).resolveConstructorBinding();
case ASTNode.ENUM_CONSTANT_DECLARATION:
return ((EnumConstantDeclaration)invocation).resolveConstructorBinding();
-
+
default:
throw new IllegalArgumentException(invocation.toString());
}
@@ -150,7 +150,7 @@
public static boolean isResolvedTypeInferredFromExpectedType(Expression invocation) {
if (invocation == null)
return false;
-
+
switch (invocation.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
return ((MethodInvocation) invocation).isResolvedTypeInferredFromExpectedType();
@@ -158,12 +158,12 @@
return ((SuperMethodInvocation) invocation).isResolvedTypeInferredFromExpectedType();
case ASTNode.CLASS_INSTANCE_CREATION:
return ((ClassInstanceCreation) invocation).isResolvedTypeInferredFromExpectedType();
-
+
default:
return false;
}
}
-
+
public static ListRewrite getInferredTypeArgumentsRewrite(ASTRewrite rewrite, Expression invocation) {
switch (invocation.getNodeType()) {
case ASTNode.METHOD_INVOCATION:
@@ -173,7 +173,7 @@
case ASTNode.CLASS_INSTANCE_CREATION:
Type type= ((ClassInstanceCreation) invocation).getType();
return rewrite.getListRewrite(type, ParameterizedType.TYPE_ARGUMENTS_PROPERTY);
-
+
default:
throw new IllegalArgumentException(invocation.toString());
}
@@ -192,7 +192,7 @@
Type type= ((ClassInstanceCreation) invocation).getType();
ITypeBinding typeBinding= type.resolveBinding();
return typeBinding == null ? null : typeBinding.getTypeArguments();
-
+
default:
throw new IllegalArgumentException(invocation.toString());
}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/NameCollector.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/NameCollector.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/NameCollector.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/NameCollector.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+import org.eclipse.jdt.internal.corext.dom.Selection;
+
+class NameCollector extends GenericVisitor {
+ private List names= new ArrayList<>();
+ private Selection fSelection;
+ public NameCollector(ASTNode node) {
+ fSelection= Selection.createFromStartLength(node.getStartPosition(), node.getLength());
+ }
+ @Override
+ protected boolean visitNode(ASTNode node) {
+ if (node.getStartPosition() > fSelection.getInclusiveEnd())
+ return true;
+ if (fSelection.coveredBy(node))
+ return true;
+ return false;
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ names.add(node.getIdentifier());
+ return super.visit(node);
+ }
+ @Override
+ public boolean visit(VariableDeclarationStatement node) {
+ return true;
+ }
+ @Override
+ public boolean visit(VariableDeclarationFragment node) {
+ boolean result= super.visit(node);
+ if (!result)
+ names.add(node.getName().getIdentifier());
+ return result;
+ }
+ @Override
+ public boolean visit(SingleVariableDeclaration node) {
+ boolean result= super.visit(node);
+ if (!result)
+ names.add(node.getName().getIdentifier());
+ return result;
+ }
+ @Override
+ public boolean visit(TypeDeclarationStatement node) {
+ names.add(node.getDeclaration().getName().getIdentifier());
+ return false;
+ }
+
+ List getNames() {
+ return names;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ParameterData.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ParameterData.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ParameterData.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/ParameterData.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Nikolay Metchev - Anonymous class using final parameter breaks method inlining - https://bugs.eclipse.org/269401
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
+
+/* package */ class ParameterData {
+
+ public static final String PROPERTY= ParameterData.class.getName();
+
+ private SingleVariableDeclaration fDeclaration;
+ private int fAccessMode;
+ private List fReferences;
+ private int fOperatorPrecedence;
+
+ public ParameterData(SingleVariableDeclaration decl) {
+ super();
+ fDeclaration= decl;
+ fAccessMode= FlowInfo.UNUSED;
+ fReferences= new ArrayList<>(2);
+ fOperatorPrecedence= -1;
+ }
+
+ public String getName() {
+ return fDeclaration.getName().getIdentifier();
+ }
+
+ public ITypeBinding getTypeBinding() {
+ return fDeclaration.resolveBinding().getType();
+ }
+
+ public void addReference(SimpleName node) {
+ fReferences.add(node);
+ }
+
+ public List references() {
+ return fReferences;
+ }
+
+ public void setAccessMode(int mode) {
+ fAccessMode= mode;
+ }
+
+ public boolean isUnused() {
+ return fAccessMode == FlowInfo.UNUSED;
+ }
+
+ public boolean isFinal() {
+ return Modifier.isFinal(fDeclaration.getModifiers());
+ }
+
+ public boolean isReadOnly() {
+ return (fAccessMode & (FlowInfo.READ | FlowInfo.READ_POTENTIAL)) != 0;
+ }
+
+ public boolean isWrite() {
+ return (fAccessMode & (FlowInfo.WRITE | FlowInfo.WRITE_POTENTIAL | FlowInfo.UNKNOWN)) != 0;
+ }
+
+ public int getSimplifiedAccessMode() {
+ if (isWrite())
+ return FlowInfo.WRITE;
+ if (isReadOnly())
+ return FlowInfo.READ;
+ return FlowInfo.UNUSED;
+ }
+
+ public int getNumberOfAccesses() {
+ return fReferences.size();
+ }
+
+ public boolean needsEvaluation() {
+ if (fReferences.size() <= 1)
+ return false;
+ return true;
+ }
+
+ public void setOperatorPrecedence(int newValue) {
+ if (newValue == -1) {
+ fOperatorPrecedence= newValue;
+ } else if (fOperatorPrecedence == -1) {
+ fOperatorPrecedence= newValue;
+ } else {
+ fOperatorPrecedence= Math.min(fOperatorPrecedence, newValue);
+ }
+ }
+
+ public int getOperatorPrecedence() {
+ return fOperatorPrecedence;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/PromoteTempToFieldRefactoring.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/PromoteTempToFieldRefactoring.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/PromoteTempToFieldRefactoring.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/PromoteTempToFieldRefactoring.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,1023 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.text.edits.TextEdit;
+
+import org.eclipse.jface.text.Document;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.Dimension;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.core.manipulation.CodeGeneration;
+import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
+import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
+import org.eclipse.jdt.core.refactoring.descriptors.ConvertLocalVariableDescriptor;
+
+import org.eclipse.jdt.internal.core.manipulation.BindingLabelProviderCore;
+import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.DimensionRewrite;
+import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
+import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalModelCore;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroupCore;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
+import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
+import org.eclipse.jdt.internal.corext.refactoring.rename.TempDeclarationFinder;
+import org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
+import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+public class PromoteTempToFieldRefactoring extends Refactoring {
+
+ private static final String ATTRIBUTE_STATIC= "static"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_FINAL= "final"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_INITIALIZE= "initialize"; //$NON-NLS-1$
+
+ private int fSelectionStart;
+ private int fSelectionLength;
+ private ICompilationUnit fCu;
+
+ public static final int INITIALIZE_IN_FIELD= 0;
+ public static final int INITIALIZE_IN_METHOD= 1;
+ public static final int INITIALIZE_IN_CONSTRUCTOR= 2;
+
+ private static final String LINKED_NAME= "name"; //$NON-NLS-1$
+
+ //------ settings ---------//
+ private String fFieldName;
+ private int fVisibility; /*see Modifier*/
+ private boolean fDeclareStatic;
+ private boolean fDeclareFinal;
+ private int fInitializeIn; /*see INITIALIZE_IN_* constraints */
+
+ //------ fields used for computations ---------//
+ private CompilationUnit fCompilationUnitNode;
+ private VariableDeclaration fTempDeclarationNode;
+ //------ analysis ---------//
+ private boolean fInitializerUsesLocalTypes;
+ private boolean fTempTypeUsesClassTypeVariables;
+ //------ scripting --------//
+ private boolean fSelfInitializing= false;
+ private LinkedProposalModelCore fLinkedProposalModel;
+
+ private Map fFormatterOptions;
+
+ /**
+ * Creates a new promote temp to field refactoring.
+ * @param unit the compilation unit, or null
if invoked by scripting
+ * @param selectionStart start
+ * @param selectionLength length
+ */
+ public PromoteTempToFieldRefactoring(ICompilationUnit unit, int selectionStart, int selectionLength){
+ Assert.isTrue(selectionStart >= 0);
+ Assert.isTrue(selectionLength >= 0);
+ fSelectionStart= selectionStart;
+ fSelectionLength= selectionLength;
+ fCu= unit;
+
+ fFieldName= ""; //$NON-NLS-1$
+ fVisibility= Modifier.PRIVATE;
+ fDeclareStatic= false;
+ fDeclareFinal= false;
+ fInitializeIn= INITIALIZE_IN_METHOD;
+ fLinkedProposalModel= null;
+ }
+
+ /**
+ * Creates a new promote temp to field refactoring.
+ * @param declaration the variable declaration node to convert to a field
+ */
+ public PromoteTempToFieldRefactoring(VariableDeclaration declaration) {
+ Assert.isTrue(declaration != null);
+ fTempDeclarationNode= declaration;
+ IVariableBinding resolveBinding= declaration.resolveBinding();
+ Assert.isTrue(resolveBinding != null && !resolveBinding.isParameter() && !resolveBinding.isField());
+
+ ASTNode root= declaration.getRoot();
+ Assert.isTrue(root instanceof CompilationUnit);
+ fCompilationUnitNode= (CompilationUnit) root;
+
+ IJavaElement input= fCompilationUnitNode.getJavaElement();
+ Assert.isTrue(input instanceof ICompilationUnit);
+ fCu= (ICompilationUnit) input;
+
+ fSelectionStart= declaration.getStartPosition();
+ fSelectionLength= declaration.getLength();
+
+ fFieldName= ""; //$NON-NLS-1$
+ fVisibility= Modifier.PRIVATE;
+ fDeclareStatic= false;
+ fDeclareFinal= false;
+ fInitializeIn= INITIALIZE_IN_METHOD;
+ fLinkedProposalModel= null;
+ }
+
+ public PromoteTempToFieldRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
+ this(null);
+ RefactoringStatus initializeStatus= initialize(arguments);
+ status.merge(initializeStatus);
+ }
+
+ @Override
+ public String getName() {
+ return RefactoringCoreMessages.PromoteTempToFieldRefactoring_name;
+ }
+
+ public int[] getAvailableVisibilities(){
+ return new int[]{Modifier.PUBLIC, Modifier.PROTECTED, Modifier.NONE, Modifier.PRIVATE};
+ }
+
+ public int getVisibility() {
+ return fVisibility;
+ }
+
+ public boolean getDeclareFinal() {
+ return fDeclareFinal;
+ }
+
+ public boolean getDeclareStatic() {
+ return fDeclareStatic;
+ }
+
+ public int getInitializeIn() {
+ return fInitializeIn;
+ }
+
+ public void setVisibility(int accessModifier) {
+ Assert.isTrue(accessModifier == Modifier.PRIVATE ||
+ accessModifier == Modifier.NONE ||
+ accessModifier == Modifier.PROTECTED ||
+ accessModifier == Modifier.PUBLIC);
+ fVisibility= accessModifier;
+ }
+
+ public void setDeclareFinal(boolean declareFinal) {
+ fDeclareFinal= declareFinal;
+ }
+
+ public void setDeclareStatic(boolean declareStatic) {
+ fDeclareStatic= declareStatic;
+ }
+
+ public void setFieldName(String fieldName) {
+ Assert.isNotNull(fieldName);
+ fFieldName= fieldName;
+ }
+
+ public void setInitializeIn(int initializeIn) {
+ Assert.isTrue( initializeIn == INITIALIZE_IN_CONSTRUCTOR ||
+ initializeIn == INITIALIZE_IN_FIELD ||
+ initializeIn == INITIALIZE_IN_METHOD);
+ fInitializeIn= initializeIn;
+ }
+
+ public boolean canEnableSettingStatic(){
+ return fInitializeIn != INITIALIZE_IN_CONSTRUCTOR &&
+ ! isTempDeclaredInStaticMethod() &&
+ ! fTempTypeUsesClassTypeVariables;
+ }
+
+ public boolean canEnableSettingFinal(){
+ if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR)
+ return canEnableSettingDeclareInConstructors() && ! tempHasAssignmentsOtherThanInitialization();
+ else if (fInitializeIn == INITIALIZE_IN_FIELD)
+ return canEnableSettingDeclareInFieldDeclaration() && ! tempHasAssignmentsOtherThanInitialization();
+ else if (getMethodDeclaration().isConstructor())
+ return !tempHasAssignmentsOtherThanInitialization();
+ else
+ return false;
+ }
+
+ private boolean tempHasAssignmentsOtherThanInitialization() {
+ TempAssignmentFinder assignmentFinder= new TempAssignmentFinder(fTempDeclarationNode);
+ fCompilationUnitNode.accept(assignmentFinder);
+ return assignmentFinder.hasAssignments();
+ }
+
+ public boolean canEnableSettingDeclareInConstructors(){
+ return ! fDeclareStatic &&
+ ! fInitializerUsesLocalTypes &&
+ ! getMethodDeclaration().isConstructor() &&
+ ! isDeclaredInAnonymousClass() &&
+ ! isTempDeclaredInStaticMethod() &&
+ tempHasInitializer();
+ }
+
+ public boolean canEnableSettingDeclareInMethod(){
+ return ! fDeclareFinal &&
+ tempHasInitializer();
+ }
+ private boolean tempHasInitializer() {
+ return getTempInitializer() != null;
+ }
+
+ public boolean canEnableSettingDeclareInFieldDeclaration(){
+ return ! fInitializerUsesLocalTypes && tempHasInitializer();
+ }
+
+ private Expression getTempInitializer() {
+ return fTempDeclarationNode.getInitializer();
+ }
+
+ private boolean isTempDeclaredInStaticMethod() {
+ return Modifier.isStatic(getMethodDeclaration().getModifiers());
+ }
+
+ private MethodDeclaration getMethodDeclaration(){
+ return ASTNodes.getParent(fTempDeclarationNode, MethodDeclaration.class);
+ }
+
+ private boolean isDeclaredInAnonymousClass() {
+ return null != ASTNodes.getParent(fTempDeclarationNode, AnonymousClassDeclaration.class);
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
+ RefactoringStatus result= Checks.validateModifiesFiles(
+ ResourceUtil.getFiles(new ICompilationUnit[]{fCu}),
+ getValidationContext());
+ if (result.hasFatalError())
+ return result;
+
+ initAST(pm);
+
+ if (fTempDeclarationNode == null)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_select_declaration);
+
+ if (! Checks.isDeclaredIn(fTempDeclarationNode, MethodDeclaration.class))
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_only_declared_in_methods);
+
+ if (isMethodParameter())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_method_parameters);
+
+ if (isTempAnExceptionInCatchBlock())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_exceptions);
+
+ ASTNode declaringType= ASTResolving.findParentType(fTempDeclarationNode);
+ if (declaringType instanceof TypeDeclaration && ((TypeDeclaration) declaringType).isInterface())
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_interface_methods);
+
+ result.merge(checkTempTypeForLocalTypeUsage());
+ if (result.hasFatalError())
+ return result;
+
+ checkTempInitializerForLocalTypeUsage();
+
+ if (!fSelfInitializing)
+ initializeDefaults();
+ return result;
+ }
+
+ private void initializeDefaults() {
+ fVisibility= Modifier.PRIVATE;
+ fDeclareStatic= Modifier.isStatic(getMethodDeclaration().getModifiers());
+ fDeclareFinal= false;
+ if (canEnableSettingDeclareInMethod())
+ fInitializeIn= INITIALIZE_IN_METHOD;
+ else if (canEnableSettingDeclareInFieldDeclaration())
+ fInitializeIn= INITIALIZE_IN_FIELD;
+ else if (canEnableSettingDeclareInConstructors())
+ fInitializeIn= INITIALIZE_IN_CONSTRUCTOR;
+ }
+
+ public String[] guessFieldNames() {
+ String rawTempName= StubUtility.getBaseName(fTempDeclarationNode.resolveBinding(), fCu.getJavaProject());
+ String[] excludedNames= getNamesOfFieldsInDeclaringType();
+ int dim= ASTNodes.getDimensions(fTempDeclarationNode);
+ return StubUtility.getFieldNameSuggestions(fCu.getJavaProject(), rawTempName, dim, getModifiers(), excludedNames);
+ }
+
+ private String getInitialFieldName() {
+ String[] suggestedNames= guessFieldNames();
+ if (suggestedNames.length > 0) {
+ if (fLinkedProposalModel != null) {
+ LinkedProposalPositionGroupCore nameGroup= fLinkedProposalModel.getPositionGroup(LINKED_NAME, true);
+ for (int i= 0; i < suggestedNames.length; i++) {
+ nameGroup.addProposal(suggestedNames[i], suggestedNames.length - i);
+ }
+ }
+ return suggestedNames[0];
+ } else {
+ return fTempDeclarationNode.getName().getIdentifier();
+ }
+ }
+
+ private String[] getNamesOfFieldsInDeclaringType() {
+ final AbstractTypeDeclaration type= getEnclosingType();
+ if (type instanceof TypeDeclaration) {
+ FieldDeclaration[] fields= ((TypeDeclaration) type).getFields();
+ List result= new ArrayList<>(fields.length);
+ for (int i= 0; i < fields.length; i++) {
+ for (Iterator iter= fields[i].fragments().iterator(); iter.hasNext();) {
+ VariableDeclarationFragment field= iter.next();
+ result.add(field.getName().getIdentifier());
+ }
+ }
+ return result.toArray(new String[result.size()]);
+ }
+ return new String[] {};
+ }
+
+ private void checkTempInitializerForLocalTypeUsage() {
+ Expression initializer= fTempDeclarationNode.getInitializer();
+ if (initializer == null)
+ return;
+
+ IMethodBinding declaringMethodBinding= getMethodDeclaration().resolveBinding();
+ ITypeBinding[] methodTypeParameters= declaringMethodBinding == null ? new ITypeBinding[0] : declaringMethodBinding.getTypeParameters();
+ LocalTypeAndVariableUsageAnalyzer localTypeAnalyer= new LocalTypeAndVariableUsageAnalyzer(methodTypeParameters);
+ initializer.accept(localTypeAnalyer);
+ fInitializerUsesLocalTypes= ! localTypeAnalyer.getUsageOfEnclosingNodes().isEmpty();
+ }
+
+ private RefactoringStatus checkTempTypeForLocalTypeUsage(){
+ VariableDeclarationStatement vds= getTempDeclarationStatement();
+ if (vds == null)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_cannot_promote);
+ Type type= vds.getType();
+ ITypeBinding binding= type.resolveBinding();
+ if (binding == null)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_cannot_promote);
+
+ IMethodBinding declaringMethodBinding= getMethodDeclaration().resolveBinding();
+ ITypeBinding[] methodTypeParameters= declaringMethodBinding == null ? new ITypeBinding[0] : declaringMethodBinding.getTypeParameters();
+ LocalTypeAndVariableUsageAnalyzer analyzer= new LocalTypeAndVariableUsageAnalyzer(methodTypeParameters);
+ type.accept(analyzer);
+ boolean usesLocalTypes= ! analyzer.getUsageOfEnclosingNodes().isEmpty();
+ fTempTypeUsesClassTypeVariables= analyzer.getClassTypeVariablesUsed();
+ if (usesLocalTypes)
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_uses_type_declared_locally);
+ return null;
+ }
+
+ private VariableDeclarationStatement getTempDeclarationStatement() {
+ return ASTNodes.getParent(fTempDeclarationNode, VariableDeclarationStatement.class);
+ }
+
+ private boolean isTempAnExceptionInCatchBlock() {
+ return (fTempDeclarationNode.getParent() instanceof CatchClause);
+ }
+
+ private boolean isMethodParameter() {
+ return (fTempDeclarationNode.getParent() instanceof MethodDeclaration);
+ }
+
+ private void initAST(IProgressMonitor pm){
+ if (fCompilationUnitNode == null) {
+ fCompilationUnitNode= RefactoringASTParser.parseWithASTProvider(fCu, true, pm);
+ fTempDeclarationNode= TempDeclarationFinder.findTempDeclaration(fCompilationUnitNode, fSelectionStart, fSelectionLength);
+ }
+ }
+
+ public RefactoringStatus validateInput(){
+ return Checks.checkFieldName(fFieldName, fCu);
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
+ try{
+ RefactoringStatus result= new RefactoringStatus();
+ result.merge(checkClashesWithExistingFields());
+ if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR)
+ result.merge(checkClashesInConstructors());
+ return result;
+ } finally {
+ pm.done();
+ }
+ }
+
+ private RefactoringStatus checkClashesInConstructors() {
+ Assert.isTrue(fInitializeIn == INITIALIZE_IN_CONSTRUCTOR);
+ Assert.isTrue(!isDeclaredInAnonymousClass());
+ final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) getMethodDeclaration().getParent();
+ if (declaration instanceof TypeDeclaration) {
+ MethodDeclaration[] methods= ((TypeDeclaration) declaration).getMethods();
+ for (int i= 0; i < methods.length; i++) {
+ MethodDeclaration method= methods[i];
+ if (!method.isConstructor())
+ continue;
+ NameCollector nameCollector= new NameCollector(method) {
+ @Override
+ protected boolean visitNode(ASTNode node) {
+ return true;
+ }
+ };
+ method.accept(nameCollector);
+ List names= nameCollector.getNames();
+ if (names.contains(fFieldName)) {
+ String[] keys= { BasicElementLabels.getJavaElementName(fFieldName), BindingLabelProviderCore.getBindingLabel(method.resolveBinding(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED)};
+ String msg= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_Name_conflict, keys);
+ return RefactoringStatus.createFatalErrorStatus(msg);
+ }
+ }
+ }
+ return null;
+ }
+
+ private RefactoringStatus checkClashesWithExistingFields(){
+ FieldDeclaration[] existingFields= getFieldDeclarations();
+ for (int i= 0; i < existingFields.length; i++) {
+ FieldDeclaration declaration= existingFields[i];
+ VariableDeclarationFragment[] fragments= (VariableDeclarationFragment[]) declaration.fragments().toArray(new VariableDeclarationFragment[declaration.fragments().size()]);
+ for (int j= 0; j < fragments.length; j++) {
+ VariableDeclarationFragment fragment= fragments[j];
+ if (fFieldName.equals(fragment.getName().getIdentifier())){
+ //cannot conflict with more than 1 name
+ RefactoringStatusContext context= JavaStatusContext.create(fCu, fragment);
+ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PromoteTempToFieldRefactoring_Name_conflict_with_field, context);
+ }
+ }
+ }
+ return null;
+ }
+
+ private FieldDeclaration[] getFieldDeclarations() {
+ List bodyDeclarations= ASTNodes.getBodyDeclarations(getMethodDeclaration().getParent());
+ List fields= new ArrayList<>(1);
+ for (Iterator iter= bodyDeclarations.iterator(); iter.hasNext();) {
+ Object each= iter.next();
+ if (each instanceof FieldDeclaration)
+ fields.add((FieldDeclaration) each);
+ }
+ return fields.toArray(new FieldDeclaration[fields.size()]);
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException {
+ pm.beginTask("", 1); //$NON-NLS-1$
+ try {
+ if (fFieldName.length() == 0) {
+ fFieldName= getInitialFieldName();
+ }
+
+ ASTRewrite rewrite= ASTRewrite.create(fCompilationUnitNode.getAST());
+ if (fInitializeIn == INITIALIZE_IN_METHOD && tempHasInitializer())
+ addLocalDeclarationSplit(rewrite);
+ else
+ addLocalDeclarationRemoval(rewrite);
+ if (fInitializeIn == INITIALIZE_IN_CONSTRUCTOR)
+ addInitializersToConstructors(rewrite);
+ addTempRenames(rewrite);
+ addFieldDeclaration(rewrite);
+
+ CompilationUnitChange result= new CompilationUnitChange(RefactoringCoreMessages.PromoteTempToFieldRefactoring_name, fCu);
+ result.setDescriptor(new RefactoringChangeDescriptor(getRefactoringDescriptor()));
+ TextEdit resultingEdits;
+ if (fFormatterOptions == null) {
+ resultingEdits= rewrite.rewriteAST();
+ } else {
+ resultingEdits= rewrite.rewriteAST(new Document(fCu.getSource()), fFormatterOptions);
+ }
+
+ TextChangeCompatibility.addTextEdit(result, RefactoringCoreMessages.PromoteTempToFieldRefactoring_editName, resultingEdits);
+ return result;
+
+ } finally {
+ pm.done();
+ }
+ }
+
+ private void addTempRenames(ASTRewrite rewrite) {
+ boolean noNameChange= fFieldName.equals(fTempDeclarationNode.getName().getIdentifier());
+ if (fLinkedProposalModel == null && noNameChange) {
+ return; // no changes needed
+ }
+ TempOccurrenceAnalyzer analyzer= new TempOccurrenceAnalyzer(fTempDeclarationNode, false);
+ analyzer.perform();
+ SimpleName[] tempRefs= analyzer.getReferenceNodes(); // no javadocs (refactoring not for parameters)
+
+
+ for (int j= 0; j < tempRefs.length; j++) {
+ SimpleName occurence= tempRefs[j];
+ if (noNameChange) {
+ addLinkedName(rewrite, occurence, false);
+ } else {
+ SimpleName newName= getAST().newSimpleName(fFieldName);
+ addLinkedName(rewrite, newName, false);
+ rewrite.replace(occurence, newName, null);
+ }
+ }
+ }
+
+ private void addInitializersToConstructors(ASTRewrite rewrite) throws CoreException {
+ Assert.isTrue(! isDeclaredInAnonymousClass());
+ final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration)getMethodDeclaration().getParent();
+ final MethodDeclaration[] constructors= getAllConstructors(declaration);
+ if (constructors.length == 0) {
+ AST ast= rewrite.getAST();
+ MethodDeclaration newConstructor= ast.newMethodDeclaration();
+ newConstructor.setConstructor(true);
+ newConstructor.modifiers().addAll(ast.newModifiers(declaration.getModifiers() & ModifierRewrite.VISIBILITY_MODIFIERS));
+ newConstructor.setName(ast.newSimpleName(declaration.getName().getIdentifier()));
+ newConstructor.setJavadoc(getNewConstructorComment(rewrite));
+ newConstructor.setBody(ast.newBlock());
+
+ addFieldInitializationToConstructor(rewrite, newConstructor);
+
+ int insertionIndex= computeInsertIndexForNewConstructor(declaration);
+ rewrite.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newConstructor, insertionIndex, null);
+ } else {
+ for (int index= 0; index < constructors.length; index++) {
+ if (shouldInsertTempInitialization(constructors[index]))
+ addFieldInitializationToConstructor(rewrite, constructors[index]);
+ }
+ }
+ }
+
+ private String getEnclosingTypeName() {
+ return getEnclosingType().getName().getIdentifier();
+ }
+
+ private AbstractTypeDeclaration getEnclosingType() {
+ return ASTNodes.getParent(getTempDeclarationStatement(), AbstractTypeDeclaration.class);
+ }
+
+ private Javadoc getNewConstructorComment(ASTRewrite rewrite) throws CoreException {
+ if (StubUtility.doAddComments(fCu.getJavaProject())){
+ String comment= CodeGeneration.getMethodComment(fCu, getEnclosingTypeName(), getEnclosingTypeName(), new String[0], new String[0], null, null, StubUtility.getLineDelimiterUsed(fCu));
+ if (comment != null && comment.length() > 0) {
+ return (Javadoc) rewrite.createStringPlaceholder(comment, ASTNode.JAVADOC);
+ }
+ }
+ return null;
+ }
+
+ private int computeInsertIndexForNewConstructor(AbstractTypeDeclaration declaration) {
+ List declarations= declaration.bodyDeclarations();
+ if (declarations.isEmpty())
+ return 0;
+ int index= findFirstMethodIndex(declaration);
+ if (index == -1)
+ return declarations.size();
+ else
+ return index;
+ }
+
+ private int findFirstMethodIndex(AbstractTypeDeclaration typeDeclaration) {
+ for (int i= 0, n= typeDeclaration.bodyDeclarations().size(); i < n; i++) {
+ if (typeDeclaration.bodyDeclarations().get(i) instanceof MethodDeclaration)
+ return i;
+ }
+ return -1;
+ }
+
+ private void addFieldInitializationToConstructor(ASTRewrite rewrite, MethodDeclaration constructor) {
+ if (constructor.getBody() == null)
+ constructor.setBody(getAST().newBlock());
+ Statement newStatement= createNewAssignmentStatement(rewrite);
+ rewrite.getListRewrite(constructor.getBody(), Block.STATEMENTS_PROPERTY).insertLast(newStatement, null);
+ }
+
+ private static boolean shouldInsertTempInitialization(MethodDeclaration constructor){
+ Assert.isTrue(constructor.isConstructor());
+ if (constructor.getBody() == null)
+ return false;
+ List statements= constructor.getBody().statements();
+ if (statements == null)
+ return false;
+ if (statements.size() > 0 && statements.get(0) instanceof ConstructorInvocation)
+ return false;
+ return true;
+ }
+
+ private static MethodDeclaration[] getAllConstructors(AbstractTypeDeclaration typeDeclaration) {
+ if (typeDeclaration instanceof TypeDeclaration) {
+ MethodDeclaration[] allMethods= ((TypeDeclaration) typeDeclaration).getMethods();
+ List result= new ArrayList<>(Math.min(allMethods.length, 1));
+ for (int i= 0; i < allMethods.length; i++) {
+ MethodDeclaration declaration= allMethods[i];
+ if (declaration.isConstructor())
+ result.add(declaration);
+ }
+ return result.toArray(new MethodDeclaration[result.size()]);
+ }
+ return new MethodDeclaration[] {};
+ }
+
+
+ private ConvertLocalVariableDescriptor getRefactoringDescriptor() {
+ final Map arguments= new HashMap<>();
+ String project= null;
+ IJavaProject javaProject= fCu.getJavaProject();
+ if (javaProject != null)
+ project= javaProject.getElementName();
+ final IVariableBinding binding= fTempDeclarationNode.resolveBinding();
+ final String description= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(binding.getName()));
+ final String header= Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_descriptor_description, new String[] { BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED), BindingLabelProviderCore.getBindingLabel(binding.getDeclaringMethod(), JavaElementLabelsCore.ALL_FULLY_QUALIFIED)});
+ final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
+ comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_original_pattern, BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)));
+ comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_field_pattern, BasicElementLabels.getJavaElementName(fFieldName)));
+ switch (fInitializeIn) {
+ case INITIALIZE_IN_CONSTRUCTOR:
+ comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_constructor);
+ break;
+ case INITIALIZE_IN_FIELD:
+ comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_declaration);
+ break;
+ case INITIALIZE_IN_METHOD:
+ comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_initialize_method);
+ break;
+ }
+ String visibility= JdtFlags.getVisibilityString(fVisibility);
+ if ("".equals(visibility)) //$NON-NLS-1$
+ visibility= RefactoringCoreMessages.PromoteTempToFieldRefactoring_default_visibility;
+ comment.addSetting(Messages.format(RefactoringCoreMessages.PromoteTempToFieldRefactoring_visibility_pattern, visibility));
+ if (fDeclareFinal && fDeclareStatic)
+ comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_final_static);
+ else if (fDeclareFinal)
+ comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_final);
+ else if (fDeclareStatic)
+ comment.addSetting(RefactoringCoreMessages.PromoteTempToFieldRefactoring_declare_static);
+ final ConvertLocalVariableDescriptor descriptor= RefactoringSignatureDescriptorFactory.createConvertLocalVariableDescriptor(project, description, comment.asString(), arguments, RefactoringDescriptor.STRUCTURAL_CHANGE);
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fCu));
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fFieldName);
+ arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + " " + Integer.valueOf(fSelectionLength).toString()); //$NON-NLS-1$
+ arguments.put(ATTRIBUTE_STATIC, Boolean.valueOf(fDeclareStatic).toString());
+ arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fDeclareFinal).toString());
+ arguments.put(ATTRIBUTE_VISIBILITY, Integer.valueOf(fVisibility).toString());
+ arguments.put(ATTRIBUTE_INITIALIZE, Integer.valueOf(fInitializeIn).toString());
+ return descriptor;
+ }
+
+ private void addLocalDeclarationSplit(ASTRewrite rewrite) {
+ VariableDeclarationStatement tempDeclarationStatement= getTempDeclarationStatement();
+ ASTNode parentStatement= tempDeclarationStatement.getParent();
+
+ ListRewrite listRewrite;
+ if (parentStatement instanceof SwitchStatement) {
+ listRewrite= rewrite.getListRewrite(parentStatement, SwitchStatement.STATEMENTS_PROPERTY);
+ } else if (parentStatement instanceof Block) {
+ listRewrite= rewrite.getListRewrite(parentStatement, Block.STATEMENTS_PROPERTY);
+ } else {
+ // should not happen. VariableDeclaration's can not be in a control statement body
+ listRewrite= null;
+ Assert.isTrue(false);
+ }
+ int statementIndex= listRewrite.getOriginalList().indexOf(tempDeclarationStatement);
+ Assert.isTrue(statementIndex != -1);
+
+ Statement newStatement= createNewAssignmentStatement(rewrite);
+
+ List fragments= tempDeclarationStatement.fragments();
+
+ int fragmentIndex= fragments.indexOf(fTempDeclarationNode);
+ Assert.isTrue(fragmentIndex != -1);
+
+ if (fragments.size() == 1) {
+ rewrite.replace(tempDeclarationStatement, newStatement, null);
+ return;
+ }
+
+ for (int i1= fragmentIndex, n = fragments.size(); i1 < n; i1++) {
+ VariableDeclarationFragment fragment= fragments.get(i1);
+ rewrite.remove(fragment, null);
+ }
+ if (fragmentIndex == 0)
+ rewrite.remove(tempDeclarationStatement, null);
+
+ Assert.isTrue(tempHasInitializer());
+
+ listRewrite.insertAt(newStatement, statementIndex + 1, null);
+
+ if (fragmentIndex + 1 < fragments.size()){
+ VariableDeclarationFragment firstFragmentAfter= fragments.get(fragmentIndex + 1);
+ VariableDeclarationFragment copyfirstFragmentAfter= (VariableDeclarationFragment)rewrite.createCopyTarget(firstFragmentAfter);
+ VariableDeclarationStatement statement= getAST().newVariableDeclarationStatement(copyfirstFragmentAfter);
+ Type type= (Type)rewrite.createCopyTarget(tempDeclarationStatement.getType());
+ statement.setType(type);
+ List modifiers= tempDeclarationStatement.modifiers();
+ if (modifiers.size() > 0) {
+ ListRewrite modifiersRewrite= rewrite.getListRewrite(tempDeclarationStatement, VariableDeclarationStatement.MODIFIERS2_PROPERTY);
+ ASTNode firstModifier= (ASTNode) modifiers.get(0);
+ ASTNode lastModifier= (ASTNode) modifiers.get(modifiers.size() - 1);
+ ASTNode modifiersCopy= modifiersRewrite.createCopyTarget(firstModifier, lastModifier);
+ statement.modifiers().add(modifiersCopy);
+ }
+ for (int i= fragmentIndex + 2; i < fragments.size(); i++) {
+ VariableDeclarationFragment fragment= fragments.get(i);
+ VariableDeclarationFragment fragmentCopy= (VariableDeclarationFragment)rewrite.createCopyTarget(fragment);
+ statement.fragments().add(fragmentCopy);
+ }
+ listRewrite.insertAt(statement, statementIndex + 2, null);
+ }
+ }
+
+ private Statement createNewAssignmentStatement(ASTRewrite rewrite) {
+ AST ast= getAST();
+ Assignment assignment= ast.newAssignment();
+ SimpleName fieldName= ast.newSimpleName(fFieldName);
+ addLinkedName(rewrite, fieldName, true);
+ assignment.setLeftHandSide(fieldName);
+ assignment.setRightHandSide(getTempInitializerCopy(rewrite));
+ return ast.newExpressionStatement(assignment);
+ }
+
+ private void addLinkedName(ASTRewrite rewrite, SimpleName fieldName, boolean isFirst) {
+ if (fLinkedProposalModel != null) {
+ fLinkedProposalModel.getPositionGroup(LINKED_NAME, true).addPosition(rewrite.track(fieldName), isFirst);
+ }
+ }
+
+ private Expression getTempInitializerCopy(ASTRewrite rewrite) {
+ final Expression initializer= (Expression) rewrite.createCopyTarget(getTempInitializer());
+ if (initializer instanceof ArrayInitializer && ASTNodes.getDimensions(fTempDeclarationNode) > 0) {
+ ArrayCreation arrayCreation= rewrite.getAST().newArrayCreation();
+ arrayCreation.setType((ArrayType) ASTNodeFactory.newType(rewrite.getAST(), fTempDeclarationNode));
+ arrayCreation.setInitializer((ArrayInitializer) initializer);
+ return arrayCreation;
+ }
+ return initializer;
+ }
+
+ private void addLocalDeclarationRemoval(ASTRewrite rewrite) {
+ VariableDeclarationStatement tempDeclarationStatement= getTempDeclarationStatement();
+ List fragments= tempDeclarationStatement.fragments();
+
+ int fragmentIndex= fragments.indexOf(fTempDeclarationNode);
+ Assert.isTrue(fragmentIndex != -1);
+ VariableDeclarationFragment fragment= fragments.get(fragmentIndex);
+ rewrite.remove(fragment, null);
+ if (fragments.size() == 1)
+ rewrite.remove(tempDeclarationStatement, null);
+ }
+
+ private void addFieldDeclaration(ASTRewrite rewrite) {
+ FieldDeclaration[] fields= getFieldDeclarations();
+ ASTNode parent= getMethodDeclaration().getParent();
+ ChildListPropertyDescriptor descriptor= ASTNodes.getBodyDeclarationsProperty(parent);
+ int insertIndex;
+ if (fields.length == 0)
+ insertIndex= 0;
+ else
+ insertIndex= ASTNodes.getBodyDeclarations(parent).indexOf(fields[fields.length - 1]) + 1;
+
+ final FieldDeclaration declaration= createNewFieldDeclaration(rewrite);
+ rewrite.getListRewrite(parent, descriptor).insertAt(declaration, insertIndex, null);
+ }
+
+ private FieldDeclaration createNewFieldDeclaration(ASTRewrite rewrite) {
+ AST ast= getAST();
+ VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
+ SimpleName variableName= ast.newSimpleName(fFieldName);
+ fragment.setName(variableName);
+ addLinkedName(rewrite, variableName, false);
+ List extraDimensions= DimensionRewrite.copyDimensions(fTempDeclarationNode.extraDimensions(), rewrite);
+ fragment.extraDimensions().addAll(extraDimensions);
+ if (fInitializeIn == INITIALIZE_IN_FIELD && tempHasInitializer()){
+ Expression initializer= (Expression)rewrite.createCopyTarget(getTempInitializer());
+ fragment.setInitializer(initializer);
+ }
+ FieldDeclaration fieldDeclaration= ast.newFieldDeclaration(fragment);
+
+ VariableDeclarationStatement vds= getTempDeclarationStatement();
+ ITypeBinding binding= vds.getType().resolveBinding();
+ Type type;
+ // "var" type cannot be initialized separately, so change to inferred type
+ if (ASTNodes.isVarType(vds, fCompilationUnitNode) && binding != null) {
+ ImportRewrite importRewrite= StubUtility.createImportRewrite(fCompilationUnitNode, true);
+ ImportRewriteContext context= new ContextSensitiveImportRewriteContext(fCompilationUnitNode, importRewrite);
+ type= importRewrite.addImport(binding, getAST(), context, TypeLocation.FIELD);
+ } else {
+ type= (Type)rewrite.createCopyTarget(vds.getType());
+ }
+ fieldDeclaration.setType(type);
+ fieldDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(ast, getModifiers()));
+ return fieldDeclaration;
+ }
+
+ private int getModifiers() {
+ int flags= fVisibility;
+ if (fDeclareFinal)
+ flags |= Modifier.FINAL;
+ if (fDeclareStatic)
+ flags |= Modifier.STATIC;
+ return flags;
+ }
+
+ private AST getAST(){
+ return fTempDeclarationNode.getAST();
+ }
+
+ private static class LocalTypeAndVariableUsageAnalyzer extends HierarchicalASTVisitor{
+ private final List fLocalDefinitions= new ArrayList<>(0); // List of IBinding (Variable and Type)
+ private final List fLocalReferencesToEnclosing= new ArrayList<>(0); // List of ASTNodes
+ private final List fMethodTypeVariables;
+ private boolean fClassTypeVariablesUsed= false;
+ public LocalTypeAndVariableUsageAnalyzer(ITypeBinding[] methodTypeVariables) {
+ fMethodTypeVariables= Arrays.asList(methodTypeVariables);
+ }
+ public List getUsageOfEnclosingNodes(){
+ return fLocalReferencesToEnclosing;
+ }
+ public boolean getClassTypeVariablesUsed() {
+ return fClassTypeVariablesUsed;
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ ITypeBinding typeBinding= node.resolveTypeBinding();
+ if (typeBinding != null && typeBinding.isLocal()) {
+ if (node.isDeclaration()) {
+ fLocalDefinitions.add(typeBinding);
+ } else if (! fLocalDefinitions.contains(typeBinding)) {
+ fLocalReferencesToEnclosing.add(node);
+ }
+ }
+ if (typeBinding != null && typeBinding.isTypeVariable()) {
+ if (node.isDeclaration()) {
+ fLocalDefinitions.add(typeBinding);
+ } else if (! fLocalDefinitions.contains(typeBinding)) {
+ if (fMethodTypeVariables.contains(typeBinding)) {
+ fLocalReferencesToEnclosing.add(node);
+ } else {
+ fClassTypeVariablesUsed= true;
+ }
+ }
+ }
+ IBinding binding= node.resolveBinding();
+ if (binding != null && binding.getKind() == IBinding.VARIABLE && ! ((IVariableBinding)binding).isField()) {
+ if (node.isDeclaration()) {
+ fLocalDefinitions.add(binding);
+ } else if (! fLocalDefinitions.contains(binding)) {
+ fLocalReferencesToEnclosing.add(node);
+ }
+ }
+ return super.visit(node);
+ }
+ }
+
+ private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
+ fSelfInitializing= true;
+ final String selection= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
+ if (selection != null) {
+ int offset= -1;
+ int length= -1;
+ final StringTokenizer tokenizer= new StringTokenizer(selection);
+ if (tokenizer.hasMoreTokens())
+ offset= Integer.valueOf(tokenizer.nextToken()).intValue();
+ if (tokenizer.hasMoreTokens())
+ length= Integer.valueOf(tokenizer.nextToken()).intValue();
+ if (offset >= 0 && length >= 0) {
+ fSelectionStart= offset;
+ fSelectionLength= length;
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION}));
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
+ final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
+ if (handle != null) {
+ final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
+ if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
+ return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.CONVERT_LOCAL_VARIABLE);
+ else
+ fCu= (ICompilationUnit) element;
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
+ final String visibility= arguments.getAttribute(ATTRIBUTE_VISIBILITY);
+ if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$
+ int flag= 0;
+ try {
+ flag= Integer.parseInt(visibility);
+ } catch (NumberFormatException exception) {
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY));
+ }
+ fVisibility= flag;
+ }
+ final String initialize= arguments.getAttribute(ATTRIBUTE_INITIALIZE);
+ if (initialize != null && !"".equals(initialize)) {//$NON-NLS-1$
+ int value= 0;
+ try {
+ value= Integer.parseInt(initialize);
+ } catch (NumberFormatException exception) {
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_INITIALIZE));
+ }
+ fInitializeIn= value;
+ }
+ final String name= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
+ if (name != null && !"".equals(name)) //$NON-NLS-1$
+ fFieldName= name;
+ else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
+ final String declareStatic= arguments.getAttribute(ATTRIBUTE_STATIC);
+ if (declareStatic != null) {
+ fDeclareStatic= Boolean.valueOf(declareStatic).booleanValue();
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_STATIC));
+ final String declareFinal= arguments.getAttribute(ATTRIBUTE_FINAL);
+ if (declareFinal != null) {
+ fDeclareFinal= Boolean.valueOf(declareFinal).booleanValue();
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FINAL));
+ return new RefactoringStatus();
+ }
+
+
+ public void setLinkedProposalModel(LinkedProposalModelCore model) {
+ fLinkedProposalModel= model;
+ }
+
+ public Map getFormatterOptions() {
+ return fFormatterOptions;
+ }
+
+ /**
+ * Set the formatter options to format the refactored code.
+ * @param formatterOptions the formatter options to format the refactored code
+ */
+ public void setFormatterOptions(Map formatterOptions) {
+ fFormatterOptions= formatterOptions;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,542 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
+ * o bug "Inline refactoring showed bogus error" (see bugzilla
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=42753)
+ * o Allow 'this' constructor to be inlined
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayAccess;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.LambdaExpression;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
+import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.manipulation.ImportReferencesCollector;
+
+import org.eclipse.jdt.internal.core.manipulation.dom.OperatorPrecedence;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.InOutFlowAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
+
+class SourceAnalyzer {
+
+ public static class NameData {
+ private String fName;
+ private List fReferences;
+ public NameData(String n) {
+ fName= n;
+ fReferences= new ArrayList<>(2);
+ }
+ public String getName() {
+ return fName;
+ }
+ public void addReference(SimpleName ref) {
+ fReferences.add(ref);
+ }
+ public List references() {
+ return fReferences;
+ }
+ }
+
+ private class ActivationAnalyzer extends ASTVisitor {
+ public RefactoringStatus status= new RefactoringStatus();
+ private ASTNode fLastNode= getLastNode();
+ private IMethodBinding fBinding= getBinding();
+ @Override
+ public boolean visit(ReturnStatement node) {
+ if (node != fLastNode) {
+ fInterruptedExecutionFlow= true;
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(EnumDeclaration node) {
+ return false;
+ }
+ @Override
+ public boolean visit(AnnotationTypeDeclaration node) {
+ return false;
+ }
+ @Override
+ public boolean visit(TypeDeclaration node) {
+ return false;
+ }
+ @Override
+ public boolean visit(AnonymousClassDeclaration node) {
+ return false;
+ }
+ @Override
+ public boolean visit(LambdaExpression node) {
+ return false;
+ }
+ @Override
+ public boolean visit(MethodInvocation node) {
+ IMethodBinding methodBinding= node.resolveMethodBinding();
+ if (methodBinding != null)
+ methodBinding.getMethodDeclaration();
+ if (fBinding != null && methodBinding != null && fBinding.isEqualTo(methodBinding) && !status.hasFatalError()) {
+ status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_recursive_call);
+ return false;
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ IBinding binding= node.resolveBinding();
+ if (binding == null && !status.hasFatalError()) {
+ // fixes bug #42753
+ if (!ASTNodes.isLabel(node)) {
+ status.addFatalError(
+ RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_declaration_has_errors,
+ JavaStatusContext.create(fTypeRoot, fDeclaration));
+ return false;
+ }
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(ThisExpression node) {
+ if (node.getQualifier() != null) {
+ status.addFatalError(
+ RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_qualified_this_expressions,
+ JavaStatusContext.create(fTypeRoot, node));
+ return false;
+ }
+ return true;
+ }
+ private ASTNode getLastNode() {
+ List statements= fDeclaration.getBody().statements();
+ if (statements.size() == 0)
+ return null;
+ return statements.get(statements.size() - 1);
+ }
+ private IMethodBinding getBinding() {
+ IMethodBinding result= fDeclaration.resolveBinding();
+ if (result != null)
+ return result.getMethodDeclaration();
+ return result;
+ }
+ }
+
+ private class UpdateCollector extends ASTVisitor {
+ private int fTypeCounter;
+ @Override
+ public boolean visit(TypeDeclaration node) {
+ return visitType(node);
+ }
+ @Override
+ public void endVisit(TypeDeclaration node) {
+ fTypeCounter--;
+ }
+ @Override
+ public boolean visit(EnumDeclaration node) {
+ return visitType(node);
+ }
+ @Override
+ public void endVisit(EnumDeclaration node) {
+ fTypeCounter--;
+ }
+ @Override
+ public boolean visit(AnnotationTypeDeclaration node) {
+ return visitType(node);
+ }
+ @Override
+ public void endVisit(AnnotationTypeDeclaration node) {
+ fTypeCounter--;
+ }
+ private boolean visitType(AbstractTypeDeclaration node) {
+ if (fTypeCounter++ == 0) {
+ addNameReference(node.getName());
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(AnonymousClassDeclaration node) {
+ fTypeCounter++;
+ return true;
+ }
+ @Override
+ public void endVisit(AnonymousClassDeclaration node) {
+ fTypeCounter--;
+ }
+ @Override
+ public boolean visit(FieldAccess node) {
+ // only visit the expression and not the simple name
+ node.getExpression().accept(this);
+ addReferencesToName(node.getName());
+ return false;
+ }
+ @Override
+ public boolean visit(MethodDeclaration node) {
+ if (node.isConstructor()) {
+ AbstractTypeDeclaration decl= ASTNodes.getParent(node, AbstractTypeDeclaration.class);
+ NameData name= fNames.get(decl.getName().resolveBinding());
+ if (name != null) {
+ name.addReference(node.getName());
+ }
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(MethodInvocation node) {
+ if (fTypeCounter == 0) {
+ Expression receiver= node.getExpression();
+ if (receiver == null && !isStaticallyImported(node.getName())) {
+ fImplicitReceivers.add(node);
+ }
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ if (fTypeCounter == 0) {
+ fHasSuperMethodInvocation= true;
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(SuperConstructorInvocation node) {
+ if (fTypeCounter == 0) {
+ fHasSuperMethodInvocation= true;
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(ClassInstanceCreation node) {
+ if (fTypeCounter == 0) {
+ Expression receiver= node.getExpression();
+ if (receiver == null) {
+ if (node.resolveTypeBinding().isLocal())
+ fImplicitReceivers.add(node);
+ }
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(SingleVariableDeclaration node) {
+ if (fTypeCounter == 0)
+ addNameReference(node.getName());
+ return true;
+ }
+ @Override
+ public boolean visit(VariableDeclarationFragment node) {
+ if (fTypeCounter == 0)
+ addNameReference(node.getName());
+ return true;
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ addReferencesToName(node);
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof ITypeBinding) {
+ ITypeBinding type= (ITypeBinding)binding;
+ if (type.isTypeVariable()) {
+ addTypeVariableReference(type, node);
+ }
+ } else if (binding instanceof IVariableBinding) {
+ IVariableBinding vb= (IVariableBinding)binding;
+ if (vb.isField() && ! isStaticallyImported(node)) {
+ Name topName= ASTNodes.getTopMostName(node);
+ if (node == topName || node == ASTNodes.getLeftMostSimpleName(topName)) {
+ StructuralPropertyDescriptor location= node.getLocationInParent();
+ if (location != SingleVariableDeclaration.NAME_PROPERTY
+ && location != VariableDeclarationFragment.NAME_PROPERTY) {
+ fImplicitReceivers.add(node);
+ }
+ }
+ } else if (!vb.isField()) {
+ // we have a local. Check if it is a parameter.
+ ParameterData data= fParameters.get(binding);
+ if (data != null) {
+ ASTNode parent= node.getParent();
+ if (parent instanceof Expression) {
+ int precedence= OperatorPrecedence.getExpressionPrecedence((Expression)parent);
+ if (precedence != Integer.MAX_VALUE) {
+ data.setOperatorPrecedence(precedence);
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(ThisExpression node) {
+ if (fTypeCounter == 0) {
+ fImplicitReceivers.add(node);
+ }
+ return true;
+ }
+ private void addReferencesToName(SimpleName node) {
+ IBinding binding= node.resolveBinding();
+ ParameterData data= fParameters.get(binding);
+ if (data != null)
+ data.addReference(node);
+
+ NameData name= fNames.get(binding);
+ if (name != null)
+ name.addReference(node);
+ }
+ private void addNameReference(SimpleName name) {
+ fNames.put(name.resolveBinding(), new NameData(name.getIdentifier()));
+ }
+ private void addTypeVariableReference(ITypeBinding variable, SimpleName name) {
+ NameData data= fTypeParameterMapping.get(variable);
+ if (data == null) {
+ data= fMethodTypeParameterMapping.get(variable);
+ }
+ data.addReference(name);
+ }
+ private boolean isStaticallyImported(Name name) {
+ return fStaticsToImport.contains(name);
+ }
+ }
+
+ private class VarargAnalyzer extends ASTVisitor {
+ private IBinding fParameter;
+ public VarargAnalyzer(IBinding parameter) {
+ fParameter= parameter;
+ }
+ @Override
+ public boolean visit(ArrayAccess node) {
+ Expression array= node.getArray();
+ if (array instanceof SimpleName && fParameter.isEqualTo(((SimpleName)array).resolveBinding())) {
+ fArrayAccess= true;
+ }
+ return true;
+ }
+ }
+
+ private ITypeRoot fTypeRoot;
+ private MethodDeclaration fDeclaration;
+ private Map fParameters;
+ private Map fNames;
+ private List fImplicitReceivers;
+
+ private boolean fArrayAccess;
+ private boolean fHasSuperMethodInvocation;
+
+ private List fTypesToImport;
+ private List fStaticsToImport;
+
+ private List fTypeParameterReferences;
+ private Map fTypeParameterMapping;
+
+ private List fMethodTypeParameterReferences;
+ private Map fMethodTypeParameterMapping;
+
+ private boolean fInterruptedExecutionFlow;
+
+ public SourceAnalyzer(ITypeRoot typeRoot, MethodDeclaration declaration) {
+ super();
+ fTypeRoot= typeRoot;
+ fDeclaration= declaration;
+ }
+
+ public boolean isExecutionFlowInterrupted() {
+ return fInterruptedExecutionFlow;
+ }
+
+ public RefactoringStatus checkActivation() throws JavaModelException {
+ RefactoringStatus result= new RefactoringStatus();
+ if (!fTypeRoot.isStructureKnown()) {
+ result.addFatalError(
+ RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_syntax_errors,
+ JavaStatusContext.create(fTypeRoot));
+ return result;
+ }
+ IProblem[] problems= ASTNodes.getProblems(fDeclaration, ASTNodes.NODE_ONLY, ASTNodes.ERROR);
+ if (problems.length > 0) {
+ result.addFatalError(
+ RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_declaration_has_errors,
+ JavaStatusContext.create(fTypeRoot, fDeclaration));
+ return result;
+ }
+ final IMethodBinding declarationBinding= fDeclaration.resolveBinding();
+ if (declarationBinding != null) {
+ final int modifiers= declarationBinding.getModifiers();
+ if (Modifier.isAbstract(modifiers)) {
+ result.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_abstract_methods, JavaStatusContext.create(fTypeRoot, fDeclaration));
+ return result;
+ } else if (Modifier.isNative(modifiers)) {
+ result.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_native_methods, JavaStatusContext.create(fTypeRoot, fDeclaration));
+ return result;
+ }
+ } else {
+ result.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_methoddeclaration_has_errors, JavaStatusContext.create(fTypeRoot));
+ return result;
+ }
+ ActivationAnalyzer analyzer= new ActivationAnalyzer();
+ fDeclaration.accept(analyzer);
+ result.merge(analyzer.status);
+ if (!result.hasFatalError()) {
+ List parameters= fDeclaration.parameters();
+ fParameters= new HashMap<>(parameters.size() * 2);
+ for (Iterator iter= parameters.iterator(); iter.hasNext();) {
+ SingleVariableDeclaration element= iter.next();
+ IVariableBinding binding= element.resolveBinding();
+ if (binding == null) {
+ result.addFatalError(
+ RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_declaration_has_errors,
+ JavaStatusContext.create(fTypeRoot, fDeclaration));
+ return result;
+ }
+ fParameters.put(binding, (ParameterData) element.getProperty(ParameterData.PROPERTY));
+ }
+ fNames= new HashMap<>();
+ fImplicitReceivers= new ArrayList<>(2);
+
+ fTypeParameterReferences= new ArrayList<>(0);
+ fTypeParameterMapping= new HashMap<>();
+ ITypeBinding declaringType= declarationBinding.getDeclaringClass();
+ if (declaringType == null) {
+ result.addFatalError(
+ RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_typedeclaration_has_errors,
+ JavaStatusContext.create(fTypeRoot));
+ return result;
+ }
+ ITypeBinding[] typeParameters= declaringType.getTypeParameters();
+ for (int i= 0; i < typeParameters.length; i++) {
+ NameData data= new NameData(typeParameters[i].getName());
+ fTypeParameterReferences.add(data);
+ fTypeParameterMapping.put(typeParameters[i], data);
+ }
+
+ fMethodTypeParameterReferences= new ArrayList<>(0);
+ fMethodTypeParameterMapping= new HashMap<>();
+ IMethodBinding method= declarationBinding;
+ typeParameters= method.getTypeParameters();
+ for (int i= 0; i < typeParameters.length; i++) {
+ NameData data= new NameData(typeParameters[i].getName());
+ fMethodTypeParameterReferences.add(data);
+ fMethodTypeParameterMapping.put(typeParameters[i], data);
+ }
+
+ }
+ if (fDeclaration.isVarargs()) {
+ List parameters= fDeclaration.parameters();
+ VarargAnalyzer vAnalyzer= new VarargAnalyzer(
+ parameters.get(parameters.size() - 1).getName().resolveBinding());
+ fDeclaration.getBody().accept(vAnalyzer);
+ }
+ return result;
+ }
+
+ public void initialize() {
+ Block body= fDeclaration.getBody();
+ // first collect the static imports. This is necessary to not mark
+ // static imported fields and methods as implicit visible.
+ fTypesToImport= new ArrayList<>();
+ fStaticsToImport= new ArrayList<>();
+ ImportReferencesCollector.collect(body, fTypeRoot.getJavaProject(), null, fTypesToImport, fStaticsToImport);
+
+ // Now collect implicit references and name references
+ body.accept(new UpdateCollector());
+
+ int numberOfLocals= LocalVariableIndex.perform(fDeclaration);
+ FlowContext context= new FlowContext(0, numberOfLocals + 1);
+ context.setConsiderAccessMode(true);
+ context.setComputeMode(FlowContext.MERGE);
+ InOutFlowAnalyzer flowAnalyzer= new InOutFlowAnalyzer(context);
+ FlowInfo info= flowAnalyzer.perform(getStatements());
+
+ for (Iterator iter= fDeclaration.parameters().iterator(); iter.hasNext();) {
+ SingleVariableDeclaration element= iter.next();
+ IVariableBinding binding= element.resolveBinding();
+ ParameterData data= (ParameterData)element.getProperty(ParameterData.PROPERTY);
+ data.setAccessMode(info.getAccessMode(context, binding));
+ }
+ }
+
+ public Collection getUsedNames() {
+ return fNames.values();
+ }
+
+ public List getImplicitReceivers() {
+ return fImplicitReceivers;
+ }
+
+ public List getTypesToImport() {
+ return fTypesToImport;
+ }
+
+ public List getStaticsToImport() {
+ return fStaticsToImport;
+ }
+
+ public List getTypeParameterReferences() {
+ return fTypeParameterReferences;
+ }
+
+ public List getMethodTypeParameterReferences() {
+ return fMethodTypeParameterReferences;
+ }
+
+ public boolean hasArrayAccess() {
+ return fArrayAccess;
+ }
+
+ public boolean hasSuperMethodInvocation() {
+ return fHasSuperMethodInvocation;
+ }
+
+ private ASTNode[] getStatements() {
+ List statements= fDeclaration.getBody().statements();
+ return statements.toArray(new ASTNode[statements.size()]);
+ }
+
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,737 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
+ * o bug "inline method - doesn't handle implicit cast" (see
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
+ * o inline call that is used in a field initializer
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
+ * o inline call a field initializer: could detect self reference
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=44417)
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.RangeMarker;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.TextEditProcessor;
+import org.eclipse.text.edits.UndoEdit;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.ChildPropertyDescriptor;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConditionalExpression;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.LabeledStatement;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.WhileStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
+
+import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+import org.eclipse.jdt.internal.core.manipulation.dom.NecessaryParenthesesChecker;
+import org.eclipse.jdt.internal.core.manipulation.util.Strings;
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
+import org.eclipse.jdt.internal.corext.refactoring.code.SourceAnalyzer.NameData;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringFileBuffers;
+import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
+
+/**
+ * A SourceProvider encapsulates a piece of code (source) and the logic
+ * to inline it into given CallContexts.
+ */
+public class SourceProvider {
+
+ private ITypeRoot fTypeRoot;
+ private IDocument fDocument;
+ private MethodDeclaration fDeclaration;
+ private SourceAnalyzer fAnalyzer;
+ private boolean fMustEvalReturnedExpression;
+ private boolean fReturnValueNeedsLocalVariable;
+ private List fReturnExpressions;
+ private IDocument fSource;
+
+ private static final int EXPRESSION_MODE= 1;
+ private static final int STATEMENT_MODE= 2;
+ private static final int RETURN_STATEMENT_MODE= 3;
+ private int fMarkerMode;
+
+
+ private class ReturnAnalyzer extends ASTVisitor {
+ @Override
+ public boolean visit(ReturnStatement node) {
+ Expression expression= node.getExpression();
+ if (!(ASTNodes.isLiteral(expression) || expression instanceof Name)) {
+ fMustEvalReturnedExpression= true;
+ }
+ if (Invocations.isInvocation(expression) || expression instanceof ClassInstanceCreation) {
+ fReturnValueNeedsLocalVariable= false;
+ }
+ fReturnExpressions.add(expression);
+ return false;
+ }
+ }
+
+ public SourceProvider(ITypeRoot typeRoot, MethodDeclaration declaration) {
+ super();
+ fTypeRoot= typeRoot;
+ fDeclaration= declaration;
+ List parameters= fDeclaration.parameters();
+ for (Iterator iter= parameters.iterator(); iter.hasNext();) {
+ SingleVariableDeclaration element= iter.next();
+ ParameterData data= new ParameterData(element);
+ element.setProperty(ParameterData.PROPERTY, data);
+ }
+ fAnalyzer= new SourceAnalyzer(fTypeRoot, fDeclaration);
+ fReturnValueNeedsLocalVariable= true;
+ fReturnExpressions= new ArrayList<>();
+ }
+
+ /**
+ * TODO: unit's source does not match contents of source document and declaration node.
+ * @param typeRoot the type root
+ * @param source document containing the content of the type root
+ * @param declaration method declaration node
+ */
+ public SourceProvider(ITypeRoot typeRoot, IDocument source, MethodDeclaration declaration) {
+ this(typeRoot, declaration);
+ fSource= source;
+ }
+
+ public RefactoringStatus checkActivation() throws JavaModelException {
+ return fAnalyzer.checkActivation();
+ }
+
+ public void initialize() throws JavaModelException {
+ fDocument= fSource == null ? new Document(fTypeRoot.getBuffer().getContents()) : fSource;
+ fAnalyzer.initialize();
+ if (hasReturnValue()) {
+ ASTNode last= getLastStatement();
+ if (last != null) {
+ ReturnAnalyzer analyzer= new ReturnAnalyzer();
+ last.accept(analyzer);
+ }
+ }
+ }
+
+ public boolean isExecutionFlowInterrupted() {
+ return fAnalyzer.isExecutionFlowInterrupted();
+ }
+
+ static class VariableReferenceFinder extends ASTVisitor {
+ private boolean fResult;
+ private IVariableBinding fBinding;
+ public VariableReferenceFinder(IVariableBinding binding) {
+ fBinding= binding;
+ }
+ public boolean getResult() {
+ return fResult;
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ if(!fResult) {
+ fResult= Bindings.equals(fBinding, node.resolveBinding());
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Checks whether variable is referenced by the method declaration or not.
+ *
+ * @param binding binding of variable to check.
+ * @return true
if variable is referenced by the method, otherwise false
+ */
+ public boolean isVariableReferenced(IVariableBinding binding) {
+ VariableReferenceFinder finder= new VariableReferenceFinder(binding);
+ fDeclaration.accept(finder);
+ return finder.getResult();
+ }
+
+ public boolean hasReturnValue() {
+ IMethodBinding binding= fDeclaration.resolveBinding();
+ return binding.getReturnType() != fDeclaration.getAST().resolveWellKnownType("void"); //$NON-NLS-1$
+ }
+
+ // returns true if the declaration has a vararg and
+ // the body contains an array access to the vararg
+ public boolean hasArrayAccess() {
+ return fAnalyzer.hasArrayAccess();
+ }
+
+ public boolean hasSuperMethodInvocation() {
+ return fAnalyzer.hasSuperMethodInvocation();
+ }
+
+ public boolean mustEvaluateReturnedExpression() {
+ return fMustEvalReturnedExpression;
+ }
+
+ public boolean returnValueNeedsLocalVariable() {
+ return fReturnValueNeedsLocalVariable;
+ }
+
+ public int getNumberOfStatements() {
+ return fDeclaration.getBody().statements().size();
+ }
+
+ public boolean isSimpleFunction() {
+ List statements= fDeclaration.getBody().statements();
+ if (statements.size() != 1)
+ return false;
+ return statements.get(0) instanceof ReturnStatement;
+ }
+
+ public boolean isLastStatementReturn() {
+ List statements= fDeclaration.getBody().statements();
+ if (statements.size() == 0)
+ return false;
+ return statements.get(statements.size() - 1) instanceof ReturnStatement;
+ }
+
+ public boolean isDangligIf() {
+ List statements= fDeclaration.getBody().statements();
+ if (statements.size() != 1)
+ return false;
+
+ ASTNode p= statements.get(0);
+
+ while (true) {
+ if (p instanceof IfStatement) {
+ return ((IfStatement) p).getElseStatement() == null;
+ } else {
+
+ ChildPropertyDescriptor childD;
+ if (p instanceof WhileStatement) {
+ childD= WhileStatement.BODY_PROPERTY;
+ } else if (p instanceof ForStatement) {
+ childD= ForStatement.BODY_PROPERTY;
+ } else if (p instanceof EnhancedForStatement) {
+ childD= EnhancedForStatement.BODY_PROPERTY;
+ } else if (p instanceof DoStatement) {
+ childD= DoStatement.BODY_PROPERTY;
+ } else if (p instanceof LabeledStatement) {
+ childD= LabeledStatement.BODY_PROPERTY;
+ } else {
+ return false;
+ }
+ Statement body= (Statement) p.getStructuralProperty(childD);
+ if (body instanceof Block) {
+ return false;
+ } else {
+ p= body;
+ }
+ }
+ }
+ }
+
+ public MethodDeclaration getDeclaration() {
+ return fDeclaration;
+ }
+
+ public String getMethodName() {
+ return fDeclaration.getName().getIdentifier();
+ }
+
+ public ITypeBinding getReturnType() {
+ return fDeclaration.resolveBinding().getReturnType();
+ }
+
+ public List getReturnExpressions() {
+ return fReturnExpressions;
+ }
+
+ public boolean returnTypeMatchesReturnExpressions() {
+ ITypeBinding returnType= getReturnType();
+ for (Iterator iter= fReturnExpressions.iterator(); iter.hasNext();) {
+ Expression expression= iter.next();
+ if (!Bindings.equals(returnType, expression.resolveTypeBinding()))
+ return false;
+ }
+ return true;
+ }
+
+ public ParameterData getParameterData(int index) {
+ SingleVariableDeclaration decl= (SingleVariableDeclaration)fDeclaration.parameters().get(index);
+ return (ParameterData)decl.getProperty(ParameterData.PROPERTY);
+ }
+
+ public ITypeRoot getTypeRoot() {
+ return fTypeRoot;
+ }
+
+ public boolean needsReturnedExpressionParenthesis(ASTNode parent, StructuralPropertyDescriptor locationInParent) {
+ ASTNode last= getLastStatement();
+ if (last instanceof ReturnStatement) {
+ return NecessaryParenthesesChecker.needsParentheses(((ReturnStatement)last).getExpression(), parent, locationInParent);
+ }
+ return false;
+ }
+
+ public boolean returnsConditionalExpression() {
+ ASTNode last= getLastStatement();
+ if (last instanceof ReturnStatement) {
+ return ((ReturnStatement)last).getExpression() instanceof ConditionalExpression;
+ }
+ return false;
+ }
+
+ public int getReceiversToBeUpdated() {
+ return fAnalyzer.getImplicitReceivers().size();
+ }
+
+ public boolean isVarargs() {
+ return fDeclaration.isVarargs();
+ }
+
+ public int getVarargIndex() {
+ return fDeclaration.parameters().size() - 1;
+ }
+
+ public TextEdit getDeleteEdit() {
+ final ASTRewrite rewriter= ASTRewrite.create(fDeclaration.getAST());
+ rewriter.remove(fDeclaration, null);
+ return rewriter.rewriteAST(fDocument, fTypeRoot.getJavaProject().getOptions(true));
+ }
+
+ public String[] getCodeBlocks(CallContext context, ImportRewrite importRewrite) throws CoreException {
+ final ASTRewrite rewriter= ASTRewrite.create(fDeclaration.getAST());
+ replaceParameterWithExpression(rewriter, context, importRewrite);
+ updateImplicitReceivers(rewriter, context);
+ makeNamesUnique(rewriter, context.scope);
+ updateTypeReferences(rewriter, context);
+ updateStaticReferences(rewriter, context);
+ updateTypeVariables(rewriter, context);
+ updateMethodTypeVariable(rewriter, context);
+
+ List ranges= null;
+ if (hasReturnValue()) {
+ if (context.callMode == ASTNode.RETURN_STATEMENT) {
+ ranges= getStatementRanges();
+ } else {
+ ranges= getExpressionRanges();
+ }
+ } else {
+ ASTNode last= getLastStatement();
+ if (last != null && last.getNodeType() == ASTNode.RETURN_STATEMENT) {
+ ranges= getReturnStatementRanges();
+ } else {
+ ranges= getStatementRanges();
+ }
+ }
+
+ final TextEdit dummy= rewriter.rewriteAST(fDocument, fTypeRoot.getJavaProject().getOptions(true));
+ int size= ranges.size();
+ RangeMarker[] markers= new RangeMarker[size];
+ for (int i= 0; i < markers.length; i++) {
+ IRegion range= ranges.get(i);
+ markers[i]= new RangeMarker(range.getOffset(), range.getLength());
+ }
+ int split;
+ if (size <= 1) {
+ split= Integer.MAX_VALUE;
+ } else {
+ IRegion region= ranges.get(0);
+ split= region.getOffset() + region.getLength();
+ }
+ TextEdit[] edits= dummy.removeChildren();
+ for (int i= 0; i < edits.length; i++) {
+ TextEdit edit= edits[i];
+ int pos= edit.getOffset() >= split ? 1 : 0;
+ markers[pos].addChild(edit);
+ }
+ MultiTextEdit root= new MultiTextEdit(0, fDocument.getLength());
+ root.addChildren(markers);
+
+ try {
+ TextEditProcessor processor= new TextEditProcessor(fDocument, root, TextEdit.CREATE_UNDO | TextEdit.UPDATE_REGIONS);
+ UndoEdit undo= processor.performEdits();
+ String[] result= getBlocks(markers);
+ // It is faster to undo the changes than coping the buffer over and over again.
+ processor= new TextEditProcessor(fDocument, undo, TextEdit.UPDATE_REGIONS);
+ processor.performEdits();
+ return result;
+ } catch (MalformedTreeException exception) {
+ JavaManipulationPlugin.log(exception);
+ } catch (BadLocationException exception) {
+ JavaManipulationPlugin.log(exception);
+ }
+ return new String[] {};
+ }
+
+ private Expression createParenthesizedExpression(Expression newExpression, AST ast) {
+ ParenthesizedExpression parenthesized= ast.newParenthesizedExpression();
+ parenthesized.setExpression(newExpression);
+ return parenthesized;
+ }
+
+ private void replaceParameterWithExpression(ASTRewrite rewriter, CallContext context, ImportRewrite importRewrite) throws CoreException {
+ Expression[] arguments= context.arguments;
+ try {
+ ITextFileBuffer buffer= RefactoringFileBuffers.acquire(context.compilationUnit);
+ for (int i= 0; i < arguments.length; i++) {
+ Expression expression= arguments[i];
+ String expressionString= null;
+ if (expression instanceof SimpleName) {
+ expressionString= ((SimpleName)expression).getIdentifier();
+ } else {
+ try {
+ expressionString= buffer.getDocument().get(expression.getStartPosition(), expression.getLength());
+ } catch (BadLocationException exception) {
+ JavaManipulationPlugin.log(exception);
+ continue;
+ }
+ }
+ ParameterData parameter= getParameterData(i);
+ List references= parameter.references();
+ for (Iterator iter= references.iterator(); iter.hasNext();) {
+ ASTNode element= iter.next();
+ Expression newExpression= (Expression)rewriter.createStringPlaceholder(expressionString, expression.getNodeType());
+ AST ast= rewriter.getAST();
+ ITypeBinding explicitCast= ASTNodes.getExplicitCast(expression, (Expression)element);
+ if (explicitCast != null) {
+ CastExpression cast= ast.newCastExpression();
+ if (NecessaryParenthesesChecker.needsParentheses(expression, cast, CastExpression.EXPRESSION_PROPERTY)) {
+ newExpression= createParenthesizedExpression(newExpression, ast);
+ }
+ cast.setExpression(newExpression);
+ ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(expression, importRewrite);
+ cast.setType(importRewrite.addImport(explicitCast, ast, importRewriteContext, TypeLocation.CAST));
+ expression= newExpression= cast;
+ }
+ if (NecessaryParenthesesChecker.needsParentheses(expression, element.getParent(), element.getLocationInParent())) {
+ newExpression= createParenthesizedExpression(newExpression, ast);
+ }
+ rewriter.replace(element, newExpression, null);
+ }
+ }
+ } finally {
+ RefactoringFileBuffers.release(context.compilationUnit);
+ }
+ }
+
+ private void makeNamesUnique(ASTRewrite rewriter, CodeScopeBuilder.Scope scope) {
+ Collection usedCalleeNames= fAnalyzer.getUsedNames();
+ for (Iterator iter= usedCalleeNames.iterator(); iter.hasNext();) {
+ SourceAnalyzer.NameData nd= iter.next();
+ if (scope.isInUse(nd.getName())) {
+ String newName= scope.createName(nd.getName(), true);
+ List references= nd.references();
+ for (Iterator refs= references.iterator(); refs.hasNext();) {
+ SimpleName element= refs.next();
+ ASTNode newNode= rewriter.createStringPlaceholder(newName, ASTNode.SIMPLE_NAME);
+ rewriter.replace(element, newNode, null);
+ }
+ }
+ }
+ }
+
+ private void updateImplicitReceivers(ASTRewrite rewriter, CallContext context) {
+ if (context.receiver == null)
+ return;
+ List implicitReceivers= fAnalyzer.getImplicitReceivers();
+ for (Iterator iter= implicitReceivers.iterator(); iter.hasNext();) {
+ ASTNode node= iter.next();
+ ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(node, context.importer);
+ if (node instanceof MethodInvocation) {
+ final MethodInvocation inv= (MethodInvocation)node;
+ rewriter.set(inv, MethodInvocation.EXPRESSION_PROPERTY, createReceiver(rewriter, context, (IMethodBinding)inv.getName().resolveBinding(), importRewriteContext), null);
+ } else if (node instanceof ClassInstanceCreation) {
+ final ClassInstanceCreation inst= (ClassInstanceCreation)node;
+ rewriter.set(inst, ClassInstanceCreation.EXPRESSION_PROPERTY, createReceiver(rewriter, context, inst.resolveConstructorBinding(), importRewriteContext), null);
+ } else if (node instanceof ThisExpression) {
+ rewriter.replace(node, rewriter.createStringPlaceholder(context.receiver, ASTNode.METHOD_INVOCATION), null);
+ } else if (node instanceof FieldAccess) {
+ final FieldAccess access= (FieldAccess)node;
+ rewriter.set(access, FieldAccess.EXPRESSION_PROPERTY, createReceiver(rewriter, context, access.resolveFieldBinding(), importRewriteContext), null);
+ } else if (node instanceof SimpleName && ((SimpleName)node).resolveBinding() instanceof IVariableBinding) {
+ IVariableBinding vb= (IVariableBinding)((SimpleName)node).resolveBinding();
+ if (vb.isField()) {
+ Expression receiver= createReceiver(rewriter, context, vb, importRewriteContext);
+ if (receiver != null) {
+ FieldAccess access= node.getAST().newFieldAccess();
+ ASTNode target= rewriter.createMoveTarget(node);
+ access.setName((SimpleName)target);
+ access.setExpression(receiver);
+ rewriter.replace(node, access, null);
+ }
+ }
+ }
+ }
+ }
+
+ private void updateTypeReferences(ASTRewrite rewriter, CallContext context) {
+ ImportRewrite importer= context.importer;
+ for (Iterator iter= fAnalyzer.getTypesToImport().iterator(); iter.hasNext();) {
+ Name element= iter.next();
+ ITypeBinding binding= ASTNodes.getTypeBinding(element);
+ if (binding != null && !binding.isLocal()) {
+ // We have collected names not types. So we have to import
+ // the declaration type if we reference a parameterized type
+ // since we have an entry for every name node (e.g. one for
+ // Vector and one for Integer in Vector.
+ if (binding.isParameterizedType()) {
+ binding= binding.getTypeDeclaration();
+ }
+ String s= importer.addImport(binding);
+ if (!ASTNodes.asString(element).equals(s)) {
+ rewriter.replace(element, rewriter.createStringPlaceholder(s, ASTNode.SIMPLE_NAME), null);
+ }
+ }
+ }
+ }
+
+ private void updateStaticReferences(ASTRewrite rewriter, CallContext context) {
+ ImportRewrite importer= context.importer;
+ for (Iterator iter= fAnalyzer.getStaticsToImport().iterator(); iter.hasNext();) {
+ Name element= iter.next();
+ IBinding binding= element.resolveBinding();
+ if (binding != null) {
+ String s= importer.addStaticImport(binding);
+ if (!ASTNodes.asString(element).equals(s)) {
+ rewriter.replace(element, rewriter.createStringPlaceholder(s, ASTNode.SIMPLE_NAME), null);
+ }
+ }
+ }
+
+ }
+
+ private Expression createReceiver(ASTRewrite rewriter, CallContext context, IMethodBinding method, ImportRewriteContext importRewriteContext) {
+ String receiver= getReceiver(context, method.getModifiers(), importRewriteContext);
+ if (receiver == null)
+ return null;
+ return (Expression)rewriter.createStringPlaceholder(receiver, ASTNode.METHOD_INVOCATION);
+ }
+
+ private Expression createReceiver(ASTRewrite rewriter, CallContext context, IVariableBinding field, ImportRewriteContext importRewriteContext) {
+ String receiver= getReceiver(context, field.getModifiers(), importRewriteContext);
+ if (receiver == null)
+ return null;
+ return (Expression)rewriter.createStringPlaceholder(receiver, ASTNode.SIMPLE_NAME);
+ }
+
+ private String getReceiver(CallContext context, int modifiers, ImportRewriteContext importRewriteContext) {
+ String receiver= context.receiver;
+ ITypeBinding invocationType= ASTNodes.getEnclosingType(context.invocation);
+ ITypeBinding sourceType= fDeclaration.resolveBinding().getDeclaringClass();
+ if (!context.receiverIsStatic && Modifier.isStatic(modifiers)) {
+ if ("this".equals(receiver) && invocationType != null && Bindings.equals(invocationType, sourceType)) { //$NON-NLS-1$
+ receiver= null;
+ } else {
+ receiver= context.importer.addImport(sourceType, importRewriteContext);
+ }
+ }
+ return receiver;
+ }
+
+ private void updateTypeVariables(ASTRewrite rewriter, CallContext context) {
+ ITypeBinding type= context.getReceiverType();
+ if (type == null)
+ return;
+ rewriteReferences(rewriter, type.getTypeArguments(), fAnalyzer.getTypeParameterReferences());
+ }
+
+ private void updateMethodTypeVariable(ASTRewrite rewriter, CallContext context) {
+ IMethodBinding method= Invocations.resolveBinding(context.invocation);
+ if (method == null)
+ return;
+ rewriteReferences(rewriter, method.getTypeArguments(), fAnalyzer.getMethodTypeParameterReferences());
+ }
+
+ private void rewriteReferences(ASTRewrite rewriter, ITypeBinding[] typeArguments, List typeParameterReferences) {
+ if (typeArguments.length == 0)
+ return;
+ Assert.isTrue(typeArguments.length == typeParameterReferences.size());
+ for (int i= 0; i < typeArguments.length; i++) {
+ SourceAnalyzer.NameData refData= typeParameterReferences.get(i);
+ List references= refData.references();
+ String newName= typeArguments[i].getName();
+ for (Iterator iter= references.iterator(); iter.hasNext();) {
+ SimpleName name= iter.next();
+ rewriter.replace(name, rewriter.createStringPlaceholder(newName, ASTNode.SIMPLE_NAME), null);
+ }
+ }
+ }
+
+ private ASTNode getLastStatement() {
+ List statements= fDeclaration.getBody().statements();
+ if (statements.isEmpty())
+ return null;
+ return statements.get(statements.size() - 1);
+ }
+
+ private List getReturnStatementRanges() {
+ fMarkerMode= RETURN_STATEMENT_MODE;
+ List result= new ArrayList<>(1);
+ List statements= fDeclaration.getBody().statements();
+ int size= statements.size();
+ if (size <= 1)
+ return result;
+ result.add(createRange(statements, size - 2));
+ return result;
+ }
+
+ private List getStatementRanges() {
+ fMarkerMode= STATEMENT_MODE;
+ List result= new ArrayList<>(1);
+ List statements= fDeclaration.getBody().statements();
+ int size= statements.size();
+ if (size == 0)
+ return result;
+ result.add(createRange(statements, size - 1));
+ return result;
+ }
+
+ private List getExpressionRanges() {
+ fMarkerMode= EXPRESSION_MODE;
+ List result= new ArrayList<>(2);
+ List statements= fDeclaration.getBody().statements();
+ ReturnStatement rs= null;
+ int size= statements.size();
+ ASTNode node;
+ switch (size) {
+ case 0:
+ return result;
+ case 1:
+ node= statements.get(0);
+ if (node.getNodeType() == ASTNode.RETURN_STATEMENT) {
+ rs= (ReturnStatement)node;
+ } else {
+ result.add(createRange(node, node));
+ }
+ break;
+ default: {
+ node= statements.get(size - 1);
+ if (node.getNodeType() == ASTNode.RETURN_STATEMENT) {
+ result.add(createRange(statements, size - 2));
+ rs= (ReturnStatement)node;
+ } else {
+ result.add(createRange(statements, size - 1));
+ }
+ break;
+ }
+ }
+ if (rs != null) {
+ Expression exp= rs.getExpression();
+ result.add(createRange(exp, exp));
+ }
+ return result;
+ }
+
+ private IRegion createRange(List statements, int end) {
+ ASTNode first= statements.get(0);
+ ASTNode last= statements.get(end);
+ return createRange(first, last);
+ }
+
+ private IRegion createRange(ASTNode first, ASTNode last) {
+ ASTNode root= first.getRoot();
+ if (root instanceof CompilationUnit) {
+ CompilationUnit unit= (CompilationUnit)root;
+ int start= unit.getExtendedStartPosition(first);
+ int length = unit.getExtendedStartPosition(last) - start + unit.getExtendedLength(last);
+ IRegion range= new Region(start, length);
+ return range;
+ } else {
+ int start= first.getStartPosition();
+ int length = last.getStartPosition() - start + last.getLength();
+ IRegion range= new Region(start, length);
+ return range;
+ }
+ }
+
+ private String[] getBlocks(RangeMarker[] markers) throws BadLocationException {
+ String[] result= new String[markers.length];
+ for (int i= 0; i < markers.length; i++) {
+ RangeMarker marker= markers[i];
+ String content= fDocument.get(marker.getOffset(), marker.getLength());
+ String lines[]= Strings.convertIntoLines(content);
+ Strings.trimIndentation(lines, fTypeRoot.getJavaProject(), false);
+ if (fMarkerMode == STATEMENT_MODE && lines.length == 2 && isSingleControlStatementWithoutBlock()) {
+ lines[1]= CodeFormatterUtil.createIndentString(1, fTypeRoot.getJavaProject()) + lines[1];
+ }
+ result[i]= Strings.concatenate(lines, TextUtilities.getDefaultLineDelimiter(fDocument));
+ }
+ return result;
+ }
+
+ private boolean isSingleControlStatementWithoutBlock() {
+ List statements= fDeclaration.getBody().statements();
+ int size= statements.size();
+ if (size != 1)
+ return false;
+ Statement statement= statements.get(size - 1);
+ int nodeType= statement.getNodeType();
+ if (nodeType == ASTNode.IF_STATEMENT) {
+ IfStatement ifStatement= (IfStatement) statement;
+ return !(ifStatement.getThenStatement() instanceof Block)
+ && !(ifStatement.getElseStatement() instanceof Block);
+ } else if (nodeType == ASTNode.FOR_STATEMENT) {
+ return !(((ForStatement)statement).getBody() instanceof Block);
+ } else if (nodeType == ASTNode.WHILE_STATEMENT) {
+ return !(((WhileStatement)statement).getBody() instanceof Block);
+ }
+ return false;
+ }
+}
diff -Nru "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java" "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java"
--- "/tmp/tmpi_HR7o/juIeokMQQU/eclipse-jdt-ui-4.12/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java" 1970-01-01 00:00:00.000000000 +0000
+++ "/tmp/tmpi_HR7o/rAY6TrwEsM/eclipse-jdt-ui-4.15/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java" 2020-02-26 15:31:57.000000000 +0000
@@ -0,0 +1,515 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
+ * o Allow 'this' constructor to be inlined
+ * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
+ * Microsoft Corporation - copied to jdt.core.manipulation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.SourceRange;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.Initializer;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchPattern;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.refactoring.CollectingSearchRequestor;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
+import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
+import org.eclipse.jdt.internal.corext.util.SearchUtils;
+
+/**
+ * A TargetProvider provides all targets that have to be adapted, i.e. all method invocations that should be inlined.
+ */
+public abstract class TargetProvider {
+
+ public abstract void initialize();
+
+ public abstract ICompilationUnit[] getAffectedCompilationUnits(RefactoringStatus status, ReferencesInBinaryContext binaryRefs, IProgressMonitor pm) throws CoreException;
+
+ public abstract BodyDeclaration[] getAffectedBodyDeclarations(ICompilationUnit unit, IProgressMonitor pm);
+
+ // constructor invocation is not an expression but a statement
+ public abstract ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm);
+
+ public abstract RefactoringStatus checkActivation() throws JavaModelException;
+
+ public abstract int getStatusSeverity();
+
+ public boolean isSingle() {
+ return false;
+ }
+
+ public static TargetProvider create(ICompilationUnit cu, MethodInvocation invocation) {
+ return new SingleCallTargetProvider(cu, invocation);
+ }
+
+ public static TargetProvider create(ICompilationUnit cu, SuperMethodInvocation invocation) {
+ return new SingleCallTargetProvider(cu, invocation);
+ }
+
+ public static TargetProvider create(ICompilationUnit cu, ConstructorInvocation invocation) {
+ return new SingleCallTargetProvider(cu, invocation);
+ }
+
+ public static TargetProvider create(MethodDeclaration declaration) {
+ IMethodBinding method= declaration.resolveBinding();
+ if (method == null)
+ return new ErrorTargetProvider(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.TargetProvider_method_declaration_not_unique));
+ ITypeBinding type= method.getDeclaringClass();
+ if (type.isLocal()) {
+ if (((IType) type.getJavaElement()).isBinary()) {
+ return new ErrorTargetProvider(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.TargetProvider_cannot_local_method_in_binary));
+ } else {
+ IType declaringClassOfLocal= (IType) type.getDeclaringClass().getJavaElement();
+ return new LocalTypeTargetProvider(declaringClassOfLocal.getCompilationUnit(), declaration);
+ }
+ } else {
+ return new MemberTypeTargetProvider(declaration.resolveBinding());
+ }
+ }
+
+ public static TargetProvider create(IMethodBinding methodBinding) {
+ return new MemberTypeTargetProvider(methodBinding);
+ }
+
+ static void fastDone(IProgressMonitor pm) {
+ if (pm == null)
+ return;
+ pm.beginTask("", 1); //$NON-NLS-1$
+ pm.worked(1);
+ pm.done();
+ }
+
+ static class ErrorTargetProvider extends TargetProvider {
+ private RefactoringStatus fErrorStatus;
+ public ErrorTargetProvider(RefactoringStatus status) {
+ fErrorStatus= status;
+ }
+ @Override
+ public RefactoringStatus checkActivation() throws JavaModelException {
+ return fErrorStatus;
+ }
+ @Override
+ public void initialize() {
+ }
+ @Override
+ public ICompilationUnit[] getAffectedCompilationUnits(RefactoringStatus status, ReferencesInBinaryContext binaryRefs, IProgressMonitor pm) throws JavaModelException {
+ return null;
+ }
+ @Override
+ public BodyDeclaration[] getAffectedBodyDeclarations(ICompilationUnit unit, IProgressMonitor pm) {
+ return null;
+ }
+ @Override
+ public ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm) {
+ return null;
+ }
+ @Override
+ public int getStatusSeverity() {
+ return 0;
+ }
+ }
+
+ static class SingleCallTargetProvider extends TargetProvider {
+ private ICompilationUnit fCUnit;
+ private ASTNode fInvocation;
+ private boolean fIterated;
+ public SingleCallTargetProvider(ICompilationUnit cu, ASTNode invocation) {
+ Assert.isNotNull(cu);
+ Assert.isNotNull(invocation);
+ Assert.isTrue(Invocations.isInvocation(invocation));
+ fCUnit= cu;
+ fInvocation= invocation;
+ }
+ @Override
+ public void initialize() {
+ fIterated= false;
+ }
+ @Override
+ public ICompilationUnit[] getAffectedCompilationUnits(RefactoringStatus status, ReferencesInBinaryContext binaryRefs, IProgressMonitor pm) {
+ return new ICompilationUnit[] { fCUnit };
+ }
+ @Override
+ public BodyDeclaration[] getAffectedBodyDeclarations(ICompilationUnit unit, IProgressMonitor pm) {
+ Assert.isTrue(unit == fCUnit);
+ if (fIterated)
+ return new BodyDeclaration[0];
+ fastDone(pm);
+ return new BodyDeclaration[] {
+ ASTNodes.getParent(fInvocation, BodyDeclaration.class)
+ };
+ }
+
+ @Override
+ public ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm) {
+ fastDone(pm);
+ if (fIterated)
+ return null;
+ fIterated= true;
+ return new ASTNode[] { fInvocation };
+ }
+ @Override
+ public RefactoringStatus checkActivation() throws JavaModelException {
+ return new RefactoringStatus();
+ }
+ @Override
+ public int getStatusSeverity() {
+ return RefactoringStatus.FATAL;
+ }
+ @Override
+ public boolean isSingle() {
+ return true;
+ }
+ }
+
+ private static class BodyData {
+ private List fInvocations;
+
+ public BodyData() {
+ }
+ public void addInvocation(ASTNode node) {
+ if (fInvocations == null)
+ fInvocations= new ArrayList<>(2);
+ fInvocations.add(node);
+ }
+ public ASTNode[] getInvocations() {
+ return fInvocations.toArray(new ASTNode[fInvocations.size()]);
+ }
+ public boolean hasInvocations() {
+ return fInvocations != null && !fInvocations.isEmpty();
+ }
+ }
+
+ private static class InvocationFinder extends ASTVisitor {
+ Map result= new HashMap<>(2);
+ Stack fBodies= new Stack<>();
+ BodyData fCurrent;
+ private IMethodBinding fBinding;
+ public InvocationFinder(IMethodBinding binding) {
+ Assert.isNotNull(binding);
+ fBinding= binding.getMethodDeclaration();
+ Assert.isNotNull(fBinding);
+ }
+ @Override
+ public boolean visit(EnumConstantDeclaration node) {
+ return visitNonTypeBodyDeclaration();
+ }
+
+ @Override
+ public void endVisit(EnumConstantDeclaration node) {
+ if (fCurrent.hasInvocations()) {
+ result.put(node, fCurrent);
+ }
+ endVisitBodyDeclaration();
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ if (node.resolveTypeBinding() != null && matches(node.resolveMethodBinding()) && fCurrent != null) {
+ fCurrent.addInvocation(node);
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ if (matches(node.getName().resolveBinding()) && fCurrent != null) {
+ fCurrent.addInvocation(node);
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(ConstructorInvocation node) {
+ if (matches(node.resolveConstructorBinding()) && fCurrent != null) {
+ fCurrent.addInvocation(node);
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(ClassInstanceCreation node) {
+ if (matches(node.resolveConstructorBinding()) && fCurrent != null) {
+ fCurrent.addInvocation(node);
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(TypeDeclaration node) {
+ return visitType();
+ }
+ @Override
+ public void endVisit(TypeDeclaration node) {
+ endVisitBodyDeclaration();
+ }
+ @Override
+ public boolean visit(EnumDeclaration node) {
+ return visitType();
+ }
+ @Override
+ public void endVisit(EnumDeclaration node) {
+ endVisitBodyDeclaration();
+ }
+ @Override
+ public boolean visit(AnnotationTypeDeclaration node) {
+ return visitType();
+ }
+ @Override
+ public void endVisit(AnnotationTypeDeclaration node) {
+ endVisitBodyDeclaration();
+ }
+ private boolean visitType() {
+ fBodies.add(fCurrent);
+ fCurrent= null;
+ return true;
+ }
+ protected boolean visitNonTypeBodyDeclaration() {
+ fBodies.add(fCurrent);
+ fCurrent= new BodyData();
+ return true;
+ }
+ protected void endVisitBodyDeclaration() {
+ fCurrent= fBodies.remove(fBodies.size() - 1);
+ }
+ @Override
+ public boolean visit(FieldDeclaration node) {
+ return visitNonTypeBodyDeclaration();
+ }
+ @Override
+ public void endVisit(FieldDeclaration node) {
+ if (fCurrent.hasInvocations()) {
+ result.put(node, fCurrent);
+ }
+ endVisitBodyDeclaration();
+ }
+ @Override
+ public boolean visit(MethodDeclaration node) {
+ return visitNonTypeBodyDeclaration();
+ }
+ @Override
+ public void endVisit(MethodDeclaration node) {
+ if (fCurrent.hasInvocations()) {
+ result.put(node, fCurrent);
+ }
+ endVisitBodyDeclaration();
+
+ }
+ @Override
+ public boolean visit(Initializer node) {
+ return visitNonTypeBodyDeclaration();
+ }
+ @Override
+ public void endVisit(Initializer node) {
+ if (fCurrent.hasInvocations()) {
+ result.put(node, fCurrent);
+ }
+ endVisitBodyDeclaration();
+ }
+ private boolean matches(IBinding binding) {
+ if (!(binding instanceof IMethodBinding))
+ return false;
+ return fBinding.isEqualTo(((IMethodBinding)binding).getMethodDeclaration());
+ }
+ }
+
+ private static class LocalTypeTargetProvider extends TargetProvider {
+ private ICompilationUnit fCUnit;
+ private MethodDeclaration fDeclaration;
+ private Map