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);
}
}
}