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