X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyDelegate.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyDelegate.java new file mode 100644 index 0000000..80677a8 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyDelegate.java @@ -0,0 +1,280 @@ +// Copyright (c) 2005 by Leif Frenzel. All rights reserved. +// See http://leiffrenzel.de +package net.sourceforge.phpdt.ltk.core; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil; +import net.sourceforge.phpeclipse.PHPeclipsePlugin; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +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.CheckConditionsContext; +import org.eclipse.ltk.core.refactoring.participants.IConditionChecker; +import org.eclipse.ltk.core.refactoring.participants.ValidateEditChecker; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; + +/** + *

+ * delegate object that contains the logic used by the processor. + *

+ * + * @author Leif Frenzel + */ +class RenamePropertyDelegate { + + // private static final String EXT_PROPERTIES = "properties"; //$NON-NLS-1$ + + private final RenamePropertyInfo info; + + // properties file with the key to rename -> offset of the key + private final Map phpFiles; + + RenamePropertyDelegate(final RenamePropertyInfo info) { + this.info = info; + phpFiles = new HashMap(); + } + + RefactoringStatus checkInitialConditions() { + RefactoringStatus result = new RefactoringStatus(); + IFile sourceFile = info.getSourceFile(); + if (sourceFile == null || !sourceFile.exists()) { + result.addFatalError(CoreTexts.renamePropertyDelegate_noSourceFile); + } else if (info.getSourceFile().isReadOnly()) { + result.addFatalError(CoreTexts.renamePropertyDelegate_roFile); + } else if (isEmpty(info.getOldName())) { + // || !isPropertyKey( info.getSourceFile(), info.getOldName() ) ) { + result.addFatalError(CoreTexts.renamePropertyDelegate_noPHPKey); + } + return result; + } + + RefactoringStatus checkFinalConditions(final IProgressMonitor pm, final CheckConditionsContext ctxt) { + RefactoringStatus result = new RefactoringStatus(); + pm.beginTask(CoreTexts.renamePropertyDelegate_checking, 100); + // do something long-running here: traverse the entire project (or even + // workspace) to look for all *.properties files with the same bundle + // base name + IContainer rootContainer; + if (info.isAllProjects()) { + rootContainer = ResourcesPlugin.getWorkspace().getRoot(); + } else { + rootContainer = info.getSourceFile().getProject(); + } + search(rootContainer, result); + pm.worked(50); + + if (ctxt != null) { + IFile[] files = new IFile[phpFiles.size()]; + phpFiles.keySet().toArray(files); + IConditionChecker checker = ctxt.getChecker(ValidateEditChecker.class); + ValidateEditChecker editChecker = (ValidateEditChecker) checker; + editChecker.addFiles(files); + } + pm.done(); + return result; + } + + void createChange(final IProgressMonitor pm, final CompositeChange rootChange) { + try { + pm.beginTask(CoreTexts.renamePropertyDelegate_collectingChanges, 100); + // the property which was directly selected by the user + rootChange.add(createRenameChange()); + pm.worked(10); + // all files in the same bundle + if (info.isUpdateBundle()) { + rootChange.addAll(createChangesForBundle()); + } + pm.worked(90); + } finally { + pm.done(); + } + } + + // helping methods + // //////////////// + + private Change createRenameChange() { + // create a change object for the file that contains the property the + // user has selected to rename + IFile file = info.getSourceFile(); + TextFileChange result = new TextFileChange(file.getName(), file); + // a file change contains a tree of edits, first add the root of them + MultiTextEdit fileChangeRootEdit = new MultiTextEdit(); + result.setEdit(fileChangeRootEdit); + + // edit object for the text replacement in the file, this is the only child + ReplaceEdit edit = new ReplaceEdit(info.getOffset(), info.getOldName().length(), info.getNewName()); + fileChangeRootEdit.addChild(edit); + return result; + } + + private Change[] createChangesForBundle() { + List result = new ArrayList(); + Iterator it = phpFiles.keySet().iterator(); + while (it.hasNext()) { + IFile file = (IFile) it.next(); + + TextFileChange tfc = new TextFileChange(file.getName(), file); + MultiTextEdit fileChangeRootEdit = new MultiTextEdit(); + tfc.setEdit(fileChangeRootEdit); + + // edit object for the text replacement in the file, this is the only + // child + ReplaceEdit edit = new ReplaceEdit(getKeyOffset(file), info.getOldName().length(), info.getNewName()); + fileChangeRootEdit.addChild(edit); + + result.add(tfc); + } + return (Change[]) result.toArray(new Change[result.size()]); + } + + private boolean isEmpty(final String candidate) { + return candidate == null || candidate.trim().length() == 0; + } + + // private boolean isPropertyKey( final IFile file, final String candidate ) { + // boolean result = false; + // try { + // Properties props = new Properties(); + // props.load( file.getContents() ); + // result = props.containsKey( candidate ); + // } catch( Exception ex ) { + // // ignore this, we just assume this is not a favourable situation + // ex.printStackTrace(); + // } + // return result; + // } + + // whether the file is a PHP file with the same base name as the + // one we refactor and contains the key that interests us + private boolean isToRefactor(final IFile file) { + return PHPFileUtil.isPHPFile(file); + // && !file.equals( info.getSourceFile() ) + // && isPropertyKey( file, info.getOldName() ); + } + + // private String getBundleBaseName() { + // String result = info.getSourceFile().getName(); + // int underscoreIndex = result.indexOf( '_' ); + // if( underscoreIndex != -1 ) { + // result = result.substring( 0, underscoreIndex ); + // } else { + // int index = result.indexOf( EXT_PROPERTIES ) - 1; + // result = result.substring( 0, index ); + // } + // return result; + // } + + private void search(final IContainer rootContainer, final RefactoringStatus status) { + try { + IResource[] members = rootContainer.members(); + for (int i = 0; i < members.length; i++) { + if (members[i] instanceof IContainer) { + search((IContainer) members[i], status); + } else { + IFile file = (IFile) members[i]; + handleFile(file, status); + } + } + } catch (final CoreException cex) { + status.addFatalError(cex.getMessage()); + } + } + + private void handleFile(final IFile file, final RefactoringStatus status) { + if (isToRefactor(file)) { + determineKeyOffsets(file, status); + // if (keyOffsets.size() > 0) { + // Integer offset = new Integer(keyOffsets); + // phpFiles.put(file, offset); + // } + } + } + + private int getKeyOffset(final IFile file) { + return ((Integer) phpFiles.get(file)).intValue(); + } + + // finds the offset of the property key to rename + // usually, this would be the job of a proper parser; + // using a primitive brute-force approach here + private void determineKeyOffsets(final IFile file, final RefactoringStatus status) { + String content = readFileContent(file, status); + int indx = 0; + while ((indx = content.indexOf(info.getOldName(), indx)) >= 0) { + phpFiles.put(file, Integer.valueOf(indx++)); + } + // int result = -1; + // int candidateIndex = content.indexOf(info.getOldName()); + // result = candidateIndex; + // while( result == -1 && candidateIndex != -1 ) { + // if( isKeyOccurrence( content, candidateIndex ) ) { + // result = candidateIndex; + // } + // } + // if( result == -1 ) { + // // still nothing found, we add a warning to the status + // // (we have checked the file contains the property, so that we can't + // // determine it's offset is probably because of the rough way employed + // // here to find it) + // String msg = CoreTexts.renamePropertyDelegate_propNotFound + // + file.getLocation().toOSString(); + // status.addWarning( msg ); + // } + // return result; + } + + private String readFileContent(final IFile file, final RefactoringStatus refStatus) { + String result = null; + try { + InputStream is = file.getContents(); + byte[] buf = new byte[1024]; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int len = is.read(buf); + while (len > 0) { + bos.write(buf, 0, len); + len = is.read(buf); + } + is.close(); + result = new String(bos.toByteArray()); + } catch (Exception ex) { + String msg = ex.toString(); + refStatus.addFatalError(msg); + String pluginId = PHPeclipsePlugin.getPluginId(); + IStatus status = new Status(IStatus.ERROR, pluginId, 0, msg, ex); + PHPeclipsePlugin.getDefault().getLog().log(status); + } + return result; + } + + // we check only that there is a separator before the next line break (this + // is not sufficient, the whole thing may be in a comment etc. ...) + // private boolean isKeyOccurrence( final String content, + // final int candidateIndex ) { + // int index = candidateIndex + info.getOldName().length(); + // // skip whitespace + // while( content.charAt( index ) == ' ' || content.charAt( index ) == '\t' ) + // { + // index++; + // } + // return content.charAt( index ) == '=' || content.charAt( index ) == ':'; + // } +}