| /******************************************************************************* |
| * Copyright (c) 2000, 2021 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.ui.text.correction.proposals; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.preferences.IScopeContext; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| |
| import org.eclipse.core.resources.ProjectScope; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.NamingConventions; |
| import org.eclipse.jdt.core.compiler.ITerminalSymbols; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.Assignment; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.CatchClause; |
| import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.EmptyStatement; |
| 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.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| import org.eclipse.jdt.core.dom.Initializer; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.Statement; |
| import org.eclipse.jdt.core.dom.ThrowStatement; |
| import org.eclipse.jdt.core.dom.TryStatement; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.UnionType; |
| import org.eclipse.jdt.core.dom.VariableDeclarationExpression; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.dom.VariableDeclarationStatement; |
| import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation; |
| |
| import org.eclipse.jdt.internal.core.manipulation.StubUtility; |
| import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; |
| import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; |
| import org.eclipse.jdt.internal.corext.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.CodeScopeBuilder; |
| import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; |
| import org.eclipse.jdt.internal.corext.dom.Selection; |
| import org.eclipse.jdt.internal.corext.dom.TokenScanner; |
| import org.eclipse.jdt.internal.corext.fix.CleanUpConstants; |
| import org.eclipse.jdt.internal.corext.fix.CleanUpPostSaveListener; |
| import org.eclipse.jdt.internal.corext.fix.CleanUpPreferenceUtil; |
| import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel; |
| import org.eclipse.jdt.internal.corext.refactoring.surround.ExceptionAnalyzer; |
| import org.eclipse.jdt.internal.corext.refactoring.surround.SurroundWithTryWithResourcesAnalyzer; |
| import org.eclipse.jdt.internal.corext.refactoring.surround.SurroundWithTryWithResourcesRefactoring; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| import org.eclipse.jdt.ui.PreferenceConstants; |
| import org.eclipse.jdt.ui.cleanup.CleanUpOptions; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.JavaPluginImages; |
| import org.eclipse.jdt.internal.ui.javaeditor.saveparticipant.AbstractSaveParticipantPreferenceConfiguration; |
| import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages; |
| import org.eclipse.jdt.internal.ui.text.correction.ModifierCorrectionSubProcessorCore; |
| |
| /** |
| * Proposals for 'Assign to variable' quick assist |
| * - Assign an expression from an ExpressionStatement to a local or field |
| * - Assign single or all parameter(s) to field(s) |
| * */ |
| public class AssignToVariableAssistProposal extends LinkedCorrectionProposal { |
| |
| public static final int LOCAL= 1; |
| public static final int FIELD= 2; |
| public static final int TRY_WITH_RESOURCES= 3; |
| |
| private final String KEY_NAME= "name"; //$NON-NLS-1$ |
| private final String KEY_TYPE= "type"; //$NON-NLS-1$ |
| private final String GROUP_EXC_TYPE= "exc_type"; //$NON-NLS-1$ |
| private final String GROUP_EXC_NAME= "exc_name"; //$NON-NLS-1$ |
| |
| private final int fVariableKind; |
| private final List<ASTNode> fNodesToAssign; // ExpressionStatement or SingleVariableDeclaration(s) |
| private final ITypeBinding fTypeBinding; |
| private final ICompilationUnit fCUnit; |
| |
| private VariableDeclarationFragment fExistingFragment; |
| |
| public AssignToVariableAssistProposal(ICompilationUnit cu, int variableKind, ExpressionStatement node, ITypeBinding typeBinding, int relevance) { |
| super("", cu, null, relevance, null); //$NON-NLS-1$ |
| |
| fCUnit= cu; |
| fVariableKind= variableKind; |
| fNodesToAssign= new ArrayList<>(); |
| fNodesToAssign.add(node); |
| |
| fTypeBinding= Bindings.normalizeForDeclarationUse(typeBinding, node.getAST()); |
| if (variableKind == LOCAL) { |
| setDisplayName(CorrectionMessages.AssignToVariableAssistProposal_assigntolocal_description); |
| setImage(JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL)); |
| } else if (variableKind == FIELD) { |
| setDisplayName(CorrectionMessages.AssignToVariableAssistProposal_assigntofield_description); |
| setImage(JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE)); |
| } else { |
| setDisplayName(CorrectionMessages.AssignToVariableAssistProposal_assignintrywithresources_description); |
| setImage(JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE)); |
| } |
| createImportRewrite((CompilationUnit) node.getRoot()); |
| } |
| |
| public AssignToVariableAssistProposal(ICompilationUnit cu, SingleVariableDeclaration parameter, VariableDeclarationFragment existingFragment, ITypeBinding typeBinding, int relevance) { |
| super("", cu, null, relevance, null); //$NON-NLS-1$ |
| |
| fCUnit= cu; |
| fVariableKind= FIELD; |
| fNodesToAssign= new ArrayList<>(); |
| fNodesToAssign.add(parameter); |
| fTypeBinding= typeBinding; |
| fExistingFragment= existingFragment; |
| |
| if (existingFragment == null) { |
| setDisplayName(CorrectionMessages.AssignToVariableAssistProposal_assignparamtofield_description); |
| } else { |
| setDisplayName(Messages.format(CorrectionMessages.AssignToVariableAssistProposal_assigntoexistingfield_description, BasicElementLabels.getJavaElementName(existingFragment.getName().getIdentifier()))); |
| } |
| setImage(JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE)); |
| } |
| |
| public AssignToVariableAssistProposal(ICompilationUnit cu, List<SingleVariableDeclaration> parameters, int relevance) { |
| super("", cu, null, relevance, null); //$NON-NLS-1$ |
| |
| fCUnit= cu; |
| fVariableKind= FIELD; |
| fNodesToAssign= new ArrayList<>(); |
| fNodesToAssign.addAll(parameters); |
| fTypeBinding= null; |
| |
| setDisplayName(CorrectionMessages.AssignToVariableAssistProposal_assignallparamstofields_description); |
| setImage(JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE)); |
| } |
| |
| @Override |
| protected ASTRewrite getRewrite() throws CoreException { |
| if (fVariableKind == FIELD) { |
| ASTRewrite rewrite= ASTRewrite.create(fNodesToAssign.get(0).getAST()); |
| if (fNodesToAssign.size() == 1) { |
| return doAddField(rewrite, fNodesToAssign.get(0), fTypeBinding, 0); |
| } else { |
| return doAddAllFields(rewrite); |
| } |
| } else { // LOCAL or TRY_WITH_RESOURCES |
| return doAddLocal(); |
| } |
| } |
| |
| private ASTRewrite doAddLocal() throws CoreException { |
| ASTNode nodeToAssign= fNodesToAssign.get(0); |
| Expression expression= ((ExpressionStatement) nodeToAssign).getExpression(); |
| AST ast= nodeToAssign.getAST(); |
| |
| ASTRewrite rewrite= ASTRewrite.create(ast); |
| |
| ImportRewrite importRewrite= createImportRewrite((CompilationUnit) nodeToAssign.getRoot()); |
| |
| String[] varNames= suggestLocalVariableNames(fTypeBinding, expression); |
| for (String varName : varNames) { |
| addLinkedPositionProposal(KEY_NAME, varName, null); |
| } |
| |
| VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment(); |
| newDeclFrag.setName(ast.newSimpleName(varNames[0])); |
| newDeclFrag.setInitializer((Expression) rewrite.createCopyTarget(expression)); |
| |
| Type type= evaluateType(ast, nodeToAssign, fTypeBinding, KEY_TYPE, TypeLocation.LOCAL_VARIABLE); |
| |
| if (fVariableKind == LOCAL) { |
| if (ASTNodes.isControlStatementBody(nodeToAssign.getLocationInParent())) { |
| Block block= ast.newBlock(); |
| block.statements().add(rewrite.createMoveTarget(nodeToAssign)); |
| rewrite.replace(nodeToAssign, block, null); |
| } |
| |
| if (needsSemicolon(expression)) { |
| VariableDeclarationStatement varStatement= ast.newVariableDeclarationStatement(newDeclFrag); |
| varStatement.setType(type); |
| rewrite.replace(expression, varStatement, null); |
| } else { |
| // trick for bug 43248: use an VariableDeclarationExpression and keep the ExpressionStatement |
| VariableDeclarationExpression varExpression= ast.newVariableDeclarationExpression(newDeclFrag); |
| varExpression.setType(type); |
| rewrite.replace(expression, varExpression, null); |
| } |
| setEndPosition(rewrite.track(nodeToAssign)); // set cursor after expression statement |
| } else { |
| TryStatement tryStatement= ast.newTryStatement(); |
| |
| VariableDeclarationExpression varExpression= ast.newVariableDeclarationExpression(newDeclFrag); |
| varExpression.setType(type); |
| tryStatement.resources().add(varExpression); |
| |
| EmptyStatement blankLine = (EmptyStatement) rewrite.createStringPlaceholder("", ASTNode.EMPTY_STATEMENT); //$NON-NLS-1$ |
| tryStatement.getBody().statements().add(blankLine); |
| |
| CatchClause catchClause= ast.newCatchClause(); |
| SingleVariableDeclaration decl= ast.newSingleVariableDeclaration(); |
| Selection selection= Selection.createFromStartLength(expression.getStartPosition(), expression.getLength()); |
| String varName= StubUtility.getExceptionVariableName(fCUnit.getJavaProject()); |
| SurroundWithTryWithResourcesAnalyzer analyzer= new SurroundWithTryWithResourcesAnalyzer(fCUnit, selection); |
| expression.getRoot().accept(analyzer); |
| CodeScopeBuilder.Scope scope= CodeScopeBuilder.perform(analyzer.getEnclosingBodyDeclaration(), selection). |
| findScope(selection.getOffset(), selection.getLength()); |
| scope.setCursor(selection.getOffset()); |
| String name= scope.createName(varName, false); |
| decl.setName(ast.newSimpleName(name)); |
| ITypeBinding[] exceptions= ExceptionAnalyzer.perform(expression.getParent(), selection, false); |
| List<ITypeBinding> allExceptions= new ArrayList<>(Arrays.asList(exceptions)); |
| List<ITypeBinding> mustRethrowList= new ArrayList<>(); |
| |
| if (fTypeBinding != null) { |
| IMethodBinding close= SurroundWithTryWithResourcesRefactoring.findAutocloseMethod(fTypeBinding); |
| if (close != null) { |
| for (ITypeBinding exceptionType : close.getExceptionTypes()) { |
| if (!allExceptions.contains(exceptionType)) { |
| allExceptions.add(exceptionType); |
| } |
| } |
| } |
| } |
| List<ITypeBinding> catchExceptions= analyzer.calculateCatchesAndRethrows(ASTNodes.filterSubtypes(allExceptions), mustRethrowList); |
| if (catchExceptions.size() > 0) { |
| ImportRewriteContext context= new ContextSensitiveImportRewriteContext(analyzer.getEnclosingBodyDeclaration(), importRewrite); |
| LinkedProposalModel linkedProposalModel= new LinkedProposalModel(); |
| int i= 0; |
| for (ITypeBinding mustThrow : mustRethrowList) { |
| CatchClause newClause= ast.newCatchClause(); |
| SingleVariableDeclaration newDecl= ast.newSingleVariableDeclaration(); |
| newDecl.setName(ast.newSimpleName(name)); |
| Type importType= importRewrite.addImport(mustThrow, ast, context, TypeLocation.EXCEPTION); |
| newDecl.setType(importType); |
| newClause.setException(newDecl); |
| ThrowStatement newThrowStatement= ast.newThrowStatement(); |
| newThrowStatement.setExpression(ast.newSimpleName(name)); |
| linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + i, true).addPosition(rewrite.track(decl.getName()), false); |
| newClause.getBody().statements().add(newThrowStatement); |
| tryStatement.catchClauses().add(newClause); |
| ++i; |
| } |
| List<ITypeBinding> filteredExceptions= ASTNodes.filterSubtypes(catchExceptions); |
| UnionType unionType= ast.newUnionType(); |
| List<Type> types= unionType.types(); |
| for (ITypeBinding exception : filteredExceptions) { |
| Type importType= importRewrite.addImport(exception, ast, context, TypeLocation.EXCEPTION); |
| types.add(importType); |
| linkedProposalModel.getPositionGroup(GROUP_EXC_TYPE + i, true).addPosition(rewrite.track(type), i == 0); |
| i++; |
| } |
| decl.setType(unionType); |
| catchClause.setException(decl); |
| linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + 0, true).addPosition(rewrite.track(decl.getName()), false); |
| Statement st= getCatchBody(rewrite, expression, "Exception", name, fCUnit.findRecommendedLineSeparator()); //$NON-NLS-1$ |
| if (st != null) { |
| catchClause.getBody().statements().add(st); |
| } |
| tryStatement.catchClauses().add(catchClause); |
| } |
| |
| rewrite.replace(expression, tryStatement, null); |
| setEndPosition(rewrite.track(blankLine)); |
| } |
| |
| addLinkedPosition(rewrite.track(newDeclFrag.getName()), true, KEY_NAME); |
| addLinkedPosition(rewrite.track(type), false, KEY_TYPE); |
| |
| return rewrite; |
| } |
| |
| private Statement getCatchBody(ASTRewrite rewrite, Expression expression, String type, String name, String lineSeparator) throws CoreException { |
| String s= StubUtility.getCatchBodyContent(fCUnit, type, name, expression, lineSeparator); |
| if (s == null) { |
| return null; |
| } else { |
| return (Statement)rewrite.createStringPlaceholder(s, ASTNode.RETURN_STATEMENT); |
| } |
| } |
| |
| private boolean needsSemicolon(Expression expression) { |
| if ((expression.getParent().getFlags() & ASTNode.RECOVERED) != 0) { |
| try { |
| TokenScanner scanner= new TokenScanner(getCompilationUnit()); |
| return scanner.readNext(expression.getStartPosition() + expression.getLength(), true) != ITerminalSymbols.TokenNameSEMICOLON; |
| } catch (CoreException e) { |
| // ignore |
| } |
| } |
| return false; |
| } |
| |
| private ASTRewrite doAddField(ASTRewrite rewrite, ASTNode nodeToAssign, ITypeBinding typeBinding, int index) { |
| boolean isParamToField= nodeToAssign.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION; |
| |
| ASTNode newTypeDecl= ASTResolving.findParentType(nodeToAssign); |
| if (newTypeDecl == null) { |
| return null; |
| } |
| |
| Expression expression= isParamToField ? ((SingleVariableDeclaration) nodeToAssign).getName() : ((ExpressionStatement) nodeToAssign).getExpression(); |
| |
| AST ast= newTypeDecl.getAST(); |
| |
| createImportRewrite((CompilationUnit) nodeToAssign.getRoot()); |
| |
| BodyDeclaration bodyDecl= ASTResolving.findParentBodyDeclaration(nodeToAssign); |
| Block body; |
| if (bodyDecl instanceof MethodDeclaration) { |
| body= ((MethodDeclaration) bodyDecl).getBody(); |
| } else if (bodyDecl instanceof Initializer) { |
| body= ((Initializer) bodyDecl).getBody(); |
| } else { |
| return null; |
| } |
| |
| IJavaProject project= getCompilationUnit().getJavaProject(); |
| boolean isAnonymous= newTypeDecl.getNodeType() == ASTNode.ANONYMOUS_CLASS_DECLARATION; |
| boolean isStatic= Modifier.isStatic(bodyDecl.getModifiers()) && !isAnonymous; |
| boolean isConstructorParam= isParamToField && nodeToAssign.getParent() instanceof MethodDeclaration && ((MethodDeclaration) nodeToAssign.getParent()).isConstructor(); |
| int modifiers= Modifier.PRIVATE; |
| if (isStatic) { |
| modifiers |= Modifier.STATIC; |
| } else if (isConstructorParam) { |
| String saveActionsKey= AbstractSaveParticipantPreferenceConfiguration.EDITOR_SAVE_PARTICIPANT_PREFIX + CleanUpPostSaveListener.POSTSAVELISTENER_ID; |
| IScopeContext[] scopes= { InstanceScope.INSTANCE, new ProjectScope(project.getProject()) }; |
| boolean safeActionsEnabled= Platform.getPreferencesService().getBoolean(JavaPlugin.getPluginId(), saveActionsKey, false, scopes); |
| if (safeActionsEnabled |
| && CleanUpOptions.TRUE.equals(PreferenceConstants.getPreference(CleanUpPreferenceUtil.SAVE_PARTICIPANT_KEY_PREFIX + CleanUpConstants.CLEANUP_ON_SAVE_ADDITIONAL_OPTIONS, project)) |
| && CleanUpOptions.TRUE.equals(PreferenceConstants.getPreference(CleanUpPreferenceUtil.SAVE_PARTICIPANT_KEY_PREFIX + CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL, project)) |
| && CleanUpOptions.TRUE.equals(PreferenceConstants.getPreference(CleanUpPreferenceUtil.SAVE_PARTICIPANT_KEY_PREFIX + CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_PRIVATE_FIELDS, project)) |
| ) { |
| int constructors= 0; |
| if (newTypeDecl instanceof AbstractTypeDeclaration) { |
| List<BodyDeclaration> bodyDeclarations= ((AbstractTypeDeclaration) newTypeDecl).bodyDeclarations(); |
| for (BodyDeclaration decl : bodyDeclarations) { |
| if (decl instanceof MethodDeclaration && ((MethodDeclaration) decl).isConstructor()) { |
| constructors++; |
| } |
| } |
| } |
| if (constructors == 1) { |
| modifiers |= Modifier.FINAL; |
| } |
| } |
| } |
| |
| VariableDeclarationFragment newDeclFrag= addFieldDeclaration(rewrite, newTypeDecl, modifiers, expression, nodeToAssign, typeBinding, index); |
| String varName= newDeclFrag.getName().getIdentifier(); |
| |
| Assignment assignment= ast.newAssignment(); |
| assignment.setRightHandSide((Expression) rewrite.createCopyTarget(expression)); |
| |
| boolean needsThis= StubUtility.useThisForFieldAccess(project); |
| if (isParamToField) { |
| needsThis |= varName.equals(((SimpleName) expression).getIdentifier()); |
| } |
| |
| SimpleName accessName= ast.newSimpleName(varName); |
| if (needsThis) { |
| FieldAccess fieldAccess= ast.newFieldAccess(); |
| fieldAccess.setName(accessName); |
| if (isStatic) { |
| String typeName= ((AbstractTypeDeclaration) newTypeDecl).getName().getIdentifier(); |
| fieldAccess.setExpression(ast.newSimpleName(typeName)); |
| } else { |
| fieldAccess.setExpression(ast.newThisExpression()); |
| } |
| assignment.setLeftHandSide(fieldAccess); |
| } else { |
| assignment.setLeftHandSide(accessName); |
| } |
| |
| ASTNode selectionNode; |
| if (isParamToField) { |
| // assign parameter to field |
| ExpressionStatement statement= ast.newExpressionStatement(assignment); |
| int insertIdx= findAssignmentInsertIndex(body.statements(), nodeToAssign) + index; |
| rewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY).insertAt(statement, insertIdx, null); |
| selectionNode= statement; |
| } else { |
| if (needsSemicolon(expression)) { |
| rewrite.replace(expression, ast.newExpressionStatement(assignment), null); |
| } else { |
| rewrite.replace(expression, assignment, null); |
| } |
| selectionNode= nodeToAssign; |
| } |
| |
| addLinkedPosition(rewrite.track(newDeclFrag.getName()), false, KEY_NAME + index); |
| if (!isParamToField) { |
| FieldDeclaration fieldDeclaration= (FieldDeclaration) newDeclFrag.getParent(); |
| addLinkedPosition(rewrite.track(fieldDeclaration.getType()), false, KEY_TYPE); |
| } |
| addLinkedPosition(rewrite.track(accessName), true, KEY_NAME + index); |
| IVariableBinding variableBinding= newDeclFrag.resolveBinding(); |
| if (variableBinding != null) { |
| for (SimpleName linkedNode : LinkedNodeFinder.findByBinding(nodeToAssign.getRoot(), variableBinding)) { |
| addLinkedPosition(rewrite.track(linkedNode), false, KEY_NAME + index); |
| } |
| } |
| setEndPosition(rewrite.track(selectionNode)); |
| |
| return rewrite; |
| } |
| |
| private ASTRewrite doAddAllFields(ASTRewrite rewrite) { |
| for (int i= 0; rewrite != null && i < fNodesToAssign.size(); i++) { |
| ASTNode nodeToAssign= fNodesToAssign.get(i); |
| ITypeBinding typeBinding= ((SingleVariableDeclaration) nodeToAssign).resolveBinding().getType(); |
| rewrite= doAddField(rewrite, nodeToAssign, typeBinding, i); |
| } |
| return rewrite; |
| } |
| |
| private VariableDeclarationFragment addFieldDeclaration(ASTRewrite rewrite, ASTNode newTypeDecl, int modifiers, Expression expression, ASTNode nodeToAssign, ITypeBinding typeBinding, |
| int index) { |
| if (fExistingFragment != null) { |
| return fExistingFragment; |
| } |
| |
| ChildListPropertyDescriptor property= ASTNodes.getBodyDeclarationsProperty(newTypeDecl); |
| List<BodyDeclaration> decls= ASTNodes.getBodyDeclarations(newTypeDecl); |
| AST ast= newTypeDecl.getAST(); |
| String[] varNames= suggestFieldNames(typeBinding, expression, modifiers, nodeToAssign); |
| for (String varName : varNames) { |
| addLinkedPositionProposal(KEY_NAME + index, varName, null); |
| } |
| String varName= varNames[0]; |
| |
| VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment(); |
| newDeclFrag.setName(ast.newSimpleName(varName)); |
| |
| FieldDeclaration newDecl= ast.newFieldDeclaration(newDeclFrag); |
| |
| Type type= evaluateType(ast, nodeToAssign, typeBinding, KEY_TYPE + index, TypeLocation.FIELD); |
| newDecl.setType(type); |
| newDecl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, modifiers)); |
| |
| ModifierCorrectionSubProcessorCore.installLinkedVisibilityProposals(getLinkedProposalModel(), rewrite, newDecl.modifiers(), false, ModifierCorrectionSubProcessorCore.KEY_MODIFIER + index); |
| |
| int insertIndex= findFieldInsertIndex(decls, nodeToAssign.getStartPosition()) + index; |
| rewrite.getListRewrite(newTypeDecl, property).insertAt(newDecl, insertIndex, null); |
| |
| return newDeclFrag; |
| } |
| |
| private Type evaluateType(AST ast, ASTNode nodeToAssign, ITypeBinding typeBinding, String groupID, TypeLocation location) { |
| for (ITypeBinding proposal : ASTResolving.getRelaxingTypes(ast, typeBinding)) { |
| if (fVariableKind != TRY_WITH_RESOURCES || Bindings.findTypeInHierarchy(proposal, "java.lang.AutoCloseable") != null) { //$NON-NLS-1$ |
| addLinkedPositionProposal(groupID, proposal); |
| } |
| } |
| ImportRewrite importRewrite= getImportRewrite(); |
| CompilationUnit cuNode= (CompilationUnit) nodeToAssign.getRoot(); |
| ImportRewriteContext context= new ContextSensitiveImportRewriteContext(cuNode, nodeToAssign.getStartPosition(), importRewrite); |
| return importRewrite.addImport(typeBinding, ast, context, location); |
| } |
| |
| private String[] suggestLocalVariableNames(ITypeBinding binding, Expression expression) { |
| IJavaProject project= getCompilationUnit().getJavaProject(); |
| return StubUtility.getVariableNameSuggestions(NamingConventions.VK_LOCAL, project, binding, expression, getUsedVariableNames(fNodesToAssign.get(0))); |
| } |
| |
| private String[] suggestFieldNames(ITypeBinding binding, Expression expression, int modifiers, ASTNode nodeToAssign) { |
| IJavaProject project= getCompilationUnit().getJavaProject(); |
| int varKind= Modifier.isStatic(modifiers) ? NamingConventions.VK_STATIC_FIELD : NamingConventions.VK_INSTANCE_FIELD; |
| return StubUtility.getVariableNameSuggestions(varKind, project, binding, expression, getUsedVariableNames(nodeToAssign)); |
| } |
| |
| private Collection<String> getUsedVariableNames(ASTNode nodeToAssign) { |
| return Arrays.asList(ASTResolving.getUsedVariableNames(nodeToAssign)); |
| } |
| |
| private int findAssignmentInsertIndex(List<Statement> statements, ASTNode nodeToAssign) { |
| |
| HashSet<String> paramsBefore= new HashSet<>(); |
| List<SingleVariableDeclaration> params = ((MethodDeclaration) nodeToAssign.getParent()).parameters(); |
| for (int i = 0; i < params.size() && (params.get(i) != nodeToAssign); i++) { |
| SingleVariableDeclaration decl= params.get(i); |
| paramsBefore.add(decl.getName().getIdentifier()); |
| } |
| |
| int i= 0; |
| for (i = 0; i < statements.size(); i++) { |
| Statement curr= statements.get(i); |
| switch (curr.getNodeType()) { |
| case ASTNode.CONSTRUCTOR_INVOCATION: |
| case ASTNode.SUPER_CONSTRUCTOR_INVOCATION: |
| break; |
| case ASTNode.EXPRESSION_STATEMENT: |
| Expression expr= ((ExpressionStatement) curr).getExpression(); |
| if (expr instanceof Assignment) { |
| Assignment assignment= (Assignment) expr; |
| Expression rightHand = assignment.getRightHandSide(); |
| if (rightHand instanceof SimpleName && paramsBefore.contains(((SimpleName) rightHand).getIdentifier())) { |
| IVariableBinding binding = Bindings.getAssignedVariable(assignment); |
| if (binding == null || binding.isField()) { |
| break; |
| } |
| } |
| } |
| return i; |
| default: |
| return i; |
| |
| } |
| } |
| return i; |
| |
| } |
| |
| private int findFieldInsertIndex(List<BodyDeclaration> decls, int currPos) { |
| for (int i= decls.size() - 1; i >= 0; i--) { |
| ASTNode curr= decls.get(i); |
| if (curr instanceof FieldDeclaration && currPos > curr.getStartPosition() + curr.getLength()) { |
| return i + 1; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * Returns the variable kind. |
| * @return int |
| */ |
| public int getVariableKind() { |
| return fVariableKind; |
| } |
| |
| |
| } |