package jetbrains.mps.vcs.platform.integration;
/*Generated by MPS */
import com.intellij.diff.merge.MergeTool;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import com.intellij.diff.merge.MergeContext;
import com.intellij.diff.merge.TextMergeRequest;
import jetbrains.mps.vcs.diff.ui.merge.MergeModelsPanel;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import com.intellij.diff.merge.MergeRequest;
import java.util.List;
import com.intellij.diff.contents.DocumentContent;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.diff.contents.FileContent;
import java.io.File;
import jetbrains.mps.vcs.platform.util.MergeBackupUtil;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.persistence.FilePerRootDataSource;
import jetbrains.mps.vfs.FileSystem;
import jetbrains.mps.project.MPSExtentions;
import jetbrains.mps.vcspersistence.VCSPersistenceUtil;
import jetbrains.mps.vcs.util.MergeConstants;
import jetbrains.mps.vcs.diff.ui.merge.ISaveMergedModel;
import com.intellij.openapi.application.ApplicationManager;
import jetbrains.mps.smodel.ModelAccess;
import jetbrains.mps.persistence.PersistenceVersionAware;
import com.intellij.openapi.ui.Messages;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import org.apache.log4j.Level;
import jetbrains.mps.util.FileUtil;
import java.io.IOException;
import javax.swing.JComponent;
import com.intellij.openapi.util.BooleanGetter;
import javax.swing.Action;
import com.intellij.diff.merge.MergeResult;
import com.intellij.diff.merge.MergeUtil;
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
import com.intellij.openapi.diff.DiffBundle;
import com.intellij.diff.contents.DiffContent;
import com.intellij.openapi.editor.Document;
import java.nio.charset.Charset;
import com.intellij.openapi.vfs.CharsetToolkit;
import jetbrains.mps.persistence.PersistenceUtil;
public class ModelMergeViewer implements MergeTool.MergeViewer {
private static final Logger LOG_276369528 = LogManager.getLogger(ModelMergeViewer.class);
private static final Logger LOG = LogManager.getLogger(ModelMergeViewer.class);
private MergeContext myMergeContext;
private TextMergeRequest myMergeRequest;
private MergeModelsPanel myPanel;
public ModelMergeViewer(MergeContext context, TextMergeRequest request, SModel base, SModel mine, SModel repo) {
myMergeContext = context;
myMergeRequest = request;
myPanel = new MergeModelsPanel(context.getProject(), base, mine, repo, request);
}
@Nullable
public static ModelMergeViewer createComponent(@NotNull MergeContext context, @NotNull MergeRequest request) {
try {
TextMergeRequest textRequest = (TextMergeRequest) request;
List<DocumentContent> contents = textRequest.getContents();
byte[][] byteContents = {getContentBytes(ListSequence.fromList(contents).getElement(0)), getContentBytes(ListSequence.fromList(contents).getElement(1)), getContentBytes(ListSequence.fromList(contents).getElement(2))};
final VirtualFile file = ((FileContent) textRequest.getOutputContent()).getFile();
final File backupFile = MergeBackupUtil.zipModel(byteContents, file);
final Wrappers._T<String> ext = new Wrappers._T<String>(file.getExtension());
if (FilePerRootDataSource.isPerRootPersistenceFile(FileSystem.getInstance().getFileByPath(file.getPath()))) {
// load model partially from per-root persistence with "normal" persistence loading
ext.value = MPSExtentions.MODEL;
}
final SModel baseModel = VCSPersistenceUtil.loadModel(byteContents[MergeConstants.ORIGINAL], ext.value);
SModel mineModel = loadModel(byteContents[MergeConstants.CURRENT], ext.value);
SModel newModel = loadModel(byteContents[MergeConstants.LAST_REVISION], ext.value);
if (baseModel != null && mineModel != null && newModel != null) {
final ModelMergeViewer viewer = new ModelMergeViewer(context, textRequest, baseModel, mineModel, newModel);
ISaveMergedModel saver = new ISaveMergedModel() {
public boolean save(MergeModelsPanel parent, final SModel resultModel) {
ApplicationManager.getApplication().assertIsDispatchThread();
final Wrappers._boolean closeDialog = new Wrappers._boolean(true);
final Wrappers._T<String> resultContent = new Wrappers._T<String>(null);
ModelAccess.instance().runReadAction(new Runnable() {
public void run() {
try {
resultContent.value = ModelMergeViewer.saveModel(resultModel, file, ext.value);
} catch (Throwable error) {
// this can be when saving in 9 persistence after merge with 8 persistence => trying to save in 8th
if (baseModel instanceof PersistenceVersionAware && resultModel instanceof PersistenceVersionAware && ((PersistenceVersionAware) baseModel).getPersistenceVersion() == 8 && ((PersistenceVersionAware) resultModel).getPersistenceVersion() == 9) {
String message = "The merged model cannot be saved using the new 9th persistence." + " The most-likely reason: one of the languages used in this model has not yet been generated." + " You can revert the changes, merge and generate the used languages first and only then merge this model again." + " Alternatively, you can save the model in old 8th persistence version and then migrate it to the latest persistence, after all used languages will have been merged manually.";
int result = Messages.showYesNoCancelDialog(viewer.getComponent(), message, "Save model " + SModelOperations.getModelName(resultModel), "Save in 8th persistence", "Revert changes", "Return to merge", Messages.getWarningIcon());
switch (result) {
case Messages.YES:
((PersistenceVersionAware) resultModel).setPersistenceVersion(8);
resultContent.value = ModelMergeViewer.saveModel(resultModel, file, ext.value);
break;
case Messages.NO:
resultContent.value = null;
break;
default:
closeDialog.value = false;
break;
}
} else {
if (LOG_276369528.isEnabledFor(Level.ERROR)) {
LOG_276369528.error("Cannot save merge resulting model " + SModelOperations.getModelName(resultModel), error);
}
}
}
}
});
if (resultContent.value != null) {
ModelAccess.instance().runWriteAction(new Runnable() {
public void run() {
try {
file.setBinaryContent(resultContent.value.getBytes(FileUtil.DEFAULT_CHARSET));
} catch (IOException e) {
if (LOG_276369528.isEnabledFor(Level.ERROR)) {
LOG_276369528.error("Cannot save merge result into " + file.getPath(), e);
}
}
}
});
MergeBackupUtil.packMergeResult(backupFile, file.getName(), resultContent.value);
}
return closeDialog.value;
}
};
viewer.myPanel.setSaver(saver);
return viewer;
}
} catch (IOException e) {
LOG.error(null, e);
}
return null;
}
@NotNull
public JComponent getComponent() {
return myPanel;
}
@Nullable
public JComponent getPreferredFocusedComponent() {
return myPanel.getPreferredFocusedComponent();
}
public MergeTool.ToolbarComponents init() {
MergeTool.ToolbarComponents components = new MergeTool.ToolbarComponents();
components.toolbarActions = myPanel.getToolbarActions();
components.closeHandler = new BooleanGetter() {
public boolean get() {
return allowCancel();
}
};
return components;
}
@Nullable
public Action getResolveAction(@NotNull final MergeResult result) {
String caption = MergeUtil.getResolveActionTitle(result, myMergeRequest, myMergeContext);
switch (result) {
case CANCEL:
return new AbstractAction(caption) {
public void actionPerformed(ActionEvent e) {
if (allowCancel()) {
myMergeContext.finishMerge(MergeResult.CANCEL);
}
}
};
case RESOLVED:
return new AbstractAction(caption) {
public void actionPerformed(ActionEvent e) {
if (myPanel.saveResults()) {
myMergeContext.finishMerge(MergeResult.RESOLVED);
}
}
};
default:
// Accept LEFT or Accept RIGHT
// can't call finishMerge(LEFT/RIGHT) directly, as we probably want accept only one root
// we can't just accept old byte[] content, because this could break model
return null;
}
}
public void dispose() {
myPanel.dispose();
}
private boolean allowCancel() {
return Messages.showYesNoDialog(getComponent().getRootPane(), DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"), DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) == Messages.YES;
}
@Nullable
private static byte[] getContentBytes(@NotNull DiffContent content) {
Document document = ((DocumentContent) content).getDocument();
Charset charset = ((DocumentContent) content).getCharset();
if (charset == null) {
charset = CharsetToolkit.getDefaultSystemCharset();
}
return document.getText().getBytes(charset);
}
@Nullable
private static SModel loadModel(byte[] bytes, String ext) {
if (bytes.length == 0) {
return null;
}
return VCSPersistenceUtil.loadModel(bytes, ext);
}
@Nullable
private static String saveModel(SModel resultModel, VirtualFile file, String ext) {
// file is just to check and select proper format for per-root persistence
if (FilePerRootDataSource.isPerRootPersistenceFile(FileSystem.getInstance().getFileByPath(file.getPath()))) {
return PersistenceUtil.savePerRootModel(resultModel, MPSExtentions.MODEL_HEADER.equals(file.getExtension()));
} else {
return PersistenceUtil.saveModel(resultModel, ext);
}
}
}