package org.jetbrains.android; import com.intellij.facet.ProjectFacetManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtilCore; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlFile; import com.intellij.refactoring.listeners.RefactoringElementListener; import com.intellij.refactoring.rename.RenamePsiElementProcessor; import com.intellij.refactoring.rename.RenamePsiPackageProcessor; import com.intellij.refactoring.rename.RenameXmlAttributeProcessor; import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import com.intellij.util.containers.HashMap; import com.intellij.util.xml.DomManager; import com.intellij.util.xml.GenericAttributeValue; import org.jetbrains.android.dom.converters.PackageClassConverter; import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Map; /** * @author Eugene.Kudelevsky */ public class AndroidApplicationPackageRenameProcessor extends RenamePsiElementProcessor { @Override public boolean canProcessElement(@NotNull PsiElement element) { if (element instanceof PsiPackage) { // possibly renaming application package return ProjectFacetManager.getInstance(element.getProject()).hasFacets(AndroidFacet.ID); } return AndroidRenameHandler.isPackageAttributeInManifest(element.getProject(), element); } @Override public void renameElement(PsiElement element, String newName, UsageInfo[] usages, @Nullable RefactoringElementListener listener) throws IncorrectOperationException { if (element instanceof PsiPackage) { final Map<GenericAttributeValue, String> newAttrValues = new HashMap<GenericAttributeValue, String>(); final Project project = element.getProject(); final String oldPackageQName = ((PsiPackage)element).getQualifiedName(); final String newPackageQName = PsiUtilCore.getQualifiedNameAfterRename(oldPackageQName, newName); for (Module module : ModuleManager.getInstance(project).getModules()) { final AndroidFacet facet = AndroidFacet.getInstance(module); final Manifest manifest = facet != null ? facet.getManifest() : null; if (manifest != null) { final XmlElement manifestElement = manifest.getXmlElement(); final PsiFile manifestPsiFile = manifestElement != null ? manifestElement.getContainingFile() : null; if (manifestPsiFile instanceof XmlFile) { final String basePackage = manifest.getPackage().getValue(); if (basePackage == null) { continue; } processAllAttributesToUpdate( (XmlFile)manifestPsiFile, basePackage, oldPackageQName, newPackageQName, new Processor<Pair<GenericAttributeValue, String>>() { @Override public boolean process(Pair<GenericAttributeValue, String> pair) { newAttrValues.put(pair.getFirst(), pair.getSecond()); return true; } }); } } } new RenamePsiPackageProcessor().renameElement(element, newName, usages, listener); for (Map.Entry<GenericAttributeValue, String> e : newAttrValues.entrySet()) { //noinspection unchecked e.getKey().setStringValue(e.getValue()); } return; } final PsiFile file = element.getContainingFile(); if (!(file instanceof XmlFile)) { return; } final Map<GenericAttributeValue, PsiClass> attr2class = buildAttr2ClassMap((XmlFile)file); new RenameXmlAttributeProcessor().renameElement(element, newName, usages, listener); for (Map.Entry<GenericAttributeValue, PsiClass> e : attr2class.entrySet()) { //noinspection unchecked e.getKey().setValue(e.getValue()); } } @Nullable private static String computeNewQName(@NotNull String name, @NotNull String oldPackageName, @NotNull String newPackageName) { if (name.startsWith(oldPackageName)) { final String suffix = name.substring(oldPackageName.length()); if (suffix.length() == 0 || suffix.charAt(0) == '.') { return newPackageName + suffix; } } return null; } @NotNull private static Map<GenericAttributeValue, PsiClass> buildAttr2ClassMap(@NotNull XmlFile file) { final Map<GenericAttributeValue, PsiClass> map = new HashMap<GenericAttributeValue, PsiClass>(); processAllClassAttrValues(file, new Processor<Pair<GenericAttributeValue, PsiClass>>() { @Override public boolean process(Pair<GenericAttributeValue, PsiClass> pair) { map.put(pair.getFirst(), pair.getSecond()); return true; } }); return map; } public static void processAllAttributesToUpdate(@NotNull XmlFile file, @NotNull final String basePackage, @NotNull final String oldPackageQName, @NotNull final String newPackageQName, @NotNull final Processor<Pair<GenericAttributeValue, String>> processor) { if (!AndroidUtils.isPackagePrefix(oldPackageQName, basePackage) && !AndroidUtils.isPackagePrefix(newPackageQName, basePackage)) { return; } processAllClassAttrValues(file, new Processor<Pair<GenericAttributeValue, PsiClass>>() { @Override public boolean process(Pair<GenericAttributeValue, PsiClass> pair) { final GenericAttributeValue domValue = pair.getFirst(); final PsiClass psiClass = pair.getSecond(); final String classPackageName = PackageClassConverter.getPackageName(psiClass); if (classPackageName != null) { final String classQName = PackageClassConverter.getQualifiedName(psiClass); if (classQName != null) { final String newClassQName = computeNewQName(classQName, oldPackageQName, newPackageQName); if (newClassQName != null) { final String newRefValue = AndroidUtils.isPackagePrefix(basePackage, newClassQName) ? newClassQName.substring(basePackage.length()) : newClassQName; processor.process(Pair.create(domValue, newRefValue)); } } } return true; } }); } private static void processAllClassAttrValues(@NotNull XmlFile file, @NotNull final Processor<Pair<GenericAttributeValue, PsiClass>> processor) { final DomManager domManager = DomManager.getDomManager(file.getProject()); file.accept(new XmlRecursiveElementVisitor() { @Override public void visitXmlAttribute(XmlAttribute attribute) { final GenericAttributeValue domAttrValue = domManager.getDomElement(attribute); if (domAttrValue != null) { final Object value = domAttrValue.getValue(); if (value instanceof PsiClass) { processor.process(Pair.create(domAttrValue, (PsiClass)value)); } } } }); } }