package jetbrains.mps.vcs.platform.integration;
/*Generated by MPS */
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import java.util.List;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.project.Project;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.TreeSet;
import java.util.Comparator;
import com.intellij.openapi.vcs.changes.Change;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.changes.ContentRevision;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import com.intellij.openapi.vcs.merge.MergeProvider;
import jetbrains.mps.internal.collections.runtime.Sequence;
import com.intellij.openapi.vcs.merge.MergeData;
import jetbrains.mps.vfs.IFile;
import jetbrains.mps.vfs.FileSystem;
import jetbrains.mps.persistence.FilePerRootDataSource;
import jetbrains.mps.project.MPSExtentions;
import org.jetbrains.mps.openapi.model.SModel;
import org.apache.log4j.Level;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.vcs.diff.merge.MergeSession;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.vcs.diff.changes.ModelChange;
import com.intellij.openapi.progress.Task;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import jetbrains.mps.progress.ProgressMonitorAdapter;
import org.jetbrains.mps.openapi.module.ModelAccess;
import org.jetbrains.mps.openapi.persistence.PersistenceFacade;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import jetbrains.mps.persistence.PersistenceUtil;
import jetbrains.mps.persistence.PersistenceVersionAware;
import jetbrains.mps.ide.ThreadUtils;
import jetbrains.mps.util.FileUtil;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import java.io.IOException;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.vcspersistence.VCSPersistenceUtil;
import com.intellij.openapi.vcs.VcsException;
public class ConflictingModelsUtil {
private static final Logger LOG = LogManager.getLogger(ConflictingModelsUtil.class);
public static List<VirtualFile> getConflictingModelFiles(Project proj) {
Set<VirtualFile> conflictedFiles = SetSequence.fromSet(new TreeSet<VirtualFile>(new Comparator<VirtualFile>() {
public int compare(VirtualFile a, VirtualFile b) {
return a.getPresentableUrl().compareTo(b.getPresentableUrl());
}
}));
for (Change change : CollectionSequence.fromCollection(ChangeListManager.getInstance(proj).getAllChanges())) {
if (change.getFileStatus() == FileStatus.MERGED_WITH_CONFLICTS) {
ContentRevision before = change.getBeforeRevision();
ContentRevision after = change.getAfterRevision();
if (before != null) {
VirtualFile file = before.getFile().getVirtualFile();
if (file != null) {
SetSequence.fromSet(conflictedFiles).addElement(file);
}
}
if (after != null) {
VirtualFile file = after.getFile().getVirtualFile();
if (file != null) {
SetSequence.fromSet(conflictedFiles).addElement(file);
}
}
}
}
return SetSequence.fromSet(conflictedFiles).where(new IWhereFilter<VirtualFile>() {
public boolean accept(VirtualFile f) {
return SetSequence.fromSet(ModelMergeTool.SUPPORTED_TYPES).contains(f.getFileType());
}
}).toListSequence();
}
public static boolean hasResolvableConflicts(Project project, MergeProvider provider, Iterable<VirtualFile> conflictedFiles) {
for (VirtualFile file : Sequence.fromIterable(conflictedFiles)) {
MergeData mergeData = loadRevisions(provider, file);
IFile iFile = FileSystem.getInstance().getFileByPath(file.getPath());
String ext = file.getExtension();
if (FilePerRootDataSource.isPerRootPersistenceFile(iFile)) {
ext = MPSExtentions.MODEL;
}
final SModel baseModel = loadModel(mergeData.ORIGINAL, ext);
final SModel mineModel = loadModel(mergeData.CURRENT, ext);
final SModel repoModel = loadModel(mergeData.LAST, ext);
if (baseModel == null || mineModel == null || repoModel == null) {
if (LOG.isEnabledFor(Level.WARN)) {
LOG.warn("Couldn't read model " + file.getPath());
}
continue;
}
// read action:
final Wrappers._T<MergeSession> mergeSession = new Wrappers._T<MergeSession>();
ProjectHelper.getModelAccess(project).runReadAction(new Runnable() {
public void run() {
mergeSession.value = MergeSession.createMergeSession(baseModel, mineModel, repoModel);
}
});
int conflictingChangesCount = Sequence.fromIterable(mergeSession.value.getAllChanges()).where(new IWhereFilter<ModelChange>() {
public boolean accept(ModelChange c) {
return Sequence.fromIterable(mergeSession.value.getConflictedWith(c)).isNotEmpty();
}
}).count();
if (conflictingChangesCount == 0) {
return true;
}
}
return false;
}
public static ConflictingModelsUtil.ModelConflictResolver getModelConflictResolverTask(Project project, MergeProvider provider, com.intellij.openapi.vcs.merge.MergeSession session, List<VirtualFile> conflictedFiles) {
return new ConflictingModelsUtil.ModelConflictResolver(project, provider, session, conflictedFiles);
}
public static class ModelConflictResolver extends Task.Modal {
private Project myProject;
private MergeProvider myProvider;
private com.intellij.openapi.vcs.merge.MergeSession mySession;
private List<VirtualFile> myConflictedModelFiles;
private List<VirtualFile> myResolvedModelFiles = ListSequence.fromList(new ArrayList<VirtualFile>());
private List<VirtualFile> myUnresolvedModelFiles = ListSequence.fromList(new ArrayList<VirtualFile>());
public ModelConflictResolver(Project project, MergeProvider provider, com.intellij.openapi.vcs.merge.MergeSession session, List<VirtualFile> conflictedFiles) {
super(project, "Resolving conflicts in models", true);
myProvider = provider;
mySession = session;
myProject = project;
myConflictedModelFiles = conflictedFiles;
}
public List<VirtualFile> getResolvedFiles() {
return myResolvedModelFiles;
}
public List<VirtualFile> getUnresolvedFiles() {
// list of old files with possible errors because of 8th persistence merge
return myUnresolvedModelFiles;
}
public void run(@NotNull ProgressIndicator indicator) {
final ProgressMonitor monitor = new ProgressMonitorAdapter(indicator);
monitor.start("Resolving...", ListSequence.fromList(myConflictedModelFiles).count());
final ModelAccess ma = ProjectHelper.getModelAccess(myProject);
try {
for (final VirtualFile file : ListSequence.fromList(myConflictedModelFiles)) {
monitor.step(file.getCanonicalPath());
final IFile iFile = FileSystem.getInstance().getFileByPath(file.getPath());
final Wrappers._T<String> ext = new Wrappers._T<String>(file.getExtension());
if (FilePerRootDataSource.isPerRootPersistenceFile(iFile)) {
ext.value = MPSExtentions.MODEL;
}
final Wrappers._T<SModel> baseModel = new Wrappers._T<SModel>(null);
final Wrappers._T<SModel> mineModel = new Wrappers._T<SModel>(null);
final Wrappers._T<SModel> repoModel = new Wrappers._T<SModel>(null);
if (PersistenceFacade.getInstance().getModelFactory(ext.value) != null) {
MergeData mergeData = loadRevisions(myProvider, file);
if (mergeData != null) {
baseModel.value = loadModel(mergeData.ORIGINAL, ext.value);
mineModel.value = loadModel(mergeData.CURRENT, ext.value);
repoModel.value = loadModel(mergeData.LAST, ext.value);
}
}
if (baseModel.value == null || mineModel.value == null || repoModel.value == null) {
monitor.advance(1);
if (monitor.isCanceled()) {
return;
}
continue;
}
final Wrappers._T<MergeSession> mergeSession = new Wrappers._T<MergeSession>(null);
// read action:
ma.runReadAction(new Runnable() {
public void run() {
mergeSession.value = MergeSession.createMergeSession(baseModel.value, mineModel.value, repoModel.value);
}
});
int conflictingChangesCount = Sequence.fromIterable(mergeSession.value.getAllChanges()).where(new IWhereFilter<ModelChange>() {
public boolean accept(ModelChange c) {
return Sequence.fromIterable(mergeSession.value.getConflictedWith(c)).isNotEmpty();
}
}).count();
if (conflictingChangesCount != 0) {
if (LOG.isInfoEnabled()) {
LOG.info("there are still conflicted changes in " + SModelOperations.getModelName(baseModel.value));
}
monitor.advance(1);
if (monitor.isCanceled()) {
return;
}
continue;
}
if (LOG.isInfoEnabled()) {
LOG.info("no conflicting changes in " + SModelOperations.getModelName(baseModel.value));
}
final Wrappers._T<String> resultContent = new Wrappers._T<String>(null);
ma.runReadAction(new Runnable() {
public void run() {
mergeSession.value.applyChanges(mergeSession.value.getAllChanges());
SModel resultModel = mergeSession.value.getResultModel();
if (resultModel == null) {
} else if (mergeSession.value.hasIdsToRestore()) {
if (LOG.isInfoEnabled()) {
LOG.info(String.format("%s: node id duplication detected, should merge in UI.", SModelOperations.getModelName(baseModel.value)));
}
} else {
try {
if (FilePerRootDataSource.isPerRootPersistenceFile(iFile)) {
resultContent.value = PersistenceUtil.savePerRootModel(resultModel, file.getExtension().equals(MPSExtentions.MODEL_HEADER));
} else {
resultContent.value = PersistenceUtil.saveModel(resultModel, ext.value);
}
} catch (Throwable error) {
// this can be when saving in 9 persistence after merge with 8 persistence => leave it for UI merge
if (baseModel.value instanceof PersistenceVersionAware && resultModel instanceof PersistenceVersionAware && ((PersistenceVersionAware) baseModel.value).getPersistenceVersion() == 8 && ((PersistenceVersionAware) resultModel).getPersistenceVersion() == 9) {
ListSequence.fromList(myUnresolvedModelFiles).addElement(file);
} else {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("Cannot save merge resulting model " + SModelOperations.getModelName(resultModel), error);
}
}
}
}
}
});
if (resultContent.value != null) {
ThreadUtils.runInUIThreadAndWait(new Runnable() {
public void run() {
ma.runWriteAction(new Runnable() {
public void run() {
try {
file.setBinaryContent(resultContent.value.getBytes(FileUtil.DEFAULT_CHARSET));
check_2bxr1q_a1a0a0a0a0a0a0u0a0d0m6(mySession, file);
VcsDirtyScopeManager.getInstance(myProject).fileDirty(file);
ListSequence.fromList(myResolvedModelFiles).addElement(file);
} catch (IOException e) {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("Cannot save merge result into " + file.getPath(), e);
}
}
}
});
}
});
}
monitor.advance(1);
if (monitor.isCanceled()) {
return;
}
}
} finally {
monitor.done();
}
}
private static void check_2bxr1q_a1a0a0a0a0a0a0u0a0d0m6(com.intellij.openapi.vcs.merge.MergeSession checkedDotOperand, VirtualFile file) {
if (null != checkedDotOperand) {
checkedDotOperand.conflictResolvedForFile(file, com.intellij.openapi.vcs.merge.MergeSession.Resolution.Merged);
}
}
}
@Nullable
private static SModel loadModel(byte[] bytes, String ext) {
if (bytes.length == 0) {
return null;
}
return VCSPersistenceUtil.loadModel(bytes, ext);
}
@Nullable
public static MergeData loadRevisions(final MergeProvider provider, final VirtualFile file) {
final Wrappers._T<MergeData> mergeData = new Wrappers._T<MergeData>(null);
ThreadUtils.runInUIThreadAndWait(new Runnable() {
public void run() {
try {
mergeData.value = provider.loadRevisions(file);
} catch (VcsException e) {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("Error loading revisions to merge", e);
}
}
}
});
return mergeData.value;
}
}