/* * Copyright 2003-2012 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.core.actions; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.compiler.ModuleCompilerUtil; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import jetbrains.mps.ide.actions.MPSCommonDataKeys; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.idea.core.project.SolutionIdea; import jetbrains.mps.project.dependency.VisibilityUtil; import jetbrains.mps.smodel.IOperationContext; import jetbrains.mps.workbench.action.BaseAction; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; 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.SReference; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SRepository; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; public class AddMissingDependencyAction extends BaseAction { protected static Logger log = LogManager.getLogger(AddMissingDependencyAction.class); public AddMissingDependencyAction() { super("Add Missing Dependency", "", null); this.setIsAlwaysVisible(false); this.setExecuteOutsideCommand(false); } @Override protected void doExecute(AnActionEvent e, Map<String, Object> params) { try { Project project = e.getProject(); if (project == null) { return; } SNode curNode = e.getData(MPSCommonDataKeys.NODE); if (curNode == null) return; IOperationContext context = e.getData(MPSCommonDataKeys.OPERATION_CONTEXT); if (context == null) return; SModule dependentModule = context.getModule(); if (!(dependentModule instanceof SolutionIdea)) return; final Module ideaDependentModule = ((SolutionIdea) dependentModule).getIdeaModule(); final List<Module> ideaModulesToDependOn = new ArrayList<Module>(); Set<Module> circularDependentModulesSet = new LinkedHashSet<Module>(); SRepository repository = ProjectHelper.getProjectRepository(project); for (SReference ref : curNode.getReferences()) { SModelReference mref = ref.getTargetSModelReference(); if (mref == null) continue; SModel model = mref.resolve(repository); if (model == null) continue; SModule moduleToDependOn = model.getModule(); if (!(moduleToDependOn instanceof SolutionIdea)) continue; if (!VisibilityUtil.isVisible(dependentModule, moduleToDependOn)) { Module ideaModuleToDependOn = ((SolutionIdea) moduleToDependOn).getIdeaModule(); ideaModulesToDependOn.add(ideaModuleToDependOn); Pair<Module, Module> circularModules = ModuleCompilerUtil.addingDependencyFormsCircularity(ideaDependentModule, ideaModuleToDependOn); if (circularModules != null) { circularDependentModulesSet.add(circularModules.getFirst()); circularDependentModulesSet.add(circularModules.getSecond()); } } } if (ideaModulesToDependOn.isEmpty()) return; if (!circularDependentModulesSet.isEmpty()) { StringBuilder message = new StringBuilder(); message.append("Adding dependency on "); if (ideaModulesToDependOn.size() == 1) { message.append("module "); } else { message.append("modules "); } for (int i = 0; i != ideaModulesToDependOn.size() - 1; ++i) { message.append("'"); message.append(ideaModulesToDependOn.get(i).getName()); message.append("', "); } message.append("'"); message.append(ideaModulesToDependOn.get(ideaModulesToDependOn.size() - 1).getName()); message.append("'"); message.append(" will introduce circular dependency between modules "); Module[] modules = circularDependentModulesSet.toArray(new Module[circularDependentModulesSet.size()]); for (int i = 0; i != modules.length - 1; ++i) { message.append("'"); message.append(modules[i].getName()); message.append("', "); } message.append("'"); message.append(modules[modules.length - 1].getName()); message.append("'"); message.append(".\nAdd dependency anyway?"); final String finalMessage = message.toString(); if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(finalMessage); ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { Project project = ideaDependentModule.getProject(); if (!(project.isOpen())) return; int ret = Messages.showOkCancelDialog(project, finalMessage, QuickFixBundle.message("orderEntry.fix.title.circular.dependency.warning"), Messages.getWarningIcon()); if (ret == 0) { addDependency(ideaDependentModule, ideaModulesToDependOn); } } }); } else { addDependency(ideaDependentModule, ideaModulesToDependOn); } } catch (Throwable t) { log.error("User's action execute method failed. Action:" + "AddMissingDependency", t); } } private void addDependency(final Module dependentModule, final List<Module> modulesToDependOn) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { final ModifiableRootModel model = ModuleRootManager.getInstance(dependentModule).getModifiableModel(); for (Module moduleToDependOn : modulesToDependOn) { model.addModuleOrderEntry(moduleToDependOn); } model.commit(); } }); } @Override public void doUpdate(AnActionEvent e, Map<String, Object> params) { try { boolean enabled = isApplicable(e); this.setEnabledState(e.getPresentation(), enabled); } catch (Throwable t) { log.error("User's action doUpdate method failed. Action:" + "RenameMethod", t); this.disable(e.getPresentation()); } } public boolean isApplicable(AnActionEvent e) { Project project = e.getProject(); if (project == null) { return false; } SNode curNode = e.getData(MPSCommonDataKeys.NODE); if (curNode == null) { return false; } IOperationContext context = e.getData(MPSCommonDataKeys.OPERATION_CONTEXT); if (context == null) return false; SModule dependentModule = context.getModule(); if (!(dependentModule instanceof SolutionIdea)) return false; SRepository repository = ProjectHelper.getProjectRepository(project); for (SReference ref : curNode.getReferences()) { SModelReference mref = ref.getTargetSModelReference(); if (mref == null) continue; SModel model = mref.resolve(repository); if (model == null) continue; SModule moduleToDependOn = model.getModule(); if (!(moduleToDependOn instanceof SolutionIdea)) continue; return !VisibilityUtil.isVisible(dependentModule, moduleToDependOn); } return false; } }