/******************************************************************************* * Copyright (c) 2006, 2015 Zend Technologies 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: * Zend Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.php.refactoring.core.rename; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; 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.dltk.core.IModelElement; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.core.search.*; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.ltk.core.refactoring.participants.*; import org.eclipse.ltk.internal.core.refactoring.resource.ResourceProcessors; import org.eclipse.php.core.ast.nodes.*; import org.eclipse.php.internal.core.PHPLanguageToolkit; import org.eclipse.php.internal.core.ast.locator.PHPElementConciliator; import org.eclipse.php.internal.core.typeinference.TraitUtils.ITraitMember; import org.eclipse.php.refactoring.core.PhpRefactoringCoreMessages; import org.eclipse.php.refactoring.core.RefactoringPlugin; import org.eclipse.php.refactoring.core.rename.logic.RenameClassMember; import org.eclipse.php.refactoring.core.utils.RefactoringUtility; /** * Class member rename processor * * @author Roy, 2007 */ @SuppressWarnings("restriction") public class RenameClassMemberProcessor extends AbstractRenameProcessor<IFile> implements ITextUpdating { private static final String RENAME_IS_PROCESSING = PhpRefactoringCoreMessages .getString("RenameClassPropertyProcessor.2"); //$NON-NLS-1$ private static final String CREATING_MODIFICATIONS_LABEL = PhpRefactoringCoreMessages .getString("RenameClassPropertyProcessor.3"); //$NON-NLS-1$ private static final String ID_RENAME_CLASS_MEMBER = "php.refactoring.ui.rename.classProperty"; //$NON-NLS-1$ protected static final String ATTRIBUTE_TEXTUAL_MATCHES = "textual"; //$NON-NLS-1$ public static final String RENAME_CLASS_MEMBER_PROCESSOR_NAME = PhpRefactoringCoreMessages .getString("RenameClassPropertyProcessor.5"); //$NON-NLS-1$ /** * The original identifier node we want to rename */ private ASTNode identifier = null; /** * holds whether or not we want to change also the in-lined text */ private boolean isUpdateTextualMatches; /** * True if need to show the override method question */ public boolean showShouldOverrideMessage = false; private ITypeBinding typeBinding; /** * Constructor for direct request * * @param operatedFile * @param locateNode */ public RenameClassMemberProcessor(IFile operatedFile, ASTNode locateNode) { super(operatedFile); this.identifier = locateNode; showShouldOverrideMessage = true; typeBinding = getCurrentType(); } /** * Derive the change */ public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { CompositeChange rootChange = new CompositeChange( PhpRefactoringCoreMessages .getString("RenameClassPropertyProcessor.7")); //$NON-NLS-1$ rootChange.markAsSynthetic(); try { pm.beginTask(RenameClassMemberProcessor.RENAME_IS_PROCESSING, participantFiles.size()); pm.setTaskName(RenameClassMemberProcessor.CREATING_MODIFICATIONS_LABEL); if (pm.isCanceled()) throw new OperationCanceledException(); // get target parameters final String newElementName = getNewElementName(); // go over the files and check for global variable usage for (Entry<IFile, Program> entry : participantFiles.entrySet()) { final IFile file = entry.getKey(); final Program program = entry.getValue(); final RenameClassMember rename = new RenameClassMember(file, getCurrentElementName(), newElementName, getUpdateTextualMatches(), typeBinding, getParent( identifier).getType(), identifier); // aggregate the changes identifiers try { program.accept(rename); } catch (Exception e) { RefactoringPlugin.logException(e); } if (pm.isCanceled()) throw new OperationCanceledException(); pm.worked(1); if (rename.hasChanges()) { TextFileChange change = acquireChange(file, program); rename.updateChange(change); rootChange.add(change); } } return rootChange; } catch (Exception e) { RefactoringPlugin.logException(e); return rootChange; } finally { pm.done(); } } public Object[] getElements() { return new Object[] { identifier }; } public String getIdentifier() { return RenameClassMemberProcessor.ID_RENAME_CLASS_MEMBER; } public String getProcessorName() { return RenameClassMemberProcessor.RENAME_CLASS_MEMBER_PROCESSOR_NAME; } public Object getNewElement() { return getNewElementName(); } public String getCurrentElementName() { if (identifier instanceof Variable) { Identifier id = (Identifier) ((Variable) identifier).getName(); return id.getName(); } if (identifier instanceof Identifier) { return ((Identifier) identifier).getName(); } if (identifier instanceof FunctionDeclaration) { Expression name = ((FunctionDeclaration) identifier) .getFunctionName(); return ((Identifier) name).getName(); } if (identifier instanceof MethodDeclaration) { Expression name = ((MethodDeclaration) identifier).getFunction() .getFunctionName(); return ((Identifier) name).getName(); } return identifier.toString(); } public boolean canEnableTextUpdating() { return true; } public String getCurrentElementQualifier() { return identifier.toString(); } public boolean getUpdateTextualMatches() { return isUpdateTextualMatches; } public void setUpdateTextualMatches(boolean update) { isUpdateTextualMatches = update; } public ITypeBinding getCurrentType() { if (identifier instanceof Identifier) { if (identifier.getParent().getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { FullyQualifiedTraitMethodReference reference = (FullyQualifiedTraitMethodReference) identifier .getParent(); return reference.getClassName().resolveTypeBinding(); } else if (identifier.getParent().getType() == ASTNode.TRAIT_ALIAS) { TraitAlias traitAlias = (TraitAlias) identifier.getParent(); List<NamespaceName> nameList = ((TraitUseStatement) traitAlias .getParent().getParent()).getTraitList(); String memberName = null; if (identifier == traitAlias.getFunctionName()) { Expression expression = traitAlias.getTraitMethod(); if (expression.getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { FullyQualifiedTraitMethodReference fqtm = (FullyQualifiedTraitMethodReference) expression; memberName = fqtm.getFunctionName().getName(); } else { memberName = ((Identifier) expression).getName(); } } else { memberName = ((Identifier) identifier).getName(); } for (NamespaceName namespaceName : nameList) { ITypeBinding typeBinding = namespaceName .resolveTypeBinding(); if (typeBinding != null && typeBinding.isTrait() && typeBinding.getPHPElement() != null) { try { IModelElement[] members = ((IType) typeBinding .getPHPElement()).getChildren(); for (IModelElement modelElement : members) { if (modelElement.getElementName().equals( memberName) || modelElement.getElementName() .equals("$" + memberName)) { //$NON-NLS-1$ return typeBinding; } } } catch (ModelException e) { } } } } } if (identifier instanceof Expression) { ASTNode parent = identifier.getParent(); Dispatch dispatch = getDispatch(parent); if (dispatch != null) { return dispatch.getDispatcher().resolveTypeBinding(); } TypeDeclaration type = RefactoringUtility.getType(identifier); if (type != null) { return type.resolveTypeBinding(); } StaticDispatch staticDispatch = getStaticDispatch(parent); if (staticDispatch != null) { return staticDispatch.getClassName().resolveTypeBinding(); } } if (identifier instanceof FieldsDeclaration) { TypeDeclaration type = RefactoringUtility.getType(identifier); if (type != null) { return type.resolveTypeBinding(); } } if (identifier instanceof FunctionDeclaration) { TypeDeclaration type = RefactoringUtility.getType(identifier); if (type != null) { return type.resolveTypeBinding(); } } if (identifier instanceof MethodDeclaration) { TypeDeclaration type = RefactoringUtility.getType(identifier); if (type != null) { return type.resolveTypeBinding(); } } if (identifier instanceof ConstantDeclaration) { TypeDeclaration type = RefactoringUtility.getType(identifier); if (type != null) { return type.resolveTypeBinding(); } } return null; } private Dispatch getDispatch(ASTNode node) { if (node == null) { return null; } ASTNode model = node; while (!(model instanceof Dispatch)) { if (node == null) { return null; } ASTNode parent = model.getParent(); if (parent == model) { return null; } model = parent; if (model instanceof Program || model == null) { return null; } } return (Dispatch) model; } private StaticDispatch getStaticDispatch(ASTNode node) { if (node == null) { return null; } ASTNode model = node; while (!(model instanceof StaticDispatch)) { if (node == null) { return null; } ASTNode parent = model.getParent(); if (parent == model) { return null; } model = parent; if (model instanceof Program || model == null) { return null; } } return (StaticDispatch) model; } @Override protected void collectReferences(Program program, IProgressMonitor pm) { final ArrayList<IResource> list = new ArrayList<IResource>(); if (identifier instanceof Identifier) { if (identifier.getParent().getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { list.add(this.identifier.getProgramRoot().getSourceModule() .getResource()); } else if (identifier.getParent().getType() == ASTNode.TRAIT_ALIAS) { list.add(this.identifier.getProgramRoot().getSourceModule() .getResource()); } } try { IModelElement[] elements = this.identifier.getProgramRoot() .getSourceModule().codeSelect(identifier.getStart(), 0); for (IModelElement modelElement : elements) { if (modelElement instanceof ITraitMember) { ITraitMember tm = (ITraitMember) modelElement; list.add(tm.getHostType().getResource()); } } } catch (ModelException e1) { } IScriptProject project = this.identifier.getProgramRoot() .getSourceModule().getScriptProject(); IDLTKSearchScope scope = SearchEngine.createSearchScope(project, getSearchFlags(false)); ASTNode node = getParent(identifier); SearchPattern pattern = null; if (node instanceof Variable || node instanceof FieldsDeclaration) { pattern = SearchPattern.createPattern( "$" + getCurrentElementName(), IDLTKSearchConstants.FIELD, //$NON-NLS-1$ IDLTKSearchConstants.ALL_OCCURRENCES, SearchPattern.R_ERASURE_MATCH, PHPLanguageToolkit.getDefault()); SearchEngine engine = new SearchEngine(); try { engine.search(pattern, new SearchParticipant[] { SearchEngine .getDefaultSearchParticipant() }, scope, new SearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { IModelElement element = (IModelElement) match .getElement(); list.add(element.getResource()); } }, new NullProgressMonitor()); } catch (CoreException e) { } } if (node instanceof ConstantDeclaration || node instanceof StaticConstantAccess) { pattern = SearchPattern.createPattern(getCurrentElementName(), IDLTKSearchConstants.FIELD, IDLTKSearchConstants.ALL_OCCURRENCES, SearchPattern.R_ERASURE_MATCH, PHPLanguageToolkit.getDefault()); SearchEngine engine = new SearchEngine(); try { engine.search(pattern, new SearchParticipant[] { SearchEngine .getDefaultSearchParticipant() }, scope, new SearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { IModelElement element = (IModelElement) match .getElement(); list.add(element.getResource()); } }, new NullProgressMonitor()); } catch (CoreException e) { } } int matchMode = SearchPattern.R_EXACT_MATCH | SearchPattern.R_ERASURE_MATCH; if (node instanceof FunctionDeclaration || node instanceof MethodDeclaration || node instanceof FunctionName) { pattern = SearchPattern.createPattern(getCurrentElementName(), IDLTKSearchConstants.METHOD, IDLTKSearchConstants.ALL_OCCURRENCES, matchMode, PHPLanguageToolkit.getDefault()); SearchEngine engine = new SearchEngine(); try { engine.search(pattern, new SearchParticipant[] { SearchEngine .getDefaultSearchParticipant() }, scope, new SearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { IModelElement element = (IModelElement) match .getElement(); list.add(element.getResource()); } }, new NullProgressMonitor()); } catch (CoreException e) { } } for (Iterator<IResource> it = list.iterator(); it.hasNext();) { IResource file = it.next(); if (file instanceof IFile) { try { participantFiles.put((IFile) file, RefactoringUtility.getProgramForFile((IFile) file)); } catch (Exception e) { } } } } private ASTNode getParent(ASTNode identifier) { ASTNode node; if (identifier instanceof Identifier) { node = identifier.getParent(); } else { node = identifier; } if (node instanceof Variable && node.getParent() instanceof FunctionName) { return node.getParent(); } return node; } public RefactoringStatus getRefactoringStatus(IFile key, Program program) { int type = PHPElementConciliator.concile(identifier); identifier.getParent(); if (type == PHPElementConciliator.CONCILIATOR_CLASS_MEMBER) { final TypeDeclaration host = RefactoringUtility.getType(identifier); if (host != null && PHPElementConciliator.classMemeberAlreadyExists(host, getNewElementName(), identifier.getParent() .getType())) { final String message = MessageFormat .format("A same class member with name {0} already exist in the same class scope", //$NON-NLS-1$ new Object[] { getNewElementName() }); return RefactoringStatus.createWarningStatus(message); } } return new RefactoringStatus(); } @Override public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { String[] affectedNatures = ResourceProcessors .computeAffectedNatures(resource); RenameArguments fRenameArguments = new RenameArguments( getNewElementName(), false); return ParticipantManager.loadRenameParticipants(status, this, identifier, fRenameArguments, null, affectedNatures, sharedParticipants); } class RenameClassMemberParticipant extends RenameParticipant { public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) throws OperationCanceledException { try { return getProcessor().checkFinalConditions(pm, context); } catch (CoreException e) { return new RefactoringStatus(); } } public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { return getProcessor().createChange(pm); } public String getName() { return getProcessorName(); } @SuppressWarnings("unchecked") protected boolean initialize(Object element) { try { final RenameClassMemberArguments arguments = (RenameClassMemberArguments) getArguments(); ((AbstractRenameProcessor<IFile>) getProcessor()) .setNewElementName(arguments.getNewName()); getProcessor() .checkInitialConditions(new NullProgressMonitor()); } catch (Exception e) { return false; } return true; } } private class RenameClassMemberArguments extends org.eclipse.ltk.core.refactoring.participants.RenameArguments { public RenameClassMemberArguments(String newName, boolean updateReferences) { super(newName, updateReferences); } } }