package jetbrains.mps.vcs.platform.integration;
/*Generated by MPS */
import com.intellij.diff.FrameDiffTool;
import com.intellij.openapi.util.Key;
import org.jetbrains.mps.openapi.model.SNodeId;
import jetbrains.mps.vcs.diff.ui.common.Bounds;
import jetbrains.mps.vcs.diff.ui.ModelDifferenceViewer;
import org.jetbrains.annotations.NotNull;
import com.intellij.diff.DiffContext;
import com.intellij.diff.requests.ContentDiffRequest;
import jetbrains.mps.project.MPSProject;
import java.util.List;
import com.intellij.diff.contents.DiffContent;
import com.intellij.openapi.fileTypes.FileType;
import jetbrains.mps.fileTypes.MPSFileTypeFactory;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import org.jetbrains.mps.openapi.model.SModel;
import com.intellij.diff.requests.DiffRequest;
import com.intellij.diff.contents.EmptyContent;
import com.intellij.diff.contents.DocumentContent;
import com.intellij.diff.contents.FileContent;
import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent;
import jetbrains.mps.vcs.diff.merge.MergeTemporaryModel;
import jetbrains.mps.smodel.SModelReference;
import jetbrains.mps.smodel.SModelId;
import jetbrains.mps.vfs.IFile;
import jetbrains.mps.ide.vfs.VirtualFileUtils;
import jetbrains.mps.vcspersistence.VCSPersistenceUtil;
import jetbrains.mps.project.Project;
import jetbrains.mps.smodel.SModelFileTracker;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.util.Computable;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple;
public class ModelDiffViewer implements FrameDiffTool.DiffViewer {
public static Key<SNodeId> DIFF_SHOW_ROOTID = new Key<SNodeId>("MPS.diff.rootid");
public static Key<Bounds> DIFF_NAVIGATE_TO = new Key<Bounds>("MPS.diff.navigateto");
private ModelDifferenceViewer myViewer;
public ModelDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
MPSProject mpsProject = context.getProject().getComponent(MPSProject.class);
List<DiffContent> contents = request.getContents();
FileType type = (contents.get(0).getContentType() != null ? contents.get(0).getContentType() : contents.get(1).getContentType());
if (MPSFileTypeFactory.MPS_ROOT_FILE_TYPE.equals(type) || MPSFileTypeFactory.MPS_HEADER_FILE_TYPE.equals(type)) {
Tuples._2<SModel, SNodeId> oldModel = getModelAndRoot(mpsProject, contents.get(0), type);
Tuples._2<SModel, SNodeId> newModel = getModelAndRoot(mpsProject, contents.get(1), type);
SNodeId rootId = (newModel._1() != null ? newModel._1() : oldModel._1());
myViewer = new ModelDifferenceViewer(mpsProject, oldModel._0(), newModel._0(), rootId, false);
} else {
SModel oldModel = ModelDiffViewer.getModel(mpsProject, contents.get(0), type);
SModel newModel = ModelDiffViewer.getModel(mpsProject, contents.get(1), type);
// show one root only if requested
SNodeId rootId = request.getUserData(DIFF_SHOW_ROOTID);
myViewer = new ModelDifferenceViewer(mpsProject, oldModel, newModel, rootId, true);
// navigate to specific place in editor if requested
Bounds scrollTo = request.getUserData(DIFF_NAVIGATE_TO);
if (scrollTo != null) {
myViewer.navigate(scrollTo);
}
}
List<String> titles = request.getContentTitles();
myViewer.setContentTitles(titles.get(0), titles.get(1));
}
public static boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) {
if (!((request instanceof ContentDiffRequest))) {
return false;
}
List<DiffContent> contents = ((ContentDiffRequest) request).getContents();
if (contents.size() != 2) {
return false;
}
if (!((canShowContent(contents.get(0)) && canShowContent(contents.get(1))))) {
return false;
}
if (contents.get(0) instanceof EmptyContent && contents.get(1) instanceof EmptyContent) {
return false;
}
if (contents.get(0) instanceof ModelDiffContent || contents.get(1) instanceof ModelDiffContent) {
return true;
}
for (FileType type : ModelDiffTool.DIFF_SUPPORTED_TYPES) {
if (sameTypes(type, contents.get(0).getContentType(), contents.get(1).getContentType())) {
return true;
}
}
return false;
}
private static boolean canShowContent(@NotNull DiffContent content) {
return content instanceof ModelDiffContent || content instanceof EmptyContent || content instanceof DocumentContent || content instanceof FileContent;
}
private static boolean sameTypes(@NotNull FileType baseType, @Nullable FileType type1, @Nullable FileType type2) {
if (type1 != null && !(baseType.equals(type1))) {
return false;
}
if (type2 != null && !(baseType.equals(type2))) {
return false;
}
if (type1 == null && type2 == null) {
return false;
}
return true;
}
@NotNull
public JComponent getComponent() {
return myViewer.getComponent();
}
@Nullable
public JComponent getPreferredFocusedComponent() {
return myViewer.getPreferredFocusedComponent();
}
@NotNull
public FrameDiffTool.ToolbarComponents init() {
return new FrameDiffTool.ToolbarComponents();
}
public void dispose() {
if (myViewer != null) {
// in EDT?
myViewer.dispose();
}
}
@Nullable
private static SModel readModel(DiffContent content, FileType type) {
if (content instanceof EmptyContent) {
return new MergeTemporaryModel(new SModelReference(null, SModelId.generate(), "<empty merge model>"), true);
}
if (content instanceof FileContent) {
IFile file = VirtualFileUtils.toIFile(((FileContent) content).getFile());
return VCSPersistenceUtil.loadModel(file);
}
if (content instanceof DocumentContent) {
String text = ((DocumentContent) content).getDocument().getText();
return VCSPersistenceUtil.loadModel(text.getBytes(), type.getDefaultExtension());
}
return null;
}
@Nullable
private static IFile getFileByContent(@NotNull DiffContent content) {
if (content instanceof FileContent) {
return VirtualFileUtils.toIFile(((FileContent) content).getFile());
}
return null;
}
@Nullable
private static SModel getModel(Project mpsProject, @NotNull DiffContent content, FileType type) {
// already a model?
if (content instanceof ModelDiffContent) {
return ((ModelDiffContent) content).getModel();
}
// try to find model in repository
IFile file = getFileByContent(content);
if (file != null) {
SModel model = SModelFileTracker.getInstance(mpsProject.getRepository()).findModel(file);
if (model != null) {
return model;
}
}
// read model from content
return readModel(content, type);
}
@Nullable
private static Tuples._2<SModel, SNodeId> getModelAndRoot(Project mpsProject, DiffContent content, FileType type) {
final Wrappers._T<SModel> model = new Wrappers._T<SModel>(null);
// first try to find model in repository
IFile file = getFileByContent(content);
if (file != null) {
model.value = SModelFileTracker.getInstance(mpsProject.getRepository()).findModel(file.getParent());
}
if (model.value == null) {
model.value = readModel(content, type);
}
if (model.value == null) {
return null;
}
SNodeId nodeId = new ModelAccessHelper(mpsProject.getModelAccess()).runReadAction(new Computable<SNodeId>() {
public SNodeId compute() {
// todo: find root for models in repository by filename (important when new root added int per-root persistence)
if (ListSequence.fromList(SModelOperations.roots(model.value, null)).count() == 1) {
return ListSequence.fromList(SModelOperations.roots(model.value, null)).getElement(0).getNodeId();
}
return null;
}
});
return MultiTuple.<SModel,SNodeId>from(model.value, nodeId);
}
}