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