diff options
author | Tor Norbye <tnorbye@google.com> | 2012-12-03 11:09:09 -0800 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2012-12-03 11:09:09 -0800 |
commit | c3b3bb82b50c5fcc81aeca72ea9043aa756cc69c (patch) | |
tree | 2d5290e42a015be93bbb20a0ebc567db1262def5 /eclipse/plugins | |
parent | bb36284a03dc87d35aff13f66c6f49bd3d050ddf (diff) | |
parent | 32654dfbd134559fbf6bec4542d6a4a8105897c9 (diff) | |
download | sdk-c3b3bb82b50c5fcc81aeca72ea9043aa756cc69c.zip sdk-c3b3bb82b50c5fcc81aeca72ea9043aa756cc69c.tar.gz sdk-c3b3bb82b50c5fcc81aeca72ea9043aa756cc69c.tar.bz2 |
Merge "Fix rename type, rename package and move type refactorings"
Diffstat (limited to 'eclipse/plugins')
20 files changed, 1028 insertions, 2200 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 3745613..67bd705 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -104,7 +104,6 @@ Export-Package: com.android.assetstudiolib;x-friends:="com.android.ide.eclipse.t com.android.ide.eclipse.adt.internal.preferences;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.project;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.properties;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.adt.internal.refactorings.changes;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.refactorings.core;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.refactorings.extractstring;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.internal.refactorings.renamepackage;x-friends:="com.android.ide.eclipse.tests", diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java index 94bd058..b79e3b0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java @@ -56,7 +56,6 @@ import java.util.Map; /** * Design-time metadata lookup for layouts, such as fragment and AdapterView bindings. */ -@SuppressWarnings("restriction") // XML DOM model public class LayoutMetadata { /** The default layout to use for list items in expandable list views */ public static final String DEFAULT_EXPANDABLE_LIST_ITEM = "simple_expandable_list_item_2"; //$NON-NLS-1$ @@ -75,6 +74,8 @@ public class LayoutMetadata { public static final String KEY_LV_FOOTER = "listfooter"; //$NON-NLS-1$ /** The property key, included in comments, which references a fragment layout to show */ public static final String KEY_FRAGMENT_LAYOUT = "layout"; //$NON-NLS-1$ + // NOTE: If you add additional keys related to resources, make sure you update the + // ResourceRenameParticipant /** Utility class, do not create instances */ private LayoutMetadata() { @@ -191,9 +192,6 @@ public class LayoutMetadata { return; } Display display = AdtPlugin.getDisplay(); - if (display == null) { - return; - } WorkbenchJob job = new WorkbenchJob(display, "Update alternate views") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidDocumentChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidDocumentChange.java deleted file mode 100644 index eb8ccc7..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidDocumentChange.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * 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.refactorings.changes; - -import com.android.SdkConstants; -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.refactorings.core.RefactoringUtil; -import com.android.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.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 -*/ -@SuppressWarnings("restriction") -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); - } - - /** - * Returns the manifest file - * - * @return the manifest - */ - public IFile getManifest() { - return mAndroidManifest; - } - - @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) { - 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 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; - // Get the current "region" (the current attribute, including its quotes.) - String region = domAttr.getValueRegionText(); - int offset = domAttr.getValueRegionStartOffset(); - if (region != null && region.length() >= 2) { - // Skip the quotes when replacing. - ReplaceEdit edit = new ReplaceEdit(offset + 1, region.length() - 2, newValue); - return edit; - } - } - 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 oldName the old value - * @param newName 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 = newName; - if (combinePackage) { - String pkg = getAppPackage(); - if (oldName.startsWith(pkg) && newName.startsWith(pkg)) { - // Heuristic: if the old value is a fully qualified name, then - // assume that's how the user wants it in the manifest and - // do *not* shorten the new value. Keep it fully qualified too. - } else { - newValue = AndroidManifest.extractActivityName(newName, pkg); - } - } - 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/refactorings/changes/AndroidLayoutChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidLayoutChange.java deleted file mode 100644 index 7bac6d7..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidLayoutChange.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * 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.refactorings.changes; - -import com.android.SdkConstants; -import com.android.ide.eclipse.adt.internal.refactorings.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.IModelManager; -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.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 -*/ -@SuppressWarnings("restriction") -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 - */ - public AndroidLayoutChange(IFile file, IDocument document, ITextFileBufferManager manager, - Set<AndroidLayoutChangeDescription> changes) { - super("", document); //$NON-NLS-1$ - mFile = file; - mDocument = document; - mManager = manager; - mChanges = changes; - try { - this.mModel = getModel(document); - } catch (Exception ignore) { - } - if (mModel != null) { - addEdits(); - } - } - - public IFile getFile() { - return mFile; - } - - @Override - public String getName() { - return mFile.getName(); - } - - @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; - } - - @Override - public void setTextType(String type) { - super.setTextType(mFile.getFileExtension()); - } - - @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); - } - } - } - - // ---- - - /** - * Adds text edits for this change - */ - private void addEdits() { - MultiTextEdit multiEdit = new MultiTextEdit(); - for (AndroidLayoutChangeDescription change : mChanges) { - if (!change.isStandalone()) { - TextEdit edit = createTextEdit(SdkConstants.VIEW, - SdkConstants.ATTR_CLASS, - 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); - } - - /** - * 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 - */ - private 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 - */ - private 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 argumentName the attribute name - * @param oldName the old value - * @param newName the new value - * - * @return the text change - */ - private 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; - } - - /** - * 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 - */ - private 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; - } - - /** - * Returns the SSE model for a document - * - * @param document the document - * @return the model - */ - private IStructuredModel getModel(IDocument document) { - - IModelManager manager = StructuredModelManager.getModelManager(); - IStructuredModel model = manager.getExistingModelForRead(document); - if (model == null && document instanceof IStructuredDocument) { - model = manager.getModelForRead((IStructuredDocument) document); - } - - return model; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidLayoutChangeDescription.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidLayoutChangeDescription.java deleted file mode 100644 index 2ddaac0..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidLayoutChangeDescription.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.refactorings.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; - } - - @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; - } - - @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; - } - - @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/refactorings/changes/AndroidLayoutFileChanges.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidLayoutFileChanges.java deleted file mode 100644 index 1d4a741..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidLayoutFileChanges.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.refactorings.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/refactorings/changes/AndroidPackageRenameChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidPackageRenameChange.java deleted file mode 100644 index 08e4f01..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidPackageRenameChange.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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.refactorings.changes; - -import com.android.ide.eclipse.adt.internal.refactorings.core.FixImportsJob; -import com.android.ide.eclipse.adt.internal.refactorings.core.RefactoringUtil; -import com.android.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 oldName, String newName, - 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); - } - - @Override - public Change perform(IProgressMonitor pm) throws CoreException { - super.perform(pm); - return new AndroidPackageRenameChange(mAndroidManifest, mManager, mDocument, mElements, - mOldName, mNewName, mIsPackage); - } - - @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/refactorings/changes/AndroidTypeMoveChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidTypeMoveChange.java deleted file mode 100644 index 618500d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidTypeMoveChange.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.refactorings.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 - */ - public AndroidTypeMoveChange(IFile androidManifest, ITextFileBufferManager manager, - IDocument document, Map<String, String> elements, String oldName, String newName) { - super(androidManifest, manager, document, elements, oldName, newName); - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidTypeRenameChange.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidTypeRenameChange.java deleted file mode 100644 index 275d412..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/changes/AndroidTypeRenameChange.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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.refactorings.changes; - -import com.android.ide.eclipse.adt.internal.refactorings.core.RefactoringUtil; -import com.android.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 - */ - public AndroidTypeRenameChange(IFile androidManifest, ITextFileBufferManager manager, - IDocument document, Map<String, String> elements, String oldName, String newName) { - super(document); - this.mDocument = document; - this.mElements = elements; - this.mOldName = oldName; - this.mNewName = newName; - this.mManager = manager; - this.mAndroidManifest = androidManifest; - 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(); - 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); - } - - @Override - public Change perform(IProgressMonitor pm) throws CoreException { - super.perform(pm); - return new AndroidTypeRenameChange(mAndroidManifest, mManager, mDocument, mElements, - mOldName, mNewName); - } - - @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/refactorings/core/AndroidPackageRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidPackageRenameParticipant.java index bd3224d..67f7d44 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidPackageRenameParticipant.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidPackageRenameParticipant.java @@ -16,20 +16,26 @@ package com.android.ide.eclipse.adt.internal.refactorings.core; +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.ATTR_CLASS; +import static com.android.SdkConstants.ATTR_CONTEXT; +import static com.android.SdkConstants.ATTR_NAME; +import static com.android.SdkConstants.ATTR_PACKAGE; +import static com.android.SdkConstants.DOT_XML; +import static com.android.SdkConstants.EXT_XML; +import static com.android.SdkConstants.TOOLS_URI; +import static com.android.SdkConstants.VIEW_FRAGMENT; +import static com.android.SdkConstants.VIEW_TAG; + import com.android.SdkConstants; +import com.android.annotations.NonNull; import com.android.ide.common.xml.ManifestData; import com.android.ide.eclipse.adt.AdtConstants; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutChange; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutChangeDescription; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutFileChanges; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidPackageRenameChange; -import com.android.xml.AndroidManifest; - -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 com.android.resources.ResourceFolderType; +import com.android.utils.SdkUtils; + import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; @@ -37,34 +43,39 @@ 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.ITypeHierarchy; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.corext.refactoring.changes.RenamePackageChange; -import org.eclipse.jface.text.IDocument; +import org.eclipse.jdt.internal.corext.refactoring.rename.RenameCompilationUnitProcessor; +import org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeProcessor; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; +import org.eclipse.ltk.core.refactoring.NullChange; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.TextFileChange; +import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; +import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor; +import org.eclipse.ltk.core.refactoring.participants.RenameParticipant; +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.IModelManager; 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.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import java.io.IOException; 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. @@ -78,13 +89,81 @@ import java.util.Set; * </p> */ @SuppressWarnings("restriction") -public class AndroidPackageRenameParticipant extends AndroidRenameParticipant { +public class AndroidPackageRenameParticipant extends RenameParticipant { + private IProject mProject; + private IFile mManifestFile; private IPackageFragment mPackageFragment; + private String mOldPackage; + private String mNewPackage; + private String mAppPackage; + private boolean mRefactoringAppPackage; - private boolean mIsPackage; + @Override + public String getName() { + return "Android Package Rename"; + } + + @Override + public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) + throws OperationCanceledException { + return new RefactoringStatus(); + } + + @Override + protected boolean initialize(final Object element) { + mRefactoringAppPackage = false; + try { + // Only propose this refactoring if the "Update References" checkbox is set. + if (!getArguments().getUpdateReferences()) { + return false; + } + + if (element instanceof IPackageFragment) { + mPackageFragment = (IPackageFragment) element; + if (!mPackageFragment.containsJavaResources()) { + return false; + } + IJavaProject javaProject = (IJavaProject) mPackageFragment + .getAncestor(IJavaElement.JAVA_PROJECT); + mProject = javaProject.getProject(); + IResource manifestResource = mProject.findMember(AdtConstants.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 " + + mProject.getName() + " project."); + return false; + } + mManifestFile = (IFile) manifestResource; + String packageName = mPackageFragment.getElementName(); + ManifestData manifestData; + manifestData = AndroidManifestHelper.parseForData(mManifestFile); + if (manifestData == null) { + return false; + } + mAppPackage = manifestData.getPackage(); + mOldPackage = packageName; + mNewPackage = getArguments().getNewName(); + if (mOldPackage == null || mNewPackage == null) { + return false; + } + + if (RefactoringUtil.isRefactorAppPackage() + && mAppPackage != null + && mAppPackage.equals(packageName)) { + mRefactoringAppPackage = true; + } + + return true; + } + } catch (JavaModelException ignore) { + } + return false; + } - private Set<AndroidLayoutFileChanges> mFileChanges = new HashSet<AndroidLayoutFileChanges>(); @Override public Change createChange(IProgressMonitor pm) throws CoreException, @@ -92,50 +171,61 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant { if (pm.isCanceled()) { return null; } - if (!getArguments().getUpdateReferences()) + if (!getArguments().getUpdateReferences()) { return null; + } + + RefactoringProcessor p = getProcessor(); + if (p instanceof RenameCompilationUnitProcessor) { + RenameTypeProcessor rtp = + ((RenameCompilationUnitProcessor) p).getRenameTypeProcessor(); + if (rtp != null) { + String pattern = rtp.getFilePatterns(); + boolean updQualf = rtp.getUpdateQualifiedNames(); + if (updQualf && pattern != null && pattern.contains("xml")) { //$NON-NLS-1$ + // Do not propose this refactoring if the + // "Update fully qualified names in non-Java files" option is + // checked and the file patterns mention XML. [c.f. SDK bug 21589] + 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); + IPath genPath = mProject.getFullPath().append(SdkConstants.FD_GEN_SOURCES); if (genPath.isPrefixOf(pkgPath)) { RefactoringUtil.logInfo(getName() + ": Cannot rename generated package."); return null; } CompositeChange result = new CompositeChange(getName()); result.markAsSynthetic(); - if (mAndroidManifest.exists()) { - if (mAndroidElements.size() > 0 || mIsPackage) { - getManifestDocument(); - Change change = new AndroidPackageRenameChange(mAndroidManifest, mManager, - mDocument, mAndroidElements, mOldName, mNewName, mIsPackage); - if (change != null) { - result.add(change); - } - } - if (mIsPackage) { - Change genChange = getGenPackageChange(pm); - if (genChange != null) { - result.add(genChange); - } + + addManifestFileChanges(result); + + // Update layout files; we don't just need to react to custom view + // changes, we need to update fragment references and even tool:context activity + // references + addLayoutFileChanges(result); + + if (mRefactoringAppPackage) { + 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 new NullChange("Update Imports") { + @Override + public Change perform(IProgressMonitor monitor) throws CoreException { + FixImportsJob job = new FixImportsJob("Fix Rename Package", + mManifestFile, mNewPackage); + job.schedule(500); + + // Not undoable: just return null instead of an undo-change. + return null; } - } + }; } + return (result.getChildren().length == 0) ? null : result; } @@ -145,15 +235,15 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant { * @param pm the progress monitor * * @return Android gen package text change - * @throws CoreException - * @throws OperationCanceledException + * @throws CoreException if an error happens + * @throws OperationCanceledException if the operation is canceled */ public Change getGenPackageChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { - if (mIsPackage) { + if (mRefactoringAppPackage) { IPackageFragment genJavaPackageFragment = getGenPackageFragment(); if (genJavaPackageFragment != null && genJavaPackageFragment.exists()) { - return new RenamePackageChange(genJavaPackageFragment, mNewName, true); + return new RenamePackageChange(genJavaPackageFragment, mNewPackage, true); } } return null; @@ -161,7 +251,6 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant { /** * Return the gen package fragment - * */ private IPackageFragment getGenPackageFragment() throws JavaModelException { IJavaProject javaProject = (IJavaProject) mPackageFragment @@ -170,7 +259,7 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant { IProject project = javaProject.getProject(); IFolder genFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES); if (genFolder.exists()) { - String javaPackagePath = mAppPackage.replace(".", "/"); + String javaPackagePath = mAppPackage.replace('.', '/'); IPath genJavaPackagePath = genFolder.getFullPath().append(javaPackagePath); IPackageFragment genPackageFragment = javaProject .findPackageFragment(genJavaPackagePath); @@ -180,123 +269,53 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant { return null; } - @Override - public String getName() { - return "Android Package Rename"; + /** + * Returns the new class name + * + * @param fqcn the fully qualified class name in the renamed package + * @return the new class name + */ + private String getNewClassName(String fqcn) { + assert isInRenamedPackage(fqcn) : fqcn; + int lastDot = fqcn.lastIndexOf('.'); + if (lastDot < 0) { + return mNewPackage; + } + String name = fqcn.substring(lastDot, fqcn.length()); + String newClassName = mNewPackage + name; + return newClassName; } - @Override - protected boolean initialize(final Object element) { - mIsPackage = false; + private void addManifestFileChanges(CompositeChange result) { + addXmlFileChanges(mManifestFile, result, true); + } + + private void addLayoutFileChanges(CompositeChange result) { 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(AdtConstants.WS_SEP - + SdkConstants.FN_ANDROID_MANIFEST_XML); + // Update references in XML resource files + IFolder resFolder = mProject.getFolder(SdkConstants.FD_RESOURCES); - 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; + IResource[] folders = resFolder.members(); + for (IResource folder : folders) { + String folderName = folder.getName(); + ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName); + if (folderType != ResourceFolderType.LAYOUT) { + continue; } - 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 (!(folder instanceof IFolder)) { + continue; } + IResource[] files = ((IFolder) folder).members(); + for (int i = 0; i < files.length; i++) { + IResource member = files[i]; + if ((member instanceof IFile) && member.exists()) { + IFile file = (IFile) member; + String fileName = member.getName(); - if (RefactoringUtil.isRefactorAppPackage() - && mAppPackage != null - && mAppPackage.equals(packageName)) { - mIsPackage = true; - } - mAndroidElements = addAndroidElements(); - - try { - final IType type = javaProject.findType(SdkConstants.CLASS_VIEW); - final HashSet<IType> elements = new HashSet<IType>(); - if (type != null) { - ITypeHierarchy hierarchy = type.newTypeHierarchy( - new NullProgressMonitor()); - IType[] allSubtypes = hierarchy.getAllSubtypes(type); - for (IType subType : allSubtypes) { - IResource resource = subType.getResource(); - // TODO: Handle library project downstream dependencies! - if (resource != null && project.equals(resource.getProject())) { - if (subType.getPackageFragment().equals(mPackageFragment)) { - elements.add(subType); - } - } + if (SdkUtils.endsWith(fileName, DOT_XML)) { + addXmlFileChanges(file, result, false); } } - - 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; - } - - /** - * 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_RES_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) { @@ -304,211 +323,164 @@ public class AndroidPackageRenameParticipant extends AndroidRenameParticipant { } } - /** - * 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; + private boolean addXmlFileChanges(IFile file, CompositeChange changes, boolean isManifest) { + IModelManager modelManager = StructuredModelManager.getModelManager(); + IStructuredModel model = 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(SdkConstants.VIEW); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - NamedNodeMap attributes = node.getAttributes(); - if (attributes != null) { - Node attributeNode = attributes - .getNamedItem(SdkConstants.ATTR_CLASS); - if (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); - } - } - } - } + model = modelManager.getExistingModelForRead(file); + if (model == null) { + model = modelManager.getModelForRead(file); + } + if (model != null) { + IStructuredDocument document = model.getStructuredDocument(); + if (model instanceof IDOMModel) { + IDOMModel domModel = (IDOMModel) model; + Element root = domModel.getDocument().getDocumentElement(); + if (root != null) { + List<TextEdit> edits = new ArrayList<TextEdit>(); + if (isManifest) { + addManifestReplacements(edits, root, document); + } else { + addLayoutReplacements(edits, root, document); } - } - 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); + if (!edits.isEmpty()) { + MultiTextEdit rootEdit = new MultiTextEdit(); + rootEdit.addChildren(edits.toArray(new TextEdit[edits.size()])); + TextFileChange change = new TextFileChange(file.getName(), file); + change.setTextType(EXT_XML); + change.setEdit(rootEdit); + changes.add(change); } } - } - } finally { - if (model != null) { - model.releaseFromRead(); + } else { + return false; } } - } catch (CoreException ignore) { + return true; + } catch (IOException e) { + AdtPlugin.log(e, null); + } catch (CoreException e) { + AdtPlugin.log(e, null); } finally { - if (lManager != null) { - try { - lManager.disconnect(file.getFullPath(), LocationKind.NORMALIZE, - new NullProgressMonitor()); - } catch (CoreException ignore) { - } + if (model != null) { + model.releaseFromRead(); } } - return changes; - } - /** - * 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; + return false; } - /** - * 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>(); + private boolean isInRenamedPackage(String fqcn) { + return fqcn.startsWith(mOldPackage) + && fqcn.length() > mOldPackage.length() + && fqcn.indexOf('.', mOldPackage.length() + 1) == -1; + } - IDocument document; - try { - document = getManifestDocument(); - } catch (CoreException e) { - RefactoringUtil.log(e); - if (mManager != null) { - try { - mManager.disconnect(mAndroidManifest.getFullPath(), LocationKind.NORMALIZE, - new NullProgressMonitor()); - } catch (CoreException e1) { - RefactoringUtil.log(e1); + private void addLayoutReplacements( + @NonNull List<TextEdit> edits, + @NonNull Element element, + @NonNull IStructuredDocument document) { + String tag = element.getTagName(); + if (isInRenamedPackage(tag)) { + int start = RefactoringUtil.getTagNameRangeStart(element, document); + if (start != -1) { + int end = start + tag.length(); + edits.add(new ReplaceEdit(start, end - start, getNewClassName(tag))); + } + } else { + Attr classNode = null; + if (tag.equals(VIEW_TAG)) { + classNode = element.getAttributeNode(ATTR_CLASS); + } else if (tag.equals(VIEW_FRAGMENT)) { + classNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); + } else if (element.hasAttributeNS(TOOLS_URI, ATTR_CONTEXT)) { + classNode = element.getAttributeNodeNS(TOOLS_URI, ATTR_CONTEXT); + if (classNode != null && classNode.getValue().startsWith(".")) { //$NON-NLS-1$ + classNode = null; } } - 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 (classNode != null) { + String fqcn = classNode.getValue(); + if (isInRenamedPackage(fqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + fqcn.length(); + edits.add(new ReplaceEdit(start, end - start, getNewClassName(fqcn))); + } } } - 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); + } + + NodeList children = element.getChildNodes(); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + addLayoutReplacements(edits, (Element) child, document); } - } finally { - if (model != null) { - model.releaseFromRead(); + } + } + + private void addManifestReplacements( + @NonNull List<TextEdit> edits, + @NonNull Element element, + @NonNull IStructuredDocument document) { + if (mRefactoringAppPackage && + element == element.getOwnerDocument().getDocumentElement()) { + // Update the app package declaration + Attr pkg = element.getAttributeNode(ATTR_PACKAGE); + if (pkg != null && pkg.getValue().equals(mOldPackage)) { + int start = RefactoringUtil.getAttributeValueRangeStart(pkg, document); + if (start != -1) { + int end = start + mOldPackage.length(); + edits.add(new ReplaceEdit(start, end - start, mNewPackage)); + } } } - return androidElements; - } + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0, n = attributes.getLength(); i < n; i++) { + Attr attr = (Attr) attributes.item(i); + if (!RefactoringUtil.isManifestClassAttribute(attr)) { + continue; + } - /** - * 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) { - String currentPackage = mPackageFragment.getElementName(); - if (fullName.lastIndexOf('.') == currentPackage.length() - && fullName.startsWith(currentPackage)) { - androidElements.put(element, value); - } - } + String value = attr.getValue(); + if (isInRenamedPackage(value)) { + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); + if (start != -1) { + int end = start + value.length(); + edits.add(new ReplaceEdit(start, end - start, getNewClassName(value))); + } + } else if (value.startsWith(".")) { + // If we're renaming the app package + String fqcn = mAppPackage + value; + if (isInRenamedPackage(fqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); + if (start != -1) { + int end = start + value.length(); + String newClassName = getNewClassName(fqcn); + if (mRefactoringAppPackage) { + newClassName = newClassName.substring(mNewPackage.length()); + } else if (newClassName.startsWith(mOldPackage) + && newClassName.charAt(mOldPackage.length()) == '.') { + newClassName = newClassName.substring(mOldPackage.length()); + } + + if (!newClassName.equals(value)) { + edits.add(new ReplaceEdit(start, end - start, newClassName)); } } } } } - } + NodeList children = element.getChildNodes(); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + addManifestReplacements(edits, (Element) child, document); + } + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidRenameParticipant.java deleted file mode 100644 index dca43a1..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidRenameParticipant.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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.refactorings.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; - - @Override - public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) - throws OperationCanceledException { - return new RefactoringStatus(); - } - - /** - * @return the document for the {@link #mAndroidManifest} - * @throws CoreException - */ - public IDocument getManifestDocument() 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/refactorings/core/AndroidTypeMoveParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipant.java index 287139a..26ae8d0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipant.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipant.java @@ -16,55 +16,60 @@ package com.android.ide.eclipse.adt.internal.refactorings.core; +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.ATTR_CLASS; +import static com.android.SdkConstants.ATTR_CONTEXT; +import static com.android.SdkConstants.ATTR_NAME; +import static com.android.SdkConstants.DOT_XML; +import static com.android.SdkConstants.EXT_XML; +import static com.android.SdkConstants.TOOLS_URI; +import static com.android.SdkConstants.VIEW_FRAGMENT; +import static com.android.SdkConstants.VIEW_TAG; + import com.android.SdkConstants; +import com.android.annotations.NonNull; import com.android.ide.common.xml.ManifestData; import com.android.ide.eclipse.adt.AdtConstants; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutChange; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutChangeDescription; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutFileChanges; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidTypeMoveChange; import com.android.resources.ResourceFolderType; -import com.android.xml.AndroidManifest; +import com.android.utils.SdkUtils; -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.TextFileChange; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.MoveParticipant; +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.IModelManager; 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.Element; 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; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * A participant to participate in refactorings that move a type in an Android project. @@ -79,21 +84,16 @@ import java.util.Set; @SuppressWarnings("restriction") 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 IProject mProject; + protected IFile mManifestFile; + protected String mOldFqcn; + protected String mNewFqcn; + protected String mAppPackage; - private Set<AndroidLayoutFileChanges> mFileChanges = new HashSet<AndroidLayoutFileChanges>(); + @Override + public String getName() { + return "Android Type Move"; + } @Override public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) @@ -102,107 +102,40 @@ public class AndroidTypeMoveParticipant extends MoveParticipant { } @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; - } - - @Override - public String getName() { - return "Android Type Move"; - } - - @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(AdtConstants.WS_SEP + mProject = javaProject.getProject(); + IResource manifestResource = mProject.findMember(AdtConstants.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() + + SdkConstants.FN_ANDROID_MANIFEST_XML + " in the " + mProject.getName() + " project."); return false; } - mAndroidManifest = (IFile) manifestResource; + mManifestFile = (IFile) manifestResource; ManifestData manifestData; - manifestData = AndroidManifestHelper.parseForData(mAndroidManifest); + manifestData = AndroidManifestHelper.parseForData(mManifestFile); if (manifestData == null) { return false; } - mJavaPackage = manifestData.getPackage(); - mOldName = type.getFullyQualifiedName(); + mAppPackage = manifestData.getPackage(); + mOldFqcn = type.getFullyQualifiedName(); Object destination = getArguments().getDestination(); if (destination instanceof IPackageFragment) { IPackageFragment packageFragment = (IPackageFragment) destination; - mNewName = packageFragment.getElementName() + "." + type.getElementName(); + mNewFqcn = packageFragment.getElementName() + "." + type.getElementName(); } else if (destination instanceof IResource) { try { IPackageFragment[] fragments = javaProject.getPackageFragments(); for (IPackageFragment fragment : fragments) { IResource resource = fragment.getResource(); if (resource.equals(destination)) { - mNewName = fragment.getElementName() + "." + type.getElementName(); + mNewFqcn = fragment.getElementName() + '.' + type.getElementName(); break; } } @@ -210,60 +143,62 @@ public class AndroidTypeMoveParticipant extends MoveParticipant { // pass } } - 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 mOldFqcn != null && mNewFqcn != null; } + return false; } - /** - * Adds layout changes for project - * - * @param project the Android project - * @param className the layout classes - * - */ - private void addLayoutChanges(IProject project, String className) { + @Override + public Change createChange(IProgressMonitor pm) throws CoreException, + OperationCanceledException { + if (pm.isCanceled()) { + return null; + } + if (!getArguments().getUpdateReferences()) { + return null; + } + CompositeChange result = new CompositeChange(getName()); + result.markAsSynthetic(); + + addManifestFileChanges(result); + + // Update layout files; we don't just need to react to custom view + // changes, we need to update fragment references and even tool:context activity + // references + addLayoutFileChanges(result); + + return (result.getChildren().length == 0) ? null : result; + } + + private void addManifestFileChanges(CompositeChange result) { + addXmlFileChanges(mManifestFile, result, true); + } + + private void addLayoutFileChanges(CompositeChange result) { try { - IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES); - for (IResource folder : resFolder.members()) { - if (!(folder instanceof IFolder)) { + // Update references in XML resource files + IFolder resFolder = mProject.getFolder(SdkConstants.FD_RESOURCES); + + IResource[] folders = resFolder.members(); + for (IResource folder : folders) { + String folderName = folder.getName(); + ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName); + if (folderType != ResourceFolderType.LAYOUT) { continue; } - ResourceFolderType type = ResourceFolderType.getFolderType(folder.getName()); - if (type != ResourceFolderType.LAYOUT) { + if (!(folder instanceof IFolder)) { continue; } - IFolder layoutFolder = (IFolder) folder; - IResource[] members = layoutFolder.members(); - for (int i = 0; i < members.length; i++) { - IResource member = members[i]; + IResource[] files = ((IFolder) folder).members(); + for (int i = 0; i < files.length; i++) { + IResource member = files[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); + String fileName = member.getName(); + + if (SdkUtils.endsWith(fileName, DOT_XML)) { + addXmlFileChanges(file, result, false); } } } @@ -273,167 +208,139 @@ public class AndroidTypeMoveParticipant extends MoveParticipant { } } - /** - * Searches the layout file for classes - * - * @param file the Android layout file - * @param className the layout classes - * - */ - private Set<AndroidLayoutChangeDescription> parse(IFile file, String className) { - Set<AndroidLayoutChangeDescription> changes = new HashSet<AndroidLayoutChangeDescription>(); - ITextFileBufferManager lManager = null; + private boolean addXmlFileChanges(IFile file, CompositeChange changes, boolean isManifest) { + IModelManager modelManager = StructuredModelManager.getModelManager(); + IStructuredModel model = 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(SdkConstants.VIEW); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - NamedNodeMap attributes = node.getAttributes(); - if (attributes != null) { - Node attributeNode = - attributes.getNamedItem(SdkConstants.ATTR_CLASS); - if (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); - } - } + model = modelManager.getExistingModelForRead(file); + if (model == null) { + model = modelManager.getModelForRead(file); + } + if (model != null) { + IStructuredDocument document = model.getStructuredDocument(); + if (model instanceof IDOMModel) { + IDOMModel domModel = (IDOMModel) model; + Element root = domModel.getDocument().getDocumentElement(); + if (root != null) { + List<TextEdit> edits = new ArrayList<TextEdit>(); + if (isManifest) { + addManifestReplacements(edits, root, document); + } else { + addLayoutReplacements(edits, root, document); + } + if (!edits.isEmpty()) { + MultiTextEdit rootEdit = new MultiTextEdit(); + rootEdit.addChildren(edits.toArray(new TextEdit[edits.size()])); + TextFileChange change = new TextFileChange(file.getName(), file); + change.setTextType(EXT_XML); + change.setEdit(rootEdit); + changes.add(change); } } - 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(); + } else { + return false; } } - } catch (CoreException ignore) { + return true; + } catch (IOException e) { + AdtPlugin.log(e, null); + } catch (CoreException e) { + AdtPlugin.log(e, null); } finally { - if (lManager != null) { - try { - lManager.disconnect(file.getFullPath(), LocationKind.NORMALIZE, - new NullProgressMonitor()); - } catch (CoreException ignore) { - } + if (model != null) { + model.releaseFromRead(); } } - return changes; - } - /** - * 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>(); + return false; + } - 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); - } + private void addLayoutReplacements( + @NonNull List<TextEdit> edits, + @NonNull Element element, + @NonNull IStructuredDocument document) { + String tag = element.getTagName(); + if (tag.equals(mOldFqcn)) { + int start = RefactoringUtil.getTagNameRangeStart(element, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); } - 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); + } else if (tag.equals(VIEW_TAG)) { + Attr classNode = element.getAttributeNode(ATTR_CLASS); + if (classNode != null && classNode.getValue().equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); } } - 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); + } else if (tag.equals(VIEW_FRAGMENT)) { + Attr classNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); + if (classNode != null && classNode.getValue().equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); + } } - } finally { - if (model != null) { - model.releaseFromRead(); + } else if (element.hasAttributeNS(TOOLS_URI, ATTR_CONTEXT)) { + Attr classNode = element.getAttributeNodeNS(TOOLS_URI, ATTR_CONTEXT); + if (classNode != null && classNode.getValue().equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); + } } } - return androidElements; + NodeList children = element.getChildNodes(); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + addLayoutReplacements(edits, (Element) child, document); + } + } } - /** - * 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); - } + private void addManifestReplacements( + @NonNull List<TextEdit> edits, + @NonNull Element element, + @NonNull IStructuredDocument document) { + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0, n = attributes.getLength(); i < n; i++) { + Attr attr = (Attr) attributes.item(i); + if (!RefactoringUtil.isManifestClassAttribute(attr)) { + continue; + } + + String value = attr.getValue(); + if (value.equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); + } + } else if (value.startsWith(".")) { //$NON-NLS-1$ + String fqcn = mAppPackage + value; + if (fqcn.equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); + if (start != -1) { + int end = start + value.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); } } } } - } + NodeList children = element.getChildNodes(); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + addManifestReplacements(edits, (Element) child, document); + } + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipant.java index 227266d..f454c68 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipant.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipant.java @@ -16,54 +16,61 @@ package com.android.ide.eclipse.adt.internal.refactorings.core; +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.ATTR_CLASS; +import static com.android.SdkConstants.ATTR_CONTEXT; +import static com.android.SdkConstants.ATTR_NAME; +import static com.android.SdkConstants.DOT_XML; +import static com.android.SdkConstants.EXT_XML; +import static com.android.SdkConstants.TOOLS_URI; +import static com.android.SdkConstants.VIEW_FRAGMENT; +import static com.android.SdkConstants.VIEW_TAG; + import com.android.SdkConstants; +import com.android.annotations.NonNull; import com.android.ide.common.xml.ManifestData; import com.android.ide.eclipse.adt.AdtConstants; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutChange; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutChangeDescription; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutFileChanges; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidTypeRenameChange; import com.android.resources.ResourceFolderType; -import com.android.xml.AndroidManifest; +import com.android.utils.SdkUtils; -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.jdt.internal.corext.refactoring.rename.RenameCompilationUnitProcessor; import org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeProcessor; -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.TextFileChange; +import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor; +import org.eclipse.ltk.core.refactoring.participants.RenameParticipant; +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.IModelManager; 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.Element; 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; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * A participant to participate in refactorings that rename a type in an Android project. @@ -76,81 +83,32 @@ import java.util.Set; * <code>org.eclipse.ltk.core.refactoring.participants.RenameParticipant</code>. */ @SuppressWarnings("restriction") -public class AndroidTypeRenameParticipant extends AndroidRenameParticipant { - - private Set<AndroidLayoutFileChanges> mFileChanges = new HashSet<AndroidLayoutFileChanges>(); - - private String mLayoutNewName; +public class AndroidTypeRenameParticipant extends RenameParticipant { + private IProject mProject; + private IFile mManifestFile; + private String mOldFqcn; + private String mNewFqcn; + private String mOldDottedName; + private String mNewDottedName; @Override - public Change createChange(IProgressMonitor pm) throws CoreException, - OperationCanceledException { - if (pm.isCanceled()) { - return null; - } - - // Only propose this refactoring if the "Update References" checkbox is set. - if (!getArguments().getUpdateReferences()) - return null; - - RefactoringProcessor p = getProcessor(); - if (p instanceof RenameCompilationUnitProcessor) { - RenameTypeProcessor rtp = ((RenameCompilationUnitProcessor) p).getRenameTypeProcessor(); - if (rtp != null) { - String pattern = rtp.getFilePatterns(); - boolean updQualf = rtp.getUpdateQualifiedNames(); - if (updQualf && pattern != null && pattern.contains("xml")) { //$NON-NLS-1$ - // Do not propose this refactoring if the - // "Update fully qualified names in non-Java files" option is - // checked and the file patterns mention XML. [c.f. SDK bug 21589] - return null; - } - } - } - - CompositeChange result = new CompositeChange(getName()); - if (mAndroidManifest.exists()) { - if (mAndroidElements.size() > 0) { - getManifestDocument(); - Change change = new AndroidTypeRenameChange(mAndroidManifest, mManager, mDocument, - mAndroidElements, mOldName, mNewName); - 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; - + public String getName() { + return "Android Type Rename"; } @Override - public String getName() { - return "Android Type Rename"; + public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) + throws OperationCanceledException { + return new RefactoringStatus(); } @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(AdtConstants.WS_SEP + mProject = javaProject.getProject(); + IResource manifestResource = mProject.findMember(AdtConstants.WS_SEP + SdkConstants.FN_ANDROID_MANIFEST_XML); if (manifestResource == null || !manifestResource.exists() @@ -158,82 +116,102 @@ public class AndroidTypeRenameParticipant extends AndroidRenameParticipant { RefactoringUtil.logInfo( String.format("Invalid or missing file %1$s in project %2$s", SdkConstants.FN_ANDROID_MANIFEST_XML, - project.getName())); + mProject.getName())); return false; } - mAndroidManifest = (IFile) manifestResource; + mManifestFile = (IFile) manifestResource; ManifestData manifestData; - manifestData = AndroidManifestHelper.parseForData(mAndroidManifest); + manifestData = AndroidManifestHelper.parseForData(mManifestFile); if (manifestData == null) { return false; } - mAppPackage = manifestData.getPackage(); - mOldName = type.getFullyQualifiedName(); + mOldDottedName = '.' + type.getElementName(); + mOldFqcn = type.getFullyQualifiedName(); String packageName = type.getPackageFragment().getElementName(); - mNewName = getArguments().getNewName(); + mNewDottedName = '.' + getArguments().getNewName(); if (packageName != null) { - mLayoutNewName = packageName + "." + getArguments().getNewName(); //$NON-NLS-1$ + mNewFqcn = packageName + mNewDottedName; } else { - mLayoutNewName = getArguments().getNewName(); + mNewFqcn = getArguments().getNewName(); } - if (mOldName == null || mNewName == null) { + if (mOldFqcn == null || mOldFqcn == null) { return false; } - if (!RefactoringUtil.isRefactorAppPackage() && mNewName.indexOf(".") == -1) { //$NON-NLS-1$ - mNewName = packageName + "." + mNewName; //$NON-NLS-1$ + if (!RefactoringUtil.isRefactorAppPackage() && mNewFqcn.indexOf('.') == -1) { + mNewFqcn = packageName + mNewDottedName; } - 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; - } + return true; + } + return false; + } + + @Override + public Change createChange(IProgressMonitor pm) throws CoreException, + OperationCanceledException { + if (pm.isCanceled()) { + return null; + } + + // Only propose this refactoring if the "Update References" checkbox is set. + if (!getArguments().getUpdateReferences()) { + return null; + } + + RefactoringProcessor p = getProcessor(); + if (p instanceof RenameCompilationUnitProcessor) { + RenameTypeProcessor rtp = + ((RenameCompilationUnitProcessor) p).getRenameTypeProcessor(); + if (rtp != null) { + String pattern = rtp.getFilePatterns(); + boolean updQualf = rtp.getUpdateQualifiedNames(); + if (updQualf && pattern != null && pattern.contains("xml")) { //$NON-NLS-1$ + // Do not propose this refactoring if the + // "Update fully qualified names in non-Java files" option is + // checked and the file patterns mention XML. [c.f. SDK bug 21589] + return null; } - } catch (JavaModelException ignore) { } - - return mAndroidElements.size() > 0 || mFileChanges.size() > 0; } - return false; + + CompositeChange result = new CompositeChange(getName()); + + // Only show the children in the refactoring preview dialog + result.markAsSynthetic(); + + addManifestFileChanges(result); + addLayoutFileChanges(result); + + return (result.getChildren().length == 0) ? null : result; + } + + private void addManifestFileChanges(CompositeChange result) { + addXmlFileChanges(mManifestFile, result, true); } - /** - * Adds layout changes for project - * - * @param project the Android project - * @param className the layout classes - * - */ - private void addLayoutChanges(IProject project, String className) { + private void addLayoutFileChanges(CompositeChange result) { try { - IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES); - for (IResource folder : resFolder.members()) { - if (!(folder instanceof IFolder)) { + // Update references in XML resource files + IFolder resFolder = mProject.getFolder(SdkConstants.FD_RESOURCES); + + IResource[] folders = resFolder.members(); + for (IResource folder : folders) { + String folderName = folder.getName(); + ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName); + if (folderType != ResourceFolderType.LAYOUT) { continue; } - ResourceFolderType type = ResourceFolderType.getFolderType(folder.getName()); - if (type != ResourceFolderType.LAYOUT) { + if (!(folder instanceof IFolder)) { continue; } - IFolder layoutFolder = (IFolder) folder; - IResource[] members = layoutFolder.members(); - for (int i = 0; i < members.length; i++) { - IResource member = members[i]; + IResource[] files = ((IFolder) folder).members(); + for (int i = 0; i < files.length; i++) { + IResource member = files[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); + String fileName = member.getName(); + + if (SdkUtils.endsWith(fileName, DOT_XML)) { + addXmlFileChanges(file, result, false); } } } @@ -243,172 +221,143 @@ public class AndroidTypeRenameParticipant extends AndroidRenameParticipant { } } - /** - * Searches the layout file for classes - * - * @param file the Android layout file - * @param className the layout classes - * - */ - private Set<AndroidLayoutChangeDescription> parse(IFile file, String className) { - Set<AndroidLayoutChangeDescription> changes = new HashSet<AndroidLayoutChangeDescription>(); - ITextFileBufferManager lManager = null; + private boolean addXmlFileChanges(IFile file, CompositeChange changes, boolean isManifest) { + IModelManager modelManager = StructuredModelManager.getModelManager(); + IStructuredModel model = 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(SdkConstants.VIEW); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - NamedNodeMap attributes = node.getAttributes(); - if (attributes != null) { - Node attributeNode = - attributes.getNamedItem(SdkConstants.ATTR_CLASS); - if (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); - } - } + model = modelManager.getExistingModelForRead(file); + if (model == null) { + model = modelManager.getModelForRead(file); + } + if (model != null) { + IStructuredDocument document = model.getStructuredDocument(); + if (model instanceof IDOMModel) { + IDOMModel domModel = (IDOMModel) model; + Element root = domModel.getDocument().getDocumentElement(); + if (root != null) { + List<TextEdit> edits = new ArrayList<TextEdit>(); + if (isManifest) { + addManifestReplacements(edits, root, document); + } else { + addLayoutReplacements(edits, root, document); + } + if (!edits.isEmpty()) { + MultiTextEdit rootEdit = new MultiTextEdit(); + rootEdit.addChildren(edits.toArray(new TextEdit[edits.size()])); + TextFileChange change = new TextFileChange(file.getName(), file); + change.setTextType(EXT_XML); + change.setEdit(rootEdit); + changes.add(change); } } - 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(); + } else { + return false; } } - } catch (CoreException ignore) { + return true; + } catch (IOException e) { + AdtPlugin.log(e, null); + } catch (CoreException e) { + AdtPlugin.log(e, null); } finally { - if (lManager != null) { - try { - lManager.disconnect(file.getFullPath(), - LocationKind.NORMALIZE, - new NullProgressMonitor()); - } catch (CoreException ignore) { - } + if (model != null) { + model.releaseFromRead(); } } - return changes; - } - /** - * 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>(); + return false; + } - IDocument document; - try { - document = getManifestDocument(); - } catch (CoreException e) { - RefactoringUtil.log(e); - if (mManager != null) { - try { - mManager.disconnect(mAndroidManifest.getFullPath(), - LocationKind.NORMALIZE, - new NullProgressMonitor()); - } catch (CoreException e1) { - RefactoringUtil.log(e1); - } + private void addLayoutReplacements( + @NonNull List<TextEdit> edits, + @NonNull Element element, + @NonNull IStructuredDocument document) { + String tag = element.getTagName(); + if (tag.equals(mOldFqcn)) { + int start = RefactoringUtil.getTagNameRangeStart(element, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); } - 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); + } else if (tag.equals(VIEW_TAG)) { + // TODO: Handle inner classes ($ vs .) ? + Attr classNode = element.getAttributeNode(ATTR_CLASS); + if (classNode != null && classNode.getValue().equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); } } - 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); + } else if (tag.equals(VIEW_FRAGMENT)) { + Attr classNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); + if (classNode != null && classNode.getValue().equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); + } } - } finally { - if (model != null) { - model.releaseFromRead(); + } else if (element.hasAttributeNS(TOOLS_URI, ATTR_CONTEXT)) { + Attr classNode = element.getAttributeNodeNS(TOOLS_URI, ATTR_CONTEXT); + if (classNode != null && classNode.getValue().equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); + } + } else if (classNode != null && classNode.getValue().equals(mOldDottedName)) { + int start = RefactoringUtil.getAttributeValueRangeStart(classNode, document); + if (start != -1) { + int end = start + mOldDottedName.length(); + edits.add(new ReplaceEdit(start, end - start, mNewDottedName)); + } } } - 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); - } - } - } + NodeList children = element.getChildNodes(); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + addLayoutReplacements(edits, (Element) child, document); } } } + private void addManifestReplacements( + @NonNull List<TextEdit> edits, + @NonNull Element element, + @NonNull IStructuredDocument document) { + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0, n = attributes.getLength(); i < n; i++) { + Attr attr = (Attr) attributes.item(i); + if (!RefactoringUtil.isManifestClassAttribute(attr)) { + continue; + } + String value = attr.getValue(); + if (value.equals(mOldFqcn)) { + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); + if (start != -1) { + int end = start + mOldFqcn.length(); + edits.add(new ReplaceEdit(start, end - start, mNewFqcn)); + } + } else if (value.equals(mOldDottedName)) { + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); + if (start != -1) { + int end = start + mOldDottedName.length(); + edits.add(new ReplaceEdit(start, end - start, mNewDottedName)); + } + } + } -} + NodeList children = element.getChildNodes(); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + addManifestReplacements(edits, (Element) child, document); + } + } + } +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringUtil.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringUtil.java index e48a685..41551a4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringUtil.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringUtil.java @@ -16,17 +16,25 @@ package com.android.ide.eclipse.adt.internal.refactorings.core; -import com.android.SdkConstants; +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.ATTR_NAME; +import static com.android.xml.AndroidManifest.ATTRIBUTE_BACKUP_AGENT; +import static com.android.xml.AndroidManifest.ATTRIBUTE_MANAGE_SPACE_ACTIVITY; +import static com.android.xml.AndroidManifest.ATTRIBUTE_PARENT_ACTIVITY_NAME; +import static com.android.xml.AndroidManifest.ATTRIBUTE_TARGET_ACTIVITY; + +import com.android.annotations.NonNull; import com.android.ide.eclipse.adt.AdtPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; import org.w3c.dom.Attr; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; +import org.w3c.dom.Element; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -74,43 +82,6 @@ public class RefactoringUtil { } /** - * 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 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 @@ -143,4 +114,86 @@ public class RefactoringUtil { public static void setRefactorAppPackage(boolean refactorAppPackage) { RefactoringUtil.sRefactorAppPackage = refactorAppPackage; } + + /** + * Returns the range of the attribute value in the given document + * + * @param attr the attribute to look up + * @param document the document containing the attribute + * @return the range of the value text, not including quotes, in the document + */ + public static int getAttributeValueRangeStart( + @NonNull Attr attr, + @NonNull IDocument document) { + IndexedRegion region = (IndexedRegion) attr; + int potentialStart = attr.getName().length() + 2; // + 2: add =" + String text; + try { + text = document.get(region.getStartOffset(), + region.getEndOffset() - region.getStartOffset()); + } catch (BadLocationException e) { + return -1; + } + String value = attr.getValue(); + int index = text.indexOf(value, potentialStart); + if (index != -1) { + return region.getStartOffset() + index; + } else { + return -1; + } + } + + /** + * Returns the start of the tag name of the given element + * + * @param element the element to look up + * @param document the document containing the attribute + * @return the index of the start tag in the document + */ + public static int getTagNameRangeStart( + @NonNull Element element, + @NonNull IDocument document) { + IndexedRegion region = (IndexedRegion) element; + int potentialStart = 1; // add '<' + String text; + try { + text = document.get(region.getStartOffset(), + region.getEndOffset() - region.getStartOffset()); + } catch (BadLocationException e) { + return -1; + } + int index = text.indexOf(element.getTagName(), potentialStart); + if (index != -1) { + return region.getStartOffset() + index; + } else { + return -1; + } + } + + /** + * Returns whether the given manifest attribute should be considered to describe + * a class name. These will be eligible for refactoring when classes are renamed + * or moved. + * <p> + * TODO: Move to {@link RefactoringUtil} + * + * @param attribute the manifest attribute + * @return true if this attribute can describe a class + */ + public static boolean isManifestClassAttribute(@NonNull Attr attribute) { + String name = attribute.getLocalName(); + if (name == null) { + return false; + } + + if (name.equals(ATTR_NAME) + || name.equals(ATTRIBUTE_TARGET_ACTIVITY) + || name.equals(ATTRIBUTE_MANAGE_SPACE_ACTIVITY) + || name.equals(ATTRIBUTE_BACKUP_AGENT) + || name.equals(ATTRIBUTE_PARENT_ACTIVITY_NAME)) { + return ANDROID_URI.equals(attribute.getNamespaceURI()); + } + + return false; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipant.java index 2b0d5d7..8bd0e13 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipant.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipant.java @@ -30,6 +30,7 @@ import static com.android.SdkConstants.PREFIX_RESOURCE_REF; import static com.android.SdkConstants.PREFIX_THEME_REF; import static com.android.SdkConstants.R_CLASS; import static com.android.SdkConstants.TAG_ITEM; +import static com.android.SdkConstants.TOOLS_URI; import com.android.SdkConstants; import com.android.annotations.NonNull; @@ -58,8 +59,6 @@ import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.internal.corext.refactoring.rename.RenameFieldProcessor; -import org.eclipse.jface.text.BadLocationException; -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; @@ -522,24 +521,6 @@ public class RenameResourceParticipant extends RenameParticipant { return false; } - private int getAttributeValueRangeStart(Attr attr, IDocument document) { - IndexedRegion region = (IndexedRegion) attr; - int potentialStart = attr.getName().length() + 2; // + 2: add =" - String text; - try { - text = document.get(region.getStartOffset(), region.getLength()); - } catch (BadLocationException e) { - return -1; - } - String value = attr.getValue(); - int index = text.indexOf(value, potentialStart); - if (index != -1) { - return region.getStartOffset() + index; - } else { - return -1; - } - } - private void addReplacements( @NonNull List<TextEdit> edits, @NonNull Element element, @@ -558,7 +539,7 @@ public class RenameResourceParticipant extends RenameParticipant { || mType.getName().equals(element.getAttribute(ATTR_TYPE))))) { Attr nameNode = element.getAttributeNode(ATTR_NAME); if (nameNode != null && nameNode.getValue().equals(mOldName)) { - int start = getAttributeValueRangeStart(nameNode, document); + int start = RefactoringUtil.getAttributeValueRangeStart(nameNode, document); if (start != -1) { int end = start + mOldName.length(); edits.add(new ReplaceEdit(start, end - start, mNewName)); @@ -575,6 +556,15 @@ public class RenameResourceParticipant extends RenameParticipant { // If not updating references, only update XML matches that define the id if (!mUpdateReferences && (!ATTR_ID.equals(attr.getLocalName()) || !ANDROID_URI.equals(attr.getNamespaceURI()))) { + + if (TOOLS_URI.equals(attr.getNamespaceURI()) && value.equals(mXmlMatch1)) { + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); + if (start != -1) { + int end = start + mXmlMatch1.length(); + edits.add(new ReplaceEdit(start, end - start, mXmlNewValue1)); + } + } + continue; } @@ -606,7 +596,7 @@ public class RenameResourceParticipant extends RenameParticipant { int end = region.getEndOffset(); edits.add(new ReplaceEdit(start, end - start, "")); } else { - int start = getAttributeValueRangeStart(attr, document); + int start = RefactoringUtil.getAttributeValueRangeStart(attr, document); if (start != -1) { int end = start + match.length(); edits.add(new ReplaceEdit(start, end - start, matchedValue)); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidPackageRenameParticipantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidPackageRenameParticipantTest.java index ec9d324..a074146 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidPackageRenameParticipantTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidPackageRenameParticipantTest.java @@ -49,10 +49,19 @@ public class AndroidPackageRenameParticipantTest extends RefactoringTestBase { " +\n" + "\n" + "\n" + + "* activity_main.xml - /testRefactor1/res/layout/activity_main.xml\n" + + " @@ -33 +33\n" + + " - <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>\n" + + " + <fragment android:name=\"my.pkg.name.MyFragment\"/>\n" + + "\n" + + "\n" + "* AndroidManifest.xml - /testRefactor1/AndroidManifest.xml\n" + " @@ -16 +16\n" + " - android:name=\"com.example.refactoringtest.MainActivity\"\n" + - " + android:name=\"my.pkg.name.MainActivity\""); + " + android:name=\"my.pkg.name.MainActivity\"\n" + + " @@ -25 +25\n" + + " - android:name=\".MainActivity2\"\n" + + " + android:name=\"my.pkg.name.MainActivity2\""); } public void testRefactor1_noreferences() throws Exception { @@ -91,6 +100,12 @@ public class AndroidPackageRenameParticipantTest extends RefactoringTestBase { " + <my.pkg.name.CustomView1\n" + "\n" + "\n" + + "* activity_main.xml - /testRefactor2/res/layout/activity_main.xml\n" + + " @@ -33 +33\n" + + " - <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>\n" + + " + <fragment android:name=\"my.pkg.name.MyFragment\"/>\n" + + "\n" + + "\n" + "* customviews.xml - /testRefactor2/res/layout-land/customviews.xml\n" + " @@ -9 +9\n" + " - <com.example.refactoringtest.CustomView1\n" + @@ -100,7 +115,10 @@ public class AndroidPackageRenameParticipantTest extends RefactoringTestBase { "* AndroidManifest.xml - /testRefactor2/AndroidManifest.xml\n" + " @@ -16 +16\n" + " - android:name=\"com.example.refactoringtest.MainActivity\"\n" + - " + android:name=\"my.pkg.name.MainActivity\""); + " + android:name=\"my.pkg.name.MainActivity\"\n" + + " @@ -25 +25\n" + + " - android:name=\".MainActivity2\"\n" + + " + android:name=\"my.pkg.name.MainActivity2\""); } public void testRefactor2_renamesub() throws Exception { @@ -127,6 +145,12 @@ public class AndroidPackageRenameParticipantTest extends RefactoringTestBase { " + <my.pkg.name.CustomView1\n" + "\n" + "\n" + + "* activity_main.xml - /testRefactor2_renamesub/res/layout/activity_main.xml\n" + + " @@ -33 +33\n" + + " - <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>\n" + + " + <fragment android:name=\"my.pkg.name.MyFragment\"/>\n" + + "\n" + + "\n" + "* customviews.xml - /testRefactor2_renamesub/res/layout-land/customviews.xml\n" + " @@ -9 +9\n" + " - <com.example.refactoringtest.CustomView1\n" + @@ -137,6 +161,9 @@ public class AndroidPackageRenameParticipantTest extends RefactoringTestBase { " @@ -16 +16\n" + " - android:name=\"com.example.refactoringtest.MainActivity\"\n" + " + android:name=\"my.pkg.name.MainActivity\"\n" + + " @@ -25 +25\n" + + " - android:name=\".MainActivity2\"\n" + + " + android:name=\"my.pkg.name.MainActivity2\"\n" + "\n" + "\n" + "* customviews.xml - /testRefactor2_renamesub/res/layout/customviews.xml\n" + diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringTestBase.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringTestBase.java index 93c3489..44fb522 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringTestBase.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RefactoringTestBase.java @@ -19,10 +19,6 @@ import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.ide.eclipse.adt.AdtUtils; import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidDocumentChange; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidLayoutChange; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidPackageRenameChange; -import com.android.ide.eclipse.adt.internal.refactorings.changes.AndroidTypeRenameChange; import com.google.common.base.Charsets; import com.google.common.base.Splitter; import com.google.common.io.ByteStreams; @@ -133,10 +129,7 @@ public abstract class RefactoringTestBase extends AdtProjectTest { sb.append('\n'); } - if (change instanceof TextFileChange - || change instanceof AndroidPackageRenameChange - || change instanceof AndroidTypeRenameChange - || change instanceof AndroidLayoutChange) { + if (change instanceof TextFileChange) { assertNotNull(file); if (file != null) { TextChange tc = (TextChange) change; @@ -221,18 +214,6 @@ public abstract class RefactoringTestBase extends AdtProjectTest { if (change instanceof TextFileChange) { TextFileChange tfc = (TextFileChange) change; return tfc.getFile(); - } else if (change instanceof AndroidPackageRenameChange) { - AndroidPackageRenameChange aprc = (AndroidPackageRenameChange) change; - return aprc.getManifest(); - } else if (change instanceof AndroidTypeRenameChange) { - AndroidTypeRenameChange aprc = (AndroidTypeRenameChange) change; - return aprc.getManifest(); - } else if (change instanceof AndroidLayoutChange) { - AndroidLayoutChange alc = (AndroidLayoutChange) change; - return alc.getFile(); - } else if (change instanceof AndroidDocumentChange) { - AndroidDocumentChange atmc = (AndroidDocumentChange) change; - return atmc.getManifest(); } return null; @@ -293,6 +274,10 @@ public abstract class RefactoringTestBase extends AdtProjectTest { " <category android:name=\"android.intent.category.LAUNCHER\" />\n" + " </intent-filter>\n" + " </activity>\n" + + " <activity\n" + + " android:name=\".MainActivity2\"\n" + + " android:label=\"@string/app_name2\" >\n" + + " </activity>\n" + " </application>\n" + "\n" + "</manifest>"; @@ -323,6 +308,31 @@ public abstract class RefactoringTestBase extends AdtProjectTest { "\n" + "}\n"; + protected static final String SAMPLE_MAIN_ACTIVITY2 = + "package com.example.refactoringtest;\n" + + "\n" + + "import android.os.Bundle;\n" + + "import android.app.Activity;\n" + + "import android.view.Menu;\n" + + "import android.view.View;\n" + + "\n" + + "public class MainActivity2 extends Activity {\n" + + "\n" + + " @Override\n" + + " protected void onCreate(Bundle savedInstanceState) {\n" + + " super.onCreate(savedInstanceState);\n" + + " }\n" + + "\n" + + "}\n"; + + protected static final String MY_FRAGMENT = + "package com.example.refactoringtest;\n" + + "import android.support.v4.app.ListFragment;\n" + + "\n" + + "public class MyFragment extends ListFragment {\n" + + "\n" + + "}\n"; + protected static final String SAMPLE_LAYOUT = "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + @@ -356,6 +366,8 @@ public abstract class RefactoringTestBase extends AdtProjectTest { " android:layout_alignParentTop=\"true\"\n" + " android:text=\"Button\" />\n" + "\n" + + " <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>" + + "\n" + "</RelativeLayout>"; protected static final String SAMPLE_LAYOUT_2 = @@ -365,6 +377,18 @@ public abstract class RefactoringTestBase extends AdtProjectTest { " android:layout_height=\"match_parent\"\n" + " tools:context=\".MainActivity\" >\n" + "\n" + + " <ListView\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " tools:listitem=\"@layout/preview\" >\n" + + " </ListView>\n" + + "\n" + + " <fragment\n" + + " android:name=\"android.support.v4.app.ListFragment\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " tools:layout=\"@layout/preview\" />\n" + + "\n" + "\n" + "</RelativeLayout>"; @@ -482,6 +506,9 @@ public abstract class RefactoringTestBase extends AdtProjectTest { "src/com/example/refactoringtest/MainActivity.java", SAMPLE_MAIN_ACTIVITY, + "src/com/example/refactoringtest/MainActivity2.java", + SAMPLE_MAIN_ACTIVITY2, + "gen/com/example/refactoringtest/R.java", SAMPLE_R, @@ -572,6 +599,9 @@ public abstract class RefactoringTestBase extends AdtProjectTest { "src/com/example/refactoringtest/subpackage/CustomView2.java", CUSTOM_VIEW_2, + "src/com/example/refactoringtest/MyFragment.java", + MY_FRAGMENT, + "gen/com/example/refactoringtest/R.java", SAMPLE_R, diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipantTest.java index b783f8d..3a6859c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipantTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceParticipantTest.java @@ -311,6 +311,25 @@ public class RenameResourceParticipantTest extends RefactoringTestBase { " + public static final int output=0x7f070000;"); } + public void testRefactor10() throws Exception { + // Check updating tools: attributes + renameResource( + TEST_PROJECT, + "@layout/preview", + true /*updateReferences*/, + "newlayout", + + "CHANGES:\n" + + "-------\n" + + "* activity_main.xml - /testRefactor10/res/layout-land/activity_main.xml\n" + + " @@ -10 +10\n" + + " - tools:listitem=\"@layout/preview\" >\n" + + " + tools:listitem=\"@layout/newlayout\" >\n" + + " @@ -17 +17\n" + + " - tools:layout=\"@layout/preview\" />\n" + + " + tools:layout=\"@layout/newlayout\" />"); + } + // ---- Test infrastructure ---- protected void renameResource( diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java index aeedf20..0fb8523 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java @@ -50,18 +50,35 @@ public class AndroidTypeMoveParticipantTest extends RefactoringTestBase { "\n" + "* Move resource 'testRefactor1/src/com/example/refactoringtest/CustomView1.java' to 'subpackage'\n" + "\n" + - "* Android Type Move\n" + + "* customviews.xml - /testRefactor1/res/layout/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.subpackage.CustomView1\n" + "\n" + - " * customviews.xml - /testRefactor1/res/layout/customviews.xml\n" + - " @@ -9 +9\n" + - " - <com.example.refactoringtest.CustomView1\n" + - " + <com.example.refactoringtest.subpackage.CustomView1\n" + "\n" + + "* customviews.xml - /testRefactor1/res/layout-land/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.subpackage.CustomView1"); + } + + public void testRefactorFragment() throws Exception { + moveType( + TEST_PROJECT2, + "com.example.refactoringtest.MyFragment", + "src/com/example/refactoringtest/subpackage", + true /*updateReferences*/, + + "CHANGES:\n" + + "-------\n" + + "* Move resource 'testRefactorFragment/src/com/example/refactoringtest/MyFragment.java' to 'subpackage'\n" + + "\n" + + "* Move resource 'testRefactorFragment/src/com/example/refactoringtest/MyFragment.java' to 'subpackage'\n" + "\n" + - " * customviews.xml - /testRefactor1/res/layout-land/customviews.xml\n" + - " @@ -9 +9\n" + - " - <com.example.refactoringtest.CustomView1\n" + - " + <com.example.refactoringtest.subpackage.CustomView1"); + "* activity_main.xml - /testRefactorFragment/res/layout/activity_main.xml\n" + + " @@ -33 +33\n" + + " - <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>\n" + + " + <fragment android:name=\"com.example.refactoringtest.subpackage.MyFragment\"/>"); } public void testRefactor1_norefs() throws Exception { diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java index 8ab0a11..f54a49a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java @@ -38,12 +38,39 @@ public class AndroidTypeRenameParticipantTest extends RefactoringTestBase { "-------\n" + "* Rename compilation unit 'MainActivity.java' to 'NewActivityName.java'\n" + "\n" + - "* Android Type Rename\n" + + "* activity_main.xml - /testRefactor1/res/layout/activity_main.xml\n" + + " @@ -5 +5\n" + + " - tools:context=\".MainActivity\" >\n" + + " + tools:context=\".NewActivityName\" >\n" + "\n" + - " * AndroidManifest.xml - /testRefactor1/AndroidManifest.xml\n" + - " @@ -16 +16\n" + - " - android:name=\"com.example.refactoringtest.MainActivity\"\n" + - " + android:name=\"com.example.refactoringtest.NewActivityName\""); + "\n" + + "* activity_main.xml - /testRefactor1/res/layout-land/activity_main.xml\n" + + " @@ -5 +5\n" + + " - tools:context=\".MainActivity\" >\n" + + " + tools:context=\".NewActivityName\" >\n" + + "\n" + + "\n" + + "* AndroidManifest.xml - /testRefactor1/AndroidManifest.xml\n" + + " @@ -16 +16\n" + + " - android:name=\"com.example.refactoringtest.MainActivity\"\n" + + " + android:name=\"com.example.refactoringtest.NewActivityName\""); + } + + public void testRefactor1b() throws Exception { + renameType( + TEST_PROJECT, + "com.example.refactoringtest.MainActivity2", + true /*updateReferences*/, + "NewActivityName", + + "CHANGES:\n" + + "-------\n" + + "* Rename compilation unit 'MainActivity2.java' to 'NewActivityName.java'\n" + + "\n" + + "* AndroidManifest.xml - /testRefactor1b/AndroidManifest.xml\n" + + " @@ -25 +25\n" + + " - android:name=\".MainActivity2\"\n" + + " + android:name=\".NewActivityName\""); } public void testRefactor1_noreferences() throws Exception { @@ -69,18 +96,33 @@ public class AndroidTypeRenameParticipantTest extends RefactoringTestBase { "-------\n" + "* Rename compilation unit 'CustomView1.java' to 'NewCustomViewName.java'\n" + "\n" + - "* Android Type Rename\n" + - "\n" + - " * customviews.xml - /testRefactor2/res/layout/customviews.xml\n" + - " @@ -9 +9\n" + - " - <com.example.refactoringtest.CustomView1\n" + - " + <com.example.refactoringtest.NewCustomViewName\n" + + "* customviews.xml - /testRefactor2/res/layout/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.NewCustomViewName\n" + "\n" + "\n" + - " * customviews.xml - /testRefactor2/res/layout-land/customviews.xml\n" + - " @@ -9 +9\n" + - " - <com.example.refactoringtest.CustomView1\n" + - " + <com.example.refactoringtest.NewCustomViewName"); + "* customviews.xml - /testRefactor2/res/layout-land/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.NewCustomViewName"); + } + + public void testRefactorFragment() throws Exception { + renameType( + TEST_PROJECT2, + "com.example.refactoringtest.MyFragment", + true /*updateReferences*/, + "NewFragmentName", + + "CHANGES:\n" + + "-------\n" + + "* Rename compilation unit 'MyFragment.java' to 'NewFragmentName.java'\n" + + "\n" + + "* activity_main.xml - /testRefactorFragment/res/layout/activity_main.xml\n" + + " @@ -33 +33\n" + + " - <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>\n" + + " + <fragment android:name=\"com.example.refactoringtest.NewFragmentName\"/>"); } // ---- Test infrastructure ---- |