/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.vcs.log.graph.impl.facade; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.intellij.openapi.util.Condition; import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.graph.*; import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo; import com.intellij.vcs.log.graph.collapsing.BranchFilterController; import com.intellij.vcs.log.graph.collapsing.CollapsedController; import com.intellij.vcs.log.graph.impl.facade.bek.BekIntMap; import com.intellij.vcs.log.graph.impl.facade.bek.BekSorter; import com.intellij.vcs.log.graph.impl.permanent.*; import com.intellij.vcs.log.graph.linearBek.LinearBekController; import com.intellij.vcs.log.graph.utils.LinearGraphUtils; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; public class PermanentGraphImpl<CommitId> implements PermanentGraph<CommitId>, PermanentGraphInfo<CommitId> { @NotNull public static <CommitId> PermanentGraphImpl<CommitId> newInstance(@NotNull List<? extends GraphCommit<CommitId>> graphCommits, @NotNull final GraphColorManager<CommitId> graphColorManager, @NotNull Set<CommitId> branchesCommitId) { PermanentLinearGraphBuilder<CommitId> permanentLinearGraphBuilder = PermanentLinearGraphBuilder.newInstance(graphCommits); NotLoadedCommitsIdsGenerator<CommitId> idsGenerator = new NotLoadedCommitsIdsGenerator<>(); PermanentLinearGraphImpl linearGraph = permanentLinearGraphBuilder.build(idsGenerator); final PermanentCommitsInfoImpl<CommitId> commitIdPermanentCommitsInfo = PermanentCommitsInfoImpl.newInstance(graphCommits, idsGenerator.getNotLoadedCommits()); GraphLayoutImpl permanentGraphLayout = GraphLayoutBuilder.build(linearGraph, (nodeIndex1, nodeIndex2) -> { CommitId commitId1 = commitIdPermanentCommitsInfo.getCommitId(nodeIndex1); CommitId commitId2 = commitIdPermanentCommitsInfo.getCommitId(nodeIndex2); return graphColorManager.compareHeads(commitId2, commitId1); }); return new PermanentGraphImpl<>(linearGraph, permanentGraphLayout, commitIdPermanentCommitsInfo, graphColorManager, branchesCommitId); } @NotNull private final PermanentCommitsInfoImpl<CommitId> myPermanentCommitsInfo; @NotNull private final PermanentLinearGraphImpl myPermanentLinearGraph; @NotNull private final GraphLayoutImpl myPermanentGraphLayout; @NotNull private final GraphColorManager<CommitId> myGraphColorManager; @NotNull private final Set<Integer> myBranchNodeIds; @NotNull private final ReachableNodes myReachableNodes; @NotNull private final Supplier<BekIntMap> myBekIntMap; public PermanentGraphImpl(@NotNull PermanentLinearGraphImpl permanentLinearGraph, @NotNull GraphLayoutImpl permanentGraphLayout, @NotNull PermanentCommitsInfoImpl<CommitId> permanentCommitsInfo, @NotNull GraphColorManager<CommitId> graphColorManager, @NotNull Set<CommitId> branchesCommitId) { myPermanentGraphLayout = permanentGraphLayout; myPermanentCommitsInfo = permanentCommitsInfo; myPermanentLinearGraph = permanentLinearGraph; myGraphColorManager = graphColorManager; myBranchNodeIds = permanentCommitsInfo.convertToNodeIds(branchesCommitId); myReachableNodes = new ReachableNodes(LinearGraphUtils.asLiteLinearGraph(permanentLinearGraph)); myBekIntMap = Suppliers.memoize( () -> BekSorter.createBekMap(myPermanentLinearGraph, myPermanentGraphLayout, myPermanentCommitsInfo.getTimestampGetter())); } @NotNull @Override public VisibleGraph<CommitId> createVisibleGraph(@NotNull SortType sortType, @Nullable Set<CommitId> visibleHeads, @Nullable Set<CommitId> matchingCommits) { CascadeController baseController; if (sortType == SortType.Normal) { baseController = new BaseController(this); } else if (sortType == SortType.LinearBek) { baseController = new LinearBekController(new BekBaseController(this, myBekIntMap.get()), this); } else { baseController = new BekBaseController(this, myBekIntMap.get()); } // TODO this code is unclear and obviously needs some refactoring // I'll leave it for later to reorganize, and add some duplication for now, in order just to fix stuff CascadeController controller; if (matchingCommits != null) { controller = new FilteredController(baseController, this, myPermanentCommitsInfo.convertToNodeIds(matchingCommits)); if (visibleHeads != null) { controller = new BranchFilterController(controller, this, myPermanentCommitsInfo.convertToNodeIds(visibleHeads, true)); } } else if (sortType == SortType.LinearBek) { if (visibleHeads != null) { controller = new BranchFilterController(baseController, this, myPermanentCommitsInfo.convertToNodeIds(visibleHeads, true)); } else { controller = baseController; } } else { Set<Integer> idOfVisibleBranches = null; if (visibleHeads != null) { idOfVisibleBranches = myPermanentCommitsInfo.convertToNodeIds(visibleHeads, true); } controller = new CollapsedController(baseController, this, idOfVisibleBranches); } return new VisibleGraphImpl<>(controller, this, myGraphColorManager); } @NotNull @Override public List<GraphCommit<CommitId>> getAllCommits() { List<GraphCommit<CommitId>> result = ContainerUtil.newArrayList(); for (int index = 0; index < myPermanentLinearGraph.nodesCount(); index++) { CommitId commitId = myPermanentCommitsInfo.getCommitId(index); List<Integer> downNodes = LinearGraphUtils.getDownNodesIncludeNotLoad(myPermanentLinearGraph, index); List<CommitId> parentsCommitIds = myPermanentCommitsInfo.convertToCommitIdList(downNodes); GraphCommit<CommitId> graphCommit = GraphCommitImpl.createCommit(commitId, parentsCommitIds, myPermanentCommitsInfo.getTimestamp(index)); result.add(graphCommit); } return result; } @NotNull @Override public List<CommitId> getChildren(@NotNull CommitId commit) { int commitIndex = myPermanentCommitsInfo.getNodeId(commit); return myPermanentCommitsInfo.convertToCommitIdList(LinearGraphUtils.getUpNodes(myPermanentLinearGraph, commitIndex)); } @NotNull @Override public Set<CommitId> getContainingBranches(@NotNull CommitId commit) { int commitIndex = myPermanentCommitsInfo.getNodeId(commit); return myPermanentCommitsInfo.convertToCommitIdSet(myReachableNodes.getContainingBranches(commitIndex, myBranchNodeIds)); } @NotNull @Override public Condition<CommitId> getContainedInBranchCondition(@NotNull final Collection<CommitId> heads) { List<Integer> headIds = ContainerUtil.map(heads, head -> myPermanentCommitsInfo.getNodeId(head)); if (!heads.isEmpty() && ContainerUtil.getFirstItem(heads) instanceof Integer) { final TIntHashSet branchNodes = new TIntHashSet(); myReachableNodes.walk(headIds, node -> branchNodes.add((Integer)myPermanentCommitsInfo.getCommitId(node))); return new IntContainedInBranchCondition<>(branchNodes); } else { final Set<CommitId> branchNodes = ContainerUtil.newHashSet(); myReachableNodes.walk(headIds, node -> branchNodes.add(myPermanentCommitsInfo.getCommitId(node))); return new ContainedInBranchCondition<>(branchNodes); } } @NotNull public PermanentCommitsInfoImpl<CommitId> getPermanentCommitsInfo() { return myPermanentCommitsInfo; } @NotNull public PermanentLinearGraphImpl getLinearGraph() { return myPermanentLinearGraph; } @NotNull public GraphLayoutImpl getPermanentGraphLayout() { return myPermanentGraphLayout; } @NotNull public Set<Integer> getBranchNodeIds() { return myBranchNodeIds; } private static class NotLoadedCommitsIdsGenerator<CommitId> implements NotNullFunction<CommitId, Integer> { @NotNull private final Map<Integer, CommitId> myNotLoadedCommits = ContainerUtil.newHashMap(); @NotNull @Override public Integer fun(CommitId dom) { int nodeId = -(myNotLoadedCommits.size() + 2); myNotLoadedCommits.put(nodeId, dom); return nodeId; } @NotNull public Map<Integer, CommitId> getNotLoadedCommits() { return myNotLoadedCommits; } } private static class IntContainedInBranchCondition<CommitId> implements Condition<CommitId> { private final TIntHashSet myBranchNodes; public IntContainedInBranchCondition(TIntHashSet branchNodes) { myBranchNodes = branchNodes; } @Override public boolean value(CommitId commitId) { return myBranchNodes.contains((Integer)commitId); } } private static class ContainedInBranchCondition<CommitId> implements Condition<CommitId> { private final Set<CommitId> myBranchNodes; public ContainedInBranchCondition(Set<CommitId> branchNodes) { myBranchNodes = branchNodes; } @Override public boolean value(CommitId commitId) { return myBranchNodes.contains(commitId); } } }