Replace the rename key code to use the Eclipse refactoring framework
diff --git a/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/AbstractKeyTreeModel.java b/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/AbstractKeyTreeModel.java
index 9ddfeeb..d88ddc6 100644
--- a/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/AbstractKeyTreeModel.java
+++ b/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/AbstractKeyTreeModel.java
@@ -14,27 +14,72 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.StringTokenizer;
 
 import org.eclipse.babel.core.message.MessagesBundleGroup;
+import org.eclipse.babel.core.message.MessagesBundleGroupAdapter;
 import org.eclipse.babel.core.message.tree.visitor.IKeyTreeVisitor;
 
 
 /**
  * Hierarchical representation of all keys making up a 
  * {@link MessagesBundleGroup}.
-
+ *
+ * Key tree model, using a delimiter to separate key sections
+ * into nodes.  For instance, a dot (.) delimiter on the following key...<p>
+ * <code>person.address.street</code><P>
+ * ... will result in the following node hierarchy:<p>
+ * <pre>
+ * person
+ *     address
+ *         street
+ * </pre>
+ * 
  * @author Pascal Essiembre
  */
-public abstract class AbstractKeyTreeModel {
+public class AbstractKeyTreeModel {
 
     private List<IKeyTreeModelListener> listeners = new ArrayList<IKeyTreeModelListener>();
     private Comparator<KeyTreeNode> comparator;
     
+    private KeyTreeNode rootNode = new KeyTreeNode(null, null, null);
+    
+    private String delimiter;
+    private MessagesBundleGroup messagesBundleGroup;
+    
     protected static final KeyTreeNode[] EMPTY_NODES = new KeyTreeNode[]{};
 
     /**
+     * Defaults to ".".
+     */
+    public AbstractKeyTreeModel(MessagesBundleGroup messagesBundleGroup) {
+        this(messagesBundleGroup, "."); //$NON-NLS-1$
+    }
+
+    /**
+     * Constructor.
+     * @param messagesBundleGroup {@link MessagesBundleGroup} instance
+     * @param delimiter key section delimiter
+     */
+    public AbstractKeyTreeModel(
+    		MessagesBundleGroup messagesBundleGroup, String delimiter) {
+        super();
+        this.messagesBundleGroup = messagesBundleGroup;
+        this.delimiter = delimiter;
+        createTree();
+        
+        messagesBundleGroup.addMessagesBundleGroupListener(
+                new MessagesBundleGroupAdapter() {
+            public void keyAdded(String key) {
+                createTreeNodes(key);
+            }
+            public void keyRemoved(String key) {
+                removeTreeNodes(key);
+            }
+        });
+    }
+
+    /**
      * Adds a key tree model listener.
      * @param listener key tree model listener
      */
@@ -76,14 +121,9 @@
      * @param parentNode root of a branch
      * @return all nodes on a branch
      */
+    // TODO inline and remove this method.
     public KeyTreeNode[] getBranch(KeyTreeNode parentNode) {
-        Set<KeyTreeNode> childNodes = new TreeSet<KeyTreeNode>();
-        childNodes.add(parentNode);
-        for (KeyTreeNode childNode : getChildren(parentNode)) {
-            childNodes.addAll(
-                    Arrays.asList(getBranch(childNode)));
-        }
-        return childNodes.toArray(EMPTY_NODES);
+    	return parentNode.getBranch().toArray(new KeyTreeNode[]{});
     }
 
     /**
@@ -103,7 +143,18 @@
         }
     }
 
-    public abstract KeyTreeNode[] getChildren(KeyTreeNode node);
+    /**
+     * Gets the child nodes of a given key tree node.
+     * @param node the node from which to get children
+     * @return child nodes
+     */
+    public KeyTreeNode[] getChildren(KeyTreeNode node) {
+        KeyTreeNode[] nodes = node.getChildren();
+        if (getComparator() != null) {
+            Arrays.sort(nodes, getComparator());
+        }
+        return nodes;
+    }
 
 	/**
      * Gets the comparator.
@@ -143,6 +194,104 @@
     	return false;
     }
     
+    /**
+     * Gets the delimiter.
+     * @return delimiter
+     */
+    public String getDelimiter() {
+        return delimiter;
+    }
+    /**
+     * Sets the delimiter.
+     * @param delimiter delimiter
+     */
+    public void setDelimiter(String delimiter) {
+        this.delimiter = delimiter;
+    }
+    
+	/**
+	 * Gets the key tree root nodes.
+	 * @return key tree root nodes
+	 */
+    public KeyTreeNode[] getRootNodes() {
+        return getChildren(rootNode);
+    }
+
+    public KeyTreeNode getRootNode() {
+    	return rootNode;
+    }
+    
+    /**
+     * Gets the parent node of the given node.
+     * @param node node from which to get parent
+     * @return parent node
+     */
+    public KeyTreeNode getParent(KeyTreeNode node) {
+        return node.getParent();
+    }
+    
+    /**
+     * Gets the messages bundle group that this key tree represents.
+     * @return messages bundle group
+     */
+    public MessagesBundleGroup getMessagesBundleGroup() {
+        //TODO consider moving this method (and part of constructor) to super
+        return messagesBundleGroup;
+    }
+
+    private void createTree() {
+        rootNode = new KeyTreeNode(null, null, null);
+        String[] keys = messagesBundleGroup.getMessageKeys();
+        for (int i = 0; i < keys.length; i++) {
+			String key = keys[i];
+            createTreeNodes(key);
+        }
+    }
+    
+    private void createTreeNodes(String bundleKey) {
+        StringTokenizer tokens = new StringTokenizer(bundleKey, delimiter);
+        KeyTreeNode node = rootNode;
+        String bundleKeyPart = ""; //$NON-NLS-1$
+        while (tokens.hasMoreTokens()) {
+            String name = tokens.nextToken();
+            bundleKeyPart += name;
+            KeyTreeNode child = node.getChild(name);
+            if (child == null) {
+                child = new KeyTreeNode(node, name, bundleKeyPart);
+                fireNodeAdded(child);
+            }
+            bundleKeyPart += delimiter;
+            node = child;
+        }
+        node.setUsedAsKey();
+    }
+    private void removeTreeNodes(String bundleKey) {
+        if (bundleKey == null) {
+            return;
+        }
+        StringTokenizer tokens = new StringTokenizer(bundleKey, delimiter);
+        KeyTreeNode node = rootNode;
+        while (tokens.hasMoreTokens()) {
+            String name = tokens.nextToken();
+            node = node.getChild(name);
+            if (node == null) {
+                System.err.println(
+                    "No RegEx node matching bundleKey to remove"); //$NON-NLS-1$
+                return;
+            }
+        }
+        KeyTreeNode parentNode = node.getParent();
+        parentNode.removeChild(node);
+        fireNodeRemoved(node);
+        while (parentNode != rootNode) {
+            if (!parentNode.hasChildren() && !messagesBundleGroup.isMessageKey(
+                    parentNode.getMessageKey())) {
+                parentNode.getParent().removeChild(parentNode);
+                fireNodeRemoved(parentNode);
+            }
+            parentNode = parentNode.getParent();
+        }
+    }
     
     
     public interface IKeyTreeNodeLeafFilter {
diff --git a/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/DefaultKeyTreeModel.java b/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/DefaultKeyTreeModel.java
deleted file mode 100644
index 38f3919..0000000
--- a/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/DefaultKeyTreeModel.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007 Pascal Essiembre.
- * 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:
- *    Pascal Essiembre - initial API and implementation
- ******************************************************************************/
-package org.eclipse.babel.core.message.tree;
-
-import java.util.Arrays;
-import java.util.StringTokenizer;
-
-import org.eclipse.babel.core.message.MessagesBundleGroup;
-import org.eclipse.babel.core.message.MessagesBundleGroupAdapter;
-
-
-/**
- * Default key tree model, using a delimiter to separate key sections
- * into nodes.  For instance, a dot (.) delimiter on the following key...<p>
- * <code>person.address.street</code><P>
- * ... will result in the following node hierarchy:<p>
- * <pre>
- * person
- *     address
- *         street
- * </p>
- * @author Pascal Essiembre
- */
-public class DefaultKeyTreeModel extends AbstractKeyTreeModel {
-
-    private KeyTreeNode rootNode = new KeyTreeNode(null, null, null);
-    
-    private String delimiter;
-    private MessagesBundleGroup messagesBundleGroup;
-    
-    /**
-     * Defaults to ".".
-     */
-    public DefaultKeyTreeModel(MessagesBundleGroup messagesBundleGroup) {
-        this(messagesBundleGroup, "."); //$NON-NLS-1$
-    }
-    /**
-     * Constructor.
-     * @param messagesBundleGroup {@link MessagesBundleGroup} instance
-     * @param delimiter key section delimiter
-     */
-    public DefaultKeyTreeModel(
-    		MessagesBundleGroup messagesBundleGroup, String delimiter) {
-        super();
-        this.messagesBundleGroup = messagesBundleGroup;
-        this.delimiter = delimiter;
-        createTree();
-        
-        messagesBundleGroup.addMessagesBundleGroupListener(
-                new MessagesBundleGroupAdapter() {
-            public void keyAdded(String key) {
-                createTreeNodes(key);
-            }
-            public void keyRemoved(String key) {
-                removeTreeNodes(key);
-            }
-        });
-    }
-
-    /**
-     * Gets the delimiter.
-     * @return delimiter
-     */
-    public String getDelimiter() {
-        return delimiter;
-    }
-    /**
-     * Sets the delimiter.
-     * @param delimiter delimiter
-     */
-    public void setDelimiter(String delimiter) {
-        this.delimiter = delimiter;
-    }
-    
-	/**
-	 * Gets the key tree root nodes.
-	 * @return key tree root nodes
-	 */
-    public KeyTreeNode[] getRootNodes() {
-        return getChildren(rootNode);
-    }
-
-    public KeyTreeNode getRootNode() {
-    	return rootNode;
-    }
-    
-    /**
-     * Gets the child nodes of a given key tree node.
-     * @param node the node from which to get children
-     * @return child nodes
-     */
-    public KeyTreeNode[] getChildren(KeyTreeNode node) {
-        KeyTreeNode[] nodes = node.getChildren();
-        if (getComparator() != null) {
-            Arrays.sort(nodes, getComparator());
-        }
-        return nodes;
-    }
-
-    /**
-     * Gets the parent node of the given node.
-     * @param node node from which to get parent
-     * @return parent node
-     */
-    public KeyTreeNode getParent(KeyTreeNode node) {
-        return node.getParent();
-    }
-    
-    /**
-     * Gets the messages bundle group that this key tree represents.
-     * @return messages bundle group
-     */
-    public MessagesBundleGroup getMessagesBundleGroup() {
-        //TODO consider moving this method (and part of constructor) to super
-        return messagesBundleGroup;
-    }
-
-    private void createTree() {
-        rootNode = new KeyTreeNode(null, null, null);
-        String[] keys = messagesBundleGroup.getMessageKeys();
-        for (int i = 0; i < keys.length; i++) {
-			String key = keys[i];
-            createTreeNodes(key);
-        }
-    }
-    
-    private void createTreeNodes(String bundleKey) {
-        if (bundleKey == null) {
-            return;
-        }
-        StringTokenizer tokens = new StringTokenizer(bundleKey, delimiter);
-        KeyTreeNode node = rootNode;
-        String bundleKeyPart = ""; //$NON-NLS-1$
-        while (tokens.hasMoreTokens()) {
-            String name = tokens.nextToken();
-            bundleKeyPart += name;
-            KeyTreeNode child = node.getChild(name);
-            if (child == null) {
-                child = new KeyTreeNode(node, name, bundleKeyPart);
-                fireNodeAdded(child);
-            }
-            bundleKeyPart += delimiter;
-            node = child;
-        }
-    }
-    private void removeTreeNodes(String bundleKey) {
-        if (bundleKey == null) {
-            return;
-        }
-        StringTokenizer tokens = new StringTokenizer(bundleKey, delimiter);
-        KeyTreeNode node = rootNode;
-        while (tokens.hasMoreTokens()) {
-            String name = tokens.nextToken();
-            node = node.getChild(name);
-            if (node == null) {
-                System.err.println(
-                    "No RegEx node matching bundleKey to remove"); //$NON-NLS-1$
-                return;
-            }
-        }
-        KeyTreeNode parentNode = node.getParent();
-        parentNode.removeChild(node);
-        fireNodeRemoved(node);
-        while (parentNode != rootNode) {
-            if (!parentNode.hasChildren() && !messagesBundleGroup.isMessageKey(
-                    parentNode.getMessageKey())) {
-                parentNode.getParent().removeChild(parentNode);
-                fireNodeRemoved(parentNode);
-            }
-            parentNode = parentNode.getParent();
-        }
-    }
-}
diff --git a/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/KeyTreeNode.java b/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/KeyTreeNode.java
index b797b4f..0c469db 100644
--- a/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/KeyTreeNode.java
+++ b/org.eclipse.babel.core/src/org/eclipse/babel/core/message/tree/KeyTreeNode.java
@@ -48,6 +48,8 @@
     private String messageKey;
     
     private final Map<String, KeyTreeNode> children = new TreeMap<String, KeyTreeNode>();
+
+	private boolean usedAsKey = false;
     
     /**
      * Constructor.
@@ -105,13 +107,13 @@
         return nodes.toArray(EMPTY_KEY_TREE_NODES);
     }
 
-    /*default*/ KeyTreeNode[] getChildren() {
+    public KeyTreeNode[] getChildren() {
         return children.values().toArray(EMPTY_KEY_TREE_NODES);
     }
     /*default*/ boolean hasChildren() {
         return !children.isEmpty();
     }
-    /*default*/ KeyTreeNode getChild(String childName) {
+    public KeyTreeNode getChild(String childName) {
         return children.get(childName);
     }
     
@@ -126,6 +128,8 @@
      * @see java.lang.Comparable#compareTo(java.lang.Object)
      */
     public int compareTo(KeyTreeNode node) {
+    	// TODO this is wrong.  For example, menu.label and textbox.label are indicated as equal,
+    	// which means they overwrite each other in the tree set!!!
         if (parent == null && node.parent != null) {
             return -1;
         }
@@ -164,6 +168,16 @@
         //TODO remove parent on child node?
     }
 
+    // TODO: remove this, or simplify it using method getDescendants
+	public Collection<KeyTreeNode> getBranch() {
+        Collection<KeyTreeNode> childNodes = new ArrayList<KeyTreeNode>();
+        childNodes.add(this);
+        for (KeyTreeNode childNode : this.getChildren()) {
+            childNodes.addAll(childNode.getBranch());
+        }
+        return childNodes;
+	}
+
 	public Collection<KeyTreeNode> getDescendants() {
 		Collection<KeyTreeNode> descendants = new ArrayList<KeyTreeNode>();
 		for (KeyTreeNode child : children.values()) {
@@ -172,5 +186,26 @@
 		}
 		return descendants;
 	}
+
+	/**
+	 * Marks this node as representing an actual key.
+	 * <P>
+	 * For example, if the bundle contains two keys:
+	 * <UL>
+	 * <LI>foo.bar</LI>
+	 * <LI>foo.bar.tooltip</LI>
+	 * </UL>
+	 * This will create three nodes, foo, which has a child
+	 * node called bar, which has a child node called tooltip.
+	 * However foo is not an actual key but is only a parent node.
+	 * foo.bar is an actual key even though it is also a parent node.
+	 */
+	public void setUsedAsKey() {
+		usedAsKey = true;
+	}
+
+	public boolean isUsedAsKey() {
+		return usedAsKey;
+	}
     
 }
diff --git a/org.eclipse.babel.editor/META-INF/MANIFEST.MF b/org.eclipse.babel.editor/META-INF/MANIFEST.MF
index 1df0acd..25870b8 100644
--- a/org.eclipse.babel.editor/META-INF/MANIFEST.MF
+++ b/org.eclipse.babel.editor/META-INF/MANIFEST.MF
@@ -14,6 +14,8 @@
  org.eclipse.ui.forms;bundle-version="3.2.0",
  org.eclipse.core.resources;bundle-version="3.2.0",
  org.eclipse.jdt.core;bundle-version="3.2.0",
+ org.eclipse.ltk.core.refactoring,
+ org.eclipse.ltk.ui.refactoring,
  org.eclipse.pde.core;bundle-version="3.2.0",
  org.junit;resolution:=optional,
  org.eclipse.babel.core
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditor.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditor.java
index 62ca038..0c30b8b 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditor.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditor.java
@@ -21,7 +21,6 @@
 import org.eclipse.babel.core.message.MessagesBundleGroup;
 import org.eclipse.babel.core.message.resource.IMessagesResource;
 import org.eclipse.babel.core.message.tree.AbstractKeyTreeModel;
-import org.eclipse.babel.core.message.tree.DefaultKeyTreeModel;
 import org.eclipse.babel.editor.builder.ToggleNatureAction;
 import org.eclipse.babel.editor.bundle.MessagesBundleGroupFactory;
 import org.eclipse.babel.editor.i18n.I18NPage;
@@ -121,7 +120,7 @@
             closeIfAreadyOpen(site, file);
             super.init(site, editorInput);
             //TODO figure out model to use based on preferences
-            keyTreeModel = new DefaultKeyTreeModel(messagesBundleGroup);
+            keyTreeModel = new AbstractKeyTreeModel(messagesBundleGroup);
 //            markerManager = new RBEMarkerManager(this);
         } else {
             throw new PartInitException(
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditorMarkers.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditorMarkers.java
index 88ef9e1..e677ec8 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditorMarkers.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/MessagesEditorMarkers.java
@@ -27,6 +27,7 @@
 import org.eclipse.babel.editor.resource.validator.MessagesBundleGroupValidator;
 import org.eclipse.babel.editor.resource.validator.ValidationFailureEvent;
 import org.eclipse.babel.editor.util.UIUtils;
+import org.eclipse.swt.widgets.Display;
 
 
 /**
@@ -66,7 +67,11 @@
             public void messagesBundleChanged(
                     MessagesBundle messagesBundle,
                     PropertyChangeEvent changeEvent) {
-                resetMarkers();
+            	Display.getDefault().asyncExec(new Runnable(){
+					public void run() {
+		                resetMarkers();
+					}
+				});
             }
             public void propertyChange(PropertyChangeEvent evt) {
                 resetMarkers();
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/bundle/NLFragmentBundleGroupStrategy.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/bundle/NLFragmentBundleGroupStrategy.java
index 7674643..ef9d134 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/bundle/NLFragmentBundleGroupStrategy.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/bundle/NLFragmentBundleGroupStrategy.java
@@ -54,27 +54,23 @@
 import org.osgi.framework.Bundle;
 
 /**
- * This strategy is used when a resource bundle that belongs to Plugin-Fragment 
+ * This strategy is used when a resource bundle that belongs to a plug-in fragment
  * project is loaded.
  * <p>
- * This class loads resource bundles following the default strategy.
- * If no root locale resource is found, it tries to locate that resource
- * inside the host-plugin of the fragment. The host plugin is searched inside
- * the workspace
- * first and if not found inside the eclipse-platform being run.
- * </p>
+ * This class loads resource bundles following the default strategy. If no root
+ * locale resource is found, it tries to locate that resource inside the
+ * host plug-in of the fragment. The host plug-in is searched inside the workspace
+ * first and if not found inside the Eclipse platform being run.
  * <p>
- * This is useful for the developement of i18n packages for eclipse plugin:
- * The best practice is to define the root locale messages inside the plugin
- * itself and to define the other locales in a fragment that host that plugin.
- * <br/>Thanks to this strategy the root locale can be used by the user when
- *  he edits
+ * This is useful for the development of i18n packages for eclipse plug-ins: The
+ * best practice is to define the root locale messages inside the plug-in itself
+ * and to define the other locales in a fragment that host that plug-in. Thanks
+ * to this strategy the root locale can be used by the user when the user edits
  * the messages defined in the fragment alone.
- * <p>
- * See Bug #214521.
- * </p>
+ * 
  * @author Pascal Essiembre
  * @author Hugues Malphettes
+ * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=214521">Bug 214521 - in the resource bundle editor take into account the resources of the "Host-Plugin" when opened bundle is in a plugin-fragment</a>
  */
 public class NLFragmentBundleGroupStrategy extends NLPluginBundleGroupStrategy {
 
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyArguments.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyArguments.java
new file mode 100644
index 0000000..f22ce92
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyArguments.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Nigel Westbury
+ * 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:
+ *    Nigel Westbury - initial implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.refactoring;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
+
+/**
+ * This class contains the data that a processor provides to its rename resource
+ * bundle key participants.
+ */
+public class RenameKeyArguments extends RefactoringArguments {
+
+	private String fNewName;
+
+	private boolean fRenameChildKeys;
+
+	private boolean fUpdateReferences;
+
+	/**
+	 * Creates new rename arguments.
+	 * 
+	 * @param newName
+	 *            the new name of the element to be renamed
+	 * @param renameChildKeys
+	 *            <code>true</code> if child keys are to be renamed;
+	 *            <code>false</code> otherwise
+	 * @param updateReferences
+	 *            <code>true</code> if reference updating is requested;
+	 *            <code>false</code> otherwise
+	 */
+	public RenameKeyArguments(String newName, boolean renameChildKeys, boolean updateReferences) {
+		Assert.isNotNull(newName);
+		fNewName= newName;
+		fRenameChildKeys = renameChildKeys;
+		fUpdateReferences= updateReferences;
+	}
+
+	/**
+	 * Returns the new element name.
+	 *
+	 * @return the new element name
+	 */
+	public String getNewName() {
+		return fNewName;
+	}
+
+	/**
+	 * Returns whether child keys are to be renamed or not.
+	 * 
+	 * @return returns <code>true</code> if child keys are to be renamed;
+	 *         <code>false</code> otherwise
+	 */
+	public boolean getRenameChildKeys() {
+		return fRenameChildKeys;
+	}
+
+	/**
+	 * Returns whether reference updating is requested or not.
+	 * 
+	 * @return returns <code>true</code> if reference updating is requested;
+	 *         <code>false</code> otherwise
+	 */
+	public boolean getUpdateReferences() {
+		return fUpdateReferences;
+	}
+
+	public String toString() {
+		return "rename to " + fNewName //$NON-NLS-1$
+		+ (fRenameChildKeys ? " (rename child keys)" : " (don't rename child keys)") //$NON-NLS-1$//$NON-NLS-2$
+		+ (fUpdateReferences ? " (update references)" : " (don't update references)"); //$NON-NLS-1$//$NON-NLS-2$
+	}
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyChange.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyChange.java
new file mode 100644
index 0000000..961caae
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyChange.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Nigel Westbury
+ * 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:
+ *    Nigel Westbury - initial implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.refactoring;
+
+import java.text.MessageFormat;
+import java.util.Collection;
+
+import org.eclipse.babel.core.message.MessagesBundleGroup;
+import org.eclipse.babel.core.message.tree.KeyTreeNode;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+/**
+ * {@link Change} that renames a resource bundle key.
+ */
+public class RenameKeyChange extends Change {
+
+	private final MessagesBundleGroup fMessagesBundleGroup;
+
+	private final String fNewName;
+
+	private final boolean fRenameChildKeys;
+
+	private final KeyTreeNode fKeyTreeNode;
+
+	private ChangeDescriptor fDescriptor;
+
+	/**
+	 * Creates the change.
+	 *
+	 * @param keyTreeNode the node in the model to rename
+	 * @param newName the new name. Must not be empty
+	 * @param renameChildKeys true if child keys are also to be renamed, false if just this one key is to be renamed
+	 */
+	protected RenameKeyChange(MessagesBundleGroup messageBundleGroup, KeyTreeNode keyTreeNode, String newName, boolean renameChildKeys) {
+		if (keyTreeNode == null || newName == null || newName.length() == 0) {
+			throw new IllegalArgumentException();
+		}
+
+		fMessagesBundleGroup = messageBundleGroup;
+		fKeyTreeNode= keyTreeNode;
+		fNewName= newName;
+		fRenameChildKeys = renameChildKeys;
+		fDescriptor= null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.Change#getDescriptor()
+	 */
+	public ChangeDescriptor getDescriptor() {
+		return fDescriptor;
+	}
+
+	/**
+	 * Sets the change descriptor to be returned by {@link Change#getDescriptor()}.
+	 *
+	 * @param descriptor the change descriptor
+	 */
+	public void setDescriptor(ChangeDescriptor descriptor) {
+		fDescriptor= descriptor;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.Change#getName()
+	 */
+	public String getName() {
+		return MessageFormat.format("Rename {0} to {1}", new Object [] { fKeyTreeNode.getMessageKey(), fNewName});
+	}
+
+	/**
+	 * Returns the new name.
+	 *
+	 * @return return the new name
+	 */
+	public String getNewName() {
+		return fNewName;
+	}
+
+	/**
+	 * This implementation of {@link Change#isValid(IProgressMonitor)} tests the modified resource using the validation method
+	 * specified by {@link #setValidationMethod(int)}.
+	 */
+	public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException {
+		pm.beginTask("", 2); //$NON-NLS-1$
+		try {
+			RefactoringStatus result = new RefactoringStatus();
+			return result;
+		} finally {
+			pm.done();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.Change#initializeValidationData(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public void initializeValidationData(IProgressMonitor pm) {
+	}
+
+	public Object getModifiedElement() {
+		return "what is this for?";
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.Change#perform(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public Change perform(IProgressMonitor pm) throws CoreException {
+		try {
+			pm.beginTask("Rename resource bundle key", 1);
+
+			// Find the root - we will need this later
+			KeyTreeNode root = fKeyTreeNode.getParent();
+			while (root.getName() != null) {
+				root = root.getParent();
+			}
+			
+			if (fRenameChildKeys) {
+				String key = fKeyTreeNode.getMessageKey();
+				String keyPrefix = fKeyTreeNode.getMessageKey() + ".";
+				Collection<KeyTreeNode> branchNodes = fKeyTreeNode.getBranch();
+				for (KeyTreeNode branchNode : branchNodes) {
+					String oldKey = branchNode.getMessageKey();
+					if (oldKey.equals(key) || oldKey.startsWith(keyPrefix)) {
+						String newKey = fNewName + oldKey.substring(key.length());
+						fMessagesBundleGroup.renameMessageKeys(oldKey, newKey);
+					}
+				}
+			} else {
+				fMessagesBundleGroup.renameMessageKeys(fKeyTreeNode.getMessageKey(), fNewName);
+			}
+			
+			String oldName= fKeyTreeNode.getMessageKey();
+		
+			// Find the node that was created with the new name
+			String segments [] = fNewName.split("\\.");
+			KeyTreeNode renamedKey = root;
+			for (String segment : segments) {
+				renamedKey = renamedKey.getChild(segment);
+			}
+			
+			assert(renamedKey != null);
+			return new RenameKeyChange(fMessagesBundleGroup, renamedKey, oldName, fRenameChildKeys);
+		} finally {
+			pm.done();
+		}
+	}
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyDescriptor.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyDescriptor.java
new file mode 100644
index 0000000..9251999
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyDescriptor.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Nigel Westbury
+ * 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:
+ *    Nigel Westbury - initial implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.refactoring;
+
+import org.eclipse.babel.core.message.MessagesBundleGroup;
+import org.eclipse.babel.core.message.tree.KeyTreeNode;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringCore;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring;
+
+/**
+ * Refactoring descriptor for the rename resource bundle key refactoring.
+ * <p>
+ * An instance of this refactoring descriptor may be obtained by calling
+ * {@link RefactoringContribution#createDescriptor()} on a refactoring
+ * contribution requested by invoking
+ * {@link RefactoringCore#getRefactoringContribution(String)} with the
+ * refactoring id ({@link #ID}).
+ */
+public final class RenameKeyDescriptor extends RefactoringDescriptor {
+
+	public static final String ID = "org.eclipse.babel.editor.refactoring.renameKey"; //$NON-NLS-1$
+
+	/** The name attribute */
+	private String fNewName;
+
+	private KeyTreeNode fKeyNode;
+	
+	private MessagesBundleGroup fMessagesBundleGroup;
+	
+	/** Configures if references will be updated */
+	private boolean fRenameChildKeys;
+
+	/**
+	 * Creates a new refactoring descriptor.
+	 * <p>
+	 * Clients should not instantiated this class but use {@link RefactoringCore#getRefactoringContribution(String)}
+	 * with {@link #ID} to get the contribution that can create the descriptor.
+	 * </p>
+	 */
+	public RenameKeyDescriptor() {
+		super(ID, null, "N/A", null, RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE);
+		fNewName = null;
+	}
+
+	/**
+	 * Sets the new name to rename the resource to.
+	 *
+	 * @param name
+	 *            the non-empty new name to set
+	 */
+	public void setNewName(final String name) {
+		Assert.isNotNull(name);
+		Assert.isLegal(!"".equals(name), "Name must not be empty"); //$NON-NLS-1$//$NON-NLS-2$
+		fNewName = name;
+	}
+
+	/**
+	 * Returns the new name to rename the resource to.
+	 *
+	 * @return
+	 *            the new name to rename the resource to
+	 */
+	public String getNewName() {
+		return fNewName;
+	}
+
+	/**
+	 * Sets the project name of this refactoring.
+	 * <p>
+	 * Note: If the resource to be renamed is of type {@link IResource#PROJECT},
+	 * clients are required to to set the project name to <code>null</code>.
+	 * </p>
+	 * <p>
+	 * The default is to associate the refactoring with the workspace.
+	 * </p>
+	 *
+	 * @param project
+	 *            the non-empty project name to set, or <code>null</code> for
+	 *            the workspace
+	 *
+	 * @see #getProject()
+	 */
+//	public void setProject(final String project) {
+//		super.setProject(project);
+//	}
+
+	/**
+	 * 	If set to <code>true</code>, this rename will also rename child keys. The default is to rename child keys.
+	 *
+	 * @param renameChildKeys  <code>true</code> if this rename will rename child keys
+	 */
+	public void setRenameChildKeys(boolean renameChildKeys) {
+		fRenameChildKeys = renameChildKeys;
+	}
+
+	public void setRenameChildKeys(KeyTreeNode keyNode, MessagesBundleGroup messagesBundleGroup) {
+		this.fKeyNode = keyNode;
+		this.fMessagesBundleGroup = messagesBundleGroup;
+	}
+
+	/**
+	 * Returns if this rename will also rename child keys
+	 *
+	 * @return returns <code>true</code> if this rename will rename child keys
+	 */
+	public boolean isRenameChildKeys() {
+		return fRenameChildKeys;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.RefactoringDescriptor#createRefactoring(org.eclipse.ltk.core.refactoring.RefactoringStatus)
+	 */
+	public Refactoring createRefactoring(RefactoringStatus status) throws CoreException {
+
+		String newName= getNewName();
+		if (newName == null || newName.length() == 0) {
+			status.addFatalError("The rename resource bundle key refactoring can not be performed as the new name is invalid");
+			return null;
+		}
+		RenameKeyProcessor processor = new RenameKeyProcessor(fKeyNode, fMessagesBundleGroup);
+		processor.setNewResourceName(newName);
+		processor.setRenameChildKeys(fRenameChildKeys);
+
+		return new RenameRefactoring(processor);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyProcessor.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyProcessor.java
new file mode 100644
index 0000000..9c21f6d
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyProcessor.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Nigel Westbury
+ * 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:
+ *    Nigel Westbury - initial implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.refactoring;
+
+import java.text.MessageFormat;
+
+import org.eclipse.babel.core.message.MessagesBundleGroup;
+import org.eclipse.babel.core.message.tree.KeyTreeNode;
+import org.eclipse.babel.editor.plugin.MessagesEditorPlugin;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+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.ResourceChangeChecker;
+import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
+
+/**
+ * A rename processor for {@link IResource}. The processor will rename the resource and
+ * load rename participants if references should be renamed as well.
+ *
+ * @since 3.4
+ */
+public class RenameKeyProcessor extends RenameProcessor {
+
+	private KeyTreeNode fKeyNode;
+
+	private MessagesBundleGroup fMessageBundleGroup;
+
+	private String fNewResourceName;
+
+	private boolean fRenameChildKeys;
+
+	private RenameKeyArguments fRenameArguments; // set after checkFinalConditions
+
+	/**
+	 * Creates a new rename resource processor.
+	 *
+	 * @param keyNode the resource to rename.
+	 * @param messagesBundleGroup 
+	 */
+	public RenameKeyProcessor(KeyTreeNode keyNode, MessagesBundleGroup messagesBundleGroup) {
+		if (keyNode == null) {
+			throw new IllegalArgumentException("key node must not be null"); //$NON-NLS-1$
+		}
+
+		fKeyNode = keyNode;
+		fMessageBundleGroup = messagesBundleGroup;
+		fRenameArguments= null;
+		fRenameChildKeys= true;
+		setNewResourceName(keyNode.getMessageKey()); // Initialize new name
+	}
+
+	/**
+	 * Returns the new key node
+	 *
+	 * @return the new key node
+	 */
+	public KeyTreeNode getNewKeyTreeNode() {
+		return fKeyNode;
+	}
+
+	/**
+	 * Returns the new resource name
+	 *
+	 * @return the new resource name
+	 */
+	public String getNewResourceName() {
+		return fNewResourceName;
+	}
+
+	/**
+	 * Sets the new resource name
+	 *
+	 * @param newName the new resource name
+	 */
+	public void setNewResourceName(String newName) {
+		Assert.isNotNull(newName);
+		fNewResourceName= newName;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
+		/*
+		 * This method allows fatal and non-fatal problems to be shown to
+		 * the user.  Currently there are none so we return null to indicate
+		 * this. 
+		 */
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
+	 */
+	public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException {
+		pm.beginTask("", 1); //$NON-NLS-1$
+		try {
+			fRenameArguments = new RenameKeyArguments(getNewResourceName(), fRenameChildKeys, false);
+
+			ResourceChangeChecker checker = (ResourceChangeChecker) context.getChecker(ResourceChangeChecker.class);
+			IResourceChangeDescriptionFactory deltaFactory = checker.getDeltaFactory();
+
+			// TODO figure out what we want to do here....
+//			ResourceModifications.buildMoveDelta(deltaFactory, fKeyNode, fRenameArguments);
+
+			return new RefactoringStatus();
+		} finally {
+			pm.done();
+		}
+	}
+
+	/**
+	 * Validates if the a name is valid. This method does not change the name settings on the refactoring. It is intended to be used
+	 * in a wizard to validate user input.
+	 *
+	 * @param newName the name to validate
+	 * @return returns the resulting status of the validation
+	 */
+	public RefactoringStatus validateNewElementName(String newName) {
+		Assert.isNotNull(newName);
+
+		if (newName.startsWith(".")) {
+        	return RefactoringStatus.createFatalErrorStatus("Key cannot start with a '.'");
+		}
+		if (newName.endsWith(".")) {
+            return RefactoringStatus.createFatalErrorStatus("Key cannot end with a '.'");
+		}
+		
+		String [] parts = newName.split("\\.");
+		for (String part : parts) {
+			if (part.length() == 0) {
+	            return RefactoringStatus.createFatalErrorStatus("Key cannot contain an empty part between two periods");
+			}
+			if (!part.matches("([A-Z]|[a-z]|[0-9])*")) {
+	            return RefactoringStatus.createFatalErrorStatus("Key can contain only letters, digits, and periods");
+			}
+		}
+		
+		if (fMessageBundleGroup.isMessageKey(newName)) {
+        	return RefactoringStatus.createFatalErrorStatus(MessagesEditorPlugin.getString("dialog.error.exists"));
+		}
+
+       	return new RefactoringStatus();
+	}
+
+	protected RenameKeyDescriptor createDescriptor() {
+		RenameKeyDescriptor descriptor= new RenameKeyDescriptor();
+		descriptor.setDescription(MessageFormat.format("Rename resource bundle key ''{0}''", fKeyNode.getMessageKey()));
+		descriptor.setComment(MessageFormat.format("Rename resource ''{0}'' to ''{1}''", new Object[] { fKeyNode.getMessageKey(), fNewResourceName }));
+		descriptor.setFlags(RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE | RefactoringDescriptor.BREAKING_CHANGE);
+		descriptor.setNewName(getNewResourceName());
+		descriptor.setRenameChildKeys(fRenameChildKeys);
+		return descriptor;
+	}
+
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#createChange(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public Change createChange(IProgressMonitor pm) throws CoreException {
+		pm.beginTask("", 1); //$NON-NLS-1$
+		try {
+			RenameKeyChange change = new RenameKeyChange(fMessageBundleGroup, getNewKeyTreeNode(), fNewResourceName, fRenameChildKeys);
+			change.setDescriptor(new RefactoringChangeDescriptor(createDescriptor()));
+			return change;
+		} finally {
+			pm.done();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getElements()
+	 */
+	public Object[] getElements() {
+		return new Object[] { fKeyNode };
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getIdentifier()
+	 */
+	public String getIdentifier() {
+		return "org.eclipse.babel.editor.refactoring.renameKeyProcessor"; //$NON-NLS-1$
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName()
+	 */
+	public String getProcessorName() {
+		return "Rename Resource Bundle Key";
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
+	 */
+	public boolean isApplicable() {
+		if (this.fKeyNode == null)
+			return false;
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#loadParticipants(org.eclipse.ltk.core.refactoring.RefactoringStatus, org.eclipse.ltk.core.refactoring.participants.SharableParticipants)
+	 */
+	public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants shared) throws CoreException {
+		// TODO: figure out participants to return here
+		return new RefactoringParticipant[0];
+		
+//		String[] affectedNatures= ResourceProcessors.computeAffectedNatures(fResource);
+//		return ParticipantManager.loadRenameParticipants(status, this, fResource, fRenameArguments, null, affectedNatures, shared);
+	}
+
+	/**
+	 * Returns <code>true</code> if the refactoring processor also renames the child keys
+	 *
+	 * @return <code>true</code> if the refactoring processor also renames the child keys
+	 */
+	public boolean getRenameChildKeys() {
+		return fRenameChildKeys;
+	}
+
+	/**
+	 * Specifies if the refactoring processor also updates the child keys. 
+	 * The default behaviour is to update the child keys.
+	 *
+	 * @param renameChildKeys <code>true</code> if the refactoring processor should also rename the child keys
+	 */
+	public void setRenameChildKeys(boolean renameChildKeys) {
+		fRenameChildKeys = renameChildKeys;
+	}
+
+}
\ No newline at end of file
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyWizard.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyWizard.java
new file mode 100644
index 0000000..6683545
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/refactoring/RenameKeyWizard.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Nigel Westbury
+ * 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:
+ *    Nigel Westbury - initial implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.refactoring;
+
+import org.eclipse.babel.core.message.tree.KeyTreeNode;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+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;
+
+/**
+ * A wizard for the rename bundle key refactoring.
+ */
+public class RenameKeyWizard extends RefactoringWizard {
+
+	/**
+	 * Creates a {@link RenameKeyWizard}.
+	 *
+	 * @param resource
+	 *             the bundle key to rename
+	 * @param refactoring 
+	 */
+	public RenameKeyWizard(KeyTreeNode resource, RenameKeyProcessor refactoring) {
+		super(new RenameRefactoring(refactoring), DIALOG_BASED_USER_INTERFACE);
+		setDefaultPageTitle("Rename Resource Bundle Key");
+		setWindowTitle("Rename Resource Bundle Key");
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ltk.ui.refactoring.RefactoringWizard#addUserInputPages()
+	 */
+	protected void addUserInputPages() {
+		RenameKeyProcessor processor = (RenameKeyProcessor) getRefactoring().getAdapter(RenameKeyProcessor.class);
+		addPage(new RenameResourceRefactoringConfigurationPage(processor));
+	}
+
+	private static class RenameResourceRefactoringConfigurationPage extends UserInputWizardPage {
+
+		private final RenameKeyProcessor fRefactoringProcessor;
+		private Text fNameField;
+
+		public RenameResourceRefactoringConfigurationPage(RenameKeyProcessor processor) {
+			super("RenameResourceRefactoringInputPage"); //$NON-NLS-1$
+			fRefactoringProcessor= processor;
+		}
+
+		/* (non-Javadoc)
+		 * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+		 */
+		public void createControl(Composite parent) {
+			Composite composite = new Composite(parent, SWT.NONE);
+			composite.setLayout(new GridLayout(2, false));
+			composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+			composite.setFont(parent.getFont());
+
+			Label label = new Label(composite, SWT.NONE);
+			label.setText("New name:");
+			label.setLayoutData(new GridData());
+
+			fNameField = new Text(composite, SWT.BORDER);
+			fNameField.setText(fRefactoringProcessor.getNewResourceName());
+			fNameField.setFont(composite.getFont());
+			fNameField.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false));
+			fNameField.addModifyListener(new ModifyListener() {
+				public void modifyText(ModifyEvent e) {
+					validatePage();
+				}
+			});
+
+			final Button includeChildKeysCheckbox = new Button(composite, SWT.CHECK);
+			if (fRefactoringProcessor.getNewKeyTreeNode().isUsedAsKey()) {
+				if (fRefactoringProcessor.getNewKeyTreeNode().getChildren().length == 0) {
+					// This is an actual key with no child keys.
+					includeChildKeysCheckbox.setSelection(false);
+					includeChildKeysCheckbox.setEnabled(false);
+				} else {
+					// This is both an actual key and it has child keys, so we 
+					// let the user choose whether to also rename the child keys.
+					includeChildKeysCheckbox.setSelection(fRefactoringProcessor.getRenameChildKeys());
+					includeChildKeysCheckbox.setEnabled(true);
+				}
+			} else {
+				// This is no an actual key, just a containing node, so the option
+				// to rename child keys must be set (otherwise this rename would not
+				// do anything).
+				includeChildKeysCheckbox.setSelection(true);
+				includeChildKeysCheckbox.setEnabled(false);
+			}
+			
+			includeChildKeysCheckbox.setText("Also rename child keys (other keys with this key as a prefix)");
+			GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+			gd.horizontalSpan= 2;
+			includeChildKeysCheckbox.setLayoutData(gd);
+			includeChildKeysCheckbox.addSelectionListener(new SelectionAdapter(){
+				public void widgetSelected(SelectionEvent e) {
+					fRefactoringProcessor.setRenameChildKeys(includeChildKeysCheckbox.getSelection());
+				}
+			});
+			
+			fNameField.selectAll();
+			setPageComplete(false);
+			setControl(composite);
+		}
+
+		public void setVisible(boolean visible) {
+			if (visible) {
+				fNameField.setFocus();
+			}
+			super.setVisible(visible);
+		}
+
+		protected final void validatePage() {
+			String text= fNameField.getText();
+			RefactoringStatus status= fRefactoringProcessor.validateNewElementName(text);
+			setPageComplete(status);
+		}
+
+		/* (non-Javadoc)
+		 * @see org.eclipse.ltk.ui.refactoring.UserInputWizardPage#performFinish()
+		 */
+		protected boolean performFinish() {
+			initializeRefactoring();
+			storeSettings();
+			return super.performFinish();
+		}
+
+		/* (non-Javadoc)
+		 * @see org.eclipse.ltk.ui.refactoring.UserInputWizardPage#getNextPage()
+		 */
+		public IWizardPage getNextPage() {
+			initializeRefactoring();
+			storeSettings();
+			return super.getNextPage();
+		}
+
+		private void storeSettings() {
+		}
+
+		private void initializeRefactoring() {
+			fRefactoringProcessor.setNewResourceName(fNameField.getText());
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/resource/EclipsePropertiesEditorResource.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/resource/EclipsePropertiesEditorResource.java
index 928dec2..b413a44 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/resource/EclipsePropertiesEditorResource.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/resource/EclipsePropertiesEditorResource.java
@@ -19,6 +19,7 @@
 import org.eclipse.jface.text.DocumentEvent;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IFileEditorInput;
 import org.eclipse.ui.editors.text.TextEditor;
@@ -130,9 +131,18 @@
      * @see org.eclipse.babel.core.bundle.resource.TextResource#setText(
      *              java.lang.String)
      */
-    public void setText(String content) {
-        textEditor.getDocumentProvider().getDocument(
-                textEditor.getEditorInput()).set(content);
+    public void setText(final String content) {
+    	/*
+    	 * We may come in from an event from another thread, so ensure
+    	 * we are on the UI thread.  This may not be the best place to do
+    	 * this???
+    	 */
+    	Display.getDefault().asyncExec(new Runnable() {
+			public void run() {
+		        textEditor.getDocumentProvider().getDocument(
+		                textEditor.getEditorInput()).set(content);
+			}
+		});
     }
 
     /**
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContentProvider.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContentProvider.java
index 05e674e..3a4a704 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContentProvider.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContentProvider.java
@@ -10,8 +10,12 @@
  ******************************************************************************/
 package org.eclipse.babel.editor.tree;
 
-import org.eclipse.babel.core.message.tree.DefaultKeyTreeModel;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.babel.core.message.tree.AbstractKeyTreeModel;
 import org.eclipse.babel.core.message.tree.KeyTreeNode;
+import org.eclipse.babel.core.message.tree.visitor.IKeyTreeVisitor;
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.jface.viewers.Viewer;
@@ -23,7 +27,7 @@
  */
 public class KeyTreeContentProvider implements ITreeContentProvider {
 
-    private DefaultKeyTreeModel keyTreeModel;
+    private AbstractKeyTreeModel keyTreeModel;
     private Viewer viewer; 
     private TreeType treeType;
     
@@ -90,17 +94,21 @@
      *              getElements(java.lang.Object)
      */
     public Object[] getElements(Object inputElement) {
-        switch (treeType) {
+		switch (treeType) {
         case Tree:
             return keyTreeModel.getRootNodes();
         case Flat:
-//        	List<KeyTreeNode> results = new ArrayList<KeyTreeNode>();
-//        	for (KeyTreeNode rootNode : keyTreeModel.getRootNodes()) {
-//        		results.addAll(Arrays.asList(keyTreeModel.getBranch(rootNode)));
-//        	}
-//    		return keyTreeModel.getBranch(keyTreeModel.getRootNode()); // results.toArray();
-    		return keyTreeModel.getRootNode().getDescendants().toArray();
-    	default:
+        	final Collection<KeyTreeNode> actualKeys = new ArrayList<KeyTreeNode>();
+        	IKeyTreeVisitor visitor = new IKeyTreeVisitor() {
+        		public void visitKeyTreeNode(KeyTreeNode node) {
+        			if (node.isUsedAsKey()) {
+        				actualKeys.add(node);
+        			}
+        		}
+        	};
+        	keyTreeModel.accept(visitor, keyTreeModel.getRootNode());
+        	return actualKeys.toArray(); 
+        default:
     		// Should not happen
     		return new KeyTreeNode[0];
         }
@@ -118,7 +126,7 @@
      */
     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
         this.viewer = (TreeViewer) viewer;
-        this.keyTreeModel = (DefaultKeyTreeModel) newInput;
+        this.keyTreeModel = (AbstractKeyTreeModel) newInput;
     }
 
 	public TreeType getTreeType() {
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContributor.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContributor.java
index 0714bc9..cffe1be 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContributor.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/KeyTreeContributor.java
@@ -14,7 +14,6 @@
 import java.util.Observer;
 
 import org.eclipse.babel.core.message.tree.AbstractKeyTreeModel;
-import org.eclipse.babel.core.message.tree.DefaultKeyTreeModel;
 import org.eclipse.babel.core.message.tree.IKeyTreeModelListener;
 import org.eclipse.babel.core.message.tree.KeyTreeNode;
 import org.eclipse.babel.editor.IMessagesEditorChangeListener;
@@ -39,6 +38,7 @@
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.Tree;
 
@@ -58,7 +58,7 @@
     public KeyTreeContributor(final MessagesEditor editor) {
         super();
         this.editor = editor;
-        this.treeModel = new DefaultKeyTreeModel(editor.getBundleGroup());
+        this.treeModel = new AbstractKeyTreeModel(editor.getBundleGroup());
         this.treeType = TreeType.Tree;
     }
 
@@ -153,7 +153,11 @@
     private void contributeMarkers(final TreeViewer treeViewer) {
         editor.getMarkers().addObserver(new Observer() {
             public void update(Observable o, Object arg) {
-                treeViewer.refresh();
+            	Display.getDefault().asyncExec(new Runnable(){
+					public void run() {
+		                treeViewer.refresh();
+					}
+				});
             }
         });
 //      editor.addChangeListener(new MessagesEditorChangeAdapter() {
@@ -228,13 +232,21 @@
         final IKeyTreeModelListener keyTreeListener = new IKeyTreeModelListener() {
             //TODO be smarter about refreshes.
             public void nodeAdded(KeyTreeNode node) {
-                treeViewer.refresh(true);
+            	Display.getDefault().asyncExec(new Runnable(){
+					public void run() {
+		                treeViewer.refresh(true);
+					}
+				});
             };
 //            public void nodeChanged(KeyTreeNode node) {
 //                treeViewer.refresh(true);
 //            };
             public void nodeRemoved(KeyTreeNode node) {
-                treeViewer.refresh(true);
+            	Display.getDefault().asyncExec(new Runnable(){
+					public void run() {
+		                treeViewer.refresh(true);
+					}
+				});
             };
         };
         treeModel.addKeyTreeModelListener(keyTreeListener);
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/actions/RenameKeyAction.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/actions/RenameKeyAction.java
index 0e3c5d3..2da68f2 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/actions/RenameKeyAction.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/tree/actions/RenameKeyAction.java
@@ -10,15 +10,15 @@
  ******************************************************************************/
 package org.eclipse.babel.editor.tree.actions;
 
-import org.eclipse.babel.core.message.MessagesBundleGroup;
 import org.eclipse.babel.core.message.tree.KeyTreeNode;
 import org.eclipse.babel.editor.MessagesEditor;
 import org.eclipse.babel.editor.plugin.MessagesEditorPlugin;
+import org.eclipse.babel.editor.refactoring.RenameKeyProcessor;
+import org.eclipse.babel.editor.refactoring.RenameKeyWizard;
 import org.eclipse.babel.editor.util.UIUtils;
-import org.eclipse.jface.dialogs.IInputValidator;
-import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.jface.window.Window;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
 
 /**
  * @author Pascal Essiembre
@@ -42,47 +42,16 @@
      */
     public void run() {
         KeyTreeNode node = getNodeSelection();
-        String key = node.getMessageKey();
-        String msgHead = null;
-        String msgBody = null;
-        if (getContentProvider().hasChildren(node)) {
-            msgHead = MessagesEditorPlugin.getString(
-                    "dialog.rename.head.multiple"); //$NON-NLS-1$
-            msgBody = MessagesEditorPlugin.getString(
-                    "dialog.rename.body.multiple", //$NON-NLS-1$
-                    key);
-        } else {
-            msgHead = MessagesEditorPlugin.getString(
-                    "dialog.rename.head.single"); //$NON-NLS-1$
-            msgBody = MessagesEditorPlugin.getString(
-                    "dialog.rename.body.single", key); //$NON-NLS-1$
-        }
+
         // Rename single item
-        InputDialog dialog = new InputDialog(
-                getShell(), msgHead, msgBody, key,  new IInputValidator() {
-                    public String isValid(String newText) {
-                        if (getBundleGroup().isMessageKey(newText)) {
-                            return  MessagesEditorPlugin.getString(
-                                    "dialog.error.exists");
-                        }
-                        return null;
-                    }
-                });
-        dialog.open();
-        if (dialog.getReturnCode() == Window.OK ) {
-            String inputKey = dialog.getValue();
-            MessagesBundleGroup messagesBundleGroup = getBundleGroup();
-            KeyTreeNode[] branchNodes = getBranchNodes(node);
-            for (int i = 0; i < branchNodes.length; i++) {
-                KeyTreeNode branchNode = branchNodes[i];
-                String oldKey = branchNode.getMessageKey();
-                if (oldKey.startsWith(key)) {
-                    String newKey = inputKey + oldKey.substring(key.length());
-                    messagesBundleGroup.renameMessageKeys(oldKey, newKey);
-                }
-            }
-        }
+		RenameKeyProcessor refactoring = new RenameKeyProcessor(node, getBundleGroup());
+		
+		RefactoringWizard wizard = new RenameKeyWizard(node, refactoring);
+		try {
+			RefactoringWizardOpenOperation operation= new RefactoringWizardOpenOperation(wizard);
+			operation.run(getShell(), "Introduce Indirection");
+		} catch (InterruptedException exception) {
+			// Do nothing
+		}
     }
-    
-    
 }