/* * Copyright 2003-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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 jetbrains.mps.idea.java.refactoring; import com.intellij.facet.FacetManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.impl.DirectoryIndex; import com.intellij.openapi.roots.impl.DirectoryInfo; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPackage; import com.intellij.refactoring.listeners.RefactoringElementListener; import com.intellij.refactoring.listeners.RefactoringElementListenerProvider; import jetbrains.mps.ide.findusages.model.scopes.ProjectScope; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.idea.core.facet.MPSFacet; import jetbrains.mps.idea.core.facet.MPSFacetType; import jetbrains.mps.java.stub.JavaPackageNameStub; import jetbrains.mps.progress.EmptyProgressMonitor; import jetbrains.mps.smodel.ModelImports; import jetbrains.mps.smodel.StaticReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeUtil; import org.jetbrains.mps.openapi.model.SReference; import org.jetbrains.mps.openapi.module.FindUsagesFacade; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SRepository; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * This class listens to package rename refactorings and updates the references to stub models that * were built from those packages. Package name changes => model reference changes. Both model imports and SReferences * must changed accordingly. * <p> * Created by danilla on 2/16/15. */ public class PackageRenameListener implements RefactoringElementListenerProvider { @Nullable @Override public RefactoringElementListener getListener(PsiElement element) { if (!(element instanceof PsiPackage)) { return null; } final PsiPackage pkg = (PsiPackage) element; final Project project = pkg.getProject(); DirectoryIndex dirIndex = DirectoryIndex.getInstance(project); final Set<SModelReference> modelRefs = new HashSet<>(); for (PsiDirectory dir : pkg.getDirectories()) { DirectoryInfo dirInfo = dirIndex.getInfoForFile(dir.getVirtualFile()); Module module = dirInfo.getModule(); if (module == null) continue; MPSFacet facet = FacetManager.getInstance(module).getFacetByType(MPSFacetType.ID); if (facet == null) continue; modelRefs.add(new JavaPackageNameStub(pkg.getQualifiedName()).asModelReference(facet.getSolution().getModuleReference())); } if (modelRefs.isEmpty()) return null; final Set<SModel> models = new HashSet<>(); ProjectHelper.getModelAccess(project).runReadAction(() -> models.addAll(FindUsagesFacade.getInstance().findModelUsages( new ProjectScope(ProjectHelper.fromIdeaProject(project)), modelRefs, new EmptyProgressMonitor() ))); if (models.isEmpty()) return null; return new RefactoringElementListener() { @Override public void elementMoved(@NotNull PsiElement newElement) { } @Override public void elementRenamed(@NotNull PsiElement newElement) { final String newPkgName = ((PsiPackage) newElement).getQualifiedName(); final SRepository repository = ProjectHelper.getProjectRepository(project); repository.getModelAccess().executeCommand(() -> { for (SModel model : models) { ModelImports modelImports = new ModelImports(model); Map<SModelReference, SModelReference> changes = new HashMap<>(); for (SModelReference mref : modelImports.getImportedModels()) { if (!modelRefs.contains(mref)) { continue; } SModule module = mref.getModuleReference().resolve(repository); SModelReference newModelRef = new JavaPackageNameStub(newPkgName).asModelReference(module.getModuleReference()); modelImports.removeModelImport(mref); modelImports.addModelImport(newModelRef); changes.put(mref, newModelRef); } updateSReferences(model, changes); } }); } }; } private void updateSReferences(SModel model, Map<SModelReference, SModelReference> modelRefChange) { for (SNode node : SNodeUtil.getDescendants(model)) { for (SReference ref : node.getReferences()) { if (!(ref instanceof StaticReference)) { continue; } SModelReference modelRef = ref.getTargetSModelReference(); if (modelRefChange.containsKey(modelRef)) { ((StaticReference) ref).setTargetSModelReference(modelRefChange.get(modelRef)); } } } } }