Basic Reafctoring functionality adapted from Leif Frenzels sources in eclipse-magazin...
authoraxelcl <axelcl>
Tue, 3 Jan 2006 23:07:05 +0000 (23:07 +0000)
committeraxelcl <axelcl>
Tue, 3 Jan 2006 23:07:05 +0000 (23:07 +0000)
See sources at:
http://www.eclipse-magazin.de/itr/ausgaben/psfile/datei/61/frenzel_lt437d8f486fb21.zip
Feature #1358828

net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/CoreTexts.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyDelegate.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyInfo.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyProcessor.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyRefactoring.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/coretexts.properties [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/UITexts.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/actions/RenamePHPIdentifier.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/uitexts.properties [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/wizards/RenamePropertyInputPage.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/wizards/RenamePropertyWizard.java [new file with mode: 0644]

diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/CoreTexts.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/CoreTexts.java
new file mode 100644 (file)
index 0000000..3c8c23f
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.core;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * <p>
+ * provides internationalized messages Strings from the coretexts resource
+ * bundle.
+ * </p>
+ *
+ * @author Leif Frenzel
+ */
+public class CoreTexts extends NLS {
+
+       private static final String BUNDLE_NAME = "net.sourceforge.phpdt.ltk.core.coretexts"; //$NON-NLS-1$
+
+       static {
+               NLS.initializeMessages(BUNDLE_NAME, CoreTexts.class);
+       }
+
+       // message fields
+       public static String renamePropertyProcessor_name;
+
+       public static String renamePropertyDelegate_noSourceFile;
+
+       public static String renamePropertyDelegate_roFile;
+
+       public static String renamePropertyDelegate_noPHPKey;
+
+       public static String renamePropertyDelegate_collectingChanges;
+
+       public static String renamePropertyDelegate_checking;
+       // public static String renamePropertyDelegate_propNotFound;
+}
\ No newline at end of file
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 (file)
index 0000000..80677a8
--- /dev/null
@@ -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;
+
+/**
+ * <p>
+ * delegate object that contains the logic used by the processor.
+ * </p>
+ *
+ * @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 ) == ':';
+       // }
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyInfo.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyInfo.java
new file mode 100644 (file)
index 0000000..b1c95a7
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.core;
+
+import org.eclipse.core.resources.IFile;
+
+
+/** <p>an info object that holds the information that is passed from
+  * the user to the refactoring.</p>
+  *
+  * @author Leif Frenzel
+  */
+public class RenamePropertyInfo {
+
+  // the offset of the property to be renamed in the file
+  private int offset;
+  // the new name for the property
+  private String newName;
+  // the old name of the property (as selected by the user)
+  private String oldName;
+  // the file that contains the property to be renamed
+  private IFile sourceFile;
+  // whether the refactoring should also change the name of the property
+  // in corresponding properties files in the same bundle (i.e. which start
+  // with the same name)
+  private boolean updateBundle;
+  // whether the refactoring should also update properties files in other
+  // projects than the current one
+  private boolean allProjects;
+  
+  
+  // interface methods of IRenamePropertyInfo
+  ///////////////////////////////////////////
+  
+  public int getOffset() {
+    return offset;
+  }
+  
+  public void setOffset( final int offset ) {
+    this.offset = offset;
+  }
+
+  public String getNewName() {
+    return newName;
+  }
+
+  public void setNewName( final String newName ) {
+    this.newName = newName;
+  }
+
+  public String getOldName() {
+    return oldName;
+  }
+
+  public void setOldName( final String oldName ) {
+    this.oldName = oldName;
+  }
+
+  public IFile getSourceFile() {
+    return sourceFile;
+  }
+
+  public void setSourceFile( final IFile sourceFile ) {
+    this.sourceFile = sourceFile;
+  }
+
+  public boolean isAllProjects() {
+    return allProjects;
+  }
+
+  public void setAllProjects( final boolean allProjects ) {
+    this.allProjects = allProjects;
+  }
+
+  public boolean isUpdateBundle() {
+    return updateBundle;
+  }
+
+  public void setUpdateBundle( final boolean updateBundle ) {
+    this.updateBundle = updateBundle;
+  }
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyProcessor.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyProcessor.java
new file mode 100644 (file)
index 0000000..46cde24
--- /dev/null
@@ -0,0 +1,71 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.core;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.ltk.core.refactoring.*;
+import org.eclipse.ltk.core.refactoring.participants.*;
+
+/** <p>The processor is where the work is delegated to if participants are 
+  * involved. The processor loads the participants and manages the lifecycle
+  * of the refactoring. In order to do that, the refactoring entry point 
+  * methods must be implemented.</p>
+  *
+  * @author Leif Frenzel
+  */
+public class RenamePropertyProcessor extends RefactoringProcessor {
+
+  private final RenamePropertyInfo info;
+  private final RenamePropertyDelegate delegate;
+
+  public RenamePropertyProcessor( final RenamePropertyInfo info ) {
+    this.info = info;
+    delegate = new RenamePropertyDelegate( info );
+  }
+
+  
+  // interface methods of RefactoringProcessor
+  ////////////////////////////////////////////
+
+  public Object[] getElements() {
+    // usually, this would be some element object in the object model on which
+    // we work (e.g. a Java element if we were in the Java Model); in this case 
+    // we have only the property name
+    return new Object[] { info.getOldName() };
+  }
+
+  public String getIdentifier() {
+    return getClass().getName();
+  }
+
+  public String getProcessorName() {
+    return CoreTexts.renamePropertyProcessor_name;
+  }
+
+  public boolean isApplicable() throws CoreException {
+    return true;
+  }
+
+  public RefactoringStatus checkInitialConditions( final IProgressMonitor pm ) {
+    return delegate.checkInitialConditions();
+  }
+
+  public RefactoringStatus checkFinalConditions( 
+            final IProgressMonitor pm,  final CheckConditionsContext context ) {
+    return delegate.checkFinalConditions( pm, context );
+  }
+
+  public Change createChange( final IProgressMonitor pm ) {
+    CompositeChange result = new CompositeChange( getProcessorName() ); 
+    delegate.createChange( pm, result );
+    return result;
+  }
+
+  public RefactoringParticipant[] loadParticipants( 
+                               final RefactoringStatus status, 
+                               final SharableParticipants sharedParticipants ) {
+    // This would be the place to load the participants via the 
+    // ParticipantManager and decide which of them are allowed to participate.
+    return new RefactoringParticipant[ 0 ];
+  }
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyRefactoring.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/RenamePropertyRefactoring.java
new file mode 100644 (file)
index 0000000..2cff300
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.core;
+
+import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring;
+import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor;
+
+/** <p>Refactoring for renaming properties in Java Properties files.</p>
+  *
+  * <p>All the actual work is done in the processor, so we just have to 
+  * keep a reference to one here.<p>
+  *
+  * @author Leif Frenzel
+  */
+public class RenamePropertyRefactoring extends ProcessorBasedRefactoring {
+
+  private final RefactoringProcessor processor;
+
+  public RenamePropertyRefactoring( final RefactoringProcessor processor ) {
+    super( processor );
+    this.processor = processor;
+  }
+
+  
+  // interface methods of ProcessorBasedRefactoring
+  /////////////////////////////////////////////////
+  
+  public RefactoringProcessor getProcessor() {
+    return processor;
+  }
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/coretexts.properties b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/core/coretexts.properties
new file mode 100644 (file)
index 0000000..d9494a8
--- /dev/null
@@ -0,0 +1,7 @@
+renamePropertyProcessor_name=Rename PHP Identifier
+renamePropertyDelegate_noSourceFile=No source file could be determined.
+renamePropertyDelegate_roFile=File is read-only.
+renamePropertyDelegate_noPHPKey=The selected text does not appear to be a valid PHP identifier.
+renamePropertyDelegate_collectingChanges=Collecting changes
+renamePropertyDelegate_checking=Checking
+renamePropertyDelegate_propNotFound=Could not find the property in file\:
\ No newline at end of file
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/UITexts.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/UITexts.java
new file mode 100644 (file)
index 0000000..949c04b
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * <p>
+ * provides internationalized String messages for the UI.
+ * </p>
+ *
+ * @author Leif Frenzel
+ */
+public class UITexts {
+
+       private static final String BUNDLE_NAME = "net.sourceforge.phpdt.ltk.ui.uitexts"; //$NON-NLS-1$
+
+       static {
+               NLS.initializeMessages(BUNDLE_NAME, UITexts.class);
+       }
+
+       // message fields
+       public static String renameProperty_refuseDlg_title;
+
+       public static String renameProperty_refuseDlg_message;
+
+       public static String renamePropertyInputPage_lblNewName;
+
+       public static String renamePropertyInputPage_cbUpdateBundle;
+
+       public static String renamePropertyInputPage_cbAllProjects;
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/actions/RenamePHPIdentifier.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/actions/RenamePHPIdentifier.java
new file mode 100644 (file)
index 0000000..9e38cd5
--- /dev/null
@@ -0,0 +1,167 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.ui.actions;
+
+import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
+import net.sourceforge.phpdt.ltk.core.RenamePropertyInfo;
+import net.sourceforge.phpdt.ltk.core.RenamePropertyProcessor;
+import net.sourceforge.phpdt.ltk.core.RenamePropertyRefactoring;
+import net.sourceforge.phpdt.ltk.ui.UITexts;
+import net.sourceforge.phpdt.ltk.ui.wizards.RenamePropertyWizard;
+import net.sourceforge.phpeclipse.phpeditor.PHPEditor;
+import net.sourceforge.phpeclipse.phpeditor.php.PHPWordExtractor;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorActionDelegate;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * <p>
+ * action that is triggered from the editor context menu.
+ * </p>
+ *
+ * <p>
+ * This action is declared in the <code>plugin.xml</code>.
+ * </p>
+ *
+ * @author Leif Frenzel
+ */
+public class RenamePHPIdentifier implements IEditorActionDelegate {
+
+       private static final String EXT_PROPERTIES = "properties"; //$NON-NLS-1$
+
+       private ISelection selection;
+
+       private IEditorPart targetEditor;
+
+       private boolean onPropertiesFile;
+
+       private RenamePropertyInfo info = new RenamePropertyInfo();
+
+       // interface methods of IEditorActionDelegate
+       // ///////////////////////////////////////////
+
+       public void setActiveEditor(final IAction action, final IEditorPart targetEditor) {
+               this.targetEditor = targetEditor;
+               onPropertiesFile = false;
+               IFile file = getFile();
+
+               if (file != null && PHPFileUtil.isPHPFile(file)) {
+                       onPropertiesFile = true;
+               }
+       }
+
+       public void run(final IAction action) {
+               if (!onPropertiesFile) {
+                       refuse();
+               } else {
+                       if (selection != null && selection instanceof ITextSelection) {
+                               String word = null;
+                               Point point = null;
+                               if (targetEditor != null && (targetEditor instanceof PHPEditor)) {
+                                       PHPEditor editor = (PHPEditor) targetEditor;
+                                       if (editor != null) {
+                                               ITextSelection textSelection = (ITextSelection) editor.getSelectionProvider().getSelection();
+                                               IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput());
+                                               int pos = textSelection.getOffset();
+                                               point = PHPWordExtractor.findWord(doc, pos);
+                                               if (point != null) {
+                                                       try {
+                                                               word = doc.get(point.x, point.y);
+                                                       } catch (BadLocationException e) {
+                                                       }
+                                               }
+                                       }
+                               }
+                               applySelection((ITextSelection) selection, word, point);
+                               if (saveAll()) {
+                                       openWizard();
+                               }
+                       }
+               }
+       }
+
+       public void selectionChanged(final IAction action, final ISelection selection) {
+               this.selection = selection;
+       }
+
+       // helping methods
+       // ////////////////
+
+       private void applySelection(final ITextSelection textSelection, String word, Point point) {
+               if (word != null) {
+                       info.setOldName(word);
+                       info.setNewName(word);
+                       info.setOffset(point.x);
+               } else {
+                       info.setOldName(textSelection.getText());
+                       info.setNewName(textSelection.getText());
+                       info.setOffset(textSelection.getOffset());
+               }
+               info.setSourceFile(getFile());
+       }
+
+       private void refuse() {
+               String title = UITexts.renameProperty_refuseDlg_title;
+               String message = UITexts.renameProperty_refuseDlg_message;
+               MessageDialog.openInformation(getShell(), title, message);
+       }
+
+       private static boolean saveAll() {
+               IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+               return IDE.saveAllEditors(new IResource[] { workspaceRoot }, false);
+       }
+
+       private void openWizard() {
+               RefactoringProcessor processor = new RenamePropertyProcessor(info);
+               RenamePropertyRefactoring ref = new RenamePropertyRefactoring(processor);
+               RenamePropertyWizard wizard = new RenamePropertyWizard(ref, info);
+               RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+               try {
+                       String titleForFailedChecks = ""; //$NON-NLS-1$
+                       op.run(getShell(), titleForFailedChecks);
+               } catch (final InterruptedException irex) {
+                       // operation was cancelled
+               }
+       }
+
+       private Shell getShell() {
+               Shell result = null;
+               if (targetEditor != null) {
+                       result = targetEditor.getSite().getShell();
+               } else {
+                       result = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+               }
+               return result;
+       }
+
+       private final IFile getFile() {
+               IFile result = null;
+               if (targetEditor instanceof ITextEditor) {
+                       ITextEditor editor = (ITextEditor) targetEditor;
+                       IEditorInput input = editor.getEditorInput();
+                       if (input instanceof IFileEditorInput) {
+                               result = ((IFileEditorInput) input).getFile();
+                       }
+               }
+               return result;
+       }
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/uitexts.properties b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/uitexts.properties
new file mode 100644 (file)
index 0000000..f3ad862
--- /dev/null
@@ -0,0 +1,5 @@
+renameProperty_refuseDlg_title=Rename PHP identifier
+renameProperty_refuseDlg_message=This refactoring is only available on PHP files.
+renamePropertyInputPage_lblNewName=New name\:
+renamePropertyInputPage_cbUpdateBundle=Update all properties files in the same bundle
+renamePropertyInputPage_cbAllProjects=Update all projects in the workspace (forces preview)
\ No newline at end of file
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/wizards/RenamePropertyInputPage.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/wizards/RenamePropertyInputPage.java
new file mode 100644 (file)
index 0000000..2f7eb74
--- /dev/null
@@ -0,0 +1,176 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.ui.wizards;
+
+import net.sourceforge.phpdt.ltk.core.RenamePropertyInfo;
+import net.sourceforge.phpdt.ltk.ui.UITexts;
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+
+/** <p>the input page for the Rename Property refactoring, where users can
+  * control the effects of the refactoring; to be shown in the wizard.</p>
+  *
+  * <p>We let the user enter the new name for the property, and we let her
+  * decide whether other property files in the bundle should be affected, and
+  * whether the operation is supposed to span the entire workspace or only
+  * the current project.</p>
+  *
+  * @author Leif Frenzel
+  */
+public class RenamePropertyInputPage extends UserInputWizardPage {
+
+  private static final String DS_KEY = RenamePropertyInputPage.class.getName();
+  private static final String DS_UPDATE_BUNDLE = "UPDATE_BUNDLE"; //$NON-NLS-1$
+  private static final String DS_ALL_PROJECTS  = "ALL_PROJECTS"; //$NON-NLS-1$
+
+  private final RenamePropertyInfo info;
+
+  private IDialogSettings dialogSettings;
+  private Text txtNewName;
+  private Button cbUpdateBundle;
+  private Button cbAllProjects;
+
+
+  public RenamePropertyInputPage( final RenamePropertyInfo info ) {
+    super( RenamePropertyInputPage.class.getName() );
+    this.info = info;
+    initDialogSettings();
+  }
+
+
+  // interface methods of UserInputWizardPage
+  ///////////////////////////////////////////
+
+  public void createControl( final Composite parent ) {
+    Composite composite = createRootComposite( parent );
+    setControl( composite );
+
+    createLblNewName( composite );
+    createTxtNewName( composite );
+    createCbUpdateBundle( composite );
+    createCbAllProjects( composite );
+
+    validate();
+  }
+
+
+  // UI creation methods
+  //////////////////////
+
+  private Composite createRootComposite( final Composite parent ) {
+    Composite result = new Composite( parent, SWT.NONE );
+    GridLayout gridLayout = new GridLayout( 2, false );
+    gridLayout.marginWidth = 10;
+    gridLayout.marginHeight = 10;
+    result.setLayout( gridLayout );
+    initializeDialogUnits( result );
+    Dialog.applyDialogFont( result );
+    return result;
+  }
+
+  private void createLblNewName( final Composite composite ) {
+    Label lblNewName = new Label( composite, SWT.NONE );
+    lblNewName.setText( UITexts.renamePropertyInputPage_lblNewName );
+  }
+
+  private void createTxtNewName(Composite composite) {
+    txtNewName = new Text( composite, SWT.BORDER );
+    txtNewName.setText( info.getOldName() );
+    txtNewName.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
+    txtNewName.selectAll();
+    txtNewName.addKeyListener( new KeyAdapter() {
+      public void keyReleased( final KeyEvent e ) {
+        info.setNewName( txtNewName.getText() );
+        validate();
+      }
+    } );
+  }
+
+  private void createCbUpdateBundle( final Composite composite ) {
+    String texts = UITexts.renamePropertyInputPage_cbUpdateBundle;
+    cbUpdateBundle = createCheckbox( composite, texts );
+    cbUpdateBundle.addSelectionListener( new SelectionAdapter() {
+      public void widgetSelected( final SelectionEvent event ) {
+        boolean selected = cbUpdateBundle.getSelection();
+        dialogSettings.put( DS_UPDATE_BUNDLE, selected );
+        info.setUpdateBundle( selected );
+      }
+    } );
+    initUpdateBundleOption();
+  }
+
+  private void createCbAllProjects( final Composite composite ) {
+    String text = UITexts.renamePropertyInputPage_cbAllProjects;
+    cbAllProjects = createCheckbox( composite, text );
+    cbAllProjects.addSelectionListener( new SelectionAdapter() {
+      public void widgetSelected( final SelectionEvent event ) {
+        boolean selected = cbAllProjects.getSelection();
+        dialogSettings.put( DS_ALL_PROJECTS, selected );
+        info.setAllProjects( selected );
+        // for demonstration purposes, we enforce the preview for refactorings
+        // that span the entire workspace
+        getRefactoringWizard().setForcePreviewReview( selected );
+      }
+    } );
+    initAllProjectsOption();
+  }
+
+  private Button createCheckbox( final Composite composite,
+                                 final String text ) {
+    Button result = new Button( composite, SWT.CHECK );
+    result.setText( text );
+
+    GridData gridData = new GridData( GridData.FILL_HORIZONTAL );
+    gridData.horizontalSpan = 2;
+    result.setLayoutData( gridData );
+
+    return result;
+  }
+
+
+  // helping methods
+  //////////////////
+
+  private void initDialogSettings() {
+    IDialogSettings ds = PHPeclipsePlugin.getDefault().getDialogSettings();
+    dialogSettings = ds.getSection( DS_KEY );
+    if( dialogSettings == null ) {
+      dialogSettings = ds.addNewSection( DS_KEY );
+      // init default values
+      dialogSettings.put( DS_UPDATE_BUNDLE, true );
+      dialogSettings.put( DS_ALL_PROJECTS, false );
+    }
+  }
+
+  private void validate() {
+    String txt = txtNewName.getText();
+    setPageComplete( txt.length() > 0 && !txt.equals( info.getOldName() ) );
+  }
+
+  private void initUpdateBundleOption() {
+    boolean updateRefs = dialogSettings.getBoolean( DS_UPDATE_BUNDLE );
+    cbUpdateBundle.setSelection( updateRefs );
+    info.setUpdateBundle( updateRefs );
+  }
+
+  private void initAllProjectsOption() {
+    boolean allProjects = dialogSettings.getBoolean( DS_ALL_PROJECTS );
+    cbAllProjects.setSelection( allProjects );
+    info.setAllProjects( allProjects );
+  }
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/wizards/RenamePropertyWizard.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ltk/ui/wizards/RenamePropertyWizard.java
new file mode 100644 (file)
index 0000000..4c5084c
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright (c) 2005 by Leif Frenzel. All rights reserved.
+// See http://leiffrenzel.de
+package net.sourceforge.phpdt.ltk.ui.wizards;
+
+import net.sourceforge.phpdt.ltk.core.RenamePropertyInfo;
+import net.sourceforge.phpdt.ltk.core.RenamePropertyRefactoring;
+
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+
+
+
+/** <p>The wizard that is shown to the user for entering the necessary 
+  * information for property renaming.</p>
+  * 
+  * <p>The wizard class is primarily needed for deciding which pages are 
+  * shown to the user. The actual user interface creation goes on the 
+  * pages.</p>
+  * 
+  * @author Leif Frenzel
+  */
+public class RenamePropertyWizard extends RefactoringWizard {
+
+  private final RenamePropertyInfo info;
+
+
+  public RenamePropertyWizard( final RenamePropertyRefactoring refactoring,
+                               final RenamePropertyInfo info ) {
+    super( refactoring, DIALOG_BASED_USER_INTERFACE );
+    this.info = info;
+  }
+
+
+  // interface methods of RefactoringWizard
+  /////////////////////////////////////////
+
+  protected void addUserInputPages() {
+    setDefaultPageTitle( getRefactoring().getName() );
+    addPage( new RenamePropertyInputPage( info ) );
+  }
+}