/******************************************************************************* * Copyright (c) 2007, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.text.correction; import org.eclipse.che.ide.ext.java.jdt.Images; import org.eclipse.che.ide.ext.java.jdt.codeassistant.api.IProblemLocation; import org.eclipse.che.ide.ext.java.jdt.core.compiler.IProblem; import org.eclipse.che.ide.ext.java.jdt.core.dom.AST; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.Expression; import org.eclipse.che.ide.ext.java.jdt.core.dom.IBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IMethodBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IVariableBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.Modifier; import org.eclipse.che.ide.ext.java.jdt.core.dom.Name; import org.eclipse.che.ide.ext.java.jdt.core.dom.QualifiedName; import org.eclipse.che.ide.ext.java.jdt.core.dom.SimpleName; import org.eclipse.che.ide.ext.java.jdt.core.dom.SuperFieldAccess; import org.eclipse.che.ide.ext.java.jdt.core.dom.SuperMethodInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ASTResolving; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.GetterSetterUtil; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodes; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.Bindings; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ASTRewriteCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ChangeCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.quickassist.api.InvocationContext; import org.eclipse.che.ide.ext.java.jdt.text.Document; import java.util.ArrayList; import java.util.Collection; public class GetterSetterCorrectionSubProcessor { public static final String SELF_ENCAPSULATE_FIELD_ID = "org.eclipse.jdt.ui.correction.encapsulateField.assist"; //$NON-NLS-1$ private static class ProposalParameter { public final boolean useSuper; public final ASTRewrite astRewrite; public final Expression accessNode; public final Expression qualifier; public final IVariableBinding variableBinding; public final Document document; public ProposalParameter(Document document, boolean useSuper, ASTRewrite rewrite, Expression accessNode, Expression qualifier, IVariableBinding variableBinding) { this.document = document; this.useSuper = useSuper; this.astRewrite = rewrite; this.accessNode = accessNode; this.qualifier = qualifier; this.variableBinding = variableBinding; } } // public static class SelfEncapsulateFieldProposal extends ChangeCorrectionProposal // { // public for tests // // private boolean fNoDialog; // // public SelfEncapsulateFieldProposal(int relevance, IField field) // { // super(getDescription(field), null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE)); // fField = field; // fNoDialog = false; // setCommandId(SELF_ENCAPSULATE_FIELD_ID); // } // // public IField getField() // { // return fField; // } // // public void setNoDialog(boolean noDialog) // { // fNoDialog = noDialog; // } // // public TextFileChange getChange(IFile file) throws CoreException // { // final SelfEncapsulateFieldRefactoring refactoring = new SelfEncapsulateFieldRefactoring(fField); // refactoring.setVisibility(Flags.AccPublic); // refactoring.setConsiderVisibility(false);//private field references are just searched in local file // refactoring.checkInitialConditions(new NullProgressMonitor()); // refactoring.checkFinalConditions(new NullProgressMonitor()); // Change createdChange = refactoring.createChange(new NullProgressMonitor()); // if (createdChange instanceof CompositeChange) // { // Change[] children = ((CompositeChange)createdChange).getChildren(); // for (int i = 0; i < children.length; i++) // { // Change curr = children[i]; // if (curr instanceof TextFileChange && ((TextFileChange)curr).getFile().equals(file)) // { // return (TextFileChange)curr; // } // } // } // return null; // } // // private static String getDescription(IField field) // { // return Messages.format( // CorrectionMessages.INSTANCE.GetterSetterCorrectionSubProcessor_creategetterunsingencapsulatefield_description, // BasicElementLabels.getJavaElementName(field.getElementName())); // } // // /* // * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension5#getAdditionalProposalInfo(org.eclipse.core.runtime // .IProgressMonitor) // * @since 3.5 // */ // @Override // public Object getAdditionalProposalInfo(IProgressMonitor monitor) // { // return CorrectionMessages.INSTANCE.GetterSetterCorrectionSubProcessor_additional_info; // } // // @Override // public void apply(IDocument document) // { // try // { // final SelfEncapsulateFieldRefactoring refactoring = new SelfEncapsulateFieldRefactoring(fField); // refactoring.setVisibility(Flags.AccPublic); // refactoring.setConsiderVisibility(false);//private field references are just searched in local file // if (fNoDialog) // { // IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); // final RefactoringExecutionHelper helper = // new RefactoringExecutionHelper(refactoring, RefactoringStatus.ERROR, // RefactoringSaveHelper.SAVE_REFACTORING, JavaPlugin.getActiveWorkbenchShell(), window); // if (Display.getCurrent() != null) // { // try // { // helper.perform(false, false); // } // catch (InterruptedException e) // { // JavaPlugin.log(e); // } // catch (InvocationTargetException e) // { // JavaPlugin.log(e); // } // } // else // { // Display.getDefault().syncExec(new Runnable() // { // public void run() // { // try // { // helper.perform(false, false); // } // catch (InterruptedException e) // { // JavaPlugin.log(e); // } // catch (InvocationTargetException e) // { // JavaPlugin.log(e); // } // } // }); // } // } // else // { // new RefactoringStarter().activate(new SelfEncapsulateFieldWizard(refactoring), // JavaPlugin.getActiveWorkbenchShell(), "", RefactoringSaveHelper.SAVE_REFACTORING); //$NON-NLS-1$ // } // } // catch (JavaModelException e) // { // ExceptionHandler.handle(e, // CorrectionMessages.INSTANCE.GetterSetterCorrectionSubProcessor_encapsulate_field_error_title, // CorrectionMessages.INSTANCE.GetterSetterCorrectionSubProcessor_encapsulate_field_error_message); // } // } // } /** * Used by quick assist * * @param context * the invocation context * @param coveringNode * the covering node * @param locations * the problems at the corrent location * @param resultingCollections * the resulting proposals * @return <code>true</code> if the quick assist is applicable at this offset */ public static boolean addGetterSetterProposal(InvocationContext context, ASTNode coveringNode, IProblemLocation[] locations, ArrayList<ICommandAccess> resultingCollections) { if (locations != null) { for (int i = 0; i < locations.length; i++) { int problemId = locations[i].getProblemId(); if (problemId == IProblem.UnusedPrivateField) return false; if (problemId == IProblem.UnqualifiedFieldAccess) return false; } } return addGetterSetterProposal(context, coveringNode, resultingCollections, 7); } public static void addGetterSetterProposal(InvocationContext context, IProblemLocation location, Collection<ICommandAccess> proposals, int relevance) { addGetterSetterProposal(context, location.getCoveringNode(context.getASTRoot()), proposals, relevance); } private static boolean addGetterSetterProposal(InvocationContext context, ASTNode coveringNode, Collection<ICommandAccess> proposals, int relevance) { if (!(coveringNode instanceof SimpleName)) { return false; } SimpleName sn = (SimpleName)coveringNode; IBinding binding = sn.resolveBinding(); if (!(binding instanceof IVariableBinding)) return false; IVariableBinding variableBinding = (IVariableBinding)binding; if (!variableBinding.isField()) return false; if (proposals == null) return true; ChangeCorrectionProposal proposal = getProposal(context.getDocument(), sn, variableBinding, relevance); if (proposal != null) proposals.add(proposal); return true; } private static ChangeCorrectionProposal getProposal(Document document, SimpleName sn, IVariableBinding variableBinding, int relevance) { Expression accessNode = sn; Expression qualifier = null; boolean useSuper = false; ASTNode parent = sn.getParent(); switch (parent.getNodeType()) { case ASTNode.QUALIFIED_NAME: accessNode = (Expression)parent; qualifier = ((QualifiedName)parent).getQualifier(); break; case ASTNode.SUPER_FIELD_ACCESS: accessNode = (Expression)parent; qualifier = ((SuperFieldAccess)parent).getQualifier(); useSuper = true; break; } ASTRewrite rewrite = ASTRewrite.create(sn.getAST()); ProposalParameter gspc = new ProposalParameter(document, useSuper, rewrite, accessNode, qualifier, variableBinding); if (ASTResolving.isWriteAccess(sn)) return addSetterProposal(gspc, relevance); else return addGetterProposal(gspc, relevance); } /** * Proposes a getter for this field. * * @param context * the proposal parameter * @param relevance * relevance of this proposal * @return the proposal if available or null */ private static ChangeCorrectionProposal addGetterProposal(ProposalParameter context, int relevance) { IMethodBinding method = findGetter(context); if (method != null) { Expression mi = createMethodInvocation(context, method, null); context.astRewrite.replace(context.accessNode, mi, null); String label = CorrectionMessages.INSTANCE. GetterSetterCorrectionSubProcessor_replacewithgetter_description(ASTNodes.asString(context.accessNode)); Images image = Images.correction_change; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, context.astRewrite, relevance, context.document, image); return proposal; } else { //TODO // IJavaElement element = context.variableBinding.getJavaElement(); // if (element instanceof IField) // { // IField field = (IField)element; // try // { // if (RefactoringAvailabilityTester.isSelfEncapsulateAvailable(field)) // return new SelfEncapsulateFieldProposal(relevance, field); // } // catch (JavaModelException e) // { // JavaPlugin.log(e); // } // } } return null; } private static IMethodBinding findGetter(ProposalParameter context) { ITypeBinding returnType = context.variableBinding.getType(); String getterName = GetterSetterUtil.getGetterName(context.variableBinding, null, isBoolean(context)); ITypeBinding declaringType = context.variableBinding.getDeclaringClass(); if (declaringType == null) return null; IMethodBinding getter = Bindings.findMethodInHierarchy(declaringType, getterName, new ITypeBinding[0]); if (getter != null && getter.getReturnType().isAssignmentCompatible(returnType) && Modifier.isStatic(getter.getModifiers()) == Modifier.isStatic(context.variableBinding.getModifiers())) return getter; return null; } private static Expression createMethodInvocation(ProposalParameter context, IMethodBinding method, Expression argument) { AST ast = context.astRewrite.getAST(); Expression qualifier = context.qualifier; if (context.useSuper) { SuperMethodInvocation invocation = ast.newSuperMethodInvocation(); invocation.setName(ast.newSimpleName(method.getName())); if (qualifier != null) invocation.setQualifier((Name)context.astRewrite.createCopyTarget(qualifier)); if (argument != null) invocation.arguments().add(argument); return invocation; } else { MethodInvocation invocation = ast.newMethodInvocation(); invocation.setName(ast.newSimpleName(method.getName())); if (qualifier != null) invocation.setExpression((Expression)context.astRewrite.createCopyTarget(qualifier)); if (argument != null) invocation.arguments().add(argument); return invocation; } } /** * Proposes a setter for this field. * * @param context * the proposal parameter * @param relevance * relevance of this proposal * @return the proposal if available or null */ private static ChangeCorrectionProposal addSetterProposal(ProposalParameter context, int relevance) { boolean isBoolean = isBoolean(context); String setterName = GetterSetterUtil.getSetterName(context.variableBinding, null, isBoolean); ITypeBinding declaringType = context.variableBinding.getDeclaringClass(); if (declaringType == null) return null; IMethodBinding method = Bindings.findMethodInHierarchy(declaringType, setterName, new ITypeBinding[]{context.variableBinding.getType()}); if (method != null && Bindings.isVoidType(method.getReturnType()) && (Modifier.isStatic(method.getModifiers()) == Modifier.isStatic(context.variableBinding.getModifiers()))) { Expression assignedValue = getAssignedValue(context); if (assignedValue == null) return null; //we don't know how to handle those cases. Expression mi = createMethodInvocation(context, method, assignedValue); context.astRewrite.replace(context.accessNode.getParent(), mi, null); String label = CorrectionMessages.INSTANCE.GetterSetterCorrectionSubProcessor_replacewithsetter_description(ASTNodes .asString( context.accessNode)); Images image = Images.correction_change; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, context.astRewrite, relevance, context.document, image); return proposal; } else { //TODO // IJavaElement element = context.variableBinding.getJavaElement(); // if (element instanceof IField) // { // IField field = (IField)element; // try // { // if (RefactoringAvailabilityTester.isSelfEncapsulateAvailable(field)) // return new SelfEncapsulateFieldProposal(relevance, field); // } // catch (JavaModelException e) // { // JavaPlugin.log(e); // } // } } return null; } private static boolean isBoolean(ProposalParameter context) { AST ast = context.astRewrite.getAST(); boolean isBoolean = ast.resolveWellKnownType("boolean") == context.variableBinding.getType(); //$NON-NLS-1$ if (!isBoolean) isBoolean = ast.resolveWellKnownType("java.lang.Boolean") == context.variableBinding.getType(); //$NON-NLS-1$ return isBoolean; } private static Expression getAssignedValue(ProposalParameter context) { ASTNode parent = context.accessNode.getParent(); ASTRewrite astRewrite = context.astRewrite; IMethodBinding getter = findGetter(context); Expression getterExpression = null; if (getter != null) { getterExpression = astRewrite.getAST().newSimpleName("placeholder"); //$NON-NLS-1$ } ITypeBinding type = context.variableBinding.getType(); boolean is50OrHigher = true; //JavaModelUtil.is50OrHigher(javaProject); Expression result = GetterSetterUtil.getAssignedValue(parent, astRewrite, getterExpression, type, is50OrHigher); if (result != null && getterExpression != null && getterExpression.getParent() != null) { getterExpression.getParent().setStructuralProperty(getterExpression.getLocationInParent(), createMethodInvocation(context, getter, null)); } return result; } }