aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse/plugins
diff options
context:
space:
mode:
authorRaphaƫl Moll <ralf@android.com>2010-12-02 16:13:02 -0800
committerAndroid Code Review <code-review@android.com>2010-12-02 16:13:02 -0800
commit4af76166617f120d9222f35f98e3c6a937c8ea52 (patch)
treef347da79fb681776ca450c3ab9d112a5e06c55bf /eclipse/plugins
parente9b709525d03726e5575af5b6194ef760fa21a6c (diff)
parentac7c72825e49c1d48e66d283182e865265b3555f (diff)
downloadsdk-4af76166617f120d9222f35f98e3c6a937c8ea52.zip
sdk-4af76166617f120d9222f35f98e3c6a937c8ea52.tar.gz
sdk-4af76166617f120d9222f35f98e3c6a937c8ea52.tar.bz2
Merge "Contribute android changes to Rename package, Rename Type and Move Type Java refactoring"
Diffstat (limited to 'eclipse/plugins')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml60
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java300
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChange.java307
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChangeDescription.java131
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutFileChanges.java61
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidPackageRenameChange.java142
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeMoveChange.java46
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeRenameChange.java128
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java550
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidRenameParticipant.java91
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java442
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java398
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/FixImportsJob.java150
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/IConstants.java35
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java189
15 files changed, 3030 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index d635de8..e99fba1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -806,4 +806,64 @@
class="com.android.ide.eclipse.adt.SourceRevealer">
</revealer>
</extension>
+ <extension
+ point="org.eclipse.ltk.core.refactoring.renameParticipants">
+ <renameParticipant
+ class="com.android.ide.eclipse.adt.internal.refactoring.core.AndroidTypeRenameParticipant"
+ id="com.android.ide.eclipse.adt.internal.refactoring.core.AndroidTypeRenameParticipant"
+ name="Android Rename Type Participant">
+ <enablement>
+ <with
+ variable="element">
+ <instanceof
+ value="org.eclipse.jdt.core.IType">
+ </instanceof>
+ </with>
+ <with variable="affectedNatures">
+ <iterate operator="or">
+ <equals value="com.android.ide.eclipse.adt.AndroidNature"/>
+ </iterate>
+ </with>
+ </enablement>
+ </renameParticipant>
+ <renameParticipant
+ class="com.android.ide.eclipse.adt.internal.refactoring.core.AndroidPackageRenameParticipant"
+ id="com.android.ide.eclipse.adt.internal.refactoring.core.AndroidPackageRenameParticipant"
+ name="Android Rename Package Participant">
+ <enablement>
+ <with
+ variable="element">
+ <instanceof
+ value="org.eclipse.jdt.core.IPackageFragment">
+ </instanceof>
+ </with>
+ <with variable="affectedNatures">
+ <iterate operator="or">
+ <equals value="com.android.ide.eclipse.adt.AndroidNature"/>
+ </iterate>
+ </with>
+ </enablement>
+ </renameParticipant>
+ </extension>
+ <extension
+ point="org.eclipse.ltk.core.refactoring.moveParticipants">
+ <moveParticipant
+ class="com.android.ide.eclipse.adt.internal.refactoring.core.AndroidTypeMoveParticipant"
+ id="com.android.ide.eclipse.adt.internal.refactoring.core.androidTypeMoveParticipant"
+ name="Android Move Type Participant">
+ <enablement>
+ <with
+ variable="element">
+ <instanceof
+ value="org.eclipse.jdt.core.IType">
+ </instanceof>
+ </with>
+ <with variable="affectedNatures">
+ <iterate operator="or">
+ <equals value="com.android.ide.eclipse.adt.AndroidNature"/>
+ </iterate>
+ </with>
+ </enablement>
+ </moveParticipant>
+ </extension>
</plugin>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java
new file mode 100755
index 0000000..542a090
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidDocumentChange.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.changes;
+
+import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
+import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener;
+import com.android.ide.eclipse.adt.internal.refactoring.core.IConstants;
+import com.android.ide.eclipse.adt.internal.refactoring.core.RefactoringUtil;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.xml.AndroidManifest;
+
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.DocumentChange;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * A text change that operates on android manifest using WTP SSE model.
+ * It is base class for Rename Package and Rename Type changes
+*/
+public class AndroidDocumentChange extends DocumentChange {
+
+ protected IFile mAndroidManifest;
+
+ protected String mAppPackage;
+
+ protected IStructuredModel mModel;
+
+ protected IDocument mDocument;
+
+ protected Map<String, String> mElements;
+
+ protected String mNewName;
+
+ protected String mOldName;
+
+ protected ITextFileBufferManager mManager;
+
+ /**
+ * Creates a new <code>AndroidDocumentChange</code> for the given
+ * {@link IDocument}.
+ *
+ * @param document the document this change is working on
+ */
+ public AndroidDocumentChange(IDocument document) {
+ super(SdkConstants.FN_ANDROID_MANIFEST_XML , document);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = super.isValid(pm);
+ if (mModel == null) {
+ status.addFatalError("File " + SdkConstants.FN_ANDROID_MANIFEST_XML + " is invalid.");
+ } else {
+ mAppPackage = getAppPackage();
+ if (mAppPackage == null) {
+ status.addFatalError("Invalid package in the "
+ + SdkConstants.FN_ANDROID_MANIFEST_XML + " file.");
+ }
+ }
+ BasicXmlErrorListener errorListener = new BasicXmlErrorListener();
+ AndroidManifestHelper.parseForError(mAndroidManifest, errorListener);
+
+ if (errorListener.mHasXmlError == true) {
+ status.addFatalError("File " + SdkConstants.FN_ANDROID_MANIFEST_XML + " is invalid.");
+ }
+ return status;
+ }
+
+ /**
+ * Finds the attribute with values oldName
+ *
+ * @param xmlDoc the document
+ * @param element the element
+ * @param attributeName the attribute
+ * @param oldName the value
+ *
+ * @return the attribute
+ */
+ private Attr findAttribute(IDOMDocument xmlDoc, String element, String attributeName,
+ String oldName) {
+ NodeList nodes = xmlDoc.getElementsByTagName(element);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Attr attribute = RefactoringUtil.findAndroidAttributes(attributes, attributeName);
+ if (attribute != null) {
+ String value = attribute.getValue();
+ if (value != null) {
+ String fullName = AndroidManifest.combinePackageAndClassName(
+ getAppPackage(), value);
+ if (fullName != null && fullName.equals(oldName)) {
+ return attribute;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the attribute value
+ *
+ * @param xmlDoc the document
+ * @param element the element
+ * @param attributeName the attribute
+ *
+ * @return the attribute value
+ */
+ protected String getElementAttribute(IDOMDocument xmlDoc, String element,
+ String attributeName, boolean useNamespace) {
+ NodeList nodes = xmlDoc.getElementsByTagName(element);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Attr attribute;
+ if (useNamespace) {
+ attribute = RefactoringUtil.findAndroidAttributes(attributes, attributeName);
+ } else {
+ attribute = (Attr) attributes.getNamedItem(attributeName);
+ }
+ if (attribute != null) {
+ return attribute.getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the SSE model for a document
+ *
+ * @param document the document
+ * @return the model
+ *
+ */
+
+ protected IStructuredModel getModel(IDocument document) throws IOException, CoreException {
+ if (mModel != null) {
+ return mModel;
+ }
+ IStructuredModel model;
+ model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+ if (model == null) {
+ if (document instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) document;
+ model = StructuredModelManager.getModelManager()
+ .getModelForRead(structuredDocument);
+ }
+ }
+ return model;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTextType(String type) {
+ super.setTextType(mAndroidManifest.getFileExtension());
+ }
+
+ /**
+ * Returns the SSE DOM document
+ *
+ * @return the attribute value
+ */
+ protected IDOMDocument getDOMDocument() {
+ IDOMModel xmlModel = (IDOMModel) mModel;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ return xmlDoc;
+ }
+
+ /**
+ * Returns the application package
+ *
+ * @return the package name
+ */
+ protected String getAppPackage() {
+ if (mAppPackage == null) {
+ IDOMDocument xmlDoc = getDOMDocument();
+ mAppPackage = getElementAttribute(xmlDoc, AndroidManifest.NODE_MANIFEST,
+ AndroidManifest.ATTRIBUTE_PACKAGE, false);
+ }
+ return mAppPackage;
+ }
+
+ /**
+ * Returns the text change that set new value of attribute
+ *
+ * @param attribute the attribute
+ * @param newValue the new value
+ *
+ * @return the text change
+ */
+ protected TextEdit createTextEdit(Attr attribute, String newValue) {
+ if (attribute == null)
+ return null;
+
+ if (attribute instanceof IDOMAttr) {
+ IDOMAttr domAttr = (IDOMAttr) attribute;
+ String region = domAttr.getValueRegionText();
+ int offset = domAttr.getValueRegionStartOffset();
+ if (region != null && region.length() >= 2) {
+ return new ReplaceEdit(offset + 1, region.length() - 2, newValue);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the text change that change the value of attribute from oldValue to newValue
+ * and combine package
+ *
+ * @param elementName the element name
+ * @param attributeName the attribute name
+ * @param oldValue the old value
+ * @param newValue the new value
+ *
+ * @return the text change
+ */
+ protected TextEdit createTextEdit(String elementName, String attributeName, String oldValue,
+ String newValue) {
+ return createTextEdit(elementName, attributeName, oldValue, newValue, true);
+ }
+
+ /**
+ * Returns the text change that change the value of attribute from oldValue to newValue
+ *
+ * @param elementName the element name
+ * @param attributeName the attribute name
+ * @param oldValue the old value
+ * @param newValue the new value
+ * @param combinePackage combine package ?
+ *
+ * @return the text change
+ */
+ protected TextEdit createTextEdit(String elementName, String attributeName, String oldName,
+ String newName, boolean combinePackage) {
+ IDOMDocument xmlDoc = getDOMDocument();
+ String name = null;
+ Attr attr = findAttribute(xmlDoc, elementName, attributeName, oldName);
+ if (attr != null) {
+ name = attr.getValue();
+ }
+ if (name != null) {
+ String newValue;
+ if (combinePackage) {
+ newValue = RefactoringUtil.getNewValue(getAppPackage(), name, newName);
+ } else {
+ newValue = newName;
+ }
+ if (newValue != null) {
+ TextEdit edit = createTextEdit(attr, newValue);
+ return edit;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChange.java
new file mode 100755
index 0000000..c7c684d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChange.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.changes;
+
+import com.android.ide.eclipse.adt.internal.refactoring.core.IConstants;
+import com.android.ide.eclipse.adt.internal.refactoring.core.RefactoringUtil;
+
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.DocumentChange;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A text change that operates on android layout using WTP SSE model.
+ * It is base class for Rename Package and Rename Type changes
+*/
+public class AndroidLayoutChange extends DocumentChange {
+
+ private IDocument mDocument;
+
+ private ITextFileBufferManager mManager;
+
+ private IFile mFile;
+
+ private IStructuredModel mModel;
+
+ private Set<AndroidLayoutChangeDescription> mChanges;
+
+ /**
+ * Creates a new <code>AndroidLayoutChange</code>
+ *
+ * @param file the layout file
+ * @param document the document
+ * @param manager the buffer manager
+ * @param changes the list of changes
+ * @param androidLayoutChangeDescription
+ */
+ public AndroidLayoutChange(IFile file, IDocument document, ITextFileBufferManager manager,
+ Set<AndroidLayoutChangeDescription> changes) {
+ super("", document); //$NON-NLS-1$
+ this.mFile = file;
+ this.mDocument = document;
+ this.mManager = manager;
+ this.mChanges = changes;
+ try {
+ this.mModel = getModel(document);
+ } catch (Exception ignore) {
+ }
+ if (mModel != null) {
+ addEdits();
+ }
+ }
+
+ /**
+ * Adds text edits for this change
+ */
+ private void addEdits() {
+ MultiTextEdit multiEdit = new MultiTextEdit();
+ for (AndroidLayoutChangeDescription change : mChanges) {
+ if (!change.isStandalone()) {
+ TextEdit edit = createTextEdit(IConstants.ANDROID_LAYOUT_VIEW_ELEMENT,
+ IConstants.ANDROID_LAYOUT_CLASS_ARGUMENT, change.getClassName(),
+ change.getNewName());
+ if (edit != null) {
+ multiEdit.addChild(edit);
+ }
+ } else {
+ List<TextEdit> edits = createElementTextEdit(change.getClassName(),
+ change.getNewName());
+ for (TextEdit edit : edits) {
+ multiEdit.addChild(edit);
+ }
+ }
+ }
+ setEdit(multiEdit);
+ }
+
+ /**
+ * (non-Javadoc) Returns the text changes which change class (custom layout viewer) in layout file
+ *
+ * @param className the class name
+ * @param newName the new class name
+ *
+ * @return list of text changes
+ */
+ private List<TextEdit> createElementTextEdit(String className, String newName) {
+ IDOMDocument xmlDoc = getDOMDocument();
+ List<TextEdit> edits = new ArrayList<TextEdit>();
+ NodeList nodes = xmlDoc.getElementsByTagName(className);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ if (node instanceof IDOMElement) {
+ IDOMElement domNode = (IDOMElement) node;
+ IStructuredDocumentRegion firstRegion = domNode.getFirstStructuredDocumentRegion();
+ if (firstRegion != null) {
+ int offset = firstRegion.getStartOffset();
+ edits.add(new ReplaceEdit(offset + 1, className.length(), newName));
+ }
+ IStructuredDocumentRegion endRegion = domNode.getEndStructuredDocumentRegion();
+ if (endRegion != null) {
+ int offset = endRegion.getStartOffset();
+ edits.add(new ReplaceEdit(offset + 2, className.length(), newName));
+ }
+ }
+
+ }
+ return edits;
+ }
+
+ /**
+ * Returns the SSE DOM document
+ *
+ * @return the attribute value
+ */
+ protected IDOMDocument getDOMDocument() {
+ IDOMModel xmlModel = (IDOMModel) mModel;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ return xmlDoc;
+ }
+
+ /**
+ * Returns the text change that set new value of attribute
+ *
+ * @param attribute the attribute
+ * @param newValue the new value
+ *
+ * @return the text change
+ */
+ protected TextEdit createTextEdit(Attr attribute, String newValue) {
+ if (attribute == null)
+ return null;
+
+ if (attribute instanceof IDOMAttr) {
+ IDOMAttr domAttr = (IDOMAttr) attribute;
+ String region = domAttr.getValueRegionText();
+ int offset = domAttr.getValueRegionStartOffset();
+ if (region != null && region.length() >= 2) {
+ return new ReplaceEdit(offset + 1, region.length() - 2, newValue);
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Returns the text change that change the value of attribute from oldValue to newValue
+ *
+ * @param elementName the element name
+ * @param attributeName the attribute name
+ * @param oldValue the old value
+ * @param newValue the new value
+ *
+ * @return the text change
+ */
+ protected TextEdit createTextEdit(String elementName, String argumentName, String oldName,
+ String newName) {
+ IDOMDocument xmlDoc = getDOMDocument();
+ String name = null;
+ Attr attr = findAttribute(xmlDoc, elementName, argumentName, oldName);
+ if (attr != null) {
+ name = attr.getValue();
+ }
+ if (name != null && newName != null) {
+ TextEdit edit = createTextEdit(attr, newName);
+ return edit;
+ }
+ return null;
+ }
+
+ /**
+ * (non-Javadoc) Finds the attribute with values oldName
+ *
+ * @param xmlDoc the document
+ * @param element the element
+ * @param attributeName the attribute
+ * @param oldValue the value
+ *
+ * @return the attribute
+ */
+ public Attr findAttribute(IDOMDocument xmlDoc, String element, String attributeName,
+ String oldValue) {
+ NodeList nodes = xmlDoc.getElementsByTagName(element);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Attr attribute = RefactoringUtil.findAndroidAttributes(attributes, attributeName);
+ if (attribute != null) {
+ String value = attribute.getValue();
+ if (value != null && value.equals(oldValue)) {
+ return attribute;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return mFile.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ RefactoringStatus status = super.isValid(pm);
+ if (mModel == null) {
+ status.addFatalError("Invalid the " + getName() + " file.");
+ }
+ return status;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTextType(String type) {
+ super.setTextType(mFile.getFileExtension());
+ }
+
+ /**
+ * Returns the SSE model for a document
+ *
+ * @param document the document
+ *
+ * @return the model
+ *
+ */
+ protected IStructuredModel getModel(IDocument document) throws IOException, CoreException {
+ if (mModel != null) {
+ return mModel;
+ }
+ IStructuredModel model;
+ model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+ if (model == null) {
+ if (document instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) document;
+ model = StructuredModelManager.getModelManager()
+ .getModelForRead(structuredDocument);
+ }
+ }
+ return model;
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ RefactoringUtil.fixModel(mModel, mDocument);
+
+ if (mManager != null) {
+ try {
+ mManager.disconnect(mFile.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChangeDescription.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChangeDescription.java
new file mode 100755
index 0000000..cde4533
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutChangeDescription.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.changes;
+
+/**
+ * This class describes the text changes of android layout files
+ *
+ */
+public class AndroidLayoutChangeDescription {
+
+ private String mClassName;
+
+ private String mNewName;
+
+ private int mType;
+
+ /**
+ * the view layout
+ */
+ public static final int VIEW_TYPE = 0;
+
+ /**
+ * the standalone layout
+ */
+ public static final int STANDALONE_TYPE = 1;
+
+ /**
+ * Creates a new <code>AndroidDocumentChange</code>
+ *
+ * @param className the old layout class name
+ * @param newName the new layout class name
+ * @param type the layout type; valid value are VIEW_TYPE and STANDALONE_TYPE
+ */
+ public AndroidLayoutChangeDescription(String className, String newName, int type) {
+ this.mClassName = className;
+ this.mNewName = newName;
+ this.mType = type;
+ }
+
+ /**
+ * @return the old class name
+ */
+ public String getClassName() {
+ return mClassName;
+ }
+
+ /**
+ * @return the new class name
+ */
+ public String getNewName() {
+ return mNewName;
+ }
+
+ /**
+ * @return the layout type
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * @return true if the layout is standalone
+ */
+ public boolean isStandalone() {
+ return mType == STANDALONE_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mClassName == null) ? 0 : mClassName.hashCode());
+ result = prime * result + ((mNewName == null) ? 0 : mNewName.hashCode());
+ result = prime * result + mType;
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AndroidLayoutChangeDescription other = (AndroidLayoutChangeDescription) obj;
+ if (mClassName == null) {
+ if (other.mClassName != null)
+ return false;
+ } else if (!mClassName.equals(other.mClassName))
+ return false;
+ if (mNewName == null) {
+ if (other.mNewName != null)
+ return false;
+ } else if (!mNewName.equals(other.mNewName))
+ return false;
+ if (mType != other.mType)
+ return false;
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "AndroidLayoutChangeDescription [className=" + mClassName + ", newName=" + mNewName
+ + ", type=" + mType + "]";
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutFileChanges.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutFileChanges.java
new file mode 100755
index 0000000..a20f72b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidLayoutFileChanges.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.changes;
+
+import org.eclipse.core.resources.IFile;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Set of layout files with required text changes
+ *
+ */
+public class AndroidLayoutFileChanges {
+ private IFile mFile;
+
+ private Set<AndroidLayoutChangeDescription> mChanges =
+ new HashSet<AndroidLayoutChangeDescription>();
+
+ /**
+ * Creates a new <code>AndroidLayoutFileChanges</code>
+ *
+ * @param file the layout file
+ */
+ public AndroidLayoutFileChanges(IFile file) {
+ this.mFile = file;
+ }
+
+ /**
+ * Return the layout file
+ *
+ * @return the file
+ */
+ public IFile getFile() {
+ return mFile;
+ }
+
+ /**
+ * Return the text changes
+ *
+ * @return the set of changes
+ */
+ public Set<AndroidLayoutChangeDescription> getChanges() {
+ return mChanges;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidPackageRenameChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidPackageRenameChange.java
new file mode 100755
index 0000000..23a3a26
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidPackageRenameChange.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.changes;
+
+import com.android.ide.eclipse.adt.internal.refactoring.core.FixImportsJob;
+import com.android.ide.eclipse.adt.internal.refactoring.core.IConstants;
+import com.android.ide.eclipse.adt.internal.refactoring.core.RefactoringUtil;
+import com.android.sdklib.xml.AndroidManifest;
+
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.TextEdit;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A text change that operates on android manifest when execute Java Rename package refactoring
+*/
+public class AndroidPackageRenameChange extends AndroidDocumentChange {
+
+ private boolean mIsPackage;
+
+ /**
+ * Creates a new <code>AndroidPackageRenameChange</code>
+ *
+ * @param androidManifest the android manifest file
+ * @param manager the text buffer manager
+ * @param document the document
+ * @param elements the elements
+ * @param newName the new name
+ * @param oldName the old name
+ * @param isPackage is the application package
+ */
+ public AndroidPackageRenameChange(IFile androidManifest, ITextFileBufferManager manager,
+ IDocument document, Map<String, String> elements, String newName, String oldName,
+ boolean isPackage) {
+ super(document);
+ this.mDocument = document;
+ this.mIsPackage = isPackage;
+ this.mElements = elements;
+ this.mNewName = newName;
+ this.mOldName = oldName;
+ this.mManager = manager;
+ this.mAndroidManifest = androidManifest;
+ try {
+ this.mModel = getModel(document);
+ } catch (Exception ignore) {
+ }
+ if (mModel != null) {
+ this.mAppPackage = getAppPackage();
+ addEdits();
+ }
+ }
+
+ /**
+ * Adds text edits for this change
+ */
+ private void addEdits() {
+ MultiTextEdit multiEdit = new MultiTextEdit();
+
+ if (mIsPackage) {
+ TextEdit edit = createTextEdit(AndroidManifest.NODE_MANIFEST,
+ AndroidManifest.ATTRIBUTE_PACKAGE, mOldName, mNewName, false);
+ if (edit != null) {
+ multiEdit.addChild(edit);
+ }
+ }
+ Set<String> keys = mElements.keySet();
+ for (String key : keys) {
+ String value = mElements.get(key);
+ String oldValue = AndroidManifest.combinePackageAndClassName(mAppPackage, value);
+ String newValue = oldValue.replaceFirst(mOldName, mNewName);
+ TextEdit edit = createTextEdit(key, AndroidManifest.ATTRIBUTE_NAME, oldValue,
+ newValue);
+ if (edit != null) {
+ multiEdit.addChild(edit);
+ }
+ if (AndroidManifest.NODE_ACTIVITY.equals(key)) {
+ TextEdit alias = createTextEdit(AndroidManifest.NODE_ACTIVITY_ALIAS,
+ AndroidManifest.ATTRIBUTE_TARGET_ACTIVITY, oldValue, newValue);
+ if (alias != null) {
+ multiEdit.addChild(alias);
+ }
+ }
+ }
+ setEdit(multiEdit);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Change perform(IProgressMonitor pm) throws CoreException {
+ super.perform(pm);
+ return new AndroidPackageRenameChange(mAndroidManifest, mManager, mDocument, mElements,
+ mOldName, mNewName, mIsPackage);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dispose() {
+ super.dispose();
+ RefactoringUtil.fixModel(mModel, mDocument);
+
+ if (mManager != null) {
+ try {
+ mManager.disconnect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ }
+ }
+ if (mIsPackage) {
+ new FixImportsJob("Fix Rename Package", mAndroidManifest, mNewName).schedule(500);
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeMoveChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeMoveChange.java
new file mode 100755
index 0000000..9d57114
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeMoveChange.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.changes;
+
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.IDocument;
+
+import java.util.Map;
+
+/**
+ * A text change that operates on android manifest when execute Java Move type refactoring
+*/
+public class AndroidTypeMoveChange extends AndroidTypeRenameChange {
+
+ /**
+ * Creates a new <code>AndroidTypeMoveChange</code>
+ *
+ * @param androidManifest the android manifest file
+ * @param manager the text buffer manager
+ * @param document the document
+ * @param elements the elements
+ * @param newName the new name
+ * @param oldName the old name
+ * @param isPackage is the application package
+ */
+ public AndroidTypeMoveChange(IFile androidManifest, ITextFileBufferManager manager,
+ IDocument document, Map<String, String> elements, String newName, String oldName) {
+ super(androidManifest, manager, document, elements, newName, oldName);
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeRenameChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeRenameChange.java
new file mode 100755
index 0000000..f87674a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/changes/AndroidTypeRenameChange.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.changes;
+
+import com.android.ide.eclipse.adt.internal.refactoring.core.IConstants;
+import com.android.ide.eclipse.adt.internal.refactoring.core.RefactoringUtil;
+import com.android.sdklib.xml.AndroidManifest;
+
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.TextEdit;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A text change that operates on android manifest when execute Java Rename type refactoring
+*/
+public class AndroidTypeRenameChange extends AndroidDocumentChange {
+
+ /**
+ * * Creates a new <code>AndroidTypeRenameChange</code>
+ *
+ * @param androidManifest the android manifest file
+ * @param manager the text buffer manager
+ * @param document the document
+ * @param elements the elements
+ * @param newName the new name
+ * @param oldName the old name
+ * @param isPackage is the application package
+ */
+ public AndroidTypeRenameChange(IFile androidManifest, ITextFileBufferManager manager,
+ IDocument document, Map<String, String> elements, String newName, String oldName) {
+ super(document);
+ this.mDocument = document;
+ this.mElements = elements;
+ this.mNewName = newName;
+ this.mOldName = oldName;
+ this.mManager = manager;
+ this.mAndroidManifest = androidManifest;
+ try {
+ this.mModel = getModel(document);
+ } catch (Exception ignore) {
+ }
+ if (mModel != null) {
+ addEdits();
+ }
+ }
+
+ /**
+ * (non-Javadoc) Adds text edits for this change
+ */
+ private void addEdits() {
+ MultiTextEdit multiEdit = new MultiTextEdit();
+ Set<String> keys = mElements.keySet();
+ for (String key : keys) {
+ TextEdit edit = createTextEdit(key, AndroidManifest.ATTRIBUTE_NAME, mOldName,
+ mNewName);
+ if (edit != null) {
+ multiEdit.addChild(edit);
+ }
+ if (AndroidManifest.NODE_ACTIVITY.equals(key)) {
+ TextEdit alias = createTextEdit(AndroidManifest.NODE_ACTIVITY_ALIAS,
+ AndroidManifest.ATTRIBUTE_TARGET_ACTIVITY, mOldName, mNewName);
+ if (alias != null) {
+ multiEdit.addChild(alias);
+ }
+ TextEdit manageSpaceActivity = createTextEdit(
+ AndroidManifest.NODE_APPLICATION,
+ AndroidManifest.ATTRIBUTE_MANAGE_SPACE_ACTIVITY, mOldName, mNewName);
+ if (manageSpaceActivity != null) {
+ multiEdit.addChild(manageSpaceActivity);
+ }
+ }
+ }
+ setEdit(multiEdit);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Change perform(IProgressMonitor pm) throws CoreException {
+ super.perform(pm);
+ return new AndroidTypeRenameChange(mAndroidManifest, mManager, mDocument, mElements,
+ mOldName, mNewName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dispose() {
+ super.dispose();
+ RefactoringUtil.fixModel(mModel, mDocument);
+
+ if (mManager != null) {
+ try {
+ mManager.disconnect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ }
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java
new file mode 100755
index 0000000..d9343cc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidPackageRenameParticipant.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.core;
+
+import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChange;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChangeDescription;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutFileChanges;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidPackageRenameChange;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.xml.AndroidManifest;
+import com.android.sdklib.xml.ManifestData;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.SearchRequestor;
+import org.eclipse.jdt.internal.corext.refactoring.changes.RenamePackageChange;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A participant to participate in refactorings that rename a package in an Android project.
+ * The class updates android manifest and the layout file
+ * The user can suppress refactoring by disabling the "Update references" checkbox
+ * <p>
+ * Rename participants are registered via the extension point <code>
+ * org.eclipse.ltk.core.refactoring.renameParticipants</code>.
+ * Extensions to this extension point must therefore extend
+ * <code>org.eclipse.ltk.core.refactoring.participants.RenameParticipant</code>.
+ * </p>
+ */
+public class AndroidPackageRenameParticipant extends AndroidRenameParticipant {
+
+ private IPackageFragment mPackageFragment;
+
+ private boolean mIsPackage;
+
+ private Set<AndroidLayoutFileChanges> mFileChanges = new HashSet<AndroidLayoutFileChanges>();
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * createChange(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ if (pm.isCanceled()) {
+ return null;
+ }
+ if (!getArguments().getUpdateReferences())
+ return null;
+ IPath pkgPath = mPackageFragment.getPath();
+ IJavaProject javaProject = (IJavaProject) mPackageFragment
+ .getAncestor(IJavaElement.JAVA_PROJECT);
+ IProject project = javaProject.getProject();
+ IPath genPath = project.getFullPath().append(SdkConstants.FD_GEN_SOURCES);
+ if (genPath.isPrefixOf(pkgPath)) {
+ RefactoringUtil.logInfo(getName() + ": Cannot rename generated package.");
+ return null;
+ }
+ CompositeChange result = new CompositeChange(getName());
+ if (mAndroidManifest.exists()) {
+ if (mAndroidElements.size() > 0 || mIsPackage) {
+ getDocument();
+ Change change = new AndroidPackageRenameChange(mAndroidManifest, mManager,
+ mDocument, mAndroidElements, mNewName, mOldName, mIsPackage);
+ if (change != null) {
+ result.add(change);
+ }
+ }
+ if (mIsPackage) {
+ Change genChange = getGenPackageChange(pm);
+ if (genChange != null) {
+ result.add(genChange);
+ }
+ }
+ // add layoutChange
+ for (AndroidLayoutFileChanges fileChange : mFileChanges) {
+ IFile file = fileChange.getFile();
+ ITextFileBufferManager lManager = FileBuffers.getTextFileBufferManager();
+ lManager.connect(file.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ ITextFileBuffer buffer = lManager.getTextFileBuffer(file.getFullPath(),
+ LocationKind.NORMALIZE);
+ IDocument lDocument = buffer.getDocument();
+ Change layoutChange = new AndroidLayoutChange(file, lDocument, lManager,
+ fileChange.getChanges());
+ if (layoutChange != null) {
+ result.add(layoutChange);
+ }
+ }
+ }
+ return (result.getChildren().length == 0) ? null : result;
+ }
+
+ /**
+ * Returns Android gen package text change
+ *
+ * @param pm the progress monitor
+ *
+ * @return Android gen package text change
+ * @throws CoreException
+ * @throws OperationCanceledException
+ */
+ public Change getGenPackageChange(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ if (mIsPackage) {
+ IPackageFragment genJavaPackageFragment = getGenPackageFragment();
+ if (genJavaPackageFragment != null && genJavaPackageFragment.exists()) {
+ return new RenamePackageChange(genJavaPackageFragment, mNewName, true);
+ }
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc) return the gen package fragment
+ *
+ */
+ private IPackageFragment getGenPackageFragment() throws JavaModelException {
+ IJavaProject javaProject = (IJavaProject) mPackageFragment
+ .getAncestor(IJavaElement.JAVA_PROJECT);
+ if (javaProject != null && javaProject.isOpen()) {
+ IProject project = javaProject.getProject();
+ IFolder genFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES);
+ if (genFolder.exists()) {
+ String javaPackagePath = mAppPackage.replace(".", "/");
+ IPath genJavaPackagePath = genFolder.getFullPath().append(javaPackagePath);
+ IPackageFragment genPackageFragment = javaProject
+ .findPackageFragment(genJavaPackagePath);
+ return genPackageFragment;
+ }
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * getName()
+ */
+ @Override
+ public String getName() {
+ return "Android Package Rename";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * initialize(java.lang.Object)
+ */
+ @Override
+ protected boolean initialize(final Object element) {
+ mIsPackage = false;
+ try {
+ if (element instanceof IPackageFragment) {
+ mPackageFragment = (IPackageFragment) element;
+ if (!mPackageFragment.containsJavaResources())
+ return false;
+ IJavaProject javaProject = (IJavaProject) mPackageFragment
+ .getAncestor(IJavaElement.JAVA_PROJECT);
+ IProject project = javaProject.getProject();
+ IResource manifestResource = project.findMember(AndroidConstants.WS_SEP
+ + SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ if (manifestResource == null || !manifestResource.exists()
+ || !(manifestResource instanceof IFile)) {
+ RefactoringUtil.logInfo("Invalid or missing the "
+ + SdkConstants.FN_ANDROID_MANIFEST_XML + " in the " + project.getName()
+ + " project.");
+ return false;
+ }
+ mAndroidManifest = (IFile) manifestResource;
+ String packageName = mPackageFragment.getElementName();
+ ManifestData manifestData;
+ manifestData = AndroidManifestHelper.parseForData(mAndroidManifest);
+ if (manifestData == null) {
+ return false;
+ }
+ mAppPackage = manifestData.getPackage();
+ mOldName = packageName;
+ mNewName = getArguments().getNewName();
+ if (mOldName == null || mNewName == null) {
+ return false;
+ }
+
+ if (RefactoringUtil.isRefactorAppPackage()
+ && mAppPackage != null
+ && mAppPackage.equals(packageName)) {
+ mIsPackage = true;
+ }
+ mAndroidElements = addAndroidElements();
+ try {
+ final IType type = javaProject.findType(SdkConstants.CLASS_VIEW);
+ SearchPattern pattern = SearchPattern.createPattern("*",
+ IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS,
+ SearchPattern.R_REGEXP_MATCH);
+ IJavaSearchScope scope =SearchEngine.createJavaSearchScope(
+ new IJavaElement[] { mPackageFragment });
+ final HashSet<IType> elements = new HashSet<IType>();
+ SearchRequestor requestor = new SearchRequestor() {
+
+ @Override
+ public void acceptSearchMatch(SearchMatch match) throws CoreException {
+ Object elem = match.getElement();
+ if (elem instanceof IType) {
+ IType eType = (IType) elem;
+ IType[] superTypes = JavaModelUtil.getAllSuperTypes(eType,
+ new NullProgressMonitor());
+ for (int i = 0; i < superTypes.length; i++) {
+ if (superTypes[i].equals(type)) {
+ elements.add(eType);
+ break;
+ }
+ }
+ }
+
+ }
+ };
+ SearchEngine searchEngine = new SearchEngine();
+ searchEngine.search(pattern, new SearchParticipant[] {
+ SearchEngine.getDefaultSearchParticipant()
+ }, scope, requestor, null);
+ List<String> views = new ArrayList<String>();
+ for (IType elem : elements) {
+ views.add(elem.getFullyQualifiedName());
+ }
+ if (views.size() > 0) {
+ String[] classNames = views.toArray(new String[0]);
+ addLayoutChanges(project, classNames);
+ }
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ }
+
+ return mIsPackage || mAndroidElements.size() > 0 || mFileChanges.size() > 0;
+ }
+ } catch (JavaModelException ignore) {
+ }
+ return false;
+ }
+
+ /**
+ * (non-Javadoc) Adds layout changes for project
+ *
+ * @param project the Android project
+ * @param classNames the layout classes
+ *
+ */
+ private void addLayoutChanges(IProject project, String[] classNames) {
+ try {
+ IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES);
+ IResource[] layoutMembers = resFolder.members();
+ for (int j = 0; j < layoutMembers.length; j++) {
+ IResource resource = layoutMembers[j];
+ if (resource instanceof IFolder
+ && resource.exists()
+ && resource.getName().startsWith(SdkConstants.FD_LAYOUT)) {
+ IFolder layoutFolder = (IFolder) resource;
+ IResource[] members = layoutFolder.members();
+ for (int i = 0; i < members.length; i++) {
+ IResource member = members[i];
+ if ((member instanceof IFile)
+ && member.exists()
+ && member.getName().endsWith(".xml")) { //$NON-NLS-1$
+ IFile file = (IFile) member;
+ Set<AndroidLayoutChangeDescription> changes =
+ parse(file, classNames);
+ if (changes.size() > 0) {
+ AndroidLayoutFileChanges fileChange =
+ new AndroidLayoutFileChanges(file);
+ fileChange.getChanges().addAll(changes);
+ mFileChanges.add(fileChange);
+ }
+ }
+ }
+ }
+ }
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ }
+ }
+
+ /**
+ * (non-Javadoc) Searches the layout file for classes
+ *
+ * @param file the Android layout file
+ * @param classNames the layout classes
+ *
+ */
+ private Set<AndroidLayoutChangeDescription> parse(IFile file, String[] classNames) {
+ Set<AndroidLayoutChangeDescription> changes =
+ new HashSet<AndroidLayoutChangeDescription>();
+ ITextFileBufferManager lManager = null;
+ try {
+ lManager = FileBuffers.getTextFileBufferManager();
+ lManager.connect(file.getFullPath(),
+ LocationKind.NORMALIZE, new NullProgressMonitor());
+ ITextFileBuffer buffer = lManager.getTextFileBuffer(file.getFullPath(),
+ LocationKind.NORMALIZE);
+ IDocument lDocument = buffer.getDocument();
+ IStructuredModel model = null;
+ try {
+ model = StructuredModelManager.getModelManager().
+ getExistingModelForRead(lDocument);
+ if (model == null) {
+ if (lDocument instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) lDocument;
+ model = StructuredModelManager.getModelManager().getModelForRead(
+ structuredDocument);
+ }
+ }
+ if (model != null) {
+ IDOMModel xmlModel = (IDOMModel) model;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ NodeList nodes = xmlDoc
+ .getElementsByTagName(IConstants.ANDROID_LAYOUT_VIEW_ELEMENT);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Node attributeNode = attributes
+ .getNamedItem(IConstants.ANDROID_LAYOUT_CLASS_ARGUMENT);
+ if (attributeNode != null || attributeNode instanceof Attr) {
+ Attr attribute = (Attr) attributeNode;
+ String value = attribute.getValue();
+ if (value != null) {
+ for (int j = 0; j < classNames.length; j++) {
+ String className = classNames[j];
+ if (value.equals(className)) {
+ String newClassName = getNewClassName(className);
+ AndroidLayoutChangeDescription layoutChange =
+ new AndroidLayoutChangeDescription(
+ className, newClassName,
+ AndroidLayoutChangeDescription.VIEW_TYPE);
+ changes.add(layoutChange);
+ }
+ }
+ }
+ }
+ }
+ }
+ for (int j = 0; j < classNames.length; j++) {
+ String className = classNames[j];
+ nodes = xmlDoc.getElementsByTagName(className);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ String newClassName = getNewClassName(className);
+ AndroidLayoutChangeDescription layoutChange =
+ new AndroidLayoutChangeDescription(
+ className, newClassName,
+ AndroidLayoutChangeDescription.STANDALONE_TYPE);
+ changes.add(layoutChange);
+ }
+ }
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ } catch (CoreException ignore) {
+ } finally {
+ if (lManager != null) {
+ try {
+ lManager.disconnect(file.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException ignore) {
+ }
+ }
+ }
+ return changes;
+ }
+
+ /**
+ * (non-Javadoc) Returns the new class name
+ *
+ * @param className the class name
+ * @return the new class name
+ *
+ */
+ private String getNewClassName(String className) {
+ int lastDot = className.lastIndexOf("."); //$NON-NLS-1$
+ if (lastDot < 0) {
+ return mNewName;
+ }
+ String name = className.substring(lastDot, className.length());
+ String newClassName = mNewName + name;
+ return newClassName;
+ }
+
+ /**
+ * (non-Javadoc) Returns the elements (activity, receiver, service ...)
+ * which have to be renamed
+ *
+ * @return the android elements
+ *
+ */
+ private Map<String, String> addAndroidElements() {
+ Map<String, String> androidElements = new HashMap<String, String>();
+
+ IDocument document;
+ try {
+ document = getDocument();
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ if (mManager != null) {
+ try {
+ mManager.disconnect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException e1) {
+ RefactoringUtil.log(e1);
+ }
+ }
+ document = null;
+ return androidElements;
+ }
+
+ IStructuredModel model = null;
+ try {
+ model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+ if (model == null) {
+ if (document instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) document;
+ model = StructuredModelManager.getModelManager().getModelForRead(
+ structuredDocument);
+ }
+ }
+ if (model != null) {
+ IDOMModel xmlModel = (IDOMModel) model;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ add(xmlDoc, androidElements, AndroidManifest.NODE_ACTIVITY,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_APPLICATION,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_PROVIDER,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_RECEIVER,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_SERVICE,
+ AndroidManifest.ATTRIBUTE_NAME);
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ return androidElements;
+ }
+
+ /**
+ * (non-Javadoc) Adds the element (activity, receiver, service ...) to the map
+ *
+ * @param xmlDoc the document
+ * @param androidElements the map
+ * @param element the element
+ */
+ private void add(IDOMDocument xmlDoc, Map<String, String> androidElements, String element,
+ String argument) {
+ NodeList nodes = xmlDoc.getElementsByTagName(element);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Attr attribute = RefactoringUtil.findAndroidAttributes(attributes, argument);
+ if (attribute != null) {
+ String value = attribute.getValue();
+ if (value != null) {
+ String fullName = AndroidManifest.combinePackageAndClassName(mAppPackage,
+ value);
+ if (RefactoringUtil.isRefactorAppPackage()) {
+ if (fullName != null && fullName.startsWith(mAppPackage)) {
+ boolean startWithDot = (value.charAt(0) == '.');
+ boolean hasDot = (value.indexOf('.') != -1);
+ if (!startWithDot && hasDot) {
+ androidElements.put(element, value);
+ }
+ }
+ } else {
+ if (fullName != null) {
+ androidElements.put(element, value);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidRenameParticipant.java
new file mode 100755
index 0000000..2f4ceed
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidRenameParticipant.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.core;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
+import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
+
+import java.util.Map;
+
+/**
+ * The abstract class for Rename Package and Rename type participants
+ *
+ */
+public abstract class AndroidRenameParticipant extends RenameParticipant {
+
+ protected IFile mAndroidManifest;
+
+ protected ITextFileBufferManager mManager;
+
+ protected String mOldName;
+
+ protected String mNewName;
+
+ protected IDocument mDocument;
+
+ protected String mAppPackage;
+
+ protected Map<String, String> mAndroidElements;
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * checkConditions(org.eclipse.core.runtime.IProgressMonitor,
+ * org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
+ */
+ @Override
+ public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
+ throws OperationCanceledException {
+ return new RefactoringStatus();
+ }
+
+ /**
+ * @return the document
+ * @throws CoreException
+ */
+ public IDocument getDocument() throws CoreException {
+ if (mDocument == null) {
+ mManager = FileBuffers.getTextFileBufferManager();
+ mManager.connect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ ITextFileBuffer buffer = mManager.getTextFileBuffer(mAndroidManifest.getFullPath(),
+ LocationKind.NORMALIZE);
+ mDocument = buffer.getDocument();
+ }
+ return mDocument;
+ }
+
+ /**
+ * @return the android manifest file
+ */
+ public IFile getAndroidManifest() {
+ return mAndroidManifest;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java
new file mode 100755
index 0000000..9498421
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeMoveParticipant.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.core;
+
+import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChange;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChangeDescription;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutFileChanges;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidTypeMoveChange;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.xml.AndroidManifest;
+import com.android.sdklib.xml.ManifestData;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.text.IDocument;
+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.participants.CheckConditionsContext;
+import org.eclipse.ltk.core.refactoring.participants.MoveParticipant;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A participant to participate in refactorings that move a type in an Android project.
+ * The class updates android manifest and the layout file
+ * The user can suppress refactoring by disabling the "Update references" checkbox
+ * <p>
+ * Rename participants are registered via the extension point <code>
+ * org.eclipse.ltk.core.refactoring.moveParticipants</code>.
+ * Extensions to this extension point must therefore extend <code>org.eclipse.ltk.core.refactoring.participants.MoveParticipant</code>.
+ * </p>
+ */
+public class AndroidTypeMoveParticipant extends MoveParticipant {
+
+ protected IFile mAndroidManifest;
+
+ protected ITextFileBufferManager mManager;
+
+ protected String mOldName;
+
+ protected String mNewName;
+
+ protected IDocument mDocument;
+
+ protected String mJavaPackage;
+
+ protected Map<String, String> mAndroidElements;
+
+ private Set<AndroidLayoutFileChanges> mFileChanges = new HashSet<AndroidLayoutFileChanges>();
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * checkConditions(org.eclipse.core.runtime.IProgressMonitor,
+ * org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
+ */
+ @Override
+ public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
+ throws OperationCanceledException {
+ return new RefactoringStatus();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * createChange(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ if (pm.isCanceled()) {
+ return null;
+ }
+ if (!getArguments().getUpdateReferences())
+ return null;
+ CompositeChange result = new CompositeChange(getName());
+ if (mAndroidManifest.exists()) {
+ if (mAndroidElements.size() > 0) {
+ getDocument();
+ Change change = new AndroidTypeMoveChange(mAndroidManifest, mManager, mDocument,
+ mAndroidElements, mNewName, mOldName);
+ if (change != null) {
+ result.add(change);
+ }
+ }
+
+ for (AndroidLayoutFileChanges fileChange : mFileChanges) {
+ IFile file = fileChange.getFile();
+ ITextFileBufferManager lManager = FileBuffers.getTextFileBufferManager();
+ lManager.connect(file.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ ITextFileBuffer buffer = lManager.getTextFileBuffer(file.getFullPath(),
+ LocationKind.NORMALIZE);
+ IDocument lDocument = buffer.getDocument();
+ Change layoutChange = new AndroidLayoutChange(file, lDocument, lManager,
+ fileChange.getChanges());
+ if (layoutChange != null) {
+ result.add(layoutChange);
+ }
+ }
+ }
+ return (result.getChildren().length == 0) ? null : result;
+
+ }
+
+ /**
+ * @return the document
+ * @throws CoreException
+ */
+ public IDocument getDocument() throws CoreException {
+ if (mDocument == null) {
+ mManager = FileBuffers.getTextFileBufferManager();
+ mManager.connect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ ITextFileBuffer buffer = mManager.getTextFileBuffer(mAndroidManifest.getFullPath(),
+ LocationKind.NORMALIZE);
+ mDocument = buffer.getDocument();
+ }
+ return mDocument;
+ }
+
+ /**
+ * @return the android manifest file
+ */
+ public IFile getAndroidManifest() {
+ return mAndroidManifest;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * getName()
+ */
+ @Override
+ public String getName() {
+ return "Android Type Move";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * initialize(java.lang.Object)
+ */
+ @Override
+ protected boolean initialize(Object element) {
+
+ if (element instanceof IType) {
+ IType type = (IType) element;
+ IJavaProject javaProject = (IJavaProject) type.getAncestor(IJavaElement.JAVA_PROJECT);
+ IProject project = javaProject.getProject();
+ IResource manifestResource = project.findMember(AndroidConstants.WS_SEP
+ + SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ if (manifestResource == null || !manifestResource.exists()
+ || !(manifestResource instanceof IFile)) {
+ RefactoringUtil.logInfo("Invalid or missing the "
+ + SdkConstants.FN_ANDROID_MANIFEST_XML + " in the " + project.getName()
+ + " project.");
+ return false;
+ }
+ mAndroidManifest = (IFile) manifestResource;
+ ManifestData manifestData;
+ manifestData = AndroidManifestHelper.parseForData(mAndroidManifest);
+ if (manifestData == null) {
+ return false;
+ }
+ mJavaPackage = manifestData.getPackage();
+ mOldName = type.getFullyQualifiedName();
+ Object destination = getArguments().getDestination();
+ if (destination instanceof IPackageFragment) {
+ IPackageFragment packageFragment = (IPackageFragment) destination;
+ mNewName = packageFragment.getElementName() + "." + type.getElementName();
+ }
+ if (mOldName == null || mNewName == null) {
+ return false;
+ }
+ mAndroidElements = addAndroidElements();
+ try {
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(null);
+ if (typeHierarchy == null) {
+ return false;
+ }
+ IType[] superTypes = typeHierarchy.getAllSuperclasses(type);
+ for (int i = 0; i < superTypes.length; i++) {
+ IType superType = superTypes[i];
+ String className = superType.getFullyQualifiedName();
+ if (className.equals(SdkConstants.CLASS_VIEW)) {
+ addLayoutChanges(project, type.getFullyQualifiedName());
+ break;
+ }
+ }
+ } catch (JavaModelException ignore) {
+ }
+ return mAndroidElements.size() > 0 || mFileChanges.size() > 0;
+ }
+ return false;
+ }
+
+ /**
+ * (non-Javadoc) Adds layout changes for project
+ *
+ * @param project the Android project
+ * @param classNames the layout classes
+ *
+ */
+ private void addLayoutChanges(IProject project, String className) {
+ try {
+ IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES);
+ IFolder layoutFolder = resFolder.getFolder(SdkConstants.FD_LAYOUT);
+ IResource[] members = layoutFolder.members();
+ for (int i = 0; i < members.length; i++) {
+ IResource member = members[i];
+ if ((member instanceof IFile) && member.exists()) {
+ IFile file = (IFile) member;
+ Set<AndroidLayoutChangeDescription> changes = parse(file, className);
+ if (changes.size() > 0) {
+ AndroidLayoutFileChanges fileChange = new AndroidLayoutFileChanges(file);
+ fileChange.getChanges().addAll(changes);
+ mFileChanges.add(fileChange);
+ }
+ }
+ }
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ }
+ }
+
+ /**
+ * (non-Javadoc) Searches the layout file for classes
+ *
+ * @param file the Android layout file
+ * @param classNames the layout classes
+ *
+ */
+ private Set<AndroidLayoutChangeDescription> parse(IFile file, String className) {
+ Set<AndroidLayoutChangeDescription> changes = new HashSet<AndroidLayoutChangeDescription>();
+ ITextFileBufferManager lManager = null;
+ try {
+ lManager = FileBuffers.getTextFileBufferManager();
+ lManager.connect(file.getFullPath(), LocationKind.NORMALIZE, new NullProgressMonitor());
+ ITextFileBuffer buffer = lManager.getTextFileBuffer(file.getFullPath(),
+ LocationKind.NORMALIZE);
+ IDocument lDocument = buffer.getDocument();
+ IStructuredModel model = null;
+ try {
+ model = StructuredModelManager.getModelManager().getExistingModelForRead(lDocument);
+ if (model == null) {
+ if (lDocument instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) lDocument;
+ model = StructuredModelManager.getModelManager().getModelForRead(
+ structuredDocument);
+ }
+ }
+ if (model != null) {
+ IDOMModel xmlModel = (IDOMModel) model;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ NodeList nodes = xmlDoc
+ .getElementsByTagName(IConstants.ANDROID_LAYOUT_VIEW_ELEMENT);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Node attributeNode = attributes
+ .getNamedItem(IConstants.ANDROID_LAYOUT_CLASS_ARGUMENT);
+ if (attributeNode != null || attributeNode instanceof Attr) {
+ Attr attribute = (Attr) attributeNode;
+ String value = attribute.getValue();
+ if (value != null && value.equals(className)) {
+ AndroidLayoutChangeDescription layoutChange =
+ new AndroidLayoutChangeDescription(className, mNewName,
+ AndroidLayoutChangeDescription.VIEW_TYPE);
+ changes.add(layoutChange);
+ }
+ }
+ }
+ }
+ nodes = xmlDoc.getElementsByTagName(className);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ AndroidLayoutChangeDescription layoutChange =
+ new AndroidLayoutChangeDescription(className, mNewName,
+ AndroidLayoutChangeDescription.STANDALONE_TYPE);
+ changes.add(layoutChange);
+ }
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ } catch (CoreException ignore) {
+ } finally {
+ if (lManager != null) {
+ try {
+ lManager.disconnect(file.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException ignore) {
+ }
+ }
+ }
+ return changes;
+ }
+
+ /**
+ * (non-Javadoc) Returns the elements (activity, receiver, service ...)
+ * which have to be renamed
+ *
+ * @return the android elements
+ *
+ */
+ private Map<String, String> addAndroidElements() {
+ Map<String, String> androidElements = new HashMap<String, String>();
+
+ IDocument document;
+ try {
+ document = getDocument();
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ if (mManager != null) {
+ try {
+ mManager.disconnect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException e1) {
+ RefactoringUtil.log(e1);
+ }
+ }
+ document = null;
+ return androidElements;
+ }
+
+ IStructuredModel model = null;
+ try {
+ model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+ if (model == null) {
+ if (document instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) document;
+ model = StructuredModelManager.getModelManager().getModelForRead(
+ structuredDocument);
+ }
+ }
+ if (model != null) {
+ IDOMModel xmlModel = (IDOMModel) model;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ add(xmlDoc, androidElements, AndroidManifest.NODE_ACTIVITY,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_APPLICATION,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_PROVIDER,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_RECEIVER,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_SERVICE,
+ AndroidManifest.ATTRIBUTE_NAME);
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ return androidElements;
+ }
+
+ /**
+ * (non-Javadoc) Adds the element (activity, receiver, service ...) to the map
+ *
+ * @param xmlDoc the document
+ * @param androidElements the map
+ * @param element the element
+ */
+ private void add(IDOMDocument xmlDoc, Map<String, String> androidElements, String element,
+ String argument) {
+ NodeList nodes = xmlDoc.getElementsByTagName(element);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Attr attribute = RefactoringUtil.findAndroidAttributes(attributes, argument);
+ if (attribute != null) {
+ String value = attribute.getValue();
+ if (value != null) {
+ String fullName = AndroidManifest.combinePackageAndClassName(mJavaPackage,
+ value);
+ if (fullName != null && fullName.equals(mOldName)) {
+ androidElements.put(element, value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java
new file mode 100755
index 0000000..acf8caa
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/AndroidTypeRenameParticipant.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.core;
+
+import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChange;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutChangeDescription;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidLayoutFileChanges;
+import com.android.ide.eclipse.adt.internal.refactoring.changes.AndroidTypeRenameChange;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.xml.AndroidManifest;
+import com.android.sdklib.xml.ManifestData;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A participant to participate in refactorings that rename a type in an Android project.
+ * The class updates android manifest and the layout file
+ * The user can suppress refactoring by disabling the "Update references" checkbox
+ * <p>
+ * Rename participants are registered via the extension point <code>
+ * org.eclipse.ltk.core.refactoring.renameParticipants</code>.
+ * Extensions to this extension point must therefore extend <code>org.eclipse.ltk.core.refactoring.participants.RenameParticipant</code>.
+ * </p>
+ */
+public class AndroidTypeRenameParticipant extends AndroidRenameParticipant {
+
+ private Set<AndroidLayoutFileChanges> mFileChanges = new HashSet<AndroidLayoutFileChanges>();
+
+ private String mLayoutNewName;
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * createChange(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ if (pm.isCanceled()) {
+ return null;
+ }
+ if (!getArguments().getUpdateReferences())
+ return null;
+ CompositeChange result = new CompositeChange(getName());
+ if (mAndroidManifest.exists()) {
+ if (mAndroidElements.size() > 0) {
+ getDocument();
+ Change change = new AndroidTypeRenameChange(mAndroidManifest, mManager, mDocument,
+ mAndroidElements, mNewName, mOldName);
+ if (change != null) {
+ result.add(change);
+ }
+ }
+ // add layoutChange
+ for (AndroidLayoutFileChanges fileChange : mFileChanges) {
+ IFile file = fileChange.getFile();
+ ITextFileBufferManager lManager = FileBuffers.getTextFileBufferManager();
+ lManager.connect(file.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ ITextFileBuffer buffer = lManager.getTextFileBuffer(file.getFullPath(),
+ LocationKind.NORMALIZE);
+ IDocument lDocument = buffer.getDocument();
+ Change layoutChange = new AndroidLayoutChange(file, lDocument, lManager,
+ fileChange.getChanges());
+ if (layoutChange != null) {
+ result.add(layoutChange);
+ }
+ }
+ }
+ return (result.getChildren().length == 0) ? null : result;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * getName()
+ */
+ @Override
+ public String getName() {
+ return "Android Type Rename";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant#
+ * initialize(java.lang.Object)
+ */
+ @Override
+ protected boolean initialize(Object element) {
+
+ if (element instanceof IType) {
+ IType type = (IType) element;
+ IJavaProject javaProject = (IJavaProject) type.getAncestor(IJavaElement.JAVA_PROJECT);
+ IProject project = javaProject.getProject();
+ IResource manifestResource = project.findMember(AndroidConstants.WS_SEP
+ + SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ if (manifestResource == null || !manifestResource.exists()
+ || !(manifestResource instanceof IFile)) {
+ RefactoringUtil.logInfo("Invalid or missing the "
+ + SdkConstants.FN_ANDROID_MANIFEST_XML + " in the " + project.getName()
+ + " project.");
+ return false;
+ }
+ mAndroidManifest = (IFile) manifestResource;
+ ManifestData manifestData;
+ manifestData = AndroidManifestHelper.parseForData(mAndroidManifest);
+ if (manifestData == null) {
+ return false;
+ }
+ mAppPackage = manifestData.getPackage();
+ mOldName = type.getFullyQualifiedName();
+ String packageName = type.getPackageFragment().getElementName();
+ mNewName = getArguments().getNewName();
+ if (packageName != null) {
+ mLayoutNewName = packageName + "." + getArguments().getNewName(); //$NON-NLS-1$
+ } else {
+ mLayoutNewName = getArguments().getNewName();
+ }
+ if (mOldName == null || mNewName == null) {
+ return false;
+ }
+ if (!RefactoringUtil.isRefactorAppPackage() && mNewName.indexOf(".") == -1) { //$NON-NLS-1$
+ mNewName = packageName + "." + mNewName; //$NON-NLS-1$
+ }
+ mAndroidElements = addAndroidElements();
+ try {
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(null);
+ if (typeHierarchy == null) {
+ return false;
+ }
+ IType[] superTypes = typeHierarchy.getAllSuperclasses(type);
+ for (int i = 0; i < superTypes.length; i++) {
+ IType superType = superTypes[i];
+ String className = superType.getFullyQualifiedName();
+ if (className.equals(SdkConstants.CLASS_VIEW)) {
+ addLayoutChanges(project, type.getFullyQualifiedName());
+ break;
+ }
+ }
+ } catch (JavaModelException ignore) {
+ }
+
+ return mAndroidElements.size() > 0 || mFileChanges.size() > 0;
+ }
+ return false;
+ }
+
+ /**
+ * (non-Javadoc) Adds layout changes for project
+ *
+ * @param project the Android project
+ * @param classNames the layout classes
+ *
+ */
+ private void addLayoutChanges(IProject project, String className) {
+ try {
+ IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES);
+ IFolder layoutFolder = resFolder.getFolder(SdkConstants.FD_LAYOUT);
+ IResource[] members = layoutFolder.members();
+ for (int i = 0; i < members.length; i++) {
+ IResource member = members[i];
+ if ((member instanceof IFile) && member.exists()) {
+ IFile file = (IFile) member;
+ Set<AndroidLayoutChangeDescription> changes = parse(file, className);
+ if (changes.size() > 0) {
+ AndroidLayoutFileChanges fileChange = new AndroidLayoutFileChanges(file);
+ fileChange.getChanges().addAll(changes);
+ mFileChanges.add(fileChange);
+ }
+ }
+ }
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ }
+ }
+
+ /**
+ * (non-Javadoc) Searches the layout file for classes
+ *
+ * @param file the Android layout file
+ * @param classNames the layout classes
+ *
+ */
+ private Set<AndroidLayoutChangeDescription> parse(IFile file, String className) {
+ Set<AndroidLayoutChangeDescription> changes = new HashSet<AndroidLayoutChangeDescription>();
+ ITextFileBufferManager lManager = null;
+ try {
+ lManager = FileBuffers.getTextFileBufferManager();
+ lManager.connect(file.getFullPath(), LocationKind.NORMALIZE, new NullProgressMonitor());
+ ITextFileBuffer buffer = lManager.getTextFileBuffer(file.getFullPath(),
+ LocationKind.NORMALIZE);
+ IDocument lDocument = buffer.getDocument();
+ IStructuredModel model = null;
+ try {
+ model = StructuredModelManager.getModelManager().getExistingModelForRead(lDocument);
+ if (model == null) {
+ if (lDocument instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) lDocument;
+ model = StructuredModelManager.getModelManager().getModelForRead(
+ structuredDocument);
+ }
+ }
+ if (model != null) {
+ IDOMModel xmlModel = (IDOMModel) model;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ NodeList nodes = xmlDoc
+ .getElementsByTagName(IConstants.ANDROID_LAYOUT_VIEW_ELEMENT);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Node attributeNode = attributes
+ .getNamedItem(IConstants.ANDROID_LAYOUT_CLASS_ARGUMENT);
+ if (attributeNode != null || attributeNode instanceof Attr) {
+ Attr attribute = (Attr) attributeNode;
+ String value = attribute.getValue();
+ if (value != null && value.equals(className)) {
+ AndroidLayoutChangeDescription layoutChange =
+ new AndroidLayoutChangeDescription(className, mLayoutNewName,
+ AndroidLayoutChangeDescription.VIEW_TYPE);
+ changes.add(layoutChange);
+ }
+ }
+ }
+ }
+ nodes = xmlDoc.getElementsByTagName(className);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ AndroidLayoutChangeDescription layoutChange =
+ new AndroidLayoutChangeDescription(className, mLayoutNewName,
+ AndroidLayoutChangeDescription.STANDALONE_TYPE);
+ changes.add(layoutChange);
+ }
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ } catch (CoreException ignore) {
+ } finally {
+ if (lManager != null) {
+ try {
+ lManager.disconnect(file.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException ignore) {
+ }
+ }
+ }
+ return changes;
+ }
+
+ /**
+ * (non-Javadoc) Returns the elements (activity, receiver, service ...)
+ * which have to be renamed
+ *
+ * @return the android elements
+ *
+ */
+ private Map<String, String> addAndroidElements() {
+ Map<String, String> androidElements = new HashMap<String, String>();
+
+ IDocument document;
+ try {
+ document = getDocument();
+ } catch (CoreException e) {
+ RefactoringUtil.log(e);
+ if (mManager != null) {
+ try {
+ mManager.disconnect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE,
+ new NullProgressMonitor());
+ } catch (CoreException e1) {
+ RefactoringUtil.log(e1);
+ }
+ }
+ document = null;
+ return androidElements;
+ }
+
+ IStructuredModel model = null;
+ try {
+ model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+ if (model == null) {
+ if (document instanceof IStructuredDocument) {
+ IStructuredDocument structuredDocument = (IStructuredDocument) document;
+ model = StructuredModelManager.getModelManager().getModelForRead(
+ structuredDocument);
+ }
+ }
+ if (model != null) {
+ IDOMModel xmlModel = (IDOMModel) model;
+ IDOMDocument xmlDoc = xmlModel.getDocument();
+ add(xmlDoc, androidElements, AndroidManifest.NODE_ACTIVITY,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_APPLICATION,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_PROVIDER,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_RECEIVER,
+ AndroidManifest.ATTRIBUTE_NAME);
+ add(xmlDoc, androidElements, AndroidManifest.NODE_SERVICE,
+ AndroidManifest.ATTRIBUTE_NAME);
+ }
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+
+ return androidElements;
+ }
+
+ /**
+ * (non-Javadoc) Adds the element (activity, receiver, service ...) to the map
+ *
+ * @param xmlDoc the document
+ * @param androidElements the map
+ * @param element the element
+ */
+ private void add(IDOMDocument xmlDoc, Map<String, String> androidElements, String element,
+ String argument) {
+ NodeList nodes = xmlDoc.getElementsByTagName(element);
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes != null) {
+ Attr attribute = RefactoringUtil.findAndroidAttributes(attributes, argument);
+ if (attribute != null) {
+ String value = attribute.getValue();
+ if (value != null) {
+ String fullName = AndroidManifest.combinePackageAndClassName(mAppPackage,
+ value);
+ if (fullName != null && fullName.equals(mOldName)) {
+ androidElements.put(element, value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/FixImportsJob.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/FixImportsJob.java
new file mode 100755
index 0000000..4a84bc2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/FixImportsJob.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.core;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.WorkspaceJob;
+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.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.search.TypeNameMatch;
+import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
+import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation;
+import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation.IChooseImportQuery;
+import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter;
+import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
+import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
+import org.eclipse.jdt.ui.SharedASTProvider;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.IProgressService;
+
+/**
+ * The helper class which fixes the import errors after refactoring
+ *
+ */
+public class FixImportsJob extends WorkspaceJob {
+
+ private IFile mAndroidManifest;
+
+ private String mJavaPackage;
+
+ /**
+ * Creates a new <code>FixImportsJob</code>
+ *
+ * @param name the job name
+ * @param androidManifest the android manifest file
+ * @param javaPackage the android java package
+ */
+ public FixImportsJob(String name, IFile androidManifest, String javaPackage) {
+ super(name);
+ this.mAndroidManifest = androidManifest;
+ this.mJavaPackage = javaPackage;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.core.resources.WorkspaceJob#runInWorkspace(org.eclipse.core
+ * .runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runInWorkspace(final IProgressMonitor monitor) throws CoreException {
+ if (mJavaPackage == null || mAndroidManifest == null || !mAndroidManifest.exists()) {
+ return Status.CANCEL_STATUS;
+ }
+ IProject project = mAndroidManifest.getProject();
+ IJavaProject javaProject = JavaCore.create(project);
+ if (javaProject == null || !javaProject.isOpen()) {
+ return Status.CANCEL_STATUS;
+ }
+ project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
+ IMarker[] markers = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
+ for (int i = 0; i < markers.length; i++) {
+ IMarker marker = markers[i];
+ IResource resource = marker.getResource();
+ try {
+ IJavaElement element = JavaCore.create(resource);
+ if (element != null && (element instanceof ICompilationUnit)) {
+ final ICompilationUnit cu = (ICompilationUnit) element;
+ IPackageFragment packageFragment = (IPackageFragment) cu
+ .getAncestor(IJavaElement.PACKAGE_FRAGMENT);
+ if (packageFragment != null && packageFragment.exists()) {
+ String packageName = packageFragment.getElementName();
+ if (packageName != null && packageName.startsWith(mJavaPackage)) {
+ CompilationUnit astRoot = SharedASTProvider.getAST(cu,
+ SharedASTProvider.WAIT_ACTIVE_ONLY, null);
+ CodeGenerationSettings settings = JavaPreferencesSettings
+ .getCodeGenerationSettings(cu.getJavaProject());
+ final boolean hasAmbiguity[] = new boolean[] {
+ false
+ };
+ IChooseImportQuery query = new IChooseImportQuery() {
+ public TypeNameMatch[] chooseImports(TypeNameMatch[][] openChoices,
+ ISourceRange[] ranges) {
+ hasAmbiguity[0] = true;
+ return new TypeNameMatch[0];
+ }
+ };
+ final OrganizeImportsOperation op = new OrganizeImportsOperation(cu,
+ astRoot, settings.importIgnoreLowercase, !cu.isWorkingCopy(),
+ true, query);
+ Display.getDefault().asyncExec(new Runnable() {
+
+ public void run() {
+ try {
+ IProgressService progressService = PlatformUI
+ .getWorkbench().getProgressService();
+ progressService.run(
+ true,
+ true,
+ new WorkbenchRunnableAdapter(op, op
+ .getScheduleRule()));
+ IEditorPart openEditor = EditorUtility.isOpenInEditor(cu);
+ if (openEditor != null) {
+ openEditor.doSave(monitor);
+ }
+ } catch (Throwable e) {
+ RefactoringUtil.log(e);
+ }
+ }
+ });
+
+ }
+ }
+ }
+ } catch (Throwable e) {
+ RefactoringUtil.log(e);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/IConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/IConstants.java
new file mode 100755
index 0000000..218ed99
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/IConstants.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.core;
+
+/**
+ * Declares some constants used in refactoring
+ *
+ */
+public interface IConstants {
+
+ /**
+ * the android view argument
+ */
+ static final String ANDROID_LAYOUT_VIEW_ELEMENT = "view"; //$NON-NLS-1$
+
+ /**
+ * the android class argument
+ */
+ static final String ANDROID_LAYOUT_CLASS_ARGUMENT = "class"; //$NON-NLS-1$
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java
new file mode 100644
index 0000000..c75dd0f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactoring/core/RefactoringUtil.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.refactoring.core;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * The utility class for android refactoring
+ *
+ */
+public class RefactoringUtil {
+
+ private static boolean sRefactorAppPackage = false;
+
+ /**
+ * Returns the new class name combined with a package name
+ * the oldName and newName are class names as found in the manifest
+ * (for instance with a leading dot or with a single element,
+ * that needs to be recombined with a package name)
+ *
+ * @param javaPackage the package name
+ * @param oldName the old name
+ * @param newName the new name
+ *
+ * @return the new name
+ */
+ public static String getNewValue(String javaPackage, String oldName, String newName) {
+ if (oldName == null || oldName.length() == 0) {
+ return null;
+ }
+ if (javaPackage == null || javaPackage.length() == 0) {
+ return null;
+ }
+ if (newName == null || newName.length() == 0) {
+ return null;
+ }
+ if (!newName.startsWith(javaPackage + ".")) { //$NON-NLS-1$
+ return newName;
+ } else if (newName.length() > (javaPackage.length() + 1)) {
+ String value = newName.substring(javaPackage.length() + 1);
+ return value;
+ }
+ boolean startWithDot = (oldName.charAt(0) == '.');
+ boolean hasDot = (oldName.indexOf('.') != -1);
+ if (startWithDot || !hasDot) {
+
+ if (startWithDot) {
+ return "." + newName;
+ } else {
+ int lastPeriod = newName.lastIndexOf(".");
+ return newName.substring(lastPeriod + 1);
+ }
+ } else {
+ return newName;
+ }
+ }
+
+ /**
+ * Releases SSE read model; saves SSE model if exists edit model
+ * Called in dispose method of refactoring change classes
+ *
+ * @param model the SSE model
+ * @param document the document
+ */
+ public static void fixModel(IStructuredModel model, IDocument document) {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ model = null;
+ if (document == null) {
+ return;
+ }
+ try {
+ model = StructuredModelManager.getModelManager().getExistingModelForEdit(document);
+ if (model != null) {
+ model.save();
+ }
+ } catch (UnsupportedEncodingException e1) {
+ // ignore
+ } catch (IOException e1) {
+ // ignore
+ } catch (CoreException e1) {
+ // ignore
+ } finally {
+ if (model != null) {
+ model.releaseFromEdit();
+ }
+ }
+ }
+
+ /**
+ * Finds attribute by name in android namespace
+ *
+ * @param attributes the attributes collection
+ * @param localName the local part of the qualified name
+ *
+ * @return the first attribute with this name in android namespace
+ */
+ public static Attr findAndroidAttributes(final NamedNodeMap attributes,
+ final String localName) {
+ Attr attribute = null;
+ for (int j = 0; j < attributes.getLength(); j++) {
+ Node attNode = attributes.item(j);
+ if (attNode != null || attNode instanceof Attr) {
+ Attr attr = (Attr) attNode;
+ String name = attr.getLocalName();
+ String namespace = attr.getNamespaceURI();
+ if (SdkConstants.NS_RESOURCES.equals(namespace)
+ && name != null
+ && name.equals(localName)) {
+ attribute = attr;
+ break;
+ }
+ }
+ }
+ return attribute;
+ }
+
+ /**
+ * Logs the error message
+ *
+ * @param message the message
+ */
+ public static void logError(String message) {
+ AdtPlugin.log(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message);
+ }
+
+ /**
+ * Logs the info message
+ *
+ * @param message the message
+ */
+ public static void logInfo(String message) {
+ AdtPlugin.log(IStatus.INFO, AdtPlugin.PLUGIN_ID, message);
+ }
+
+ /**
+ * Logs the the exception
+ *
+ * @param e the exception
+ */
+ public static void log(Throwable e) {
+ AdtPlugin.log(e, e.getMessage());
+ }
+
+ /**
+ * @return true if Rename/Move package needs to change the application package
+ * default is false
+ *
+ */
+ public static boolean isRefactorAppPackage() {
+ return sRefactorAppPackage;
+ }
+
+ /**
+ * @param refactorAppPackage true if Rename/Move package needs to change the application package
+ */
+ public static void setRefactorAppPackage(boolean refactorAppPackage) {
+ RefactoringUtil.sRefactorAppPackage = refactorAppPackage;
+ }
+}