SearchEngine
.
+ * Only collects matches in CUs ands offers a scanner to trim match ranges.
+ * If a {@link ReferencesInBinaryContext} is passed, matches that are
+ * inside a binary element are not collected (but added to the context if they are accurate).
+ */
+public class CuCollectingSearchRequestor extends CollectingSearchRequestor {
+
+ private IJavaProject fProjectCache;
+ private IScanner fScannerCache;
+
+ public CuCollectingSearchRequestor() {
+ this(null);
+ }
+
+ public CuCollectingSearchRequestor(ReferencesInBinaryContext binaryRefs) {
+ super(binaryRefs);
+ }
+
+ protected IScanner getScanner(ICompilationUnit unit) {
+ IJavaProject project= unit.getJavaProject();
+ if (project.equals(fProjectCache))
+ return fScannerCache;
+
+ fProjectCache= project;
+ String sourceLevel= project.getOption(JavaCore.COMPILER_SOURCE, true);
+ String complianceLevel= project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
+ fScannerCache= ToolFactory.createScanner(false, false, false, sourceLevel, complianceLevel);
+ return fScannerCache;
+ }
+
+ /**
+ * This is an internal method. Do not call from subclasses!
+ * Use {@link #collectMatch(SearchMatch)} instead.
+ * @param match
+ * @throws CoreException
+ * @deprecated
+ */
+ @Deprecated
+ @Override
+ public final void acceptSearchMatch(SearchMatch match) throws CoreException {
+ if (filterMatch(match))
+ return;
+
+ ICompilationUnit unit= SearchUtils.getCompilationUnit(match);
+ if (unit != null) {
+ acceptSearchMatch(unit, match);
+ }
+ }
+
+ /**
+ * Handles the given match in the given compilation unit.
+ * The default implementation accepts all matches.
+ * Subclasses can override and call {@link #collectMatch(SearchMatch)} to collect matches.
+ *
+ * @param unit the enclosing CU of the match, never null
+ * @param match the match
+ * @throws CoreException if something bad happens
+ */
+ protected void acceptSearchMatch(ICompilationUnit unit, SearchMatch match) throws CoreException {
+ collectMatch(match);
+ }
+
+ @Override
+ public void endReporting() {
+ fProjectCache= null;
+ fScannerCache= null;
+ }
+}
+
+
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ExceptionInfo.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ExceptionInfo.java
new file mode 100644
index 0000000000..ea0980b5a0
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ExceptionInfo.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.refactoring;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeParameter;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+
+
+public class ExceptionInfo {
+ private final IJavaElement fElement;
+ private final ITypeBinding fTypeBinding;
+ private int fKind;
+
+ public static final int OLD= 0;
+ public static final int ADDED= 1;
+ public static final int DELETED= 2;
+
+ public ExceptionInfo(IJavaElement element, int kind, ITypeBinding binding) {
+ Assert.isNotNull(element);
+ Assert.isTrue(element instanceof IType || element instanceof ITypeParameter);
+ fElement= element;
+ fKind= kind;
+ fTypeBinding= binding;
+ }
+
+ public static ExceptionInfo createInfoForOldException(IJavaElement element, ITypeBinding binding){
+ return new ExceptionInfo(element, OLD, binding);
+ }
+ public static ExceptionInfo createInfoForAddedException(IType type){
+ return new ExceptionInfo(type, ADDED, null);
+ }
+
+ public void markAsDeleted(){
+ Assert.isTrue(! isAdded());//added exception infos should be simply removed from the list
+ fKind= DELETED;
+ }
+
+ public void markAsOld(){
+ Assert.isTrue(isDeleted());
+ fKind= OLD;
+ }
+
+ public boolean isAdded(){
+ return fKind == ADDED;
+ }
+
+ public boolean isDeleted(){
+ return fKind == DELETED;
+ }
+
+ public boolean isOld(){
+ return fKind == OLD;
+ }
+
+ public IJavaElement getElement() {
+ return fElement;
+ }
+
+ public String getFullyQualifiedName() {
+ return fElement instanceof IType ? ((IType) fElement).getFullyQualifiedName('.') : fElement.getElementName();
+ }
+
+ public int getKind() {
+ return fKind;
+ }
+
+ /**
+ * @return ITypeBinding the typeBinding (for OLD and DELETED exceptions) or null
+ */
+ public ITypeBinding getTypeBinding() {
+ return fTypeBinding;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result= new StringBuilder();
+ switch (fKind) {
+ case OLD : result.append("OLD: "); break; //$NON-NLS-1$
+ case ADDED : result.append("ADDED: "); break; //$NON-NLS-1$
+ case DELETED : result.append("DELETED: "); break; //$NON-NLS-1$
+ }
+ if (fElement == null)
+ result.append("null"); //$NON-NLS-1$
+ else
+ result.append(fElement.toString());
+ return result.toString();
+ }
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ParameterInfo.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ParameterInfo.java
new file mode 100644
index 0000000000..685d438853
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ParameterInfo.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.refactoring;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+
+
+public class ParameterInfo {
+
+ public static final int INDEX_FOR_ADDED= -1;
+ public static final String ELLIPSIS= "..."; //$NON-NLS-1$
+
+ private IVariableBinding fOldBinding;
+ private ITypeBinding fOldTypeBinding;
+ private final String fOldName;
+ private final String fOldTypeName;
+ private final int fOldIndex;
+
+ private String fNewTypeName;
+ private ITypeBinding fNewTypeBinding;
+ private String fDefaultValue;
+ private String fNewName;
+ private boolean fIsDeleted;
+ private boolean fCreateField=true;
+ private boolean fInlined;
+ private boolean fResolve= true;
+
+ public ParameterInfo(String type, String name, int index) {
+ this(null, null, type, name, index);
+ }
+
+ public ParameterInfo(IVariableBinding binding, String type, String name, int index) {
+ this(binding, null, type, name, index);
+ }
+
+ private ParameterInfo(IVariableBinding binding, ITypeBinding typeBinding, String type, String name, int index) {
+ fOldBinding= binding;
+ fOldTypeBinding= typeBinding;
+ fNewTypeBinding= typeBinding;
+ fOldTypeName= type;
+ fNewTypeName= type;
+ fOldName= name;
+ fNewName= name;
+ fOldIndex= index;
+ fDefaultValue= ""; //$NON-NLS-1$
+ fIsDeleted= false;
+ }
+
+
+ /**
+ * Creates a new ParameterInfo. Parameter is marked as added and not resolvable
+ * @param type the fullyqualified type
+ * @param name the name
+ * @return the parameter info object
+ */
+ public static ParameterInfo createInfoForAddedParameter(String type, String name) {
+ ParameterInfo info= new ParameterInfo("", "", INDEX_FOR_ADDED); //$NON-NLS-1$ //$NON-NLS-2$
+ info.setNewTypeName(type);
+ info.setNewName(name);
+ info.setResolve(false);
+ return info;
+ }
+
+ private void setResolve(boolean resolve) {
+ fResolve= resolve;
+ }
+
+ public static ParameterInfo createInfoForAddedParameter(String type, String name, String defaultValue) {
+ ParameterInfo info= new ParameterInfo("", "", INDEX_FOR_ADDED); //$NON-NLS-1$ //$NON-NLS-2$
+ info.setNewTypeName(type);
+ info.setNewName(name);
+ info.setDefaultValue(defaultValue);
+ return info;
+ }
+
+ public static ParameterInfo createInfoForAddedParameter(ITypeBinding typeBinding, String type, String name, String defaultValue) {
+ ParameterInfo info= new ParameterInfo(null, typeBinding, "", "", INDEX_FOR_ADDED); //$NON-NLS-1$ //$NON-NLS-2$
+ info.setNewTypeName(type);
+ info.setNewName(name);
+ info.setDefaultValue(defaultValue);
+ return info;
+ }
+
+ public int getOldIndex() {
+ return fOldIndex;
+ }
+
+ public boolean isDeleted(){
+ return fIsDeleted;
+ }
+
+ public void markAsDeleted(){
+ Assert.isTrue(! isAdded());//added param infos should be simply removed from the list
+ fIsDeleted= true;
+ }
+
+ public boolean isAdded(){
+ return fOldIndex == INDEX_FOR_ADDED;
+ }
+
+ public boolean isTypeNameChanged() {
+ return !fOldTypeName.equals(fNewTypeName);
+ }
+
+ public boolean isRenamed() {
+ return !fOldName.equals(fNewName);
+ }
+
+ public boolean isVarargChanged() {
+ return isOldVarargs() != isNewVarargs();
+ }
+
+ public IVariableBinding getOldBinding() {
+ return fOldBinding;
+ }
+
+ public String getOldTypeName() {
+ return fOldTypeName;
+ }
+
+ public String getNewTypeName() {
+ return fNewTypeName;
+ }
+
+ public void setNewTypeName(String type){
+ Assert.isNotNull(type);
+ fNewTypeName= type;
+ }
+
+ public ITypeBinding getNewTypeBinding() {
+ return fNewTypeBinding;
+ }
+
+ public void setNewTypeBinding(ITypeBinding typeBinding){
+ fNewTypeBinding= typeBinding;
+ }
+
+ public boolean isOldVarargs() {
+ return isVarargs(fOldTypeName);
+ }
+
+ public boolean isNewVarargs() {
+ return isVarargs(fNewTypeName);
+ }
+
+ public String getOldName() {
+ return fOldName;
+ }
+
+ public String getNewName() {
+ return fNewName;
+ }
+
+ public void setNewName(String newName) {
+ Assert.isNotNull(newName);
+ fNewName= newName;
+ }
+
+ public String getDefaultValue(){
+ return fDefaultValue;
+ }
+
+ public void setDefaultValue(String value){
+ Assert.isNotNull(value);
+ fDefaultValue= value;
+ }
+
+ @Override
+ public String toString() {
+ return fOldTypeName + " " + fOldName + " @" + fOldIndex + " -> " //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+ + fNewTypeName + " " + fNewName + ": " + fDefaultValue //$NON-NLS-1$//$NON-NLS-2$
+ + (fIsDeleted ? " (deleted)" : " (stays)"); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ public static String stripEllipsis(String typeName) {
+ if (isVarargs(typeName))
+ return typeName.substring(0, typeName.length() - 3);
+ else
+ return typeName;
+ }
+
+ public static boolean isVarargs(String typeName) {
+ return typeName.endsWith("..."); //$NON-NLS-1$
+ }
+
+ public ITypeBinding getOldTypeBinding() {
+ return fOldTypeBinding;
+ }
+
+ public boolean isCreateField() {
+ return fCreateField;
+ }
+
+ public void setCreateField(boolean createField) {
+ fIsDeleted= createField;
+ fCreateField= createField;
+ }
+
+ public void setOldBinding(IVariableBinding binding) {
+ //The variableBinding is needed by IPOR to check what modifier were present
+ fOldBinding=binding;
+ fOldTypeBinding=binding.getType();
+ fNewTypeBinding=binding.getType();
+ }
+
+ public void setInlined(boolean inlined) {
+ fInlined=inlined;
+ }
+
+ public boolean isInlined() {
+ return fInlined;
+ }
+
+ public boolean isResolve() {
+ return fResolve;
+ }
+
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ReturnTypeInfo.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ReturnTypeInfo.java
new file mode 100644
index 0000000000..e737bc7f93
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/ReturnTypeInfo.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.refactoring;
+
+import org.eclipse.core.runtime.Assert;
+
+import org.eclipse.jdt.core.dom.ITypeBinding;
+
+
+public class ReturnTypeInfo {
+
+ private final String fOldTypeName;
+ private String fNewTypeName;
+ private ITypeBinding fNewTypeBinding;
+
+ public ReturnTypeInfo(String returnType) {
+ fOldTypeName= returnType;
+ fNewTypeName= returnType;
+ }
+
+ public String getOldTypeName() {
+ return fOldTypeName;
+ }
+
+ public String getNewTypeName() {
+ return fNewTypeName;
+ }
+
+ public void setNewTypeName(String type){
+ Assert.isNotNull(type);
+ fNewTypeName= type;
+ }
+
+ public ITypeBinding getNewTypeBinding() {
+ return fNewTypeBinding;
+ }
+
+ public void setNewTypeBinding(ITypeBinding typeBinding){
+ fNewTypeBinding= typeBinding;
+ }
+
+ public boolean isTypeNameChanged() {
+ return !fOldTypeName.equals(fNewTypeName);
+ }
+
+ @Override
+ public String toString() {
+ return fOldTypeName + " -> " + fNewTypeName; //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/StubTypeContext.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/StubTypeContext.java
new file mode 100644
index 0000000000..54e66ad636
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/StubTypeContext.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.refactoring;
+
+ import org.eclipse.jdt.core.ICompilationUnit;
+
+
+ public class StubTypeContext {
+ private String fBeforeString;
+ private String fAfterString;
+ private final ICompilationUnit fCuHandle;
+
+ public StubTypeContext(ICompilationUnit cuHandle, String beforeString, String afterString) {
+ fCuHandle= cuHandle;
+ fBeforeString= beforeString;
+ fAfterString= afterString;
+ }
+
+ public ICompilationUnit getCuHandle() {
+ return fCuHandle;
+ }
+
+ public String getBeforeString() {
+ return fBeforeString;
+ }
+
+ public String getAfterString() {
+ return fAfterString;
+ }
+ }
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/TypeContextChecker.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/TypeContextChecker.java
new file mode 100644
index 0000000000..3a8e501b6a
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/TypeContextChecker.java
@@ -0,0 +1,864 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+ package org.eclipse.jdt.internal.corext.refactoring;
+
+ import java.util.ArrayList;
+ import java.util.Collections;
+ 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.NullProgressMonitor;
+
+ import org.eclipse.core.resources.IProject;
+
+ import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+ import org.eclipse.jdt.core.Flags;
+ import org.eclipse.jdt.core.ICompilationUnit;
+ import org.eclipse.jdt.core.IJavaProject;
+ import org.eclipse.jdt.core.IMethod;
+ import org.eclipse.jdt.core.IPackageFragment;
+ import org.eclipse.jdt.core.IPackageFragmentRoot;
+ import org.eclipse.jdt.core.ISourceRange;
+ import org.eclipse.jdt.core.IType;
+ import org.eclipse.jdt.core.ITypeParameter;
+ import org.eclipse.jdt.core.JavaCore;
+ import org.eclipse.jdt.core.JavaModelException;
+ import org.eclipse.jdt.core.WorkingCopyOwner;
+ 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.AnnotationTypeDeclaration;
+ import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
+ import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+ import org.eclipse.jdt.core.dom.ArrayType;
+ 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.CompilationUnit;
+ import org.eclipse.jdt.core.dom.EnumDeclaration;
+ import org.eclipse.jdt.core.dom.IExtendedModifier;
+ import org.eclipse.jdt.core.dom.ITypeBinding;
+ import org.eclipse.jdt.core.dom.ImportDeclaration;
+ 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.NameQualifiedType;
+ import org.eclipse.jdt.core.dom.NodeFinder;
+ import org.eclipse.jdt.core.dom.PackageDeclaration;
+ 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.RecordDeclaration;
+ import org.eclipse.jdt.core.dom.SimpleName;
+ import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+ 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.manipulation.TypeNameMatchCollector;
+ 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.SearchPattern;
+ import org.eclipse.jdt.core.search.TypeNameMatch;
+
+ import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+ import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+ import org.eclipse.jdt.internal.corext.dom.ASTFlattener;
+ 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.Selection;
+ import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer;
+ import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
+ import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+ import org.eclipse.jdt.internal.corext.util.Messages;
+
+ import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaTypeCompletionProcessorCore;
+
+ public class TypeContextChecker {
+ private static class MethodTypesChecker {
+
+ private static final String METHOD_NAME= "__$$__"; //$NON-NLS-1$
+
+ private final IMethod fMethod;
+ private final StubTypeContext fStubTypeContext;
+ private final Listtrue
by default, subclasses can override
+ */
+ public boolean needsParameterUsedCheck() {
+ return true;
+ }
+
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java
new file mode 100644
index 0000000000..c5835d2e37
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java
@@ -0,0 +1,2914 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.structure;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+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.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+import org.eclipse.core.resources.IFile;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
+import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
+import org.eclipse.ltk.core.refactoring.TextChange;
+import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
+import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
+import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor;
+import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
+
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.ICompilationUnit;
+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.Signature;
+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.ASTParser;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+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.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.Dimension;
+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.ExpressionMethodReference;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.LambdaExpression;
+import org.eclipse.jdt.core.dom.MemberRef;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.MethodRef;
+import org.eclipse.jdt.core.dom.MethodRefParameter;
+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.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.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.TextElement;
+import org.eclipse.jdt.core.dom.Type;
+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;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
+import org.eclipse.jdt.core.refactoring.descriptors.ChangeMethodSignatureDescriptor;
+import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
+import org.eclipse.jdt.core.refactoring.participants.ChangeMethodSignatureArguments;
+import org.eclipse.jdt.core.refactoring.participants.ChangeMethodSignatureArguments.Parameter;
+import org.eclipse.jdt.core.refactoring.participants.ChangeMethodSignatureArguments.ThrownException;
+import org.eclipse.jdt.core.refactoring.participants.IRefactoringProcessorIds;
+import org.eclipse.jdt.core.refactoring.participants.JavaParticipantManager;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.MethodReferenceMatch;
+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.core.manipulation.JavaElementLabelsCore;
+import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
+import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
+import org.eclipse.jdt.internal.corext.CorextCore;
+import org.eclipse.jdt.internal.corext.SourceRangeFactory;
+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.Bindings;
+import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
+import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
+import org.eclipse.jdt.internal.corext.dom.Selection;
+import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.CuCollectingSearchRequestor;
+import org.eclipse.jdt.internal.corext.refactoring.ExceptionInfo;
+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.ParameterInfo;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
+import org.eclipse.jdt.internal.corext.refactoring.ReturnTypeInfo;
+import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
+import org.eclipse.jdt.internal.corext.refactoring.StubTypeContext;
+import org.eclipse.jdt.internal.corext.refactoring.TypeContextChecker;
+import org.eclipse.jdt.internal.corext.refactoring.base.JavaStringStatusContext;
+import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes;
+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.code.Invocations;
+import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateMethodCreator;
+import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors;
+import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks;
+import org.eclipse.jdt.internal.corext.refactoring.rename.RefactoringAnalyzeUtil;
+import org.eclipse.jdt.internal.corext.refactoring.rename.RippleMethodFinder2;
+import org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.tagging.IDelegateUpdating;
+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.JavadocUtil;
+import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
+import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
+import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
+import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+import org.eclipse.jdt.internal.corext.util.Messages;
+import org.eclipse.jdt.internal.corext.util.SearchUtils;
+import org.eclipse.jdt.ls.core.internal.corext.refactoring.RefactoringAvailabilityTester;
+
+
+public class ChangeSignatureProcessor extends RefactoringProcessor implements IDelegateUpdating {
+
+ private static final String ATTRIBUTE_RETURN= "return"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_PARAMETER= "parameter"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_DEFAULT= "default"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_KIND= "kind"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_DELEGATE= "delegate"; //$NON-NLS-1$
+ private static final String ATTRIBUTE_DEPRECATE= "deprecate"; //$NON-NLS-1$
+
+ private Listnull
if invoked by scripting framework
+ * @throws JavaModelException if something's wrong with the given method
+ */
+ public ChangeSignatureProcessor(IMethod method) throws JavaModelException {
+ fMethod= method;
+ fOldVarargIndex= -1;
+ fDelegateUpdating= false;
+ fDelegateDeprecation= true;
+ if (fMethod != null) {
+ fParameterInfos= createParameterInfoList(method);
+ // fExceptionInfos is created in checkInitialConditions
+ fReturnTypeInfo= new ReturnTypeInfo(Signature.toString(Signature.getReturnType(fMethod.getSignature())));
+ fMethodName= fMethod.getElementName();
+ fVisibility= JdtFlags.getVisibilityCode(fMethod);
+ }
+ }
+
+ private static ListParameterInfo
objects.
+ */
+ public ListExceptionInfo
objects.
+ */
+ public Listtrue
if the problem needs to be reported
+ */
+ protected boolean shouldReport(IProblem problem, CompilationUnit cu) {
+ if (! problem.isError())
+ return false;
+ if (problem.getID() == IProblem.UndefinedType) //reported when trying to import
+ return false;
+ return true;
+ }
+
+ private String getOldMethodParameters() {
+ StringBuilder buff= new StringBuilder();
+ int i= 0;
+ for (Iteratornull
for an empty vararg argument
+ */
+ protected abstract N createNewParamgument(ParameterInfo info, ListTagElement just before a new TagElement
with name
+ * tagName
, or null
.
+ */
+ private TagElement findTagElementToInsertAfter(List tags, String tagName) {
+ List tagOrder= Arrays.asList(
+ TagElement.TAG_AUTHOR,
+ TagElement.TAG_VERSION,
+ TagElement.TAG_PARAM,
+ TagElement.TAG_RETURN,
+ TagElement.TAG_THROWS,
+ TagElement.TAG_EXCEPTION,
+ TagElement.TAG_SEE,
+ TagElement.TAG_SINCE,
+ TagElement.TAG_SERIAL,
+ TagElement.TAG_SERIALFIELD,
+ TagElement.TAG_SERIALDATA,
+ TagElement.TAG_DEPRECATED,
+ TagElement.TAG_VALUE
+ );
+ int goalOrdinal= tagOrder.indexOf(tagName);
+ if (goalOrdinal == -1) // unknown tag -> to end
+ return (tags.isEmpty()) ? null : (TagElement) tags.get(tags.size());
+ for (int i= 0; i < tags.size(); i++) {
+ int tagOrdinal= tagOrder.indexOf(tags.get(i).getTagName());
+ if (tagOrdinal >= goalOrdinal)
+ return (i == 0) ? null : (TagElement) tags.get(i - 1);
+ }
+ return (tags.isEmpty()) ? null : (TagElement) tags.get(tags.size() - 1);
+ }
+
+
+ @Override
+ protected SingleVariableDeclaration createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) {
+ return createNewSingleVariableDeclaration(info);
+ }
+
+ @Override
+ protected ASTNode getNode() {
+ return fMethDecl;
+ }
+
+ @Override
+ protected VariableDeclaration getParameter(int index) {
+ return (VariableDeclaration) fMethDecl.parameters().get(index);
+ }
+
+ @Override
+ protected SimpleName getMethodNameNode() {
+ return fMethDecl.getName();
+ }
+
+ }
+
+ class DocReferenceUpdate extends OccurrenceUpdate {
+ /** instanceof MemberRef || MethodRef */
+ private ASTNode fNode;
+
+ protected DocReferenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) {
+ super(cuRewrite, cuRewrite.createGroupDescription(RefactoringCoreMessages.ChangeSignatureRefactoring_update_javadoc_reference), result);
+ fNode= node;
+ }
+
+ @Override
+ public void updateNode() {
+ if (fNode instanceof MethodRef) {
+ changeParamguments();
+ reshuffleElements();
+ }
+ if (canChangeNameAndReturnType())
+ changeMethodName();
+ }
+
+ @Override
+ protected MethodRefParameter createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) {
+ return createNewMethodRefParameter(info);
+ }
+
+ private MethodRefParameter createNewMethodRefParameter(ParameterInfo info) {
+ MethodRefParameter newP= getASTRewrite().getAST().newMethodRefParameter();
+
+ // only add name iff first parameter already has a name:
+ List extends ASTNode> parameters= getParamgumentsRewrite().getOriginalList();
+ if (parameters.size() > 0)
+ if (((MethodRefParameter) parameters.get(0)).getName() != null)
+ newP.setName(getASTRewrite().getAST().newSimpleName(info.getNewName()));
+
+ newP.setType(createNewDocRefType(info));
+ newP.setVarargs(info.isNewVarargs());
+ return newP;
+ }
+
+ private Type createNewDocRefType(ParameterInfo info) {
+ String newTypeName= ParameterInfo.stripEllipsis(info.getNewTypeName());
+ ITypeBinding newTypeBinding= info.getNewTypeBinding();
+ if (newTypeBinding != null)
+ newTypeBinding= newTypeBinding.getErasure(); //see bug 83127: Javadoc references are raw (erasures)
+ return createNewTypeNode(newTypeName, newTypeBinding);
+ }
+
+ @Override
+ protected SimpleName getMethodNameNode() {
+ if (fNode instanceof MemberRef)
+ return ((MemberRef) fNode).getName();
+
+ if (fNode instanceof MethodRef)
+ return ((MethodRef) fNode).getName();
+
+ return null;
+ }
+
+ /** @return {@inheritDoc} (element type: MethodRefParameter) */
+ @Override
+ protected ListRewrite getParamgumentsRewrite() {
+ return getASTRewrite().getListRewrite(fNode, MethodRef.PARAMETERS_PROPERTY);
+ }
+
+ @Override
+ protected void changeParamgumentName(ParameterInfo info) {
+ if (! (fNode instanceof MethodRef))
+ return;
+
+ MethodRefParameter oldParam= (MethodRefParameter) ((MethodRef) fNode).parameters().get(info.getOldIndex());
+ SimpleName oldParamName= oldParam.getName();
+ if (oldParamName != null)
+ getASTRewrite().set(oldParamName, SimpleName.IDENTIFIER_PROPERTY, info.getNewName(), fDescription);
+ }
+
+ @Override
+ protected void changeParamgumentType(ParameterInfo info) {
+ if (! (fNode instanceof MethodRef))
+ return;
+
+ MethodRefParameter oldParam= (MethodRefParameter) ((MethodRef) fNode).parameters().get(info.getOldIndex());
+ Type oldTypeNode= oldParam.getType();
+ Type newTypeNode= createNewDocRefType(info);
+ if (info.isNewVarargs()) {
+ if (info.isOldVarargs() && ! oldParam.isVarargs()) {
+ // leave as array reference if old reference was not vararg
+ newTypeNode= ASTNodeFactory.newArrayType(newTypeNode);
+ } else {
+ getASTRewrite().set(oldParam, MethodRefParameter.VARARGS_PROPERTY, Boolean.TRUE, fDescription);
+ }
+ } else {
+ if (oldParam.isVarargs()) {
+ getASTRewrite().set(oldParam, MethodRefParameter.VARARGS_PROPERTY, Boolean.FALSE, fDescription);
+ }
+ }
+
+ getASTRewrite().replace(oldTypeNode, newTypeNode, fDescription);
+ registerImportRemoveNode(oldTypeNode);
+ }
+ }
+
+ class StaticImportUpdate extends OccurrenceUpdate {
+
+ private final ImportDeclaration fImportDecl;
+
+ public StaticImportUpdate(ImportDeclaration importDecl, CompilationUnitRewrite cuRewrite, RefactoringStatus result) {
+ super(cuRewrite, null, result);
+ fImportDecl= importDecl;
+ }
+
+ @Override
+ public void updateNode() throws JavaModelException {
+ ImportRewrite importRewrite= fCuRewrite.getImportRewrite();
+ QualifiedName name= (QualifiedName) fImportDecl.getName();
+ //will be removed by importRemover if not used elsewhere ... importRewrite.removeStaticImport(name.getFullyQualifiedName());
+ importRewrite.addStaticImport(name.getQualifier().getFullyQualifiedName(), fMethodName, false);
+ }
+
+ @Override
+ protected ListRewrite getParamgumentsRewrite() {
+ return null;
+ }
+
+ @Override
+ protected ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) {
+ return null;
+ }
+
+ @Override
+ protected SimpleName getMethodNameNode() {
+ return null;
+ }
+ }
+
+ class NullOccurrenceUpdate extends OccurrenceUpdate {
+ private ASTNode fNode;
+ protected NullOccurrenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) {
+ super(cuRewrite, null, result);
+ fNode= node;
+ }
+ @Override
+ public void updateNode() throws JavaModelException {
+ int start= fNode.getStartPosition();
+ int length= fNode.getLength();
+ String msg= "Cannot update found node: nodeType=" + fNode.getNodeType() + "; " //$NON-NLS-1$//$NON-NLS-2$
+ + fNode.toString() + "[" + start + ", " + length + "] in " + fCuRewrite.getCu(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+ JavaManipulationPlugin.log(new Exception(msg + ":\n" + fCuRewrite.getCu().getSource().substring(start, start + length))); //$NON-NLS-1$
+ fResult.addError(msg, JavaStatusContext.create(fCuRewrite.getCu(), fNode));
+ }
+ @Override
+ protected ListRewrite getParamgumentsRewrite() {
+ return null;
+ }
+ @Override
+ protected ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) {
+ return null;
+ }
+ @Override
+ protected SimpleName getMethodNameNode() {
+ return null;
+ }
+ }
+
+ private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
+ 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.METHOD)
+ return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.CHANGE_METHOD_SIGNATURE);
+ else {
+ fMethod= (IMethod) element;
+ fMethodName= fMethod.getElementName();
+ try {
+ fVisibility= JdtFlags.getVisibilityCode(fMethod);
+ fReturnTypeInfo= new ReturnTypeInfo(Signature.toString(Signature.getReturnType(fMethod.getSignature())));
+ } catch (JavaModelException exception) {
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { Integer.valueOf(fVisibility),
+ ATTRIBUTE_VISIBILITY }));
+ }
+ }
+ } 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) {
+ fMethodName= name;
+ final RefactoringStatus status= Checks.checkMethodName(fMethodName, fMethod);
+ if (status.hasError())
+ return status;
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
+ final String type= arguments.getAttribute(ATTRIBUTE_RETURN);
+ if (type != null && !"".equals(type)) //$NON-NLS-1$
+ fReturnTypeInfo.setNewTypeName(type);
+ 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;
+ }
+ int count= 1;
+ String attribute= ATTRIBUTE_PARAMETER + count;
+ String value= null;
+ fParameterInfos= new ArrayList<>(3);
+ while ((value= arguments.getAttribute(attribute)) != null) {
+ StringTokenizer tokenizer= new StringTokenizer(value, " "); //$NON-NLS-1$
+ if (tokenizer.countTokens() < 6)
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { value, ATTRIBUTE_PARAMETER }));
+ String oldTypeName= tokenizer.nextToken();
+ String oldName= tokenizer.nextToken();
+ String oldIndex= tokenizer.nextToken();
+ String newTypeName= tokenizer.nextToken();
+ String newName= tokenizer.nextToken();
+ String deleted= tokenizer.nextToken();
+ ParameterInfo info= null;
+ try {
+ int index= Integer.parseInt(oldIndex);
+ if (index == -1) {
+ String result= arguments.getAttribute(ATTRIBUTE_DEFAULT + count);
+ if (result == null)
+ result= ""; //$NON-NLS-1$
+ info= ParameterInfo.createInfoForAddedParameter(newTypeName, newName, result);
+ } else {
+ info= new ParameterInfo(oldTypeName, oldName, index);
+ info.setNewTypeName(newTypeName);
+ info.setNewName(newName);
+ if (Boolean.parseBoolean(deleted))
+ info.markAsDeleted();
+ }
+ fParameterInfos.add(info);
+ } catch (NumberFormatException exception) {
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { value, ATTRIBUTE_PARAMETER }));
+ }
+ count++;
+ attribute= ATTRIBUTE_PARAMETER + count;
+ }
+ count= 1;
+ fExceptionInfos= new ArrayList<>(2);
+ attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count;
+ while ((value= arguments.getAttribute(attribute)) != null) {
+ ExceptionInfo info= null;
+ final String kind= arguments.getAttribute(ATTRIBUTE_KIND + count);
+ if (kind != null) {
+ final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), value, false);
+ if (element == null || !element.exists())
+ return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.CHANGE_METHOD_SIGNATURE);
+ else {
+ try {
+ info= new ExceptionInfo(element, Integer.parseInt(kind), null);
+ } catch (NumberFormatException exception) {
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { kind, ATTRIBUTE_KIND }));
+ }
+ }
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { kind, ATTRIBUTE_KIND }));
+ fExceptionInfos.add(info);
+ count++;
+ attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count;
+ }
+ final String deprecate= arguments.getAttribute(ATTRIBUTE_DEPRECATE);
+ if (deprecate != null) {
+ fDelegateDeprecation= Boolean.parseBoolean(deprecate);
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DEPRECATE));
+ final String delegate= arguments.getAttribute(ATTRIBUTE_DELEGATE);
+ if (delegate != null) {
+ fDelegateUpdating= Boolean.parseBoolean(delegate);
+ } else
+ return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELEGATE));
+ return new RefactoringStatus();
+ }
+
+ /**
+ * If this occurrence update is called from within a declaration update
+ * (i.e., to update the call inside the newly created delegate), the old
+ * node does not yet exist and therefore cannot be a move target.
+ *
+ * Normally, always use createMoveTarget as this has the advantage of
+ * being able to add changes inside changed nodes (for example, a method
+ * call within a method call, see test case #4) and preserving comments
+ * inside calls.
+ * @param oldNode original node
+ * @param rewrite an AST rewrite
+ * @return the node to insert at the target location
+ */
+ protected T moveNode(T oldNode, ASTRewrite rewrite) {
+ T movedNode;
+ if (ASTNodes.isExistingNode(oldNode))
+ movedNode= ASTNodes.createMoveTarget(rewrite, oldNode); //node must be one of ast
+ else
+ movedNode= ASTNodes.copySubtree(rewrite.getAST(), oldNode);
+ return movedNode;
+ }
+
+ public IDefaultValueAdvisor getDefaultValueAdvisor() {
+ return fDefaultValueAdvisor;
+ }
+
+ public void setDefaultValueAdvisor(IDefaultValueAdvisor defaultValueAdvisor) {
+ fDefaultValueAdvisor= defaultValueAdvisor;
+ }
+
+ @Override
+ public Object[] getElements() {
+ return new Object[] { fMethod };
+ }
+
+ @Override
+ public String getIdentifier() {
+ return IRefactoringProcessorIds.CHANGE_METHOD_SIGNATURE_PROCESSOR;
+ }
+
+ @Override
+ public boolean isApplicable() throws CoreException {
+ return RefactoringAvailabilityTester.isChangeSignatureAvailable(fMethod);
+ }
+
+ @Override
+ public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException {
+ String[] affectedNatures= JavaProcessors.computeAffectedNatures(fMethod);
+ return JavaParticipantManager.loadChangeMethodSignatureParticipants(status, this, fMethod, getParticipantArguments(), null, affectedNatures, sharedParticipants);
+ }
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/IDefaultValueAdvisor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/IDefaultValueAdvisor.java
new file mode 100644
index 0000000000..65d48ac87f
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/IDefaultValueAdvisor.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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.refactoring.structure;
+
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Type;
+
+import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
+
+public interface IDefaultValueAdvisor {
+
+ /**
+ * Creates a default expression for an added parameter for a given method invocation.
+ *
+ * @param invocationArguments arguments of the method invocation
+ * @param addedInfo the added ParamterInfo object
+ * @param parameterInfos all ParameterInfo objects, including the added ParameterInfo
+ * @param enclosingMethod the Method that encloses the invocation. Can be null if there is no enclosing method
+ * @param isRecursive true if called from a recursive invocation
+ * @param cuRewrite the CompilationUnitRewrite to use for rewrite, imports etc..
+ * @return a new Expression to be used as argument for the new parameter
+ */
+ Expression createDefaultExpression(List invocationArguments, ParameterInfo addedInfo, List parameterInfos, MethodDeclaration enclosingMethod, boolean isRecursive, CompilationUnitRewrite cuRewrite);
+
+ /**
+ * Create a type for the added parameter.
+ *
+ * @param newTypeName the fully qualified name of the type
+ * @param startPosition the position where the type is defined in a compilation unit
+ * @param cuRewrite the CompilationUnitRewrite to use for rewrite, imports etc..
+ * @return the new type to be used in default expressions
+ */
+ Type createType(String newTypeName, int startPosition, CompilationUnitRewrite cuRewrite);
+
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/IntroduceParameterObjectProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/IntroduceParameterObjectProcessor.java
new file mode 100644
index 0000000000..25e8b10a5a
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/IntroduceParameterObjectProcessor.java
@@ -0,0 +1,746 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.structure;
+
+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 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.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
+import org.eclipse.ltk.core.refactoring.resource.ResourceChange;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.NamingConventions;
+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.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.ArrayType;
+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.Message;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.NullLiteral;
+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.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.core.refactoring.descriptors.IntroduceParameterObjectDescriptor;
+import org.eclipse.jdt.core.refactoring.descriptors.IntroduceParameterObjectDescriptor.Parameter;
+import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
+import org.eclipse.jdt.core.refactoring.participants.IRefactoringProcessorIds;
+
+import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
+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.ASTNodes;
+import org.eclipse.jdt.internal.corext.refactoring.Checks;
+import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
+import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+public class IntroduceParameterObjectProcessor extends ChangeSignatureProcessor {
+
+ private final class ParameterObjectCreator implements IDefaultValueAdvisor {
+ @Override
+ public Expression createDefaultExpression(List invocationArguments, ParameterInfo addedInfo, List parameterInfos, MethodDeclaration enclosingMethod, boolean isRecursive, CompilationUnitRewrite cuRewrite) {
+ final AST ast= cuRewrite.getAST();
+ final ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ if (isRecursive && canReuseParameterObject(invocationArguments, addedInfo, parameterInfos, enclosingMethod)) {
+ return ast.newSimpleName(addedInfo.getNewName());
+ }
+ ClassInstanceCreation classCreation= ast.newClassInstanceCreation();
+
+ int startPosition= enclosingMethod != null ? enclosingMethod.getStartPosition() : cuRewrite.getRoot().getStartPosition();
+ ContextSensitiveImportRewriteContext context= fParameterObjectFactory.createParameterClassAwareContext(fCreateAsTopLevel, cuRewrite, startPosition);
+ classCreation.setType(fParameterObjectFactory.createType(fCreateAsTopLevel, cuRewrite, startPosition));
+ List constructorArguments= classCreation.arguments();
+ for (Iterator iter= parameterInfos.iterator(); iter.hasNext();) {
+ ParameterInfo pi= iter.next();
+ if (isValidField(pi)) {
+ if (pi.isOldVarargs()) {
+ boolean isLastParameter= !iter.hasNext();
+ constructorArguments.addAll(computeVarargs(invocationArguments, pi, isLastParameter, cuRewrite, context));
+ } else {
+ Expression exp= invocationArguments.get(pi.getOldIndex());
+ importNodeTypes(exp, cuRewrite, context);
+ constructorArguments.add(moveNode(exp, rewrite));
+ }
+ }
+ }
+ return classCreation;
+ }
+
+ @Override
+ public Type createType(String newTypeName, int startPosition, CompilationUnitRewrite cuRewrite) {
+ return fParameterObjectFactory.createType(fCreateAsTopLevel, cuRewrite, startPosition);
+ }
+
+ private boolean canReuseParameterObject(List invocationArguments, ParameterInfo addedInfo, List parameterInfos, MethodDeclaration enclosingMethod) {
+ Assert.isNotNull(enclosingMethod);
+ List parameters= enclosingMethod.parameters();
+ for (ParameterInfo pi : parameterInfos) {
+ if (isValidField(pi)) {
+ if (!pi.isInlined())
+ return false;
+ ASTNode node= invocationArguments.get(pi.getOldIndex());
+ if (!isParameter(pi, node, parameters, addedInfo.getNewName())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private List computeVarargs(List invocationArguments, ParameterInfo varArgPI, boolean isLastParameter, CompilationUnitRewrite cuRewrite, ContextSensitiveImportRewriteContext context) {
+ boolean isEmptyVarArg= varArgPI.getOldIndex() >= invocationArguments.size();
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ AST ast= cuRewrite.getAST();
+ ASTNode lastNode= isEmptyVarArg ? null : invocationArguments.get(varArgPI.getOldIndex());
+ List constructorArguments= new ArrayList<>();
+ if (lastNode instanceof ArrayCreation) {
+ ArrayCreation creation= (ArrayCreation) lastNode;
+ ITypeBinding arrayType= creation.resolveTypeBinding();
+ if (arrayType != null && arrayType.isAssignmentCompatible(varArgPI.getNewTypeBinding())) {
+ constructorArguments.add(moveNode(creation, rewrite));
+ return constructorArguments;
+ }
+ }
+ if (isLastParameter) {
+ // copy all varargs
+ for (int i= varArgPI.getOldIndex(); i < invocationArguments.size(); i++) {
+ Expression node= invocationArguments.get(i);
+ importNodeTypes(node, cuRewrite, context);
+ constructorArguments.add(moveNode(node, rewrite));
+ }
+ } else { // new signature would be String...args, int
+ if (lastNode instanceof NullLiteral) {
+ NullLiteral nullLiteral= (NullLiteral) lastNode;
+ constructorArguments.add(moveNode(nullLiteral, rewrite));
+ } else {
+ ArrayCreation creation= ast.newArrayCreation();
+ creation.setType((ArrayType) importBinding(varArgPI.getNewTypeBinding(), cuRewrite, context));
+ ArrayInitializer initializer= ast.newArrayInitializer();
+ List expressions= initializer.expressions();
+ for (int i= varArgPI.getOldIndex(); i < invocationArguments.size(); i++) {
+ Expression node= invocationArguments.get(i);
+ importNodeTypes(node, cuRewrite, context);
+ expressions.add(moveNode(node, rewrite));
+ }
+ if (expressions.isEmpty())
+ creation.dimensions().add(ast.newNumberLiteral("0")); //$NON-NLS-1$
+ else
+ creation.setInitializer(initializer);
+ constructorArguments.add(creation);
+ }
+ }
+ return constructorArguments;
+ }
+
+ public Type importBinding(ITypeBinding newTypeBinding, CompilationUnitRewrite cuRewrite, ImportRewriteContext context) {
+ Type type= cuRewrite.getImportRewrite().addImport(newTypeBinding, cuRewrite.getAST(), context);
+ cuRewrite.getImportRemover().registerAddedImports(type);
+ return type;
+ }
+
+ private void importNodeTypes(ASTNode node, final CompilationUnitRewrite cuRewrite, final ImportRewriteContext context) {
+ ASTResolving.visitAllBindings(node, nodeBinding -> {
+ importBinding(nodeBinding, cuRewrite, context);
+ return false;
+ });
+ }
+ }
+
+ private boolean isParameter(ParameterInfo pi, ASTNode node, List enclosingMethodParameters, String qualifier) {
+ if (node instanceof Name) {
+ Name name= (Name) node;
+ IVariableBinding binding= ASTNodes.getVariableBinding(name);
+ if (binding != null && binding.isParameter()) {
+ return binding.getName().equals(getNameInScope(pi, enclosingMethodParameters));
+ } else {
+ if (node instanceof QualifiedName) {
+ QualifiedName qn= (QualifiedName) node;
+ return qn.getFullyQualifiedName().equals(JavaModelUtil.concatenateName(qualifier, getNameInScope(pi, enclosingMethodParameters)));
+ }
+ }
+ }
+ return false;
+ }
+
+ private final class RewriteParameterBody extends BodyUpdater {
+ @Override
+ public void updateBody(MethodDeclaration methodDeclaration, final CompilationUnitRewrite cuRewrite, RefactoringStatus result) throws CoreException {
+ // ensure that the parameterObject is imported
+ fParameterObjectFactory.createType(fCreateAsTopLevel, cuRewrite, methodDeclaration.getStartPosition());
+ if (cuRewrite.getCu().equals(getCompilationUnit()) && !fParameterClassCreated) {
+ createParameterClass(methodDeclaration, cuRewrite);
+ fParameterClassCreated= true;
+ }
+ Block body= methodDeclaration.getBody();
+ final List parameters= methodDeclaration.parameters();
+ if (body != null) { // abstract methods don't have bodies
+ final ASTRewrite rewriter= cuRewrite.getASTRewrite();
+ ListRewrite bodyStatements= rewriter.getListRewrite(body, Block.STATEMENTS_PROPERTY);
+ ImportRewriteContext context=new ContextSensitiveImportRewriteContext(body, cuRewrite.getImportRewrite());
+ for (ParameterInfo pi : getParameterInfos()) {
+ if (isValidField(pi)) {
+ if (isReadOnly(pi, body, parameters, null)) {
+ body.accept(new ASTVisitor(false) {
+
+ @Override
+ public boolean visit(SimpleName node) {
+ updateSimpleName(rewriter, pi, node, parameters, cuRewrite.getCu().getJavaProject());
+ return false;
+ }
+
+ });
+ pi.setInlined(true);
+ } else {
+ ExpressionStatement initializer= fParameterObjectFactory.createInitializer(pi, getParameterName(), cuRewrite, context);
+ bodyStatements.insertFirst(initializer, null);
+ }
+ }
+ }
+ }
+
+
+ }
+
+ private void updateSimpleName(ASTRewrite rewriter, ParameterInfo pi, SimpleName node, List enclosingParameters, IJavaProject project) {
+ AST ast= rewriter.getAST();
+ IBinding binding= node.resolveBinding();
+ Expression replacementNode= fParameterObjectFactory.createFieldReadAccess(pi, getParameterName(), ast, project, false, null);
+ if (binding instanceof IVariableBinding) {
+ IVariableBinding variable= (IVariableBinding) binding;
+ if (variable.isParameter() && variable.getName().equals(getNameInScope(pi, enclosingParameters))) {
+ rewriter.replace(node, replacementNode, null);
+ }
+ } else {
+ ASTNode parent= node.getParent();
+ if (!(parent instanceof QualifiedName)
+ && !(parent instanceof FieldAccess)
+ && !(parent instanceof SuperFieldAccess)) {
+ if (node.getIdentifier().equals(getNameInScope(pi, enclosingParameters))) {
+ rewriter.replace(node, replacementNode, null);
+ }
+ }
+ }
+ }
+
+ private boolean isReadOnly(final ParameterInfo pi, Block block, final List enclosingMethodParameters, final String qualifier) {
+ class NotWrittenDetector extends ASTVisitor {
+ boolean notWritten= true;
+
+ @Override
+ public boolean visit(SimpleName node) {
+ if (isParameter(pi, node, enclosingMethodParameters, qualifier) && ASTResolving.isWriteAccess(node))
+ notWritten= false;
+ return false;
+ }
+
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ return false;
+ }
+ }
+ NotWrittenDetector visitor= new NotWrittenDetector();
+ block.accept(visitor);
+ return visitor.notWritten;
+ }
+
+ @Override
+ public boolean needsParameterUsedCheck() {
+ return false;
+ }
+
+ }
+
+ private static final String PARAMETER_CLASS_APPENDIX= "Parameter"; //$NON-NLS-1$
+
+ private static final String DEFAULT_PARAMETER_OBJECT_NAME= "parameterObject"; //$NON-NLS-1$
+
+ private MethodDeclaration fMethodDeclaration;
+
+ private ParameterObjectFactory fParameterObjectFactory;
+
+ private boolean fCreateAsTopLevel= true;
+
+ private ParameterInfo fParameterObjectReference;
+
+ private boolean fParameterClassCreated= false;
+
+ private List fOtherChanges;
+
+ public IntroduceParameterObjectProcessor(IntroduceParameterObjectDescriptor descriptor) throws JavaModelException {
+ super(descriptor.getMethod());
+ IMethod method= descriptor.getMethod();
+ Assert.isNotNull(method);
+ initializeFields(method);
+ setBodyUpdater(new RewriteParameterBody());
+ setDefaultValueAdvisor(new ParameterObjectCreator());
+ configureRefactoring(descriptor, this);
+ }
+
+ private void configureRefactoring(final IntroduceParameterObjectDescriptor parameter, IntroduceParameterObjectProcessor ref) {
+ ref.setCreateAsTopLevel(parameter.isTopLevel());
+ ref.setCreateGetter(parameter.isGetters());
+ ref.setCreateSetter(parameter.isSetters());
+ ref.setDelegateUpdating(parameter.isDelegate());
+ ref.setDeprecateDelegates(parameter.isDeprecateDelegate());
+ if (parameter.getClassName() != null)
+ ref.setClassName(parameter.getClassName());
+ if (parameter.getPackageName() != null)
+ ref.setPackage(parameter.getPackageName());
+ if (parameter.getParameterName() != null)
+ ref.setParameterName(parameter.getParameterName());
+ List pis= ref.getParameterInfos();
+ Parameter[] parameters= parameter.getParameters();
+ if (parameters == null)
+ parameters= IntroduceParameterObjectDescriptor.createParameters(getMethod());
+ Map paramIndex= new HashMap<>();
+ for (ParameterInfo pi : pis) {
+ paramIndex.put(pi.getOldIndex(), pi);
+ }
+ paramIndex.put(ParameterInfo.INDEX_FOR_ADDED, fParameterObjectReference);
+ pis.clear();
+ for (Parameter param : parameters) {
+ ParameterInfo pi= paramIndex.get(Integer.valueOf(param.getIndex()));
+ pis.add(pi);
+ if (param != IntroduceParameterObjectDescriptor.PARAMETER_OBJECT) {
+ pi.setCreateField(param.isCreateField());
+ if (pi.isCreateField()) {
+ String fieldName= param.getFieldName();
+ if (fieldName != null)
+ pi.setNewName(fieldName);
+ }
+ }
+ }
+ }
+
+ private void initializeFields(IMethod method) {
+ fParameterObjectFactory= new ParameterObjectFactory();
+ String methodName= method.getElementName();
+ String className= String.valueOf(Character.toUpperCase(methodName.charAt(0)));
+ if (methodName.length() > 1)
+ className+= methodName.substring(1);
+ className+= PARAMETER_CLASS_APPENDIX;
+
+ fParameterObjectReference= ParameterInfo.createInfoForAddedParameter(className, DEFAULT_PARAMETER_OBJECT_NAME);
+ fParameterObjectFactory.setClassName(className);
+
+ IType declaringType= method.getDeclaringType();
+ Assert.isNotNull(declaringType);
+ fParameterObjectFactory.setPackage(declaringType.getPackageFragment().getElementName());
+
+ updateReferenceType();
+ }
+
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException {
+ RefactoringStatus status= new RefactoringStatus();
+ IMethod method= getMethod();
+ // TODO: Check for availability
+ status.merge(Checks.checkTypeName(fParameterObjectFactory.getClassName(), method));
+ status.merge(Checks.checkIdentifier(getParameterName(), method));
+ if (status.hasFatalError())
+ return status;
+ status.merge(super.checkFinalConditions(pm, context));
+ return status;
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
+ RefactoringStatus status= new RefactoringStatus();
+ status.merge(super.checkInitialConditions(pm));
+ if (status.hasFatalError())
+ return status;
+ CompilationUnit astRoot= getBaseCuRewrite().getRoot();
+ ISourceRange nameRange= getMethod().getNameRange();
+ ASTNode selectedNode= NodeFinder.perform(astRoot, nameRange.getOffset(), nameRange.getLength());
+ if (selectedNode == null) {
+ return mappingErrorFound(status, selectedNode);
+ }
+ fMethodDeclaration= ASTNodes.getParent(selectedNode, MethodDeclaration.class);
+ if (fMethodDeclaration == null) {
+ return mappingErrorFound(status, selectedNode);
+ }
+ IMethodBinding resolveBinding= fMethodDeclaration.resolveBinding();
+ if (resolveBinding == null) {
+ if (!processCompilerError(status, selectedNode))
+ status.addFatalError(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_error_cannot_resolve_type);
+ return status;
+ }
+
+ ITypeBinding declaringClass= resolveBinding.getDeclaringClass();
+ if (fParameterObjectFactory.getPackage() == null)
+ fParameterObjectFactory.setPackage(declaringClass.getPackage().getName());
+ if (fParameterObjectFactory.getEnclosingType() == null)
+ fParameterObjectFactory.setEnclosingType(declaringClass.getQualifiedName());
+
+ List parameterInfos= super.getParameterInfos();
+ for (ParameterInfo pi : parameterInfos) {
+ if (!pi.isAdded()) {
+ if (pi.getOldName().equals(pi.getNewName())) // may have been
+ // set to
+ // something
+ // else after
+ // creation
+ pi.setNewName(getFieldName(pi));
+ }
+ }
+ if (!parameterInfos.contains(fParameterObjectReference)) {
+ parameterInfos.add(0, fParameterObjectReference);
+ }
+ Map bindingMap= new HashMap<>();
+ for (Iterator iter= fMethodDeclaration.parameters().iterator(); iter.hasNext();) {
+ SingleVariableDeclaration sdv= iter.next();
+ bindingMap.put(sdv.getName().getIdentifier(), sdv.resolveBinding());
+ }
+ for (ParameterInfo pi : parameterInfos) {
+ if (pi != fParameterObjectReference)
+ pi.setOldBinding(bindingMap.get(pi.getOldName()));
+ }
+ fParameterObjectFactory.setVariables(parameterInfos);
+ return status;
+ }
+
+ @Override
+ protected boolean shouldReport(IProblem problem, CompilationUnit cu) {
+ if (!super.shouldReport(problem, cu))
+ return false;
+ ASTNode node= ASTNodeSearchUtil.getAstNode(cu, problem.getSourceStart(), problem.getSourceEnd() - problem.getSourceStart() + 1);
+ if (node instanceof Type) {
+ Type type= (Type) node;
+ if (problem.getID() == IProblem.UndefinedType && getClassName().equals(ASTNodes.getTypeName(type))) {
+ return false;
+ }
+ }
+ if (node instanceof Name) {
+ Name name= (Name) node;
+ if (problem.getID() == IProblem.ImportNotFound && getPackage().indexOf(name.getFullyQualifiedName()) != -1)
+ return false;
+ if (problem.getID() == IProblem.MissingTypeInMethod) {
+ StructuralPropertyDescriptor locationInParent= name.getLocationInParent();
+ String[] arguments= problem.getArguments();
+ if ((locationInParent == MethodInvocation.NAME_PROPERTY || locationInParent == SuperMethodInvocation.NAME_PROPERTY)
+ && arguments.length > 3
+ && arguments[3].endsWith(getClassName()))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public String getClassName() {
+ return fParameterObjectFactory.getClassName();
+ }
+
+ public ITypeBinding getContainingClass() {
+ return fMethodDeclaration.resolveBinding().getDeclaringClass();
+ }
+
+ private String getMappingErrorMessage() {
+ return RefactoringCoreMessages.IntroduceParameterObjectRefactoring_cannotalanyzemethod_mappingerror;
+ }
+
+ public String getFieldName(ParameterInfo element) {
+ IJavaProject javaProject= getCompilationUnit().getJavaProject();
+ String stripped= NamingConventions.getBaseName(NamingConventions.VK_PARAMETER, element.getOldName(), javaProject);
+ int dim= element.getNewTypeBinding() != null ? element.getNewTypeBinding().getDimensions() : 0;
+ return StubUtility.getVariableNameSuggestions(NamingConventions.VK_INSTANCE_FIELD, javaProject, stripped, dim, null, true)[0];
+ }
+
+ @Override
+ public Change[] getAllChanges() {
+ ArrayList changes= new ArrayList<>(Arrays.asList(super.getAllChanges()));
+ changes.addAll(fOtherChanges);
+ return changes.toArray(new Change[changes.size()]);
+ }
+
+ @Override
+ protected void clearManagers() {
+ super.clearManagers();
+ fOtherChanges= new ArrayList<>();
+ fParameterClassCreated= false;
+ }
+
+ @Override
+ public String getProcessorName() {
+ return RefactoringCoreMessages.IntroduceParameterObjectRefactoring_refactoring_name;
+ }
+
+ @Override
+ public String getIdentifier() {
+ return IRefactoringProcessorIds.INTRODUCE_PARAMETER_OBJECT_PROCESSOR;
+ }
+
+ @Override
+ public JavaRefactoringDescriptor createDescriptor() {
+ IntroduceParameterObjectDescriptor ipod= RefactoringSignatureDescriptorFactory.createIntroduceParameterObjectDescriptor();
+ ipod.setMethod(getMethod());
+ ipod.setClassName(getClassName());
+ ipod.setDelegate(getDelegateUpdating());
+ ipod.setDeprecateDelegate(getDeprecateDelegates());
+ ipod.setGetters(isCreateGetter());
+ ipod.setSetters(isCreateSetter());
+ ipod.setPackageName(getPackage());
+ ipod.setParameterName(getParameterName());
+ ipod.setTopLevel(isCreateAsTopLevel());
+
+ ArrayList parameters= new ArrayList<>();
+ List pis= getParameterInfos();
+ for (ParameterInfo pi : pis) {
+ if (pi.isAdded()) {
+ parameters.add(IntroduceParameterObjectDescriptor.PARAMETER_OBJECT);
+ } else {
+ IntroduceParameterObjectDescriptor.Parameter parameter= new IntroduceParameterObjectDescriptor.Parameter(pi.getOldIndex());
+ if (pi.isCreateField()) {
+ parameter.setCreateField(true);
+ parameter.setFieldName(pi.getNewName());
+ }
+ parameters.add(parameter);
+ }
+ }
+ ipod.setParameters(parameters.toArray(new Parameter[parameters.size()]));
+ String project= getCompilationUnit().getJavaProject().getElementName();
+ try {
+ ipod.setComment(createComment(project).asString());
+ } catch (JavaModelException e) {
+ JavaManipulationPlugin.log(e);
+ }
+ ipod.setProject(project);
+ ipod.setDescription(getProcessorName());
+ ipod.setFlags(getDescriptorFlags());
+ return ipod;
+ }
+
+ private JDTRefactoringDescriptorComment createComment(String project) throws JavaModelException {
+ String header= Messages.format(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_description, getOldMethodSignature());
+ JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
+ comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_object_class, BasicElementLabels.getJavaElementName(fParameterObjectFactory.getClassName())));
+ if (fCreateAsTopLevel) {
+ comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_package, BasicElementLabels.getJavaElementName(fParameterObjectFactory.getPackage())));
+ } else {
+ comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_enclosing_type, BasicElementLabels.getJavaElementName(fParameterObjectFactory.getEnclosingType())));
+ }
+ List kept= new ArrayList<>();
+ List fields= new ArrayList<>();
+ for (ParameterInfo pi : getParameterInfos()) {
+ if (pi.isCreateField()) {
+ fields.add(pi.getNewName());
+ } else {
+ if (!pi.isAdded()) {
+ kept.add(pi.getNewName());
+ }
+ }
+ }
+
+ comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_fields, fields.toArray(new String[0])));
+ if (!kept.isEmpty())
+ comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_keep_parameter, kept.toArray(new String[0])));
+ if (fParameterObjectFactory.isCreateGetter())
+ comment.addSetting(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_create_getter);
+ if (fParameterObjectFactory.isCreateSetter())
+ comment.addSetting(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_descriptor_create_setter);
+ return comment;
+ }
+
+ @Override
+ protected String doGetRefactoringChangeName() {
+ return getProcessorName();
+ }
+
+ public String getParameterName() {
+ return fParameterObjectReference.getNewName();
+ }
+
+ public boolean isCreateGetter() {
+ return fParameterObjectFactory.isCreateGetter();
+ }
+
+ public boolean isCreateSetter() {
+ return fParameterObjectFactory.isCreateSetter();
+ }
+
+ public boolean isCreateAsTopLevel() {
+ return fCreateAsTopLevel;
+ }
+
+ /**
+ * Checks if the given parameter info has been selected for field creation
+ *
+ * @param pi parameter info
+ * @return true if the given parameter info has been selected for field
+ * creation
+ */
+ private boolean isValidField(ParameterInfo pi) {
+ return pi.isCreateField() & !pi.isAdded();
+ }
+
+ private RefactoringStatus mappingErrorFound(RefactoringStatus result, ASTNode node) {
+ if (node != null && (node.getFlags() & ASTNode.MALFORMED) != 0 && processCompilerError(result, node))
+ return result;
+ result.addFatalError(getMappingErrorMessage());
+ return result;
+ }
+
+ public void moveFieldDown(ParameterInfo selected) {
+ fParameterObjectFactory.moveDown(selected);
+ }
+
+ public void moveFieldUp(ParameterInfo selected) {
+ fParameterObjectFactory.moveUp(selected);
+ }
+
+ private boolean processCompilerError(RefactoringStatus result, ASTNode node) {
+ Message[] messages= ASTNodes.getMessages(node, ASTNodes.INCLUDE_ALL_PARENTS);
+ if (messages.length == 0)
+ return false;
+ result.addFatalError(Messages.format(RefactoringCoreMessages.IntroduceParameterObjectRefactoring_cannotanalysemethod_compilererror,
+ new String[] { messages[0].getMessage() }));
+ return true;
+ }
+
+ public void setClassName(String className) {
+ fParameterObjectFactory.setClassName(className);
+ updateReferenceType();
+ }
+
+ private void updateReferenceType() {
+ if (fCreateAsTopLevel)
+ fParameterObjectReference.setNewTypeName(JavaModelUtil.concatenateName(fParameterObjectFactory.getPackage(), fParameterObjectFactory
+ .getClassName()));
+ else
+ fParameterObjectReference.setNewTypeName(JavaModelUtil.concatenateName(fParameterObjectFactory.getEnclosingType(),
+ fParameterObjectFactory.getClassName()));
+ }
+
+ public void setCreateGetter(boolean createGetter) {
+ fParameterObjectFactory.setCreateGetter(createGetter);
+ }
+
+ public void setCreateSetter(boolean createSetter) {
+ fParameterObjectFactory.setCreateSetter(createSetter);
+ }
+
+ public void setPackageName(String packageName) {
+ fParameterObjectFactory.setPackage(packageName);
+ updateReferenceType();
+ }
+
+ public void setParameterName(String paramName) {
+ this.fParameterObjectReference.setNewName(paramName);
+ }
+
+ public void setCreateAsTopLevel(boolean topLevel) {
+ this.fCreateAsTopLevel= topLevel;
+ updateReferenceType();
+ }
+
+ public void updateParameterPosition() {
+ fParameterObjectFactory.updateParameterPosition(fParameterObjectReference);
+ }
+
+ private void createParameterClass(MethodDeclaration methodDeclaration, CompilationUnitRewrite cuRewrite) throws CoreException {
+ if (fCreateAsTopLevel) {
+ IPackageFragmentRoot root= (IPackageFragmentRoot) cuRewrite.getCu().getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
+ fOtherChanges.addAll(fParameterObjectFactory.createTopLevelParameterObject(root));
+ } else {
+ ASTRewrite rewriter= cuRewrite.getASTRewrite();
+ TypeDeclaration enclosingType= (TypeDeclaration) methodDeclaration.getParent();
+ ContextSensitiveImportRewriteContext context=new ContextSensitiveImportRewriteContext(enclosingType, cuRewrite.getImportRewrite());
+ ListRewrite bodyRewrite= rewriter.getListRewrite(enclosingType, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
+ String fqn= enclosingType.getName().getFullyQualifiedName();
+ TypeDeclaration classDeclaration= fParameterObjectFactory.createClassDeclaration(fqn, cuRewrite, null, context);
+ classDeclaration.modifiers().add(rewriter.getAST().newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ classDeclaration.modifiers().add(rewriter.getAST().newModifier(ModifierKeyword.STATIC_KEYWORD));
+ bodyRewrite.insertBefore(classDeclaration, methodDeclaration, null);
+ }
+ }
+
+ public String getPackage() {
+ return fParameterObjectFactory.getPackage();
+ }
+
+ public void setPackage(String typeQualifier) {
+ fParameterObjectFactory.setPackage(typeQualifier);
+ }
+
+ private String getNameInScope(ParameterInfo pi, List enclosingMethodParameters) {
+ Assert.isNotNull(enclosingMethodParameters);
+ boolean emptyVararg= pi.getOldIndex() >= enclosingMethodParameters.size();
+ if (!emptyVararg) {
+ SingleVariableDeclaration svd= enclosingMethodParameters.get(pi.getOldIndex());
+ return svd.getName().getIdentifier();
+ }
+ return null;
+ }
+
+ public String getNewTypeName() {
+ return fParameterObjectReference.getNewTypeName();
+ }
+
+ public ICompilationUnit getCompilationUnit() {
+ return getBaseCuRewrite().getCu();
+ }
+
+ @Override
+ protected int getDescriptorFlags() {
+ return super.getDescriptorFlags() | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
+ }
+
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/ParameterObjectFactory.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/ParameterObjectFactory.java
new file mode 100644
index 0000000000..cd781a1d01
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/corext/refactoring/structure/ParameterObjectFactory.java
@@ -0,0 +1,728 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 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
+ * Samrat Dhillon samrat.dhillon@gmail.com https://bugs.eclipse.org/bugs/show_bug.cgi?id=395558 , https://bugs.eclipse.org/bugs/show_bug.cgi?id=395561 and https://bugs.eclipse.org/bugs/show_bug.cgi?id=394548
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.structure;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.text.edits.TextEdit;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+
+import org.eclipse.ltk.core.refactoring.resource.ResourceChange;
+
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.NamingConventions;
+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.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+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.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.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.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.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+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.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.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.manipulation.JavaManipulation;
+import org.eclipse.jdt.internal.core.manipulation.StubUtility;
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.TokenScanner;
+import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
+import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
+import org.eclipse.jdt.internal.corext.refactoring.changes.CreateCompilationUnitChange;
+import org.eclipse.jdt.internal.corext.refactoring.changes.CreatePackageChange;
+import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+
+public class ParameterObjectFactory {
+
+ private String fClassName;
+ private boolean fCreateGetter;
+ private boolean fCreateSetter;
+ private String fEnclosingType;
+ private String fPackage;
+ private List fVariables;
+
+ public ParameterObjectFactory() {
+ super();
+ }
+
+ public static class CreationListener {
+ /**
+ * Notifies that a getter has been created
+ * @param cuRewrite the rewriter
+ * @param getter the new getter
+ * @param pi the parameter info
+ */
+ public void getterCreated(CompilationUnitRewrite cuRewrite, MethodDeclaration getter, ParameterInfo pi){}
+ /**
+ * Notifies that a setter has been created
+ * @param cuRewrite the rewriter
+ * @param setter the new setter
+ * @param pi the parameter info
+ */
+ public void setterCreated(CompilationUnitRewrite cuRewrite, MethodDeclaration setter, ParameterInfo pi){}
+ /**
+ * Notifies that a field has been created
+ * @param cuRewrite the rewriter
+ * @param field the new field
+ * @param pi the parameter info
+ */
+ public void fieldCreated(CompilationUnitRewrite cuRewrite, FieldDeclaration field, ParameterInfo pi){}
+ /**
+ * Notifies that a constructor has been created
+ * @param cuRewrite the rewriter
+ * @param constructor the new constructor
+ */
+ public void constructorCreated(CompilationUnitRewrite cuRewrite, MethodDeclaration constructor){}
+ /**
+ * Notifies that a type declaration has been created
+ * @param cuRewrite the rewriter
+ * @param declaration the new declaration
+ */
+ public void typeCreated(CompilationUnitRewrite cuRewrite, TypeDeclaration declaration) {}
+
+ protected static ASTNode moveNode(CompilationUnitRewrite cuRewrite, ASTNode node) {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ if (rewrite.getAST() != node.getAST()) {
+ String str= ASTNodes.getNodeSource(node, true, true);
+ if (str != null) {
+ return rewrite.createStringPlaceholder(str, node.getNodeType());
+ }
+ return ASTNode.copySubtree(rewrite.getAST(), node);
+ }
+ return rewrite.createMoveTarget(node);
+ }
+ /**
+ * Return whether the setter should be created for this field. This method is only called when
+ * the global createSetters is set and the parameterInfo is marked for field creation.
+ * @param pi the parameter info
+ * @return true
if a setter should be created
+ */
+ public boolean isCreateSetter(ParameterInfo pi) {
+ return !Modifier.isFinal(pi.getOldBinding().getModifiers());
+ }
+ /**
+ * Return whether the getter should be created for this field. This method is only called when
+ * the global createGetters is set and the parameterInfo is marked for field creation.
+ * @param pi the parameter info
+ * @return true
if a getter should be created
+ */
+ public boolean isCreateGetter(ParameterInfo pi) {
+ return true;
+ }
+ /**
+ * Return whether the field should appear in the constructor
+ * @param pi the parameter info
+ * @return true
if the field should appear
+ */
+ public boolean isUseInConstructor(ParameterInfo pi) {
+ return true;
+ }
+ }
+
+ /**
+ * Creates a new TypeDeclaration for the parameterInfo objects.
+ *
+ * @param declaringType the fully qualified name of the type
+ * @param cuRewrite the {@link CompilationUnitRewrite} that will be used for creation
+ * @param listener the creation listener or null
+ * @param context the import rewrite context or null
+ * @return the new declaration
+ * @throws CoreException if creation failed
+ */
+ public TypeDeclaration createClassDeclaration(String declaringType, CompilationUnitRewrite cuRewrite, CreationListener listener, ImportRewriteContext context) throws CoreException {
+ AST ast= cuRewrite.getAST();
+ if (listener == null)
+ listener= new CreationListener();
+ TypeDeclaration typeDeclaration= ast.newTypeDeclaration();
+ typeDeclaration.setName(ast.newSimpleName(fClassName));
+ List body= typeDeclaration.bodyDeclarations();
+ for (ParameterInfo pi : fVariables) {
+ if (isValidField(pi)) {
+ FieldDeclaration declaration= createField(pi, cuRewrite, context);
+ listener.fieldCreated(cuRewrite, declaration, pi);
+ body.add(declaration);
+ ITypeBinding oldTypeBinding= pi.getOldTypeBinding();
+ if(oldTypeBinding != null && oldTypeBinding.isTypeVariable()){
+ TypeParameter param= ast.newTypeParameter();
+ param.setName(ast.newSimpleName(pi.getNewTypeName()));
+ typeDeclaration.typeParameters().add(param);
+ }
+ }
+ }
+ MethodDeclaration constructor= createConstructor(declaringType, cuRewrite, listener, context);
+ listener.constructorCreated(cuRewrite, constructor);
+ body.add(constructor);
+ for (ParameterInfo pi : fVariables) {
+ if (fCreateGetter && isValidField(pi) && listener.isCreateGetter(pi)) {
+ MethodDeclaration getter= createGetter(pi, declaringType, cuRewrite, context);
+ listener.getterCreated(cuRewrite, getter, pi);
+ body.add(getter);
+ }
+ if (fCreateSetter && isValidField(pi) && listener.isCreateSetter(pi)) {
+ MethodDeclaration setter= createSetter(pi, declaringType, cuRewrite, context);
+ listener.setterCreated(cuRewrite, setter, pi);
+ body.add(setter);
+ }
+ }
+ listener.typeCreated(cuRewrite, typeDeclaration);
+ return typeDeclaration;
+ }
+
+ private MethodDeclaration createConstructor(String declaringTypeName, CompilationUnitRewrite cuRewrite, CreationListener listener, ImportRewriteContext context) throws CoreException {
+ AST ast= cuRewrite.getAST();
+ ICompilationUnit unit= cuRewrite.getCu();
+ IJavaProject project= unit.getJavaProject();
+
+ MethodDeclaration methodDeclaration= ast.newMethodDeclaration();
+ methodDeclaration.setName(ast.newSimpleName(fClassName));
+ methodDeclaration.setConstructor(true);
+ methodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ String lineDelimiter= StubUtility.getLineDelimiterUsed(unit);
+ if (createComments(project)) {
+ String comment= CodeGeneration.getMethodComment(unit, declaringTypeName, methodDeclaration, null, lineDelimiter);
+ if (comment != null) {
+ Javadoc doc= (Javadoc) cuRewrite.getASTRewrite().createStringPlaceholder(comment, ASTNode.JAVADOC);
+ methodDeclaration.setJavadoc(doc);
+ }
+ }
+ List parameters= methodDeclaration.parameters();
+ Block block= ast.newBlock();
+ methodDeclaration.setBody(block);
+ List statements= block.statements();
+ List validParameter= new ArrayList<>();
+ for (ParameterInfo pi : fVariables) {
+ if (isValidField(pi) && listener.isUseInConstructor(pi)) {
+ validParameter.add(pi);
+ }
+ }
+
+ ArrayList usedParameter= new ArrayList<>();
+ for (Iterator iter= validParameter.iterator(); iter.hasNext();) {
+ ParameterInfo pi= iter.next();
+ SingleVariableDeclaration svd= ast.newSingleVariableDeclaration();
+ ITypeBinding typeBinding= pi.getNewTypeBinding();
+ if (!iter.hasNext() && typeBinding.isArray() && pi.isOldVarargs()) {
+ int dimensions= typeBinding.getDimensions();
+ if (dimensions == 1) {
+ typeBinding= typeBinding.getComponentType();
+ } else {
+ typeBinding= typeBinding.createArrayType(dimensions - 1);
+ }
+ svd.setVarargs(true);
+ }
+
+ String paramName= getParameterName(pi, project, usedParameter);
+ usedParameter.add(paramName);
+
+ Type fieldType= importBinding(typeBinding, cuRewrite, context, TypeLocation.PARAMETER);
+ svd.setType(fieldType);
+ svd.setName(ast.newSimpleName(paramName));
+ parameters.add(svd);
+ Expression leftHandSide;
+ if (paramName.equals(pi.getNewName()) || StubUtility.useThisForFieldAccess(project)) {
+ FieldAccess fieldAccess= ast.newFieldAccess();
+ fieldAccess.setName(ast.newSimpleName(pi.getNewName()));
+ fieldAccess.setExpression(ast.newThisExpression());
+ leftHandSide= fieldAccess;
+ } else {
+ leftHandSide= ast.newSimpleName(pi.getNewName());
+ }
+ Assignment assignment= ast.newAssignment();
+ assignment.setLeftHandSide(leftHandSide);
+ assignment.setRightHandSide(ast.newSimpleName(paramName));
+ statements.add(ast.newExpressionStatement(assignment));
+ }
+ return methodDeclaration;
+ }
+
+ private String getParameterName(ParameterInfo pi, IJavaProject project, ArrayList usedParameter) {
+ String fieldName= pi.getNewName();
+ String strippedName= NamingConventions.getBaseName(NamingConventions.VK_INSTANCE_FIELD, fieldName, project);
+ String[] suggestions= StubUtility.getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, strippedName, 0, usedParameter, true);
+ return suggestions[0];
+ }
+
+
+ public static Type importBinding(ITypeBinding typeBinding, CompilationUnitRewrite cuRewrite, ImportRewriteContext context, TypeLocation typeLocation) {
+ int declaredModifiers= typeBinding.getModifiers();
+ AST ast= cuRewrite.getAST();
+ if (Modifier.isPrivate(declaredModifiers) || Modifier.isProtected(declaredModifiers)) {
+ return ast.newSimpleType(ast.newSimpleName(typeBinding.getName()));
+ }
+ Type type= cuRewrite.getImportRewrite().addImport(typeBinding, cuRewrite.getAST(), context, typeLocation);
+ cuRewrite.getImportRemover().registerAddedImports(type);
+ return type;
+ }
+
+ private FieldDeclaration createField(ParameterInfo pi, CompilationUnitRewrite cuRewrite, ImportRewriteContext context) throws CoreException {
+ AST ast= cuRewrite.getAST();
+ ICompilationUnit unit= cuRewrite.getCu();
+
+ VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
+ String lineDelim= StubUtility.getLineDelimiterUsed(unit);
+ SimpleName fieldName= ast.newSimpleName(pi.getNewName());
+ fragment.setName(fieldName);
+ FieldDeclaration declaration= ast.newFieldDeclaration(fragment);
+ if (createComments(unit.getJavaProject())) {
+ String comment= StubUtility.getFieldComment(unit, pi.getNewTypeName(), pi.getNewName(), lineDelim);
+ if (comment != null) {
+ Javadoc doc= (Javadoc) cuRewrite.getASTRewrite().createStringPlaceholder(comment, ASTNode.JAVADOC);
+ declaration.setJavadoc(doc);
+ }
+ }
+ List modifiers= new ArrayList<>();
+ if (fCreateGetter) {
+ modifiers.add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD));
+ } else {
+ modifiers.add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ }
+ declaration.modifiers().addAll(modifiers);
+ declaration.setType(importBinding(pi.getNewTypeBinding(), cuRewrite, context, TypeLocation.FIELD));
+ return declaration;
+ }
+
+ public Expression createFieldReadAccess(ParameterInfo pi, String paramName, AST ast, IJavaProject project, boolean useSuper, Expression qualifier) {
+ Expression completeQualifier= generateQualifier(paramName, ast, useSuper, qualifier);
+ if (fCreateGetter) {
+ MethodInvocation mi= ast.newMethodInvocation();
+ mi.setName(ast.newSimpleName(getGetterName(pi, ast, project)));
+ mi.setExpression(completeQualifier);
+ return mi;
+ }
+ return createFieldAccess(pi, ast, completeQualifier);
+ }
+
+ public Expression createFieldWriteAccess(ParameterInfo pi, String paramName, AST ast, IJavaProject project, Expression assignedValue, boolean useSuper, Expression qualifier) {
+ Expression completeQualifier= generateQualifier(paramName, ast, useSuper, qualifier);
+ if (fCreateSetter) {
+ MethodInvocation mi= ast.newMethodInvocation();
+ mi.setName(ast.newSimpleName(getSetterName(pi, ast, project)));
+ mi.setExpression(completeQualifier);
+ mi.arguments().add(assignedValue);
+ return mi;
+ }
+ return createFieldAccess(pi, ast, completeQualifier);
+ }
+
+ private Expression generateQualifier(String paramName, AST ast, boolean useSuper, Expression qualifier) {
+ SimpleName paramSimpleName= ast.newSimpleName(paramName);
+ if (useSuper) {
+ SuperFieldAccess sf= ast.newSuperFieldAccess();
+ sf.setName(paramSimpleName);
+ if (qualifier instanceof Name) {
+ sf.setQualifier((Name) qualifier);
+ }
+ return sf;
+ }
+ if (qualifier != null) {
+ FieldAccess parameterAccess= ast.newFieldAccess();
+ parameterAccess.setExpression(qualifier);
+ parameterAccess.setName(paramSimpleName);
+ return parameterAccess;
+ }
+ return paramSimpleName;
+ }
+
+
+
+ private Expression createFieldAccess(ParameterInfo pi, AST ast, Expression qualifier) {
+ if (qualifier instanceof Name) {
+ Name name= (Name) qualifier; //create FQN for IPOR
+ return ast.newName(JavaModelUtil.concatenateName(name.getFullyQualifiedName(), pi.getNewName()));
+ }
+ FieldAccess fa= ast.newFieldAccess();
+ fa.setName(ast.newSimpleName(pi.getNewName()));
+ fa.setExpression(qualifier);
+ return fa;
+ }
+
+ private MethodDeclaration createGetter(ParameterInfo pi, String declaringType, CompilationUnitRewrite cuRewrite, ImportRewriteContext context) throws CoreException {
+ AST ast= cuRewrite.getAST();
+ ICompilationUnit cu= cuRewrite.getCu();
+ IJavaProject project= cu.getJavaProject();
+
+ MethodDeclaration methodDeclaration= ast.newMethodDeclaration();
+ String fieldName= pi.getNewName();
+ String getterName= getGetterName(pi, ast, project);
+ String lineDelim= StubUtility.getLineDelimiterUsed(cu);
+ String bareFieldname= NamingConventions.getBaseName(NamingConventions.VK_INSTANCE_FIELD, fieldName, project);
+ if (createComments(project)) {
+ String comment= CodeGeneration.getGetterComment(cu, declaringType, getterName, fieldName, pi.getNewTypeName(), bareFieldname, lineDelim);
+ if (comment != null)
+ methodDeclaration.setJavadoc((Javadoc) cuRewrite.getASTRewrite().createStringPlaceholder(comment, ASTNode.JAVADOC));
+ }
+ methodDeclaration.setName(ast.newSimpleName(getterName));
+ methodDeclaration.setReturnType2(importBinding(pi.getNewTypeBinding(), cuRewrite, context, TypeLocation.RETURN_TYPE));
+ methodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ Block block= ast.newBlock();
+ methodDeclaration.setBody(block);
+ boolean useThis= StubUtility.useThisForFieldAccess(project);
+ if (useThis) {
+ fieldName= "this." + fieldName; //$NON-NLS-1$
+ }
+ String bodyContent= CodeGeneration.getGetterMethodBodyContent(cu, declaringType, getterName, fieldName, lineDelim);
+ ASTNode getterBody= cuRewrite.getASTRewrite().createStringPlaceholder(bodyContent, ASTNode.EXPRESSION_STATEMENT);
+ block.statements().add(getterBody);
+ return methodDeclaration;
+ }
+
+ public ExpressionStatement createInitializer(ParameterInfo pi, String paramName, CompilationUnitRewrite cuRewrite, ImportRewriteContext context) {
+ AST ast= cuRewrite.getAST();
+
+ VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
+ fragment.setName(ast.newSimpleName(pi.getOldName()));
+ fragment.setInitializer(createFieldReadAccess(pi, paramName, ast, cuRewrite.getCu().getJavaProject(), false, null));
+ VariableDeclarationExpression declaration= ast.newVariableDeclarationExpression(fragment);
+ IVariableBinding variable= pi.getOldBinding();
+ declaration.setType(importBinding(pi.getNewTypeBinding(), cuRewrite, context, TypeLocation.LOCAL_VARIABLE));
+ int modifiers= variable.getModifiers();
+ List newModifiers= ast.newModifiers(modifiers);
+ declaration.modifiers().addAll(newModifiers);
+ return ast.newExpressionStatement(declaration);
+ }
+
+ private MethodDeclaration createSetter(ParameterInfo pi, String declaringType, CompilationUnitRewrite cuRewrite, ImportRewriteContext context) throws CoreException {
+ AST ast= cuRewrite.getAST();
+ ICompilationUnit cu= cuRewrite.getCu();
+ IJavaProject project= cu.getJavaProject();
+
+ MethodDeclaration methodDeclaration= ast.newMethodDeclaration();
+ String fieldName= pi.getNewName();
+ String setterName= getSetterName(pi, ast, project);
+ String lineDelim= StubUtility.getLineDelimiterUsed(cu);
+ String bareFieldname= NamingConventions.getBaseName(NamingConventions.VK_INSTANCE_FIELD, fieldName, project);
+ String paramName= StubUtility.suggestArgumentName(project, bareFieldname, null);
+ if (createComments(project)) {
+ String comment= CodeGeneration.getSetterComment(cu, declaringType, setterName, fieldName, pi.getNewTypeName(), paramName, bareFieldname, lineDelim);
+ if (comment != null)
+ methodDeclaration.setJavadoc((Javadoc) cuRewrite.getASTRewrite().createStringPlaceholder(comment, ASTNode.JAVADOC));
+ }
+ methodDeclaration.setName(ast.newSimpleName(setterName));
+ methodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ SingleVariableDeclaration variable= ast.newSingleVariableDeclaration();
+ variable.setType(importBinding(pi.getNewTypeBinding(), cuRewrite, context, TypeLocation.PARAMETER));
+ variable.setName(ast.newSimpleName(paramName));
+ methodDeclaration.parameters().add(variable);
+ Block block= ast.newBlock();
+ methodDeclaration.setBody(block);
+ boolean useThis= StubUtility.useThisForFieldAccess(project);
+ if (useThis || fieldName.equals(paramName)) {
+ fieldName= "this." + fieldName; //$NON-NLS-1$
+ }
+ String bodyContent= CodeGeneration.getSetterMethodBodyContent(cu, declaringType, setterName, fieldName, paramName, lineDelim);
+ ASTNode setterBody= cuRewrite.getASTRewrite().createStringPlaceholder(bodyContent, ASTNode.EXPRESSION_STATEMENT);
+ block.statements().add(setterBody);
+ return methodDeclaration;
+ }
+
+ public Type createType(boolean asTopLevelClass, CompilationUnitRewrite cuRewrite, int position) {
+ String qualifier= asTopLevelClass ? fPackage : fEnclosingType;
+ String concatenateName= JavaModelUtil.concatenateName(qualifier, fClassName);
+
+ ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+ ContextSensitiveImportRewriteContext context= createParameterClassAwareContext(asTopLevelClass, cuRewrite, position);
+ String addedImport= importRewrite.addImport(concatenateName, context);
+ cuRewrite.getImportRemover().registerAddedImport(addedImport);
+ AST ast= cuRewrite.getAST();
+ return ast.newSimpleType(ast.newName(addedImport));
+ }
+
+ ContextSensitiveImportRewriteContext createParameterClassAwareContext(final boolean asTopLevelClass, final CompilationUnitRewrite cuRewrite, int position) {
+ ContextSensitiveImportRewriteContext context= new ContextSensitiveImportRewriteContext(cuRewrite.getRoot(), position, cuRewrite.getImportRewrite()) {
+ @Override
+ public int findInContext(String qualifier, String name, int kind) {
+ String parameterClassName= getClassName();
+ if (kind == ImportRewriteContext.KIND_TYPE && parameterClassName.equals(name)) {
+ String parameterClassQualifier= asTopLevelClass ? getPackage() : getEnclosingType();
+ if (super.findInContext(qualifier, "", kind) == ImportRewriteContext.RES_NAME_FOUND) { //$NON-NLS-1$ // TODO: should be "*", not " "!
+ if (parameterClassQualifier.equals(qualifier)) {
+ return ImportRewriteContext.RES_NAME_FOUND;
+ } else {
+ return ImportRewriteContext.RES_NAME_CONFLICT;
+ }
+ }
+ }
+ return super.findInContext(qualifier, name, kind);
+ }
+ };
+ return context;
+ }
+
+ public String getClassName() {
+ return fClassName;
+ }
+
+ public String getEnclosingType() {
+ return fEnclosingType;
+ }
+
+ private String getGetterName(ParameterInfo pi, AST ast, IJavaProject project) {
+ ITypeBinding type= pi.getNewTypeBinding();
+ boolean isBoolean= ast.resolveWellKnownType("boolean").isEqualTo(type) || ast.resolveWellKnownType("java.lang.Boolean").isEqualTo(type); //$NON-NLS-1$//$NON-NLS-2$
+ return NamingConventions.suggestGetterName(project, pi.getNewName(), Flags.AccPublic, isBoolean, null);
+ }
+
+ public String getPackage() {
+ return fPackage;
+ }
+
+ public ParameterInfo getParameterInfo(String identifier) {
+ for (ParameterInfo pi : fVariables) {
+ if (pi.getOldName().equals(identifier))
+ return pi;
+ }
+ return null;
+ }
+
+ private String getSetterName(ParameterInfo pi, AST ast, IJavaProject project) {
+ ITypeBinding type= pi.getNewTypeBinding();
+ boolean isBoolean= ast.resolveWellKnownType("boolean").isEqualTo(type) || ast.resolveWellKnownType("java.lang.Boolean").isEqualTo(type); //$NON-NLS-1$//$NON-NLS-2$
+ return NamingConventions.suggestSetterName(project, pi.getNewName(), Flags.AccPublic, isBoolean, null);
+ }
+
+ public boolean isCreateGetter() {
+ return fCreateGetter;
+ }
+
+ public boolean isCreateSetter() {
+ return fCreateSetter;
+ }
+
+ private boolean isValidField(ParameterInfo pi) {
+ return pi.isCreateField() && !pi.isAdded();
+ }
+
+ public void moveDown(ParameterInfo selected) {
+ int idx= fVariables.indexOf(selected);
+ Assert.isTrue(idx >= 0 && idx < fVariables.size() - 1);
+ int nextIdx= idx + 1;
+ ParameterInfo next= fVariables.get(nextIdx);
+ if (next.isAdded()) {
+ nextIdx++;
+ Assert.isTrue(nextIdx <= fVariables.size() - 1);
+ next= fVariables.get(nextIdx);
+ }
+ fVariables.set(idx, next);
+ fVariables.set(nextIdx, selected);
+ }
+
+ public void moveUp(ParameterInfo selected) {
+ int idx= fVariables.indexOf(selected);
+ Assert.isTrue(idx > 0);
+ int prevIdx= idx - 1;
+ ParameterInfo prev= fVariables.get(prevIdx);
+ if (prev.isAdded()) {
+ prevIdx--;
+ Assert.isTrue(prevIdx >= 0);
+ prev= fVariables.get(prevIdx);
+ }
+ fVariables.set(idx, prev);
+ fVariables.set(prevIdx, selected);
+ }
+
+ public void setClassName(String className) {
+ fClassName= className;
+ }
+
+ public void setCreateGetter(boolean createGetter) {
+ fCreateGetter= createGetter;
+ }
+
+ public void setCreateSetter(boolean createSetter) {
+ fCreateSetter= createSetter;
+ }
+
+ public void setEnclosingType(String enclosingType) {
+ fEnclosingType= enclosingType;
+ }
+
+ public void setPackage(String typeQualifier) {
+ fPackage= typeQualifier;
+ }
+
+ public void setVariables(List parameters) {
+ fVariables= parameters;
+ }
+
+ /**
+ * Updates the position of the newly inserted parameterObject so that it is
+ * directly after the first checked parameter
+ *
+ * @param parameterObjectReference the inserted parameterObject
+ */
+ public void updateParameterPosition(ParameterInfo parameterObjectReference) {
+ fVariables.remove(parameterObjectReference);
+ for (ListIterator iterator= fVariables.listIterator(); iterator.hasNext();) {
+ ParameterInfo pi= iterator.next();
+ if (isValidField(pi)) {
+ iterator.add(parameterObjectReference);
+ return;
+ }
+ }
+ }
+
+ private boolean createComments(IJavaProject project) {
+ return StubUtility.doAddComments(project);
+ }
+
+
+ public List createTopLevelParameterObject(IPackageFragmentRoot packageFragmentRoot, CreationListener listener) throws CoreException {
+ List changes= new ArrayList<>();
+ IPackageFragment packageFragment= packageFragmentRoot.getPackageFragment(getPackage());
+ if (!packageFragment.exists()) {
+ changes.add(new CreatePackageChange(packageFragment));
+ }
+ ICompilationUnit unit= packageFragment.getCompilationUnit(getClassName() + JavaModelUtil.DEFAULT_CU_SUFFIX);
+ Assert.isTrue(!unit.exists());
+ IJavaProject javaProject= unit.getJavaProject();
+ ICompilationUnit workingCopy= unit.getWorkingCopy(null);
+
+ try {
+ // create stub with comments and dummy type
+ String lineDelimiter= StubUtility.getLineDelimiterUsed(javaProject);
+ String fileComment= getFileComment(workingCopy, lineDelimiter);
+ String typeComment= getTypeComment(workingCopy, lineDelimiter);
+ String content= CodeGeneration.getCompilationUnitContent(workingCopy, fileComment, typeComment, "class " + getClassName() + "{}", lineDelimiter); //$NON-NLS-1$ //$NON-NLS-2$
+ workingCopy.getBuffer().setContents(content);
+
+ CompilationUnitRewrite cuRewrite= new CompilationUnitRewrite(workingCopy);
+ ASTRewrite rewriter= cuRewrite.getASTRewrite();
+ CompilationUnit root= cuRewrite.getRoot();
+ AST ast= cuRewrite.getAST();
+ ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+ ContextSensitiveImportRewriteContext context=new ContextSensitiveImportRewriteContext(root, cuRewrite.getImportRewrite());
+
+ // retrieve&replace dummy type with real class
+ ListRewrite types= rewriter.getListRewrite(root, CompilationUnit.TYPES_PROPERTY);
+ ASTNode dummyType= (ASTNode) types.getOriginalList().get(0);
+ String newTypeName= JavaModelUtil.concatenateName(getPackage(), getClassName());
+ TypeDeclaration classDeclaration= createClassDeclaration(newTypeName, cuRewrite, listener, context);
+ classDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ Javadoc javadoc= (Javadoc) dummyType.getStructuralProperty(TypeDeclaration.JAVADOC_PROPERTY);
+ rewriter.set(classDeclaration, TypeDeclaration.JAVADOC_PROPERTY, javadoc, null);
+ types.replace(dummyType, classDeclaration, null);
+
+ // Apply rewrites and discard workingcopy
+ // Using CompilationUnitRewrite.createChange() leads to strange
+ // results
+ String charset= ResourceUtil.getFile(unit).getCharset(false);
+ Document document= new Document(content);
+ try {
+ rewriter.rewriteAST().apply(document);
+ TextEdit rewriteImports= importRewrite.rewriteImports(null);
+ rewriteImports.apply(document);
+ } catch (BadLocationException e) {
+ throw new CoreException(new Status(IStatus.ERROR, JavaManipulation.ID_PLUGIN, RefactoringCoreMessages.IntroduceParameterObjectRefactoring_parameter_object_creation_error, e));
+ }
+ String docContent= document.get();
+ CreateCompilationUnitChange compilationUnitChange= new CreateCompilationUnitChange(unit, docContent, charset);
+ changes.add(compilationUnitChange);
+ } finally {
+ workingCopy.discardWorkingCopy();
+ }
+ return changes;
+ }
+
+ public List createTopLevelParameterObject(IPackageFragmentRoot packageFragmentRoot) throws CoreException {
+ return createTopLevelParameterObject(packageFragmentRoot, null);
+ }
+
+ protected String getFileComment(ICompilationUnit parentCU, String lineDelimiter) throws CoreException {
+ if (StubUtility.doAddComments(parentCU.getJavaProject())) {
+ return CodeGeneration.getFileComment(parentCU, lineDelimiter);
+ }
+ return null;
+
+ }
+
+ protected String getTypeComment(ICompilationUnit parentCU, String lineDelimiter) throws CoreException {
+ IJavaProject javaProject= parentCU.getJavaProject();
+ if (StubUtility.doAddComments(javaProject)) {
+ StringBuilder typeName= new StringBuilder();
+ typeName.append(getClassName());
+ String[] typeParamNames= new String[0];
+ String comment= CodeGeneration.getTypeComment(parentCU, typeName.toString(), typeParamNames, lineDelimiter);
+ if (comment != null && isValidComment(comment, javaProject)) {
+ return comment;
+ }
+ }
+ return null;
+ }
+
+ private boolean isValidComment(String template, IJavaProject javaProject) {
+ IScanner scanner;
+ if (javaProject != null) {
+ String sourceLevel = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
+ String complianceLevel = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
+ scanner = ToolFactory.createScanner(true, false, false, sourceLevel, complianceLevel);
+ } else {
+ scanner= ToolFactory.createScanner(true, false, false, false);
+ }
+ scanner.setSource(template.toCharArray());
+ try {
+ int next= scanner.getNextToken();
+ while (TokenScanner.isComment(next)) {
+ next= scanner.getNextToken();
+ }
+ return next == ITerminalSymbols.TokenNameEOF;
+ } catch (InvalidInputException e) {
+ }
+ return false;
+ }
+
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/ui/refactoring/contentassist/JavaTypeCompletionProcessorCore.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/ui/refactoring/contentassist/JavaTypeCompletionProcessorCore.java
new file mode 100644
index 0000000000..8e089b43a2
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/internal/ui/refactoring/contentassist/JavaTypeCompletionProcessorCore.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2023 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 - based this file on JavaTypeCompletionProcessor
+ *******************************************************************************/
+package org.eclipse.jdt.internal.ui.refactoring.contentassist;
+
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+/**
+ * @since 1.18
+ */
+public class JavaTypeCompletionProcessorCore {
+ public static final String DUMMY_CLASS_NAME= "$$__$$"; //$NON-NLS-1$
+ /**
+ * The CU name to be used if no parent ICompilationUnit is available.
+ * The main type of this class will be filtered out from the proposals list.
+ */
+ public static final String DUMMY_CU_NAME= DUMMY_CLASS_NAME + JavaModelUtil.DEFAULT_CU_SUFFIX;
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java
index 03a1b9d394..741c793044 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/ChangeUtil.java
@@ -48,6 +48,7 @@
import org.eclipse.jdt.ls.core.internal.corext.refactoring.changes.RenameCompilationUnitChange;
import org.eclipse.jdt.ls.core.internal.corext.refactoring.changes.RenamePackageChange;
import org.eclipse.jdt.ls.core.internal.corext.refactoring.nls.changes.CreateFileChange;
+import org.eclipse.lsp4j.AnnotatedTextEdit;
import org.eclipse.lsp4j.CreateFile;
import org.eclipse.lsp4j.CreateFileOptions;
import org.eclipse.lsp4j.DeleteFile;
@@ -87,34 +88,48 @@ public class ChangeUtil {
* @throws CoreException
*/
public static WorkspaceEdit convertToWorkspaceEdit(Change change) throws CoreException {
+ return convertToWorkspaceEdit(change, null);
+ }
+
+ /**
+ * Converts Change to WorkspaceEdit for further consumption.
+ *
+ * @param change
+ * {@link Change} to convert
+ * @param annotationId
+ * {@link AnnotatedTextEdit} the annotation id of the AnnotatedTextEdit
+ * @return {@link WorkspaceEdit} converted from the change
+ * @throws CoreException
+ */
+ public static WorkspaceEdit convertToWorkspaceEdit(Change change, String annotationId) throws CoreException {
WorkspaceEdit edit = new WorkspaceEdit();
if (change instanceof CompositeChange compositeChange) {
- convertCompositeChange(compositeChange, edit);
+ convertCompositeChange(compositeChange, edit, annotationId);
} else {
- convertSingleChange(change, edit);
+ convertSingleChange(change, edit, annotationId);
}
return edit;
}
- private static void convertSingleChange(Change change, WorkspaceEdit edit) throws CoreException {
+ private static void convertSingleChange(Change change, WorkspaceEdit edit, String annotationId) throws CoreException {
if (change instanceof CompositeChange) {
return;
}
if (change instanceof TextChange textChange) {
- convertTextChange(textChange, edit);
+ convertTextChange(textChange, edit, annotationId);
} else if (change instanceof ResourceChange resourceChange) {
- convertResourceChange(resourceChange, edit);
+ convertResourceChange(resourceChange, edit, annotationId);
}
}
- private static void convertCompositeChange(CompositeChange change, WorkspaceEdit edit) throws CoreException {
+ private static void convertCompositeChange(CompositeChange change, WorkspaceEdit edit, String annotationId) throws CoreException {
Change[] changes = change.getChildren();
for (Change ch : changes) {
if (ch instanceof CompositeChange compositeChange) {
- convertCompositeChange(compositeChange, edit);
+ convertCompositeChange(compositeChange, edit, annotationId);
} else {
- convertSingleChange(ch, edit);
+ convertSingleChange(ch, edit, annotationId);
}
}
}
@@ -129,7 +144,7 @@ private static void convertCompositeChange(CompositeChange change, WorkspaceEdit
* instance of workspace edit changes
* @throws CoreException
*/
- private static void convertResourceChange(ResourceChange resourceChange, WorkspaceEdit edit) throws CoreException {
+ private static void convertResourceChange(ResourceChange resourceChange, WorkspaceEdit edit, String annotationId) throws CoreException {
if (!JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isResourceOperationSupported()) {
return;
}
@@ -144,17 +159,17 @@ private static void convertResourceChange(ResourceChange resourceChange, Workspa
if (resourceChange instanceof RenameCompilationUnitChange renameCUChange) {
convertCUResourceChange(edit, renameCUChange);
} else if (resourceChange instanceof RenamePackageChange renamePackageChange) {
- convertRenamePackcageChange(edit, renamePackageChange);
+ convertRenamePackcageChange(edit, renamePackageChange, annotationId);
} else if (resourceChange instanceof MoveCompilationUnitChange moveCUChange) {
- convertMoveCompilationUnitChange(edit, moveCUChange);
+ convertMoveCompilationUnitChange(edit, moveCUChange, annotationId);
} else if (resourceChange instanceof CreateFileChange createFileChange) {
convertCreateFileChange(edit, createFileChange);
} else if (resourceChange instanceof CreateCompilationUnitChange createCUChange) {
- convertCreateCompilationUnitChange(edit, createCUChange);
+ convertCreateCompilationUnitChange(edit, createCUChange, annotationId);
}
}
- private static void convertMoveCompilationUnitChange(WorkspaceEdit edit, MoveCompilationUnitChange change) throws JavaModelException {
+ private static void convertMoveCompilationUnitChange(WorkspaceEdit edit, MoveCompilationUnitChange change, String annotationId) throws JavaModelException {
IPackageFragment newPackage = change.getDestinationPackage();
ICompilationUnit unit = change.getCu();
CompilationUnit astCU = RefactoringASTParser.parseWithASTProvider(unit, true, new NullProgressMonitor());
@@ -165,7 +180,7 @@ private static void convertMoveCompilationUnitChange(WorkspaceEdit edit, MoveCom
if (!Objects.equals(oldPackageName, newPackage.getElementName())) {
// update the package declaration
if (updatePackageStatement(astCU, newPackage.getElementName(), rewrite, unit)) {
- convertTextEdit(edit, unit, rewrite.rewriteAST());
+ convertTextEdit(edit, unit, rewrite.rewriteAST(), annotationId);
}
}
@@ -177,17 +192,17 @@ private static void convertMoveCompilationUnitChange(WorkspaceEdit edit, MoveCom
edit.getDocumentChanges().add(Either.forRight(cuResourceChange));
}
- private static void convertCreateCompilationUnitChange(WorkspaceEdit edit, CreateCompilationUnitChange change) {
+ private static void convertCreateCompilationUnitChange(WorkspaceEdit edit, CreateCompilationUnitChange change, String annotationId) {
ICompilationUnit unit = change.getCu();
CreateFile createFile = new CreateFile();
createFile.setUri(JDTUtils.toURI(unit));
createFile.setOptions(new CreateFileOptions(false, true));
edit.getDocumentChanges().add(Either.forRight(createFile));
InsertEdit textEdit = new InsertEdit(0, change.getPreview());
- convertTextEdit(edit, unit, textEdit);
+ convertTextEdit(edit, unit, textEdit, annotationId);
}
- private static void convertRenamePackcageChange(WorkspaceEdit edit, RenamePackageChange packageChange) throws CoreException {
+ private static void convertRenamePackcageChange(WorkspaceEdit edit, RenamePackageChange packageChange, String annotationId) throws CoreException {
IPackageFragment pack = (IPackageFragment) packageChange.getModifiedElement();
IPath newPackageFragment = new Path(packageChange.getNewName().replace('.', IPath.SEPARATOR));
IPath oldPackageFragment = new Path(packageChange.getOldName().replace('.', IPath.SEPARATOR));
@@ -198,7 +213,7 @@ private static void convertRenamePackcageChange(WorkspaceEdit edit, RenamePackag
for (IPackageFragment currentPackage : allPackages) {
String newPkgName = packageChange.getNewName() + currentPackage.getElementName().substring(oldPrefix.length());
//update package's declaration
- convertPackageUpdateEdit(currentPackage.getCompilationUnits(), newPkgName, edit);
+ convertPackageUpdateEdit(currentPackage.getCompilationUnits(), newPkgName, edit, annotationId);
}
RenameFile renameFile = new RenameFile();
@@ -207,7 +222,7 @@ private static void convertRenamePackcageChange(WorkspaceEdit edit, RenamePackag
edit.getDocumentChanges().add(Either.forRight(renameFile));
} else {
//update package's declaration
- convertPackageUpdateEdit(pack.getCompilationUnits(), packageChange.getNewName(), edit);
+ convertPackageUpdateEdit(pack.getCompilationUnits(), packageChange.getNewName(), edit, annotationId);
CreateFile createFile = new CreateFile();
createFile.setUri(ResourceUtils.fixURI(newPackagePath.append(TEMP_FILE_NAME).toFile().toURI()));
@@ -249,7 +264,7 @@ private static void convertCreateFileChange(WorkspaceEdit edit, CreateFileChange
edit.getDocumentChanges().add(Either.forRight(createFile));
}
- private static void convertTextChange(TextChange textChange, WorkspaceEdit rootEdit) {
+ private static void convertTextChange(TextChange textChange, WorkspaceEdit rootEdit, String annotationId) {
Object modifiedElement = textChange.getModifiedElement();
if (!(modifiedElement instanceof IJavaElement)) {
return;
@@ -260,16 +275,16 @@ private static void convertTextChange(TextChange textChange, WorkspaceEdit rootE
return;
}
ICompilationUnit compilationUnit = (ICompilationUnit) ((IJavaElement) modifiedElement).getAncestor(IJavaElement.COMPILATION_UNIT);
- convertTextEdit(rootEdit, compilationUnit, textEdits);
+ convertTextEdit(rootEdit, compilationUnit, textEdits, annotationId);
}
- private static void convertTextEdit(WorkspaceEdit root, ICompilationUnit unit, TextEdit edit) {
+ private static void convertTextEdit(WorkspaceEdit root, ICompilationUnit unit, TextEdit edit, String annotationId) {
if (edit == null) {
return;
}
TextEditConverter converter = new TextEditConverter(unit, edit);
- List textEdits = filterTextEdits(converter.convert());
+ List textEdits = filterTextEdits(converter.convert(annotationId));
if (textEdits == null || textEdits.isEmpty()) {
return;
}
@@ -332,18 +347,18 @@ private static boolean isPrimaryType(IType type) {
return type.getDeclaringType() == null && JavaCore.removeJavaLikeExtension(cuName).equals(typeName);
}
- private static void convertPackageUpdateEdit(ICompilationUnit[] cus, String newPkgName, WorkspaceEdit rootEdit) throws JavaModelException {
+ private static void convertPackageUpdateEdit(ICompilationUnit[] cus, String newPkgName, WorkspaceEdit rootEdit, String annotationId) throws JavaModelException {
for (ICompilationUnit cu : cus) {
- convertPackageUpdateEdit(cu, newPkgName, rootEdit);
+ convertPackageUpdateEdit(cu, newPkgName, rootEdit, annotationId);
}
}
- private static void convertPackageUpdateEdit(ICompilationUnit cu, String newPkgName, WorkspaceEdit rootEdit) throws JavaModelException {
+ private static void convertPackageUpdateEdit(ICompilationUnit cu, String newPkgName, WorkspaceEdit rootEdit, String annotationId) throws JavaModelException {
CompilationUnit unit = new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL).parse(cu, true);
ASTRewrite rewrite = ASTRewrite.create(unit.getAST());
if (updatePackageStatement(unit, newPkgName, rewrite, cu)) {
TextEdit textEdit = rewrite.rewriteAST();
- convertTextEdit(rootEdit, cu, textEdit);
+ convertTextEdit(rootEdit, cu, textEdit, annotationId);
}
}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaCodeActionKind.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaCodeActionKind.java
index d09b54b8a1..25e0a06ef9 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaCodeActionKind.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaCodeActionKind.java
@@ -113,6 +113,11 @@ public interface JavaCodeActionKind {
*/
public static final String REFACTOR_INTRODUCE_PARAMETER = CodeActionKind.Refactor + ".introduce.parameter";
+ /**
+ * Change Signature
+ */
+ public static final String REFACTOR_CHANGE_SIGNATURE = CodeActionKind.Refactor + ".change.signature";
+
/**
* Base kind for "quickassist" code actions
*/
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/TextEditConverter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/TextEditConverter.java
index a7daa0a780..f0015d4033 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/TextEditConverter.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/TextEditConverter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2016-2017 Red Hat Inc. and others.
+ * Copyright (c) 2016-2023 Red Hat Inc. and others.
* All rights reserved. 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
@@ -9,6 +9,7 @@
*
* Contributors:
* Red Hat Inc. - initial API and implementation
+ * Microsoft Corporation - Support AnnotatedTextEdit
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal;
@@ -22,6 +23,7 @@
import org.eclipse.jdt.internal.core.util.SimpleDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
+import org.eclipse.lsp4j.AnnotatedTextEdit;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.eclipse.text.edits.CopySourceEdit;
@@ -38,28 +40,39 @@
import org.eclipse.text.edits.TextEditVisitor;
/**
- * Converts an {@link org.eclipse.text.edits.TextEdit} to {@link org.eclipse.lsp4j.TextEdit}
+ * Converts an {@link org.eclipse.text.edits.TextEdit} to
+ * {@link org.eclipse.lsp4j.TextEdit}
*
* @author Gorkem Ercan
*
*/
-public class TextEditConverter extends TextEditVisitor{
+public class TextEditConverter extends TextEditVisitor {
private final TextEdit source;
+ private boolean isChangeAnnotationSupported;
+ private String changeAnnotation;
protected ICompilationUnit compilationUnit;
protected List converted;
public TextEditConverter(ICompilationUnit unit, TextEdit edit) {
this.source = edit;
this.converted = new ArrayList<>();
- if(unit == null ){
+ if (unit == null) {
throw new IllegalArgumentException("Compilation unit can not be null");
}
this.compilationUnit = unit;
+ this.isChangeAnnotationSupported = JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isChangeAnnotationSupport();
}
- public List convert(){
- if(this.source != null){
+ public List convert() {
+ return this.convert(null);
+ }
+
+ public List convert(String changeAnnotation) {
+ if (this.isChangeAnnotationSupported && changeAnnotation != null) {
+ this.changeAnnotation = changeAnnotation;
+ }
+ if (this.source != null) {
this.source.accept(this);
}
return converted;
@@ -78,9 +91,9 @@ public TextDocumentEdit convertToTextDocumentEdit(int version) {
@Override
public boolean visit(InsertEdit edit) {
try {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setNewText(edit.getText());
- te.setRange(JDTUtils.toRange(compilationUnit,edit.getOffset(),edit.getLength()));
+ te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
converted.add(te);
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Error converting TextEdits", e);
@@ -95,7 +108,7 @@ public boolean visit(InsertEdit edit) {
public boolean visit(CopySourceEdit edit) {
try {
if (edit.getTargetEdit() != null) {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
Document doc = new Document(compilationUnit.getSource());
edit.apply(doc, TextEdit.UPDATE_REGIONS);
@@ -119,9 +132,9 @@ public boolean visit(CopySourceEdit edit) {
@Override
public boolean visit(DeleteEdit edit) {
try {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setNewText("");
- te.setRange(JDTUtils.toRange(compilationUnit,edit.getOffset(),edit.getLength()));
+ te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
converted.add(te);
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Error converting TextEdits", e);
@@ -135,7 +148,7 @@ public boolean visit(DeleteEdit edit) {
@Override
public boolean visit(MultiTextEdit edit) {
try {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
Document doc = new Document(compilationUnit.getSource());
edit.apply(doc, TextEdit.UPDATE_REGIONS);
@@ -155,9 +168,9 @@ public boolean visit(MultiTextEdit edit) {
@Override
public boolean visit(ReplaceEdit edit) {
try {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setNewText(edit.getText());
- te.setRange(JDTUtils.toRange(compilationUnit,edit.getOffset(),edit.getLength()));
+ te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
converted.add(te);
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Error converting TextEdits", e);
@@ -175,7 +188,7 @@ public boolean visit(ReplaceEdit edit) {
public boolean visit(CopyTargetEdit edit) {
try {
if (edit.getSourceEdit() != null) {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
Document doc = new Document(compilationUnit.getSource());
@@ -207,7 +220,7 @@ public boolean visit(MoveSourceEdit edit) {
// If MoveSourcedEdit & MoveTargetEdit are the same level, should delete the original contenxt.
// See issue#https://github.com/redhat-developer/vscode-java/issues/253
if (edit.getParent() != null && edit.getTargetEdit() != null && edit.getParent().equals(edit.getTargetEdit().getParent())) {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setNewText("");
te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
converted.add(te);
@@ -229,7 +242,7 @@ public boolean visit(MoveSourceEdit edit) {
public boolean visit(MoveTargetEdit edit) {
try {
if (edit.getSourceEdit() != null) {
- org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
+ org.eclipse.lsp4j.TextEdit te = this.createTextEdit(this.changeAnnotation);
te.setRange(JDTUtils.toRange(compilationUnit, edit.getOffset(), edit.getLength()));
Document doc = new Document(compilationUnit.getSource());
@@ -266,4 +279,13 @@ private String applySourceModifier(String content, ISourceModifier modifier) {
}
return subDocument.get();
}
+
+ private org.eclipse.lsp4j.TextEdit createTextEdit(String changeAnnotation) {
+ if (changeAnnotation == null) {
+ return new org.eclipse.lsp4j.TextEdit();
+ }
+ AnnotatedTextEdit ate = new org.eclipse.lsp4j.AnnotatedTextEdit();
+ ate.setAnnotationId(changeAnnotation);
+ return ate;
+ }
}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringAvailabilityTester.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringAvailabilityTester.java
index 633aa21d62..266721354e 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringAvailabilityTester.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringAvailabilityTester.java
@@ -167,7 +167,7 @@ public static IType getTopLevelType(final IMember[] members) {
}
public static boolean isChangeSignatureAvailable(final IMethod method) throws JavaModelException {
- return Checks.isAvailable(method) && !Flags.isAnnotation(method.getDeclaringType().getFlags());
+ return (method != null) && Checks.isAvailable(method) && !Flags.isAnnotation(method.getDeclaringType().getFlags());
}
// public static boolean isChangeSignatureAvailable(final IStructuredSelection selection) throws JavaModelException {
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringCoreMessages.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringCoreMessages.java
index 1c8fd489d1..8f7937828d 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringCoreMessages.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/RefactoringCoreMessages.java
@@ -69,6 +69,8 @@ public final class RefactoringCoreMessages extends NLS {
public static String ChangeSignatureRefactoring_change_signature;
+ public static String ChangeSignatureRefactoring_change_signature_for;
+
public static String ChangeSignatureRefactoring_changed_parameter_pattern;
public static String ChangeSignatureRefactoring_changed_parameters;
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/refactoring.properties b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/refactoring.properties
index f90ff05690..70340605a1 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/refactoring.properties
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/refactoring.properties
@@ -814,6 +814,7 @@ ChangeSignatureRefactoring_constructor_name=The method should not have the same
ChangeSignatureRefactoring_no_exception_binding=Cannot resolve the type binding of a thrown exception. Compilation errors must be fixed before this refactoring can be performed.
ChangeSignatureRefactoring_param_name_not_empty=Enter the name for parameter {0}.
ChangeSignatureRefactoring_change_signature=Change signature
+ChangeSignatureRefactoring_change_signature_for=Change signature for ''{0}''
ChangeSignatureRefactoring_changed_parameters=Changed parameters:
ChangeSignatureRefactoring_update_reference=Update reference
ChangeSignatureRefactoring_new_name_pattern=New name: ''{0}''
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/RefactorProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/RefactorProcessor.java
index dc18ef5c88..8dfce6461d 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/RefactorProcessor.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/RefactorProcessor.java
@@ -153,6 +153,7 @@ public List getProposals(CodeActionParams params, IInv
getAssignToVariableProposals(context, coveringNode, locations, proposals, params);
getIntroduceParameterProposals(params, context, coveringNode, locations, proposals);
getExtractInterfaceProposal(params, context, proposals);
+ getChangeSignatureProposal(params, context, proposals);
}
return proposals;
}
@@ -1004,4 +1005,19 @@ private boolean getExtractInterfaceProposal(CodeActionParams params, IInvocation
proposals.add(proposal);
return true;
}
+
+ private boolean getChangeSignatureProposal(CodeActionParams params, IInvocationContext context, Collection proposals) {
+ if (proposals == null) {
+ return false;
+ }
+
+ ChangeCorrectionProposal proposal = RefactorProposalUtility.getChangeSignatureProposal(params, context);
+
+ if (proposal == null) {
+ return false;
+ }
+
+ proposals.add(proposal);
+ return true;
+ }
}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/ChangeSignatureHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/ChangeSignatureHandler.java
new file mode 100644
index 0000000000..f67179195d
--- /dev/null
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/ChangeSignatureHandler.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Microsoft Corporation and others.
+ * All rights reserved. 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:
+ * Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+package org.eclipse.jdt.ls.core.internal.handlers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IImportDeclaration;
+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.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+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.SearchPattern;
+import org.eclipse.jdt.core.search.TypeNameMatch;
+import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
+import org.eclipse.jdt.internal.corext.refactoring.ExceptionInfo;
+import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ChangeSignatureProcessor;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+import org.eclipse.jdt.ls.core.internal.JDTUtils;
+import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.MessageParams;
+import org.eclipse.lsp4j.MessageType;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring;
+
+public class ChangeSignatureHandler {
+
+ public static String CHANGE_SIGNATURE_ANNOTATION_ID = "java.refactor.ChangeSignature";
+
+ public static class MethodParameter {
+ public String type;
+ public String name;
+ public String defaultValue;
+ public int originalIndex;
+
+ public MethodParameter(String type, String name, String defaultValue, int originalIndex) {
+ this.type = type;
+ this.name = name;
+ this.defaultValue = defaultValue;
+ this.originalIndex = originalIndex;
+ }
+ }
+
+ public static class MethodException {
+ public String type;
+ public String typeHandleIdentifier;
+
+ public MethodException(String type, String typeHandleIdentifier) {
+ this.type = type;
+ this.typeHandleIdentifier = typeHandleIdentifier;
+ }
+ }
+
+ public static Refactoring getChangeSignatureRefactoring(CodeActionParams params, IMethod method, boolean isDelegate, String methodName, String modifier, String returnType, List parameters,
+ List exceptions) {
+ ICompilationUnit cu = JDTUtils.resolveCompilationUnit(params.getTextDocument().getUri());
+ if (cu == null) {
+ return null;
+ }
+ IType primaryType = cu.findPrimaryType();
+ if (primaryType == null) {
+ return null;
+ }
+ try {
+ ChangeSignatureProcessor processor = new ChangeSignatureProcessor(method);
+ processor.setNewMethodName(methodName);
+ processor.setVisibility(JdtFlags.getVisibilityCode(modifier));
+ processor.setNewReturnTypeName(returnType);
+ processor.setDelegateUpdating(isDelegate);
+ RefactoringStatus status = processor.checkInitialConditions(new NullProgressMonitor());
+ if (status.hasFatalError()) {
+ if (status.hasFatalError()) {
+ logFatalError(status);
+ return null;
+ }
+ }
+ List parameterInfos = processor.getParameterInfos();
+ List newParameterInfos = new ArrayList<>();
+ for (MethodParameter param : parameters) {
+ if (param.originalIndex != ParameterInfo.INDEX_FOR_ADDED && param.originalIndex < parameterInfos.size()) {
+ ParameterInfo info = parameterInfos.get(param.originalIndex);
+ info.setNewTypeName(param.type);
+ info.setNewName(param.name);
+ newParameterInfos.add(info);
+ } else {
+ newParameterInfos.add(ParameterInfo.createInfoForAddedParameter(param.type, param.name, param.defaultValue));
+ }
+ }
+ parameterInfos.clear();
+ parameterInfos.addAll(newParameterInfos);
+ List exceptionInfos = processor.getExceptionInfos();
+ List newExceptionInfos = new ArrayList<>();
+ for (MethodException exception : exceptions) {
+ if (exception.typeHandleIdentifier != null) {
+ IJavaElement element = JavaCore.create(exception.typeHandleIdentifier);
+ if (element instanceof IType type) {
+ newExceptionInfos.add(ExceptionInfo.createInfoForAddedException(type));
+ }
+ } else {
+ IType type = null;
+ if (exception.type.equals("Exception")) {
+ // special handling for java.lang.Exception
+ type = cu.getJavaProject().findType("java.lang.Exception");
+ }
+ if (type == null) {
+ // find possible types with the fully qualified name in the project
+ type = cu.getJavaProject().findType(exception.type);
+ }
+ if (type == null) {
+ // find possible match types in existing import declarations
+ for (IImportDeclaration importDeclaration : cu.getImports()) {
+ String importName = importDeclaration.getElementName();
+ int dotIndex = importName.lastIndexOf(".");
+ if (dotIndex != -1 && dotIndex < importName.length() - 1) {
+ String typeName = importName.substring(dotIndex + 1);
+ if (typeName.equals(exception.type)) {
+ type = cu.getJavaProject().findType(importName);
+ break;
+ }
+ }
+ }
+ }
+ if (type == null) {
+ // exception.type is not FQN, or it should be found via IJavaProject.findType()
+ SearchEngine engine = new SearchEngine();
+ IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { cu.getJavaProject() }, true);
+ int qualIndex = exception.type.lastIndexOf('.');
+ if (qualIndex == -1) {
+ List foundTypes = new ArrayList<>();
+ engine.searchAllTypeNames(null, SearchPattern.R_FULL_MATCH, exception.type.toCharArray(), SearchPattern.R_FULL_MATCH, IJavaSearchConstants.TYPE, scope, new TypeNameMatchRequestor() {
+ @Override
+ public void acceptTypeNameMatch(TypeNameMatch match) {
+ IType type = match.getType();
+ try {
+ if (type.exists() && JdtFlags.isPublic(type)) {
+ foundTypes.add(type);
+ }
+ } catch (JavaModelException e) {
+ JavaLanguageServerPlugin.log(e);
+ }
+ }
+ }, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, new NullProgressMonitor());
+ if (foundTypes.size() == 1) {
+ type = foundTypes.get(0);
+ } else {
+ JavaLanguageServerPlugin.getProjectsManager().getConnection().showMessage(new MessageParams(MessageType.Error, "Ambigious exception types are found for " + exception.type + ", please use fully qualified name instead."));
+ return null;
+ }
+ }
+ }
+ if (type != null) {
+ newExceptionInfos.add(ExceptionInfo.createInfoForAddedException(type));
+ }
+ }
+ }
+ exceptionInfos.clear();
+ exceptionInfos.addAll(newExceptionInfos);
+ Refactoring refactoring = new ProcessorBasedRefactoring(processor);
+ refactoring.checkInitialConditions(new NullProgressMonitor());
+ status = refactoring.checkFinalConditions(new NullProgressMonitor());
+ if (status.hasFatalError()) {
+ logFatalError(status);
+ return null;
+ }
+ return refactoring;
+ } catch (CoreException e) {
+ JavaLanguageServerPlugin.logException(e);
+ }
+ return null;
+ }
+
+ private static void logFatalError(RefactoringStatus status) {
+ String message = status.getMessageMatchingSeverity(RefactoringStatus.FATAL);
+ if (message == null) {
+ message = status.getMessageMatchingSeverity(RefactoringStatus.ERROR);
+ }
+ JavaLanguageServerPlugin.getProjectsManager().getConnection().showMessage(new MessageParams(MessageType.Error, message));
+ JavaLanguageServerPlugin.logError(status.toString());
+ }
+}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/GetRefactorEditHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/GetRefactorEditHandler.java
index 91fa3ff9d8..466a96b30d 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/GetRefactorEditHandler.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/GetRefactorEditHandler.java
@@ -14,6 +14,7 @@
package org.eclipse.jdt.ls.core.internal.handlers;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -21,6 +22,9 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.internal.corext.fix.LinkedProposalModelCore;
import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroupCore;
@@ -29,6 +33,7 @@
import org.eclipse.jdt.ls.core.internal.ChangeUtil;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JSONUtility;
+import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.corext.refactoring.ParameterInfo;
import org.eclipse.jdt.ls.core.internal.corext.refactoring.code.IntroduceParameterRefactoring;
import org.eclipse.jdt.ls.core.internal.corrections.DiagnosticsHelper;
@@ -42,6 +47,7 @@
import org.eclipse.jdt.ls.core.internal.handlers.InferSelectionHandler.SelectionInfo;
import org.eclipse.jdt.ls.core.internal.handlers.MoveHandler.PackageNode;
import org.eclipse.jdt.ls.core.internal.text.correction.RefactorProposalUtility;
+import org.eclipse.lsp4j.ChangeAnnotation;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.FormattingOptions;
@@ -131,6 +137,43 @@ public static RefactorWorkspaceEdit getEditsForRefactor(GetRefactorEditParams pa
WorkspaceEdit edit = ChangeUtil.convertToWorkspaceEdit(change);
return new RefactorWorkspaceEdit(edit, null);
}
+ } else if (RefactorProposalUtility.CHANGE_SIGNATURE_COMMAND.equals(params.command)) {
+ if (params.commandArguments != null && params.commandArguments.size() == 8) {
+ String handleIdentifier = JSONUtility.toModel(params.commandArguments.get(0), String.class);
+ Boolean isDelegate = JSONUtility.toModel(params.commandArguments.get(1), Boolean.class);
+ String methodName = JSONUtility.toModel(params.commandArguments.get(2), String.class);
+ String modifier = JSONUtility.toModel(params.commandArguments.get(3), String.class);
+ String returnType = JSONUtility.toModel(params.commandArguments.get(4), String.class);
+ List parameters = Arrays.asList(JSONUtility.toModel(params.commandArguments.get(5), ChangeSignatureHandler.MethodParameter[].class));
+ List exceptions = Arrays.asList(JSONUtility.toModel(params.commandArguments.get(6), ChangeSignatureHandler.MethodException[].class));
+ Boolean preview = JSONUtility.toModel(params.commandArguments.get(7), Boolean.class);
+ if (handleIdentifier == null) {
+ return null;
+ }
+ IJavaElement element = JavaCore.create(handleIdentifier);
+ if (element instanceof IMethod method) {
+ Refactoring refactoring = ChangeSignatureHandler.getChangeSignatureRefactoring(params.context, method, isDelegate, methodName, modifier, returnType, parameters, exceptions);
+ if (refactoring == null) {
+ return null;
+ }
+ Change change = refactoring.createChange(new NullProgressMonitor());
+ if (change == null) {
+ return null;
+ }
+ WorkspaceEdit edit;
+ if (preview && JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isChangeAnnotationSupport()) {
+ edit = ChangeUtil.convertToWorkspaceEdit(change, ChangeSignatureHandler.CHANGE_SIGNATURE_ANNOTATION_ID);
+ Map annotations = new HashMap<>();
+ ChangeAnnotation annotation = new ChangeAnnotation("");
+ annotation.setNeedsConfirmation(true);
+ annotations.put(ChangeSignatureHandler.CHANGE_SIGNATURE_ANNOTATION_ID, annotation);
+ edit.setChangeAnnotations(annotations);
+ } else {
+ edit = ChangeUtil.convertToWorkspaceEdit(change);
+ }
+ return new RefactorWorkspaceEdit(edit, null);
+ }
+ }
}
if (proposal == null) {
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java
index 76d31f1bd6..616b41e77d 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java
@@ -400,4 +400,11 @@ public Collection excludedMarkerTypes() {
: List.of();
}
+ public boolean isChangeAnnotationSupport() {
+ return v3supported
+ && capabilities.getWorkspace() != null
+ && capabilities.getWorkspace().getWorkspaceEdit() != null
+ && capabilities.getWorkspace().getWorkspaceEdit().getChangeAnnotationSupport() != null;
+ }
+
}
diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/RefactorProposalUtility.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/RefactorProposalUtility.java
index 86cd52c95f..20aa2ce76a 100644
--- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/RefactorProposalUtility.java
+++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/RefactorProposalUtility.java
@@ -24,6 +24,7 @@
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
@@ -55,13 +56,17 @@
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.fix.LinkedProposalModelCore;
+import org.eclipse.jdt.internal.corext.refactoring.ExceptionInfo;
+import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTesterCore;
import org.eclipse.jdt.internal.corext.refactoring.code.PromoteTempToFieldRefactoring;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ChangeSignatureProcessor;
import org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaCodeActionKind;
+import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.Messages;
import org.eclipse.jdt.ls.core.internal.corext.refactoring.RefactoringAvailabilityTester;
import org.eclipse.jdt.ls.core.internal.corext.refactoring.RefactoringCoreMessages;
@@ -77,6 +82,8 @@
import org.eclipse.jdt.ls.core.internal.corrections.proposals.ChangeCorrectionProposal;
import org.eclipse.jdt.ls.core.internal.corrections.proposals.IProposalRelevance;
import org.eclipse.jdt.ls.core.internal.corrections.proposals.RefactoringCorrectionProposal;
+import org.eclipse.jdt.ls.core.internal.handlers.ChangeSignatureHandler.MethodException;
+import org.eclipse.jdt.ls.core.internal.handlers.ChangeSignatureHandler.MethodParameter;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
@@ -91,6 +98,7 @@ public class RefactorProposalUtility {
public static final String EXTRACT_METHOD_COMMAND = "extractMethod";
public static final String EXTRACT_FIELD_COMMAND = "extractField";
public static final String EXTRACT_INTERFACE_COMMAND = "extractInterface";
+ public static final String CHANGE_SIGNATURE_COMMAND = "changeSignature";
public static final String ASSIGN_FIELD_COMMAND = "assignField";
public static final String CONVERT_VARIABLE_TO_FIELD_COMMAND = "convertVariableToField";
public static final String MOVE_FILE_COMMAND = "moveFile";
@@ -842,6 +850,64 @@ public static ChangeCorrectionProposal getExtractInterfaceProposal(CodeActionPar
return null;
}
+ public static ChangeCorrectionProposal getChangeSignatureProposal(CodeActionParams params, IInvocationContext context) {
+ ICompilationUnit cu = context.getCompilationUnit();
+ if (cu == null) {
+ return null;
+ }
+ ASTNode methodNode = CodeActionUtility.inferASTNode(context.getCoveringNode(), MethodDeclaration.class);
+ if (methodNode == null) {
+ return null;
+ }
+ IMethodBinding methodBinding = ((MethodDeclaration) methodNode).resolveBinding();
+ if (methodBinding == null) {
+ return null;
+ }
+ IJavaElement element = methodBinding.getJavaElement();
+ if (element instanceof IMethod method) {
+ try {
+ ChangeSignatureProcessor processor = new ChangeSignatureProcessor(method);
+ if (RefactoringAvailabilityTester.isChangeSignatureAvailable(method) && processor.checkInitialConditions(new NullProgressMonitor()).isOK()) {
+ List parameters = new ArrayList<>();
+ for (ParameterInfo info : processor.getParameterInfos()) {
+ parameters.add(new MethodParameter(info.getOldTypeName(), info.getOldName(), info.getDefaultValue() == null ? "null" : info.getDefaultValue(), info.getOldIndex()));
+ }
+ List exceptions = new ArrayList<>();
+ for (ExceptionInfo info : processor.getExceptionInfos()) {
+ exceptions.add(new MethodException(info.getFullyQualifiedName(), info.getElement().getHandleIdentifier()));
+ }
+ ChangeSignatureInfo info = new ChangeSignatureInfo(method.getHandleIdentifier(), JdtFlags.getVisibilityString(processor.getVisibility()), processor.getReturnTypeString(), method.getElementName(),
+ parameters.toArray(MethodParameter[]::new), exceptions.toArray(MethodException[]::new));
+ String label = Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_change_signature_for, new String[] { method.getElementName() });
+ return new CUCorrectionCommandProposal(label, JavaCodeActionKind.REFACTOR_CHANGE_SIGNATURE, cu, IProposalRelevance.CHANGE_METHOD_SIGNATURE, RefactorProposalUtility.APPLY_REFACTORING_COMMAND_ID,
+ Arrays.asList(RefactorProposalUtility.CHANGE_SIGNATURE_COMMAND, params, info));
+ }
+ } catch (CoreException e) {
+ JavaLanguageServerPlugin.logException(e);
+ }
+ }
+ return null;
+ }
+
+ public static class ChangeSignatureInfo {
+
+ public String methodIdentifier;
+ public String modifier;
+ public String returnType;
+ public String methodName;
+ public MethodParameter[] parameters;
+ public MethodException[] exceptions;
+
+ public ChangeSignatureInfo(String methodIdentifier, String modifier, String returnType, String methodName, MethodParameter[] parameters, MethodException[] exceptions) {
+ this.methodIdentifier = methodIdentifier;
+ this.modifier = modifier;
+ this.returnType = returnType;
+ this.methodName = methodName;
+ this.parameters = parameters;
+ this.exceptions = exceptions;
+ }
+ }
+
public static String getUniqueMethodName(ASTNode astNode, String suggestedName) throws JavaModelException {
while (astNode != null && !(astNode instanceof TypeDeclaration || astNode instanceof AnonymousClassDeclaration)) {
astNode = astNode.getParent();
diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/ChangeSignatureHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/ChangeSignatureHandlerTest.java
new file mode 100644
index 0000000000..89d60e5ca8
--- /dev/null
+++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/ChangeSignatureHandlerTest.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Microsoft Corporation and others.
+ * All rights reserved. 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:
+ * Microsoft Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.ls.core.internal.handlers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+import org.eclipse.jdt.ls.core.internal.CodeActionUtil;
+import org.eclipse.jdt.ls.core.internal.JavaClientConnection;
+import org.eclipse.jdt.ls.core.internal.JavaCodeActionKind;
+import org.eclipse.jdt.ls.core.internal.LanguageServerWorkingCopyOwner;
+import org.eclipse.jdt.ls.core.internal.corext.refactoring.ParameterInfo;
+import org.eclipse.jdt.ls.core.internal.handlers.ChangeSignatureHandler.MethodException;
+import org.eclipse.jdt.ls.core.internal.handlers.ChangeSignatureHandler.MethodParameter;
+import org.eclipse.jdt.ls.core.internal.text.correction.RefactorProposalUtility;
+import org.eclipse.jdt.ls.core.internal.text.correction.RefactorProposalUtility.ChangeSignatureInfo;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.jsonrpc.messages.Either;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ChangeSignatureHandlerTest extends AbstractCompilationUnitBasedTest {
+
+ @Mock
+ private JavaClientConnection connection;
+ private IJavaProject fJavaProject;
+ private IPackageFragmentRoot fRoot;
+ private IPackageFragment fPackageP;
+
+ @Override
+ @Before
+ public void setup() throws Exception {
+ fJavaProject = newEmptyProject();
+ fRoot = fJavaProject.findPackageFragmentRoot(fJavaProject.getPath().append("src"));
+ assertNotNull(fRoot);
+ fPackageP = fRoot.createPackageFragment("p", true, null);
+ wcOwner = new LanguageServerWorkingCopyOwner(connection);
+ server = new JDTLanguageServer(projectsManager, this.preferenceManager);
+ }
+
+ @Test
+ public void testChangeSignatureRefactoringExists() throws JavaModelException {
+ //@formatter:off
+ ICompilationUnit unit = fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "\r\n" +
+ "public class A {\r\n" +
+ " public void getName(String input) {\r\n" +
+ " }\r\n" +
+ "}"
+ , true, null);
+ //@formatter:on
+ CodeActionParams params = CodeActionUtil.constructCodeActionParams(unit, "getName");
+ List> codeActions = server.codeAction(params).join();
+ Assert.assertNotNull(codeActions);
+ Either changeSignatureAction = CodeActionHandlerTest.findAction(codeActions, JavaCodeActionKind.REFACTOR_CHANGE_SIGNATURE);
+ Assert.assertNotNull(changeSignatureAction);
+ Command changeSignatureCommand = CodeActionHandlerTest.getCommand(changeSignatureAction);
+ Assert.assertNotNull(changeSignatureCommand);
+ Assert.assertEquals(RefactorProposalUtility.APPLY_REFACTORING_COMMAND_ID, changeSignatureCommand.getCommand());
+ List