package jetbrains.mps.vcs.changesmanager; /*Generated by MPS */ import com.intellij.openapi.components.AbstractProjectComponent; import java.util.Map; import org.jetbrains.mps.openapi.model.SModelReference; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import org.jetbrains.mps.openapi.module.SRepositoryContentAdapter; import com.intellij.openapi.vcs.FileStatusManager; import jetbrains.mps.project.MPSProject; import org.jetbrains.annotations.NotNull; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import jetbrains.mps.smodel.RepoListenerRegistrar; import jetbrains.mps.internal.collections.runtime.Sequence; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.EditableSModel; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.vfs.VirtualFile; import jetbrains.mps.vfs.IFile; import jetbrains.mps.ide.vfs.VirtualFileUtils; import jetbrains.mps.smodel.SModelFileTracker; import jetbrains.mps.internal.collections.runtime.CollectionSequence; import jetbrains.mps.smodel.ModuleRepositoryFacade; import jetbrains.mps.smodel.event.SModelCommandListener; import com.intellij.openapi.vcs.FileStatusListener; import org.jetbrains.mps.openapi.module.SModule; import jetbrains.mps.smodel.ModelsEventsCollector; import com.intellij.util.containers.MultiMap; import java.util.List; import jetbrains.mps.smodel.event.SModelEvent; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.Collection; import java.util.ArrayList; public class CurrentDifferenceRegistry extends AbstractProjectComponent { private final Map<SModelReference, CurrentDifference> myCurrentDifferences = MapSequence.fromMap(new HashMap<SModelReference, CurrentDifference>()); private final SRepositoryContentAdapter myModelRepositoryListener = new CurrentDifferenceRegistry.MyRepositoryListener(); private final SimpleCommandQueue myCommandQueue = new SimpleCommandQueue("ChangesManager command queue"); private final CurrentDifferenceRegistry.MyEventsCollector myEventsCollector = new CurrentDifferenceRegistry.MyEventsCollector(); private final CurrentDifferenceBroadcaster myGlobalBroadcaster = new CurrentDifferenceBroadcaster(myCommandQueue); private final CurrentDifferenceRegistry.MyFileStatusListener myFileStatusListener = new CurrentDifferenceRegistry.MyFileStatusListener(); private final FileStatusManager myFileStatusManager; private final MPSProject myMpsProject; public CurrentDifferenceRegistry(@NotNull Project project, MPSProject mpsProject, ProjectLevelVcsManager vcsManager, FileStatusManager fileStatusManager) { super(project); myFileStatusManager = fileStatusManager; myMpsProject = mpsProject; } @Override public void projectOpened() { myFileStatusManager.addFileStatusListener(myFileStatusListener); new RepoListenerRegistrar(myMpsProject.getRepository(), myModelRepositoryListener).attach(); } @Override public void projectClosed() { myFileStatusManager.removeFileStatusListener(myFileStatusListener); new RepoListenerRegistrar(myMpsProject.getRepository(), myModelRepositoryListener).detach(); synchronized (myCurrentDifferences) { for (CurrentDifference modelChangesManager : Sequence.fromIterable(MapSequence.fromMap(myCurrentDifferences).values())) { modelChangesManager.dispose(); } MapSequence.fromMap(myCurrentDifferences).clear(); } myEventsCollector.dispose(); myCommandQueue.dispose(); } public Project getProject() { return myProject; } /*package*/ void updateModel(@NotNull SModel model) { synchronized (myCurrentDifferences) { SModelReference modelRef = model.getReference(); if (MapSequence.fromMap(myCurrentDifferences).containsKey(modelRef)) { MapSequence.fromMap(myCurrentDifferences).get(modelRef).getChangesTracker().scheduleFullUpdate(false); return; } CurrentDifference cd = new CurrentDifference(this, (EditableSModel) model); MapSequence.fromMap(myCurrentDifferences).put(modelRef, cd); } } /*package*/ void updateModel(@Nullable VirtualFile file) { if (file == null) { return; } IFile iFile = VirtualFileUtils.toIFile(file); if (iFile == null) { return; } SModel modelDescriptor = SModelFileTracker.getInstance(myMpsProject.getRepository()).findModel(iFile); if (modelDescriptor == null || !(modelDescriptor.isLoaded())) { return; } updateModel(modelDescriptor); } public void updateLoadedModels() { for (SModel md : CollectionSequence.fromCollection(new ModuleRepositoryFacade(myMpsProject).getAllModels())) { if (md instanceof EditableSModel && !(md.isReadOnly())) { updateModel(md); } } } private void disposeModelChangesManager(@NotNull SModelReference modelReference) { synchronized (myCurrentDifferences) { if (MapSequence.fromMap(myCurrentDifferences).containsKey(modelReference)) { MapSequence.fromMap(myCurrentDifferences).get(modelReference).dispose(); MapSequence.fromMap(myCurrentDifferences).removeKey(modelReference); } } } @NotNull public CurrentDifference getCurrentDifference(@NotNull EditableSModel modelDescriptor) { synchronized (myCurrentDifferences) { SModelReference modelRef = modelDescriptor.getReference(); if (!(MapSequence.fromMap(myCurrentDifferences).containsKey(modelRef))) { MapSequence.fromMap(myCurrentDifferences).put(modelRef, new CurrentDifference(this, modelDescriptor)); } return MapSequence.fromMap(myCurrentDifferences).get(modelRef); } } public void addGlobalDifferenceListener(@NotNull CurrentDifferenceListener listener) { myGlobalBroadcaster.addDifferenceListener(listener); } public void removeGlobalDifferenceListener(@NotNull CurrentDifferenceListener listener) { myGlobalBroadcaster.removeDifferenceListener(listener); } @NotNull public SimpleCommandQueue getCommandQueue() { return myCommandQueue; } /*package*/ CurrentDifferenceBroadcaster getGlobalBroadcaster() { return myGlobalBroadcaster; } /*package*/ void addEventCollector(SModel model, SModelCommandListener listener) { myEventsCollector.addListener(model, listener); } /*package*/ void removeEventCollector(SModel model, SModelCommandListener listener) { myEventsCollector.removeListener(model, listener); } public static CurrentDifferenceRegistry getInstance(Project project) { return project.getComponent(CurrentDifferenceRegistry.class); } private class MyFileStatusListener implements FileStatusListener { public MyFileStatusListener() { } @Override public void fileStatusesChanged() { updateLoadedModels(); } @Override public void fileStatusChanged(@NotNull VirtualFile vf) { updateModel(vf); } } private class MyRepositoryListener extends SRepositoryContentAdapter { public MyRepositoryListener() { } @Override protected boolean isIncluded(SModule module) { return !(module.isReadOnly()); } @Override protected void startListening(SModel model) { if (model instanceof EditableSModel) { // we care about modelReplaced event model.addModelListener(this); updateModel(model); } } @Override protected void stopListening(SModel model) { model.removeModelListener(this); } @Override public void modelReplaced(SModel model) { if (!(model instanceof EditableSModel)) { return; } synchronized (myCurrentDifferences) { CurrentDifference difference = MapSequence.fromMap(myCurrentDifferences).get(model.getReference()); if (difference != null) { difference.getChangesTracker().scheduleFullUpdate(true); } } } @Override public void beforeModelRemoved(SModule module, SModel model) { disposeModelChangesManager(model.getReference()); super.beforeModelRemoved(module, model); } } private static class MyEventsCollector extends ModelsEventsCollector { private final MultiMap<SModelReference, SModelCommandListener> myListeners = new MultiMap<SModelReference, SModelCommandListener>(); public void addListener(SModel model, SModelCommandListener listener) { if (!(myListeners.containsKey(model.getReference()))) { // first time we see the model, tell EventCollector we are interested startListeningToModel(model); } myListeners.putValue(model.getReference(), listener); } private void removeListener(SModel model, SModelCommandListener listener) { myListeners.remove(model.getReference(), listener); if (!(myListeners.containsKey(model.getReference()))) { // no more listeners, no reason to listen any more stopListeningToModel(model); } } @Override protected void eventsHappened(List<SModelEvent> list) { MultiMap<SModelReference, SModelEvent> modelToEvents = new MultiMap<SModelReference, SModelEvent>(); for (SModelEvent event : ListSequence.fromList(list)) { SModelReference mr = event.getModel().getReference(); modelToEvents.putValue(mr, event); } for (SModelReference mr : SetSequence.fromSet(modelToEvents.keySet())) { Collection<SModelCommandListener> listeners = myListeners.get(mr); if (listeners == null) { continue; } final ArrayList<SModelEvent> eventsForTheModel = new ArrayList<SModelEvent>(modelToEvents.get(mr)); for (SModelCommandListener l : new ArrayList<SModelCommandListener>(listeners)) { l.eventsHappenedInCommand(eventsForTheModel); } } } } }