/******************************************************************************* * 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.util.*; import java.util.Map.Entry; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.runtime.*; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.search.IDLTKSearchScope; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; import org.eclipse.ltk.core.refactoring.participants.RenameProcessor; import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; import org.eclipse.php.core.PHPToolkitUtil; import org.eclipse.php.core.PHPVersion; import org.eclipse.php.core.ast.nodes.ASTParser; import org.eclipse.php.core.ast.nodes.Program; import org.eclipse.php.core.project.ProjectOptions; import org.eclipse.php.internal.core.filenetwork.FileNetworkUtility; import org.eclipse.php.internal.core.filenetwork.ReferenceTree; import org.eclipse.php.internal.core.filenetwork.ReferenceTree.Node; import org.eclipse.php.refactoring.core.PhpRefactoringCoreMessages; import org.eclipse.php.refactoring.core.changes.ProgramFileChange; import org.eclipse.php.refactoring.core.utils.RefactoringUtility; import org.eclipse.text.edits.MultiTextEdit; /** * Description: Life cycle processor for PHP rename refactoring * * @author Roy, 2007 */ public abstract class AbstractRenameProcessor<R extends IResource> extends RenameProcessor implements INameUpdating { private static final String REFACTORING_ACTION_INTERNAL_ERROR = PhpRefactoringCoreMessages .getString("RenameProcessorBase.internalerror"); //$NON-NLS-1$ private static final String IS_NOT_A_VALID_PHP_IDENTIFIER = PhpRefactoringCoreMessages .getString("RenameProcessorBase.0"); //$NON-NLS-1$ private static final RefactoringParticipant[] EMPTY_REFACTORING_PARTICIPANTS = new RefactoringParticipant[0]; protected String fNewElementName; protected Map<IFile, Program> participantFiles; /** * map of sibling changes by file received from parent processor. */ // protected Map<IFile, TextFileChange> siblingChanges; protected final R resource; public AbstractRenameProcessor(R file) { // node can be null if the renamed element is a folder // assert head != null; assert file != null; this.resource = file; } /** * Finds existing or create a new change for the given file * * @param file * @param program * @return the change */ protected TextFileChange acquireChange(final IFile file, final Program program) { ProgramFileChange change = new ProgramFileChange(file.getName(), file, program); change.setEdit(new MultiTextEdit()); change.setTextType("php"); //$NON-NLS-1$ return change; } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { boolean hasExternalDependencies = false; try { participantFiles = new HashMap<IFile, Program>(); if (resource instanceof IFile && PHPToolkitUtil.isPHPFile((IFile) resource)) { IFile file = (IFile) resource; ISourceModule sourceModule = DLTKCore.createSourceModuleFrom(file); IProject project = file.getProject(); PHPVersion version = ProjectOptions.getPHPVersion(project); ASTParser newParser = ASTParser.newParser(version, sourceModule); Program program = newParser.createAST(null); participantFiles.put(file, program); collectReferences(program, pm); } // } } catch (Exception e) { final String exceptionMessage = e.getMessage(); final String formattedString = REFACTORING_ACTION_INTERNAL_ERROR .concat(exceptionMessage == null ? "" : exceptionMessage); //$NON-NLS-1$ return RefactoringStatus.createFatalErrorStatus(formattedString); } if (hasExternalDependencies) { final String message = PhpRefactoringCoreMessages.getString("AbstractRenameProcessor.1"); //$NON-NLS-1$ return RefactoringStatus.createWarningStatus(message); } return new RefactoringStatus(); } protected void collectReferences(Program program, IProgressMonitor pm) { Collection<Node> references = getReferencingFiles(program); if (references != null) { for (Iterator<Node> it = references.iterator(); it.hasNext();) { Node node = it.next(); IFile file = (IFile) node.getFile().getResource(); try { participantFiles.put(file, RefactoringUtility.getProgramForFile(file)); } catch (Exception e) { } } } references = getReferencedFiles(program); if (references != null) { for (Iterator<Node> it = references.iterator(); it.hasNext();) { Node node = it.next(); IFile file = (IFile) node.getFile().getResource(); try { participantFiles.put(file, RefactoringUtility.getProgramForFile(file)); } catch (Exception e) { } } } } protected static int getSearchFlags(boolean includeInterp) { int flags = IDLTKSearchScope.SOURCES | IDLTKSearchScope.APPLICATION_LIBRARIES; if (includeInterp) flags |= IDLTKSearchScope.SYSTEM_LIBRARIES; return flags; } protected Collection<Node> getReferencedFiles(Program program) { ISourceModule sourceModule = program.getSourceModule(); if (sourceModule != null) { ReferenceTree tree = FileNetworkUtility.buildReferencedFilesTree(sourceModule, null); if (tree != null && tree.getRoot() != null) { return tree.getRoot().getChildren(); } } return Collections.emptyList(); } protected Collection<Node> getReferencingFiles(Program program) { ISourceModule sourceModule = program.getSourceModule(); if (sourceModule != null) { ReferenceTree tree = FileNetworkUtility.buildReferencingFilesTree(sourceModule, null); if (tree != null && tree.getRoot() != null) { return tree.getRoot().getChildren(); } } return Collections.emptyList(); } /* * (non-Javadoc) * * @seeorg.eclipse.ltk.core.refactoring.participants.RefactoringProcessor# * checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor, * org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext) */ @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { RefactoringStatus result = RefactoringStatus.create(Status.OK_STATUS); final SubProgressMonitor subProgressMonitor = new SubProgressMonitor(pm, 100); subProgressMonitor.beginTask(PhpRefactoringCoreMessages.getString("RenameProcessorBase.1"), //$NON-NLS-1$ participantFiles.size()); for (Entry<IFile, Program> entry : participantFiles.entrySet()) { final IFile key = entry.getKey(); final Program program = entry.getValue(); RefactoringStatus status = getRefactoringStatus(key, program); result.merge(status); subProgressMonitor.worked(1); } subProgressMonitor.done(); return result; } /** * Should be overridden by clients - checks for conflicts * * @param key * @param program * @return a factoring status given an AST representation */ public abstract RefactoringStatus getRefactoringStatus(IFile key, Program program); /* * (non-Javadoc) * * @see org.eclipse.php.refactoring.core.rename.INameUpdating# * getCurrentElementName () */ public abstract String getCurrentElementName(); /* * (non-Javadoc) * * @see * org.eclipse.php.refactoring.core.rename.INameUpdating#getNewElement() */ public abstract Object getNewElement() throws CoreException; public void setNewElementName(String newName) { Assert.isNotNull(newName); fNewElementName = newName; } public String getNewElementName() { return fNewElementName; } @Override public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { return AbstractRenameProcessor.EMPTY_REFACTORING_PARTICIPANTS; } /* * (non-Javadoc) * * @see * org.eclipse.php.refactoring.core.rename.INameUpdating#checkNewElementName * (java.lang.String) */ public RefactoringStatus checkNewElementName(String newName) throws CoreException { if (!isValidIdentifier(newName)) { return getFatalError(newName); } return new RefactoringStatus(); } /** * @param newName * @return true of the given string is valid PHP identifier */ public static final boolean isValidIdentifier(String newName) { if (newName == null || newName.length() == 0 || !Character.isLetter(newName.charAt(0)) && newName.charAt(0) != '_') { return false; } final int length = newName.length(); for (int i = 1; i < length; i++) { if (!Character.isJavaIdentifierPart(newName.charAt(i))) { return false; } if (newName.charAt(i) == '$') { // Seva: in addition to java rules, PHP doesn't allow dollar // signs inside element names return false; } } return true; } /** * @param newName * @return */ protected static final RefactoringStatus getFatalError(String newName) { return RefactoringStatus .createFatalErrorStatus(newName + AbstractRenameProcessor.IS_NOT_A_VALID_PHP_IDENTIFIER); } /** * @param file * @return the program node for a given file */ public Program getProgram(IFile file) { return participantFiles.get(file); } /** * Check the validity of the input source. */ @Override public boolean isApplicable() throws CoreException { if (resource == null) return false; if (!resource.isAccessible()) return false; ResourceAttributes attributes = resource.getResourceAttributes(); if (attributes == null) return false; return !resource.getResourceAttributes().isReadOnly(); } }