/******************************************************************************* * Copyright (c) 2000, 2009 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 * jens.lukowski@gmx.de - contributed code to convert prefix and postfix * expressions into a combination of setter and getter calls. * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for * bug Encapsulate field can fail when two variables in one variable declaration (see * https://bugs.eclipse.org/bugs/show_bug.cgi?id=51540). *******************************************************************************/ package org.eclipse.wst.jsdt.internal.corext.refactoring.sef; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; 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.TextChange; import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.wst.jsdt.core.Flags; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IField; import org.eclipse.wst.jsdt.core.IJavaScriptElement; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.ISourceRange; import org.eclipse.wst.jsdt.core.IType; import org.eclipse.wst.jsdt.core.JavaScriptConventions; import org.eclipse.wst.jsdt.core.JavaScriptCore; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.NamingConventions; import org.eclipse.wst.jsdt.core.compiler.IProblem; import org.eclipse.wst.jsdt.core.dom.AST; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration; import org.eclipse.wst.jsdt.core.dom.Assignment; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.BodyDeclaration; import org.eclipse.wst.jsdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation; import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; import org.eclipse.wst.jsdt.core.dom.Expression; import org.eclipse.wst.jsdt.core.dom.FieldAccess; import org.eclipse.wst.jsdt.core.dom.FieldDeclaration; import org.eclipse.wst.jsdt.core.dom.IFunctionBinding; import org.eclipse.wst.jsdt.core.dom.ITypeBinding; import org.eclipse.wst.jsdt.core.dom.IVariableBinding; import org.eclipse.wst.jsdt.core.dom.JSdoc; import org.eclipse.wst.jsdt.core.dom.Message; import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; import org.eclipse.wst.jsdt.core.dom.Modifier; import org.eclipse.wst.jsdt.core.dom.ReturnStatement; import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration; import org.eclipse.wst.jsdt.core.dom.Type; import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment; import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite; import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite; import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings; import org.eclipse.wst.jsdt.core.refactoring.descriptors.JavaScriptRefactoringDescriptor; import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchConstants; import org.eclipse.wst.jsdt.core.search.SearchPattern; import org.eclipse.wst.jsdt.internal.corext.codemanipulation.GetterSetterUtil; import org.eclipse.wst.jsdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes; import org.eclipse.wst.jsdt.internal.corext.dom.Bindings; import org.eclipse.wst.jsdt.internal.corext.dom.ModifierRewrite; import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder; import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks; import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptor; import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; import org.eclipse.wst.jsdt.internal.corext.refactoring.JavaRefactoringArguments; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringScopeFactory; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringSearchEngine; import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ScriptableRefactoring; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TextChangeManager; import org.eclipse.wst.jsdt.internal.corext.util.JdtFlags; import org.eclipse.wst.jsdt.internal.corext.util.Messages; import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; import org.eclipse.wst.jsdt.internal.ui.viewsupport.BindingLabelProvider; import org.eclipse.wst.jsdt.ui.CodeGeneration; import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels; /** * Encapsulates a field into getter and setter calls. */ public class SelfEncapsulateFieldRefactoring extends ScriptableRefactoring { private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$ private static final String ATTRIBUTE_GETTER= "getter"; //$NON-NLS-1$ private static final String ATTRIBUTE_SETTER= "setter"; //$NON-NLS-1$ private static final String ATTRIBUTE_INSERTION= "insertion"; //$NON-NLS-1$ private static final String ATTRIBUTE_COMMENTS= "comments"; //$NON-NLS-1$ private static final String ATTRIBUTE_DECLARING= "declaring"; //$NON-NLS-1$ private IField fField; private TextChangeManager fChangeManager; private JavaScriptUnit fRoot; private VariableDeclarationFragment fFieldDeclaration; private ASTRewrite fRewriter; private ImportRewrite fImportRewrite; private int fVisibility= -1; private String fGetterName; private String fSetterName; private String fArgName; private boolean fSetterMustReturnValue; private int fInsertionIndex; // -1 represents as first method. private boolean fEncapsulateDeclaringClass; private boolean fGenerateJavadoc; private List fUsedReadNames; private List fUsedModifyNames; private boolean fConsiderVisibility=true; private static final String NO_NAME= ""; //$NON-NLS-1$ /** * Creates a new self encapsulate field refactoring. * @param field the field, or <code>null</code> if invoked by scripting * @throws JavaScriptModelException */ public SelfEncapsulateFieldRefactoring(IField field) throws JavaScriptModelException { fEncapsulateDeclaringClass= true; fChangeManager= new TextChangeManager(); fField= field; if (field != null) initialize(field); } private void initialize(IField field) throws JavaScriptModelException { fGetterName= GetterSetterUtil.getGetterName(field, null); fSetterName= GetterSetterUtil.getSetterName(field, null); fArgName= NamingConventions.removePrefixAndSuffixForFieldName(field.getJavaScriptProject(), field.getElementName(), field.getFlags()); checkArgName(); } public IField getField() { return fField; } public String getGetterName() { return fGetterName; } public void setGetterName(String name) { fGetterName= name; Assert.isNotNull(fGetterName); } public String getSetterName() { return fSetterName; } public void setSetterName(String name) { fSetterName= name; Assert.isNotNull(fSetterName); } public void setInsertionIndex(int index) { fInsertionIndex= index; } public int getVisibility() { return fVisibility; } public void setVisibility(int visibility) { fVisibility= visibility; } public void setEncapsulateDeclaringClass(boolean encapsulateDeclaringClass) { fEncapsulateDeclaringClass= encapsulateDeclaringClass; } public boolean getEncapsulateDeclaringClass() { return fEncapsulateDeclaringClass; } public boolean getGenerateJavadoc() { return fGenerateJavadoc; } public void setGenerateJavadoc(boolean value) { fGenerateJavadoc= value; } //----activation checking ---------------------------------------------------------- public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { if (fVisibility < 0) fVisibility= (fField.getFlags() & (Flags.AccPublic | Flags.AccPrivate)); RefactoringStatus result= new RefactoringStatus(); result.merge(Checks.checkAvailability(fField)); if (result.hasFatalError()) return result; fRoot= new RefactoringASTParser(AST.JLS3).parse(fField.getJavaScriptUnit(), true, pm); ISourceRange sourceRange= fField.getNameRange(); ASTNode node= NodeFinder.perform(fRoot, sourceRange.getOffset(), sourceRange.getLength()); if (node == null) { return mappingErrorFound(result, node); } fFieldDeclaration= (VariableDeclarationFragment)ASTNodes.getParent(node, VariableDeclarationFragment.class); if (fFieldDeclaration == null) { return mappingErrorFound(result, node); } if (fFieldDeclaration.resolveBinding() == null) { if (!processCompilerError(result, node)) result.addFatalError(RefactoringCoreMessages.SelfEncapsulateField_type_not_resolveable); return result; } computeUsedNames(); fRewriter= ASTRewrite.create(fRoot.getAST()); return result; } 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; } 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.SelfEncapsulateField_compiler_errors_field, new String[] { fField.getElementName(), messages[0].getMessage()})); return true; } private String getMappingErrorMessage() { return Messages.format( RefactoringCoreMessages.SelfEncapsulateField_cannot_analyze_selected_field, new String[] {fField.getElementName()}); } //---- Input checking ---------------------------------------------------------- public RefactoringStatus checkMethodNames() { return checkMethodNames(isUsingLocalGetter(),isUsingLocalSetter()); } public RefactoringStatus checkMethodNames(boolean usingLocalGetter, boolean usingLocalSetter) { RefactoringStatus result= new RefactoringStatus(); IType declaringType= fField.getDeclaringType(); checkName(result, fGetterName, fUsedReadNames, declaringType, usingLocalGetter, fField); checkName(result, fSetterName, fUsedModifyNames, declaringType, usingLocalSetter, fField); return result; } private static void checkName(RefactoringStatus status, String name, List usedNames, IType type, boolean reUseExistingField, IField field) { if ("".equals(name)) { //$NON-NLS-1$ status.addFatalError(RefactoringCoreMessages.Checks_Choose_name); return; } boolean isStatic=false; try { isStatic= Flags.isStatic(field.getFlags()); } catch (JavaScriptModelException e) { } status.merge(Checks.checkMethodName(name)); for (Iterator iter= usedNames.iterator(); iter.hasNext(); ) { IFunctionBinding method= (IFunctionBinding)iter.next(); String selector= method.getName(); if (selector.equals(name)) { if (!reUseExistingField) { status.addFatalError(Messages.format(RefactoringCoreMessages.SelfEncapsulateField_method_exists, new String[] { BindingLabelProvider.getBindingLabel(method, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), type.getElementName() })); } else { boolean methodIsStatic= Modifier.isStatic(method.getModifiers()); if (methodIsStatic && !isStatic) status.addWarning(Messages.format(RefactoringCoreMessages.SelfEncapsulateFieldRefactoring_static_method_but_nonstatic_field, new String[] { method.getName(), field.getElementName() })); if (!methodIsStatic && isStatic) status.addFatalError(Messages.format(RefactoringCoreMessages.SelfEncapsulateFieldRefactoring_nonstatic_method_but_static_field, new String[] { method.getName(), field.getElementName() })); return; } } } if (reUseExistingField) status.addFatalError(Messages.format( RefactoringCoreMessages.SelfEncapsulateFieldRefactoring_methoddoesnotexist_status_fatalError, new String[] {name, type.getElementName()})); } public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { RefactoringStatus result= new RefactoringStatus(); fChangeManager.clear(); pm.beginTask(NO_NAME, 12); pm.setTaskName(RefactoringCoreMessages.SelfEncapsulateField_checking_preconditions); boolean usingLocalGetter=isUsingLocalGetter(); boolean usingLocalSetter=isUsingLocalSetter(); result.merge(checkMethodNames(usingLocalGetter,usingLocalSetter)); pm.worked(1); if (result.hasFatalError()) return result; pm.setTaskName(RefactoringCoreMessages.SelfEncapsulateField_searching_for_cunits); final SubProgressMonitor subPm= new SubProgressMonitor(pm, 5); IJavaScriptUnit[] affectedCUs= RefactoringSearchEngine.findAffectedCompilationUnits( SearchPattern.createPattern(fField, IJavaScriptSearchConstants.REFERENCES), RefactoringScopeFactory.create(fField, fConsiderVisibility), subPm, result, true); checkInHierarchy(result, usingLocalGetter, usingLocalSetter); if (result.hasFatalError()) return result; pm.setTaskName(RefactoringCoreMessages.SelfEncapsulateField_analyzing); IProgressMonitor sub= new SubProgressMonitor(pm, 5); sub.beginTask(NO_NAME, affectedCUs.length); IVariableBinding fieldIdentifier= fFieldDeclaration.resolveBinding(); ITypeBinding declaringClass= ((AbstractTypeDeclaration)ASTNodes.getParent(fFieldDeclaration, AbstractTypeDeclaration.class)).resolveBinding(); List ownerDescriptions= new ArrayList(); IJavaScriptUnit owner= fField.getJavaScriptUnit(); fImportRewrite= StubUtility.createImportRewrite(fRoot, true); for (int i= 0; i < affectedCUs.length; i++) { IJavaScriptUnit unit= affectedCUs[i]; sub.subTask(unit.getElementName()); JavaScriptUnit root= null; ASTRewrite rewriter= null; ImportRewrite importRewrite; List descriptions; if (owner.equals(unit)) { root= fRoot; rewriter= fRewriter; importRewrite= fImportRewrite; descriptions= ownerDescriptions; } else { root= new RefactoringASTParser(AST.JLS3).parse(unit, true); rewriter= ASTRewrite.create(root.getAST()); descriptions= new ArrayList(); importRewrite= StubUtility.createImportRewrite(root, true); } checkCompileErrors(result, root, unit); AccessAnalyzer analyzer= new AccessAnalyzer(this, unit, fieldIdentifier, declaringClass, rewriter, importRewrite); root.accept(analyzer); result.merge(analyzer.getStatus()); if (!fSetterMustReturnValue) fSetterMustReturnValue= analyzer.getSetterMustReturnValue(); if (result.hasFatalError()) { fChangeManager.clear(); return result; } descriptions.addAll(analyzer.getGroupDescriptions()); if (!owner.equals(unit)) createEdits(unit, rewriter, descriptions, importRewrite); sub.worked(1); if (pm.isCanceled()) throw new OperationCanceledException(); } ownerDescriptions.addAll(addGetterSetterChanges(fRoot, fRewriter, owner.findRecommendedLineSeparator(),usingLocalSetter, usingLocalGetter)); createEdits(owner, fRewriter, ownerDescriptions, fImportRewrite); sub.done(); IFile[] filesToBeModified= ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); result.merge(Checks.validateModifiesFiles(filesToBeModified, getValidationContext())); if (result.hasFatalError()) return result; ResourceChangeChecker.checkFilesToBeChanged(filesToBeModified, new SubProgressMonitor(pm, 1)); return result; } private void createEdits(IJavaScriptUnit unit, ASTRewrite rewriter, List groups, ImportRewrite importRewrite) throws CoreException { TextChange change= fChangeManager.get(unit); MultiTextEdit root= new MultiTextEdit(); change.setEdit(root); root.addChild(importRewrite.rewriteImports(null)); root.addChild(rewriter.rewriteAST()); for (Iterator iter= groups.iterator(); iter.hasNext();) { change.addTextEditGroup((TextEditGroup)iter.next()); } } public Change createChange(IProgressMonitor pm) throws CoreException { final Map arguments= new HashMap(); String project= null; IJavaScriptProject javaProject= fField.getJavaScriptProject(); if (javaProject != null) project= javaProject.getElementName(); int flags= JavaScriptRefactoringDescriptor.JAR_MIGRATION | JavaScriptRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE; final IType declaring= fField.getDeclaringType(); try { if (declaring.isAnonymous() || declaring.isLocal()) flags|= JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; } catch (JavaScriptModelException exception) { JavaScriptPlugin.log(exception); } final String description= Messages.format(RefactoringCoreMessages.SelfEncapsulateField_descriptor_description_short, fField.getElementName()); final String header= Messages.format(RefactoringCoreMessages.SelfEncapsulateFieldRefactoring_descriptor_description, new String[] { JavaScriptElementLabels.getElementLabel(fField, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getElementLabel(declaring, JavaScriptElementLabels.ALL_FULLY_QUALIFIED)}); final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); comment.addSetting(Messages.format(RefactoringCoreMessages.SelfEncapsulateField_original_pattern, JavaScriptElementLabels.getElementLabel(fField, JavaScriptElementLabels.ALL_FULLY_QUALIFIED))); comment.addSetting(Messages.format(RefactoringCoreMessages.SelfEncapsulateField_getter_pattern, fGetterName)); comment.addSetting(Messages.format(RefactoringCoreMessages.SelfEncapsulateField_setter_pattern, fSetterName)); String visibility= JdtFlags.getVisibilityString(fVisibility); if ("".equals(visibility)) //$NON-NLS-1$ visibility= RefactoringCoreMessages.SelfEncapsulateField_default_visibility; comment.addSetting(Messages.format(RefactoringCoreMessages.SelfEncapsulateField_visibility_pattern, visibility)); if (fEncapsulateDeclaringClass) comment.addSetting(RefactoringCoreMessages.SelfEncapsulateField_use_accessors); else comment.addSetting(RefactoringCoreMessages.SelfEncapsulateField_do_not_use_accessors); if (fGenerateJavadoc) comment.addSetting(RefactoringCoreMessages.SelfEncapsulateField_generate_comments); final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.ENCAPSULATE_FIELD, project, description, comment.asString(), arguments, flags); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fField)); arguments.put(ATTRIBUTE_VISIBILITY, Integer.valueOf(fVisibility).toString()); arguments.put(ATTRIBUTE_INSERTION, Integer.valueOf(fInsertionIndex).toString()); arguments.put(ATTRIBUTE_SETTER, fSetterName); arguments.put(ATTRIBUTE_GETTER, fGetterName); arguments.put(ATTRIBUTE_COMMENTS, Boolean.valueOf(fGenerateJavadoc).toString()); arguments.put(ATTRIBUTE_DECLARING, Boolean.valueOf(fEncapsulateDeclaringClass).toString()); final DynamicValidationRefactoringChange result= new DynamicValidationRefactoringChange(descriptor, getName()); TextChange[] changes= fChangeManager.getAllChanges(); pm.beginTask(NO_NAME, changes.length); pm.setTaskName(RefactoringCoreMessages.SelfEncapsulateField_create_changes); for (int i= 0; i < changes.length; i++) { result.add(changes[i]); pm.worked(1); } pm.done(); return result; } public String getName() { return RefactoringCoreMessages.SelfEncapsulateField_name; } //---- Helper methods ------------------------------------------------------------- private void checkCompileErrors(RefactoringStatus result, JavaScriptUnit root, IJavaScriptUnit element) { IProblem[] messages= root.getProblems(); for (int i= 0; i < messages.length; i++) { IProblem problem= messages[i]; if (!isIgnorableProblem(problem)) { result.addError(Messages.format( RefactoringCoreMessages.SelfEncapsulateField_compiler_errors_update, element.getElementName()), JavaStatusContext.create(element)); return; } } } private void checkInHierarchy(RefactoringStatus status, boolean usingLocalGetter, boolean usingLocalSetter) { AbstractTypeDeclaration declaration= (AbstractTypeDeclaration)ASTNodes.getParent(fFieldDeclaration, AbstractTypeDeclaration.class); ITypeBinding type= declaration.resolveBinding(); if (type != null) { ITypeBinding fieldType= fFieldDeclaration.resolveBinding().getType(); checkMethodInHierarchy(type, fGetterName, fieldType, new ITypeBinding[0], status, usingLocalGetter); checkMethodInHierarchy(type, fSetterName, fFieldDeclaration.getAST().resolveWellKnownType("void"), //$NON-NLS-1$ new ITypeBinding[] {fieldType}, status, usingLocalSetter); } } public static void checkMethodInHierarchy(ITypeBinding type, String methodName, ITypeBinding returnType, ITypeBinding[] parameters, RefactoringStatus result, boolean reUseMethod) { IFunctionBinding method= Bindings.findMethodInHierarchy(type, methodName, parameters); if (method != null) { boolean returnTypeClash= false; ITypeBinding methodReturnType= method.getReturnType(); if (returnType != null && methodReturnType != null) { String returnTypeKey= returnType.getKey(); String methodReturnTypeKey= methodReturnType.getKey(); if (returnTypeKey == null && methodReturnTypeKey == null) { returnTypeClash= returnType != methodReturnType; } else if (returnTypeKey != null && methodReturnTypeKey != null) { returnTypeClash= !returnTypeKey.equals(methodReturnTypeKey); } } ITypeBinding dc= method.getDeclaringClass(); if (returnTypeClash) { result.addError(Messages.format(RefactoringCoreMessages.Checks_methodName_returnTypeClash, new Object[] {methodName, dc.getName()}), JavaStatusContext.create(method)); } else { if (!reUseMethod) result.addError(Messages.format(RefactoringCoreMessages.Checks_methodName_overrides, new Object[] {methodName, dc.getName()}), JavaStatusContext.create(method)); } } else { if (reUseMethod){ result.addError(Messages.format(RefactoringCoreMessages.SelfEncapsulateFieldRefactoring_nosuchmethod_status_fatalError, new Object[] {methodName}), JavaStatusContext.create(method)); } } } private void computeUsedNames() { fUsedReadNames= new ArrayList(0); fUsedModifyNames= new ArrayList(0); IVariableBinding binding= fFieldDeclaration.resolveBinding(); ITypeBinding type= binding.getType(); IFunctionBinding[] methods= binding.getDeclaringClass().getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { IFunctionBinding method= methods[i]; ITypeBinding[] parameters= methods[i].getParameterTypes(); if (parameters == null || parameters.length == 0) { fUsedReadNames.add(method); } else if (parameters.length == 1 && parameters[0] == type) { fUsedModifyNames.add(method); } } } private List addGetterSetterChanges(JavaScriptUnit root, ASTRewrite rewriter, String lineDelimiter, boolean usingLocalSetter, boolean usingLocalGetter) throws CoreException { List result= new ArrayList(2); AST ast= root.getAST(); FieldDeclaration decl= (FieldDeclaration)ASTNodes.getParent(fFieldDeclaration, ASTNode.FIELD_DECLARATION); int position= 0; int numberOfMethods= 0; List members= ASTNodes.getBodyDeclarations(decl.getParent()); for (Iterator iter= members.iterator(); iter.hasNext();) { BodyDeclaration element= (BodyDeclaration)iter.next(); if (element.getNodeType() == ASTNode.FUNCTION_DECLARATION) { if (fInsertionIndex == -1) { break; } else if (fInsertionIndex == numberOfMethods) { position++; break; } numberOfMethods++; } position++; } TextEditGroup description; ListRewrite rewrite= fRewriter.getListRewrite(decl.getParent(), getBodyDeclarationsProperty(decl.getParent())); if (!JdtFlags.isFinal(fField) && !usingLocalSetter) { description= new TextEditGroup(RefactoringCoreMessages.SelfEncapsulateField_add_setter); result.add(description); rewrite.insertAt(createSetterMethod(ast, rewriter, lineDelimiter), position++, description); } if (!usingLocalGetter){ description= new TextEditGroup(RefactoringCoreMessages.SelfEncapsulateField_add_getter); result.add(description); rewrite.insertAt(createGetterMethod(ast, rewriter, lineDelimiter), position, description); } if (!JdtFlags.isPrivate(fField)) result.add(makeDeclarationPrivate(rewriter, decl)); return result; } private TextEditGroup makeDeclarationPrivate(ASTRewrite rewriter, FieldDeclaration decl) { AST ast= rewriter.getAST(); TextEditGroup description= new TextEditGroup(RefactoringCoreMessages.SelfEncapsulateField_change_visibility); if (decl.fragments().size() > 1) { //TODO: doesn't work for cases like this: int field1, field2= field1, field3= field2; // keeping refs to field rewriter.remove(fFieldDeclaration, description); ChildListPropertyDescriptor descriptor= getBodyDeclarationsProperty(decl.getParent()); VariableDeclarationFragment newField= (VariableDeclarationFragment) rewriter.createCopyTarget(fFieldDeclaration); FieldDeclaration fieldDecl= ast.newFieldDeclaration(newField); fieldDecl.setType((Type)rewriter.createCopyTarget(decl.getType())); fieldDecl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, Modifier.PRIVATE)); rewriter.getListRewrite(decl.getParent(), descriptor).insertAfter(fieldDecl, decl, description); } else { ModifierRewrite.create(rewriter, decl).setVisibility(Modifier.PRIVATE, description); } return description; } private ChildListPropertyDescriptor getBodyDeclarationsProperty(ASTNode declaration) { if (declaration instanceof AnonymousClassDeclaration) return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY; else if (declaration instanceof AbstractTypeDeclaration) return ((AbstractTypeDeclaration) declaration).getBodyDeclarationsProperty(); Assert.isTrue(false); return null; } private FunctionDeclaration createSetterMethod(AST ast, ASTRewrite rewriter, String lineDelimiter) throws CoreException { FieldDeclaration field= (FieldDeclaration)ASTNodes.getParent(fFieldDeclaration, FieldDeclaration.class); Type type= field.getType(); FunctionDeclaration result= ast.newFunctionDeclaration(); result.setName(ast.newSimpleName(fSetterName)); result.modifiers().addAll(ASTNodeFactory.newModifiers(ast, createModifiers())); if (fSetterMustReturnValue) { result.setReturnType2((Type)rewriter.createCopyTarget(type)); } SingleVariableDeclaration param= ast.newSingleVariableDeclaration(); result.parameters().add(param); param.setName(ast.newSimpleName(fArgName)); param.setType((Type)rewriter.createCopyTarget(type)); Block block= ast.newBlock(); result.setBody(block); Assignment ass= ast.newAssignment(); ass.setLeftHandSide(createFieldAccess(ast)); ass.setRightHandSide(ast.newSimpleName(fArgName)); if (fSetterMustReturnValue) { ReturnStatement rs= ast.newReturnStatement(); rs.setExpression(ass); block.statements().add(rs); } else { block.statements().add(ast.newExpressionStatement(ass)); } if (fGenerateJavadoc) { String string= CodeGeneration.getSetterComment( fField.getJavaScriptUnit() , getTypeName(field.getParent()), fSetterName, fField.getElementName(), ASTNodes.asString(type), fArgName, NamingConventions.removePrefixAndSuffixForFieldName(fField.getJavaScriptProject(), fField.getElementName(), fField.getFlags()), lineDelimiter); if (string != null) { JSdoc javadoc= (JSdoc)fRewriter.createStringPlaceholder(string, ASTNode.JSDOC); result.setJavadoc(javadoc); } } return result; } private FunctionDeclaration createGetterMethod(AST ast, ASTRewrite rewriter, String lineDelimiter) throws CoreException { FieldDeclaration field= (FieldDeclaration)ASTNodes.getParent(fFieldDeclaration, FieldDeclaration.class); Type type= field.getType(); FunctionDeclaration result= ast.newFunctionDeclaration(); result.setName(ast.newSimpleName(fGetterName)); result.modifiers().addAll(ASTNodeFactory.newModifiers(ast, createModifiers())); result.setReturnType2((Type)rewriter.createCopyTarget(type)); Block block= ast.newBlock(); result.setBody(block); ReturnStatement rs= ast.newReturnStatement(); rs.setExpression(ast.newSimpleName(fField.getElementName())); block.statements().add(rs); if (fGenerateJavadoc) { String string= CodeGeneration.getGetterComment( fField.getJavaScriptUnit() , getTypeName(field.getParent()), fGetterName, fField.getElementName(), ASTNodes.asString(type), NamingConventions.removePrefixAndSuffixForFieldName(fField.getJavaScriptProject(), fField.getElementName(), fField.getFlags()), lineDelimiter); if (string != null) { JSdoc javadoc= (JSdoc)fRewriter.createStringPlaceholder(string, ASTNode.JSDOC); result.setJavadoc(javadoc); } } return result; } private int createModifiers() throws JavaScriptModelException { int result= 0; if (Flags.isPublic(fVisibility)) result |= Modifier.PUBLIC; else if (Flags.isPrivate(fVisibility)) result |= Modifier.PRIVATE; if (JdtFlags.isStatic(fField)) result |= Modifier.STATIC; return result; } private Expression createFieldAccess(AST ast) throws JavaScriptModelException { String fieldName= fField.getElementName(); if (fArgName.equals(fieldName)) { if (JdtFlags.isStatic(fField)) { return ast.newQualifiedName( ast.newSimpleName(fField.getDeclaringType().getElementName()), ast.newSimpleName(fieldName)); } else { FieldAccess result= ast.newFieldAccess(); result.setExpression(ast.newThisExpression()); result.setName(ast.newSimpleName(fieldName)); return result; } } else { return ast.newSimpleName(fieldName); } } private void checkArgName() { String fieldName= fField.getElementName(); boolean isStatic= true; try { isStatic= JdtFlags.isStatic(fField); } catch(JavaScriptModelException e) { } IJavaScriptProject project= fField.getJavaScriptProject(); String sourceLevel= project.getOption(JavaScriptCore.COMPILER_SOURCE, true); String compliance= project.getOption(JavaScriptCore.COMPILER_COMPLIANCE, true); if ((isStatic && fArgName.equals(fieldName) && fieldName.equals(fField.getDeclaringType().getElementName())) || JavaScriptConventions.validateIdentifier(fArgName, sourceLevel, compliance).getSeverity() == IStatus.ERROR) fArgName= "_" + fArgName; //$NON-NLS-1$ } private String getTypeName(ASTNode type) { if (type instanceof AbstractTypeDeclaration) { return ((AbstractTypeDeclaration)type).getName().getIdentifier(); } else if (type instanceof AnonymousClassDeclaration) { ClassInstanceCreation node= (ClassInstanceCreation)ASTNodes.getParent(type, ClassInstanceCreation.class); return ASTNodes.asString(node.getType()); } Assert.isTrue(false, "Should not happen"); //$NON-NLS-1$ return null; } public RefactoringStatus initialize(RefactoringArguments arguments) { if (arguments instanceof JavaRefactoringArguments) { final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT); if (handle != null) { final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); if (element == null || !element.exists() || element.getElementType() != IJavaScriptElement.FIELD) return createInputFatalStatus(element, IJavaScriptRefactorings.ENCAPSULATE_FIELD); else { fField= (IField) element; try { initialize(fField); } catch (JavaScriptModelException exception) { return createInputFatalStatus(element, IJavaScriptRefactorings.ENCAPSULATE_FIELD); } } } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); String name= extended.getAttribute(ATTRIBUTE_GETTER); if (name != null && !"".equals(name)) //$NON-NLS-1$ fGetterName= name; else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_GETTER)); name= extended.getAttribute(ATTRIBUTE_SETTER); if (name != null && !"".equals(name)) //$NON-NLS-1$ fSetterName= name; else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_SETTER)); final String encapsulate= extended.getAttribute(ATTRIBUTE_DECLARING); if (encapsulate != null) { fEncapsulateDeclaringClass= Boolean.valueOf(encapsulate).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DECLARING)); final String matches= extended.getAttribute(ATTRIBUTE_COMMENTS); if (matches != null) { fGenerateJavadoc= Boolean.valueOf(matches).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_COMMENTS)); final String visibility= extended.getAttribute(ATTRIBUTE_VISIBILITY); if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$ int flag= 0; try { flag= Integer.parseInt(visibility); } catch (NumberFormatException exception) { return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY)); } fVisibility= flag; } final String insertion= extended.getAttribute(ATTRIBUTE_INSERTION); if (insertion != null && !"".equals(insertion)) {//$NON-NLS-1$ int index= 0; try { index= Integer.parseInt(insertion); } catch (NumberFormatException exception) { return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_INSERTION)); } fInsertionIndex= index; } } else return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); return new RefactoringStatus(); } public boolean isUsingLocalGetter() { IType declaringType= fField.getDeclaringType(); return checkName(fGetterName, fUsedReadNames, declaringType); } public boolean isUsingLocalSetter() { IType declaringType= fField.getDeclaringType(); return checkName(fSetterName, fUsedModifyNames, declaringType); } private static boolean checkName(String name, List usedNames, IType type) { for (Iterator iter= usedNames.iterator(); iter.hasNext(); ) { IFunctionBinding method= (IFunctionBinding)iter.next(); String selector= method.getName(); if (selector.equals(name)) { return true; } } return false; } private boolean isIgnorableProblem(IProblem problem) { if (problem.getID() == IProblem.NotVisibleField) return true; return false; } public boolean isConsiderVisibility() { return fConsiderVisibility; } public void setConsiderVisibility(boolean considerVisibility) { fConsiderVisibility= considerVisibility; } }