package jetbrains.mps.vcs.suspicious; /*Generated by MPS */ import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.VirtualFileManager; import jetbrains.mps.ide.platform.watching.ReloadManagerComponent; import jetbrains.mps.ide.platform.watching.FSChangesWatcher; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.EditableSModel; import jetbrains.mps.project.AbstractModule; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.concurrent.TimeUnit; import jetbrains.mps.smodel.SuspiciousModelHandler; import java.util.List; import java.util.Map; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Set; import java.util.HashSet; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.internal.collections.runtime.IVisitor; import jetbrains.mps.vfs.IFile; import jetbrains.mps.ide.vfs.VirtualFileUtils; import java.util.LinkedList; import jetbrains.mps.util.Computable; import org.jetbrains.mps.openapi.module.SRepository; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.ide.save.SaveRepositoryCommand; import java.util.ArrayList; import com.intellij.openapi.vcs.AbstractVcsHelper; import jetbrains.mps.smodel.ModelAccess; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import jetbrains.mps.vcs.MPSVcsManager; public class SuspiciousModelIndex implements ApplicationComponent { private final ProjectManager myProjectManager; private final VirtualFileManager myVirtualFileManager; private PlatformActivityTracker myPlatformWatcher; private SuspiciousModelIndex.MyTaskQueue myTaskQueue; private ReloadManagerComponent myReloadManager; public SuspiciousModelIndex(ProjectManager manager, FSChangesWatcher watcher, VirtualFileManager vfManager, ReloadManagerComponent reloadManager) { myProjectManager = manager; myReloadManager = reloadManager; myVirtualFileManager = vfManager; myPlatformWatcher = new PlatformActivityTracker(manager, vfManager, reloadManager); } public void addModel(SModel model, boolean isInConflict) { if (model instanceof EditableSModel && !(model.isReadOnly())) { myTaskQueue.addTask(new ConflictableModelAdapter((EditableSModel) model, isInConflict)); } } public void addModule(AbstractModule abstractModule, boolean inConflict) { myTaskQueue.addTask(new ConflictableModuleAdapter(abstractModule, inConflict)); } @NonNls @NotNull @Override public String getComponentName() { return "Suspicious Model Index"; } @Override public void initComponent() { myPlatformWatcher.activate(); myTaskQueue = new SuspiciousModelIndex.MyTaskQueue(); myTaskQueue.start(500, TimeUnit.MILLISECONDS); SuspiciousModelHandler.setHandler(new SuspiciousModelHandler() { @Override public void handleSuspiciousModel(SModel model, boolean inConflict) { addModel(model, inConflict); } @Override public void handleSuspiciousModule(AbstractModule module, boolean inConflict) { addModule(module, inConflict); } }); } @Override public void disposeComponent() { myTaskQueue.stop(); myPlatformWatcher.deactivate(); } /*package*/ void mergeLater(List<Conflictable> tasks) { // invoked from non-EDT thread (usually one of Application's pooled threads) final Map<Project, List<VirtualFile>> toMerge = new HashMap<Project, List<VirtualFile>>(); final Map<VirtualFile, Conflictable> fileToConflictable = new LinkedHashMap<VirtualFile, Conflictable>(); final Set<Conflictable> toReload = new HashSet<Conflictable>(); ListSequence.fromList(tasks).visitAll(new IVisitor<Conflictable>() { public void visit(Conflictable it) { IFile ifile = it.getFile(); if (isInConflict(ifile)) { VirtualFile vfile = VirtualFileUtils.getProjectVirtualFile(ifile); Conflictable prev = fileToConflictable.put(vfile, it); if (prev != null) { return; } Project project = getProjectForFile(vfile); List<VirtualFile> files = toMerge.get(project); if (files == null) { files = new LinkedList<VirtualFile>(); toMerge.put(project, files); } files.add(vfile); } else if (it.isConflictDetected() || it.needReloading()) { toReload.add(it); } } }); // runnable to get executed in EDT final Computable<Object> conflictableReload = new Computable<Object>() { public Object compute() { // see MPS-18743 for (Project project : toMerge.keySet()) { SRepository projectRepo = ProjectHelper.getProjectRepository(project); if (projectRepo == null) { continue; } new SaveRepositoryCommand(projectRepo).execute(); } for (final Project project : toMerge.keySet()) { List<VirtualFile> virtualFileList = new ArrayList<VirtualFile>(); virtualFileList.addAll(AbstractVcsHelper.getInstance(project).showMergeDialog(toMerge.get(project))); for (VirtualFile vfile : virtualFileList) { final Conflictable conflictable = fileToConflictable.get(vfile); if (conflictable != null) { SRepository projectRepo = ProjectHelper.getProjectRepository(project); if (projectRepo == null) { toReload.add(conflictable); } else { projectRepo.getModelAccess().executeCommand(new Runnable() { public void run() { conflictable.reloadFromDisk(); } }); } } } } // XXX no idea what to do with conflicts not from a project ModelAccess.instance().executeCommand(new Runnable() { public void run() { for (Conflictable conflictable : toReload) { conflictable.reloadFromDisk(); } } }); return null; } }; ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { myReloadManager.computeNoReload(conflictableReload); return; } }, ModalityState.defaultModalityState()); } public static SuspiciousModelIndex instance() { return ApplicationManager.getApplication().getComponent(SuspiciousModelIndex.class); } @Nullable private static Project getProjectForFile(VirtualFile f) { for (Project project : ProjectManager.getInstance().getOpenProjects()) { if (project.isDisposed()) { continue; } if (ProjectLevelVcsManager.getInstance(project).getVcsFor(f) != null) { return project; } } return null; } private static boolean isInConflict(IFile ifile) { // use of deprecated method not to get warning for non-project files (see VFU.getProjectVirtualFile impl) // However, it it possible to get IFile not from project here (e.g. reloaded model from distribution)? VirtualFile vfile = VirtualFileUtils.getVirtualFile(ifile); if ((vfile != null) && (vfile.exists())) { for (Project project : ProjectManager.getInstance().getOpenProjects()) { if (MPSVcsManager.getInstance(project).isInConflict(vfile)) { return true; } } } return false; } private class MyTaskQueue extends BaseTaskQueue<Conflictable> { @Override protected boolean isProcessingAllowed() { return myPlatformWatcher.isProcessingAllowed() && !(ModelAccess.instance().canRead()); } @Override protected void processTask(final List<Conflictable> tasks) { mergeLater(tasks); } } }