/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.adt.internal.refactoring.changes; import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener; import com.android.ide.eclipse.adt.internal.refactoring.core.RefactoringUtil; import com.android.sdklib.SdkConstants; import com.android.sdklib.xml.AndroidManifest; import org.eclipse.core.filebuffers.ITextFileBufferManager; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jface.text.IDocument; import org.eclipse.ltk.core.refactoring.DocumentChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; import org.w3c.dom.Attr; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.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); } @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; String region = domAttr.getValueRegionText(); int offset = domAttr.getValueRegionStartOffset(); if (region != null && region.length() >= 2) { return new ReplaceEdit(offset + 1, region.length() - 2, newValue); } } return null; } /** * Returns the text change that change the value of attribute from oldValue to newValue * and combine package * * @param elementName the element name * @param attributeName the attribute name * @param oldValue the old value * @param newValue the new value * * @return the text change */ protected TextEdit createTextEdit(String elementName, String attributeName, String oldValue, String newValue) { return createTextEdit(elementName, attributeName, oldValue, newValue, true); } /** * Returns the text change that change the value of attribute from oldValue to newValue * * @param elementName the element name * @param attributeName the attribute name * @param 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; if (combinePackage) { newValue = AndroidManifest.extractActivityName(newName, getAppPackage()); } else { newValue = newName; } if (newValue != null) { TextEdit edit = createTextEdit(attr, newValue); return edit; } } return null; } }