package jetbrains.mps.vcs.diff.ui;
/*Generated by MPS */
import com.intellij.openapi.actionSystem.DataProvider;
import jetbrains.mps.project.MPSProject;
import jetbrains.mps.vcs.diff.ModelChangeSet;
import org.jetbrains.mps.openapi.model.SNodeId;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import com.intellij.ui.JBSplitter;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import com.intellij.openapi.diff.ex.DiffStatusBar;
import com.intellij.openapi.diff.impl.util.TextDiffType;
import jetbrains.mps.vcs.diff.ui.common.GoToNeighbourRootActions;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.util.SNodeOperations;
import org.jetbrains.mps.openapi.model.EditableSModel;
import jetbrains.mps.vcs.diff.ui.common.DiffModelUtil;
import jetbrains.mps.vcs.diff.ChangeSetBuilder;
import com.intellij.ui.ScrollPaneFactory;
import java.awt.Dimension;
import com.intellij.openapi.util.DimensionService;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import jetbrains.mps.vcs.diff.ui.common.Bounds;
import org.jetbrains.annotations.NonNls;
import jetbrains.mps.vcs.diff.ui.common.DiffModelTree;
import com.intellij.openapi.util.Ref;
import jetbrains.mps.workbench.action.BaseAction;
import java.util.List;
import java.util.ArrayList;
import jetbrains.mps.vcs.diff.changes.ModelChange;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.internal.collections.runtime.ITranslator2;
import jetbrains.mps.internal.collections.runtime.ISelector;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.vcs.diff.changes.ChangeType;
import jetbrains.mps.vcs.diff.changes.AddRootChange;
import jetbrains.mps.vcs.diff.changes.DeleteRootChange;
import jetbrains.mps.vcs.diff.ui.common.ChangeColors;
import java.util.Arrays;
public class ModelDifferenceViewer implements DataProvider {
private final MPSProject myProject;
private ModelChangeSet myChangeSet;
private ModelChangeSet myMetadataChangeSet;
private SNodeId myRootId;
private ModelDifferenceViewer.ModelDifferenceTree myTree = null;
private JPanel myComponent = new JPanel(new BorderLayout());
private JBSplitter myPanel = new JBSplitter(true, 0.25f);
private RootDifferencePane myRootDifferencePane = null;
private final JComponent myNoRootPanel = new JLabel("Select root to show", SwingConstants.CENTER);
private DiffStatusBar myStatusBar = new DiffStatusBar(TextDiffType.DIFF_TYPES);
private GoToNeighbourRootActions myGoToNeighbourRootActions;
private String[] myContentTitles = new String[]{"before", "after"};
private boolean myEditable;
private boolean myOldRegistered;
private boolean myNewRegistered;
public ModelDifferenceViewer(MPSProject project, final SModel oldModel, final SModel newModel, SNodeId rootId, boolean showTree) {
myProject = project;
myOldRegistered = SNodeOperations.isRegistered(oldModel);
myNewRegistered = SNodeOperations.isRegistered(newModel);
myEditable = newModel instanceof EditableSModel && myNewRegistered;
// register models in repository and create changeset
project.getRepository().getModelAccess().runWriteAction(new Runnable() {
public void run() {
if (!(myNewRegistered)) {
DiffModelUtil.renameModelAndRegister(newModel, "new");
}
if (!(myOldRegistered)) {
DiffModelUtil.renameModelAndRegister(oldModel, "old");
}
}
});
project.getRepository().getModelAccess().runReadAction(new Runnable() {
public void run() {
myChangeSet = ChangeSetBuilder.buildChangeSet(oldModel, newModel, true);
}
});
project.getRepository().getModelAccess().runWriteAction(new Runnable() {
public void run() {
SModel newMetaModel = MetadataUtil.createMetadataModel(newModel, "metadata_new", myEditable);
SModel oldMetaModel = MetadataUtil.createMetadataModel(oldModel, "metadata_old", false);
myMetadataChangeSet = ChangeSetBuilder.buildChangeSet(oldMetaModel, newMetaModel, true);
}
});
// create panels
myPanel.setSplitterProportionKey(getClass().getName() + "ModelTreeSplitter");
myPanel.setSecondComponent(myNoRootPanel);
if (showTree) {
myTree = new ModelDifferenceViewer.ModelDifferenceTree();
myPanel.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree));
// actions connected to model tree
myGoToNeighbourRootActions = new ModelDifferenceViewer.MyGoToNeighbourRootActions();
myGoToNeighbourRootActions.previous().registerCustomShortcutSet(GoToNeighbourRootActions.PREV_ROOT_SHORTCUT, myComponent);
myGoToNeighbourRootActions.next().registerCustomShortcutSet(GoToNeighbourRootActions.NEXT_ROOT_SHORTCUT, myComponent);
if (rootId != null) {
setCurrentRoot(rootId);
}
} else {
setCurrentRoot(rootId);
}
myComponent.add(myPanel, BorderLayout.CENTER);
myComponent.add(myStatusBar, BorderLayout.SOUTH);
Dimension size = DimensionService.getInstance().getSize(getDimensionServiceKey());
if (size == null) {
myComponent.setPreferredSize(new Dimension(500, 700));
}
}
public String getDimensionServiceKey() {
return getClass().getName();
}
@Nullable
public JComponent getPreferredFocusedComponent() {
return (myTree != null ? myTree : null);
}
public void dispose() {
syncMetadataChanges();
myProject.getRepository().getModelAccess().runWriteAction(new Runnable() {
public void run() {
MetadataUtil.dispose(myMetadataChangeSet.getOldModel());
MetadataUtil.dispose(myMetadataChangeSet.getNewModel());
if (!(myOldRegistered)) {
DiffModelUtil.unregisterModel(myChangeSet.getOldModel());
}
if (!(myNewRegistered)) {
DiffModelUtil.unregisterModel(myChangeSet.getNewModel());
}
}
});
if (myRootDifferencePane != null) {
myRootDifferencePane.dispose();
}
}
/*package*/ void rebuildChangeSets() {
ChangeSetBuilder.rebuildChangeSet(myChangeSet);
ChangeSetBuilder.rebuildChangeSet(myMetadataChangeSet);
if (myTree != null) {
myTree.rebuildLater();
}
}
public boolean isEditable() {
return myEditable;
}
private void syncMetadataChanges() {
if (myEditable) {
myProject.getModelAccess().executeCommand(new Runnable() {
public void run() {
MetadataUtil.applyMetadataChanges(myChangeSet.getNewModel(), myMetadataChangeSet.getNewModel());
}
});
}
}
public void setContentTitles(String before, String after) {
myContentTitles[0] = before;
myContentTitles[1] = after;
}
public void resetCurrentRoot() {
if (myRootDifferencePane == null) {
return;
}
myRootDifferencePane.unregisterShortcuts(myComponent);
myPanel.setSecondComponent(myNoRootPanel);
myRootDifferencePane.dispose();
myRootDifferencePane = null;
myRootId = null;
myStatusBar.setText("");
syncMetadataChanges();
}
private void changeCurrentRoot(@Nullable final SNodeId rootId) {
if (myRootDifferencePane != null && myRootId == rootId) {
return;
}
syncMetadataChanges();
myRootId = rootId;
myProject.getRepository().getModelAccess().runReadAction(new Runnable() {
public void run() {
ModelChangeSet changeSet = (rootId == null ? myMetadataChangeSet : myChangeSet);
SNodeId nodeId = (rootId == null ? ListSequence.fromList(SModelOperations.roots(myMetadataChangeSet.getOldModel(), null)).first().getNodeId() : rootId);
if (myRootDifferencePane == null) {
myRootDifferencePane = new RootDifferencePane(myProject, changeSet, nodeId, getNameForRoot(rootId), myContentTitles, myEditable, myStatusBar);
DefaultActionGroup actionGroup = new DefaultActionGroup();
actionGroup.addAll(myRootDifferencePane.getActions());
ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actionGroup, true);
myRootDifferencePane.registerShortcuts(myComponent);
JPanel panel = new JPanel(new BorderLayout());
panel.add(toolbar.getComponent(), BorderLayout.NORTH);
panel.add(myRootDifferencePane.getPanel(), BorderLayout.CENTER);
myPanel.setSecondComponent(panel);
myRootDifferencePane.navigateInitial(null);
} else {
myRootDifferencePane.setRootId(nodeId, changeSet);
}
}
});
}
private String getNameForRoot(SNodeId rootId) {
return (myTree != null ? myTree.getNameForRoot(rootId) : "");
}
public void setCurrentRoot(@Nullable SNodeId rootId) {
if (myTree != null) {
myTree.setSelected(rootId);
}
changeCurrentRoot(rootId);
}
@Nullable
public SNodeId getCurrentRoot() {
return myRootId;
}
public JComponent getComponent() {
return myComponent;
}
public void navigate(@Nullable final Bounds scrollTo) {
assert myRootDifferencePane != null;
myProject.getModelAccess().runReadAction(new Runnable() {
public void run() {
myRootDifferencePane.navigateInitial(scrollTo);
}
});
}
@Nullable
public Object getData(@NonNls String dataId) {
if (DiffModelTree.NODE_ID_DATAKEY.is(dataId)) {
return new Ref<SNodeId>(myRootId);
}
return null;
}
public class MyGoToNeighbourRootActions extends GoToNeighbourRootActions.GoToByTree {
public MyGoToNeighbourRootActions() {
super(myTree);
}
@Nullable
@Override
protected SNodeId getCurrentNodeId() {
return getCurrentRoot();
}
@Override
public void setCurrentNodeId(@Nullable SNodeId nodeId) {
changeCurrentRoot(nodeId);
}
}
private class ModelDifferenceTree extends DiffModelTree {
private ModelDifferenceTree() {
}
@Override
protected Iterable<BaseAction> getRootActions() {
List<BaseAction> actions = ListSequence.fromList(new ArrayList<BaseAction>());
if (myEditable) {
ListSequence.fromList(actions).addElement(new RevertRootsAction("roots") {
@Override
protected Iterable<ModelChange> getChanges() {
return Sequence.fromIterable(Sequence.fromArray(getSelectedNodes(DiffModelTree.RootTreeNode.class, null))).translate(new ITranslator2<DiffModelTree.RootTreeNode, ModelChange>() {
public Iterable<ModelChange> translate(DiffModelTree.RootTreeNode r) {
return myChangeSet.getChangesForRoot(r.getRootId());
}
});
}
@Override
protected void after() {
rebuildChangeSets();
}
@Override
protected String getRevertTitle() {
Iterable<SNodeId> roots = Sequence.fromIterable(Sequence.fromArray(getSelectedNodes(DiffModelTree.RootTreeNode.class, null))).select(new ISelector<DiffModelTree.RootTreeNode, SNodeId>() {
public SNodeId select(DiffModelTree.RootTreeNode rtn) {
return rtn.getRootId();
}
});
if (Sequence.fromIterable(roots).count() == 1) {
return (Sequence.fromIterable(roots).first() == null ? "Properties" : "Root");
} else if (Sequence.fromIterable(roots).any(new IWhereFilter<SNodeId>() {
public boolean accept(SNodeId r) {
return r == null;
}
})) {
return "Roots and Properties ";
}
return "Roots";
}
});
}
return actions;
}
@Override
protected void updateRootCustomPresentation(@NotNull DiffModelTree.RootTreeNode rootTreeNode) {
ChangeType compositeChangeType = ChangeType.CHANGE;
if (rootTreeNode.getRootId() != null) {
ModelChange firstChange = Sequence.fromIterable(myChangeSet.getChangesForRoot(rootTreeNode.getRootId())).first();
if (firstChange instanceof AddRootChange || firstChange instanceof DeleteRootChange) {
compositeChangeType = firstChange.getType();
} else if (firstChange == null) {
compositeChangeType = null;
}
} else {
if (myMetadataChangeSet == null || ListSequence.fromList(myMetadataChangeSet.getModelChanges()).isEmpty()) {
compositeChangeType = null;
}
}
rootTreeNode.setColor((compositeChangeType == null ? null : ChangeColors.getForTree(compositeChangeType)));
}
@Override
protected Iterable<SModel> getModels() {
return Arrays.asList(myChangeSet.getNewModel(), myChangeSet.getOldModel());
}
@Override
protected Iterable<SNodeId> getAffectedRoots() {
return myChangeSet.getAffectedRoots();
}
@Override
protected void onUnselect() {
resetCurrentRoot();
}
@Override
protected void onSelectRoot(@Nullable SNodeId rootId) {
changeCurrentRoot(rootId);
}
}
}