All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.vcs.log.graph.collapsing.CollapsedGraph Maven / Gradle / Ivy

/*
 * 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.collapsing;

import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.graph.api.EdgeFilter;
import com.intellij.vcs.log.graph.api.LinearGraph;
import com.intellij.vcs.log.graph.api.elements.GraphEdge;
import com.intellij.vcs.log.graph.api.elements.GraphNode;
import com.intellij.vcs.log.graph.utils.UnsignedBitSet;
import com.intellij.vcs.log.graph.utils.UpdatableIntToIntMap;
import com.intellij.vcs.log.graph.utils.impl.ListIntToIntMap;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntIterator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

public class CollapsedGraph {

  public static CollapsedGraph newInstance(@NotNull LinearGraph delegateGraph, @NotNull UnsignedBitSet matchedNodeId) {
    return new CollapsedGraph(delegateGraph, matchedNodeId, matchedNodeId.clone(), new EdgeStorage());
  }

  public static CollapsedGraph updateInstance(@NotNull CollapsedGraph prevCollapsedGraph, @NotNull LinearGraph newDelegateGraph) {
    UnsignedBitSet visibleNodesId = prevCollapsedGraph.myDelegateNodesVisibility.getNodeVisibilityById();
    return new CollapsedGraph(newDelegateGraph, prevCollapsedGraph.myMatchedNodeId, visibleNodesId, prevCollapsedGraph.myEdgeStorage);
  }

  @NotNull private final LinearGraph myDelegatedGraph;
  @NotNull private final UnsignedBitSet myMatchedNodeId;
  @NotNull private final GraphNodesVisibility myDelegateNodesVisibility;
  @NotNull private final UpdatableIntToIntMap myNodesMap;
  @NotNull private final EdgeStorage myEdgeStorage;
  @NotNull private final CompiledGraph myCompiledGraph;
  @NotNull private final AtomicReference myCurrentModification = new AtomicReference(null);


  private CollapsedGraph(@NotNull LinearGraph delegatedGraph,
                         @NotNull UnsignedBitSet matchedNodeId,
                         @NotNull UnsignedBitSet visibleNodesId,
                         @NotNull EdgeStorage edgeStorage) {
    myDelegatedGraph = delegatedGraph;
    myMatchedNodeId = matchedNodeId;
    myDelegateNodesVisibility = new GraphNodesVisibility(delegatedGraph, visibleNodesId);
    myNodesMap = ListIntToIntMap.newInstance(myDelegateNodesVisibility.asFlags());
    myEdgeStorage = edgeStorage;
    myCompiledGraph = new CompiledGraph();
  }

  @NotNull
  public LinearGraph getDelegatedGraph() {
    return myDelegatedGraph;
  }

  public boolean isNodeVisible(int delegateNodeIndex) {
    return myDelegateNodesVisibility.isVisible(delegateNodeIndex);
  }

  @NotNull
  public Modification startModification() {
    Modification modification = new Modification();
    if (myCurrentModification.compareAndSet(null, modification)) {
      return modification;
    }
    throw new RuntimeException("Can not start a new modification while the other one is still running.");
  }

  @NotNull
  public LinearGraph getCompiledGraph() {
    assertNotUnderModification();
    return myCompiledGraph;
  }

  public int convertToDelegateNodeIndex(int compiledNodeIndex) {
    assertNotUnderModification();
    return myNodesMap.getLongIndex(compiledNodeIndex);
  }

  @NotNull
  public UnsignedBitSet getMatchedNodeId() {
    return myMatchedNodeId;
  }

  // todo proper name
  public boolean isMyCollapsedEdge(int upNodeIndex, int downNodeIndex) {
    return new EdgeStorageWrapper(myEdgeStorage, myDelegatedGraph).hasEdge(upNodeIndex, downNodeIndex);
  }

  // everywhere in this class "nodeIndexes" means "node indexes in delegated graph"
  public class Modification {
    private static final int COLLECTING = 0;
    private static final int APPLYING = 1;
    private static final int DONE = 2;

    @NotNull private final EdgeStorageWrapper myEdgesToAdd = EdgeStorageWrapper.createSimpleEdgeStorage();
    @NotNull private final EdgeStorageWrapper myEdgesToRemove = EdgeStorageWrapper.createSimpleEdgeStorage();
    @NotNull private final TIntHashSet myNodesToHide = new TIntHashSet();
    @NotNull private final TIntHashSet myNodesToShow = new TIntHashSet();
    private boolean myClearEdges = false;
    private boolean myClearVisibility = false;

    private volatile int myProgress = COLLECTING;
    private int minAffectedNodeIndex = Integer.MAX_VALUE;
    private int maxAffectedNodeIndex = Integer.MIN_VALUE;

    private void touchIndex(int nodeIndex) {
      assert myProgress == COLLECTING;
      minAffectedNodeIndex = Math.min(minAffectedNodeIndex, nodeIndex);
      maxAffectedNodeIndex = Math.max(maxAffectedNodeIndex, nodeIndex);
    }

    private void touchAll() {
      assert myProgress == COLLECTING;
      minAffectedNodeIndex = 0;
      maxAffectedNodeIndex = getDelegatedGraph().nodesCount() - 1;
    }

    private void touchEdge(@NotNull GraphEdge edge) {
      assert myProgress == COLLECTING;
      if (edge.getUpNodeIndex() != null) touchIndex(edge.getUpNodeIndex());
      if (edge.getDownNodeIndex() != null) touchIndex(edge.getDownNodeIndex());
    }

    public void showNode(int nodeIndex) {
      assert myProgress == COLLECTING;
      myNodesToShow.add(nodeIndex);
      touchIndex(nodeIndex);
    }

    public void hideNode(int nodeIndex) {
      assert myProgress == COLLECTING;
      myNodesToHide.add(nodeIndex);
      touchIndex(nodeIndex);
    }

    public void createEdge(@NotNull GraphEdge edge) {
      assert myProgress == COLLECTING;
      myEdgesToAdd.createEdge(edge);
      touchEdge(edge);
    }

    public void removeEdge(@NotNull GraphEdge edge) { // todo add support for removing edge from delegate graph
      assert myProgress == COLLECTING;
      myEdgesToRemove.createEdge(edge);
      touchEdge(edge);
    }

    public void removeAdditionalEdges() {
      assert myProgress == COLLECTING;
      myClearEdges = true;
      touchAll();
    }

    public void resetNodesVisibility() {
      assert myProgress == COLLECTING;
      myClearVisibility = true;
      touchAll();
    }

    // "package private" means "I'm not entirely happy about this method"
    @NotNull
    /*package private*/ EdgeStorageWrapper getEdgesToAdd() {
      assert myProgress == COLLECTING;
      return myEdgesToAdd;
    }

    /*package private*/
    boolean isNodeHidden(int nodeIndex) {
      assert myProgress == COLLECTING;
      return myNodesToHide.contains(nodeIndex);
    }

    /*package private*/
    boolean isNodeShown(int nodeIndex) {
      assert myProgress == COLLECTING;
      return myNodesToShow.contains(nodeIndex);
    }

    public void apply() {
      assert myCurrentModification.get() == this;
      myProgress = APPLYING;

      if (myClearVisibility) {
        myDelegateNodesVisibility.setNodeVisibilityById(myMatchedNodeId.clone());
      }
      if (myClearEdges) {
        myEdgeStorage.removeAll();
      }

      TIntIterator toShow = myNodesToShow.iterator();
      while (toShow.hasNext()) {
        myDelegateNodesVisibility.show(toShow.next());
      }
      TIntIterator toHide = myNodesToHide.iterator();
      while (toHide.hasNext()) {
        myDelegateNodesVisibility.hide(toHide.next());
      }

      EdgeStorageWrapper edgeStorageWrapper = new EdgeStorageWrapper(myEdgeStorage, getDelegatedGraph());
      for (GraphEdge edge : myEdgesToAdd.getEdges()) {
        edgeStorageWrapper.createEdge(edge);
      }
      for (GraphEdge edge : myEdgesToRemove.getEdges()) {
        edgeStorageWrapper.removeEdge(edge);
      }

      if (minAffectedNodeIndex != Integer.MAX_VALUE && maxAffectedNodeIndex != Integer.MIN_VALUE) {
        myNodesMap.update(minAffectedNodeIndex, maxAffectedNodeIndex);
      }

      myProgress = DONE;
      myCurrentModification.set(null);
    }
  }

  private void assertNotUnderModification() {
    Modification modification = myCurrentModification.get();
    if (modification != null && modification.myProgress == Modification.APPLYING) {
      throw new IllegalStateException("CompiledGraph is under modification");
    }
  }

  private class CompiledGraph implements LinearGraph {
    @NotNull private final EdgeStorageWrapper myEdgeStorageWrapper;

    private CompiledGraph() {
      myEdgeStorageWrapper = new EdgeStorageWrapper(myEdgeStorage, this);
    }

    @Override
    public int nodesCount() {
      assertNotUnderModification();
      return myNodesMap.shortSize();
    }

    @NotNull
    private GraphEdge createEdge(@NotNull GraphEdge delegateEdge, @Nullable Integer upNodeIndex, @Nullable Integer downNodeIndex) {
      return new GraphEdge(upNodeIndex, downNodeIndex, delegateEdge.getTargetId(), delegateEdge.getType());
    }

    @Nullable
    private Integer compiledNodeIndex(@Nullable Integer delegateNodeIndex) {
      if (delegateNodeIndex == null) return null;
      if (myDelegateNodesVisibility.isVisible(delegateNodeIndex)) {
        return myNodesMap.getShortIndex(delegateNodeIndex);
      }
      else {
        return -1;
      }
    }

    private boolean isVisibleEdge(@Nullable Integer compiledUpNode, @Nullable Integer compiledDownNode) {
      if (compiledUpNode != null && compiledUpNode == -1) return false;
      if (compiledDownNode != null && compiledDownNode == -1) return false;
      return true;
    }

    @NotNull
    @Override
    public List getAdjacentEdges(int nodeIndex, @NotNull EdgeFilter filter) {
      assertNotUnderModification();
      List result = ContainerUtil.newSmartList();
      int delegateIndex = myNodesMap.getLongIndex(nodeIndex);

      // add delegate edges
      for (GraphEdge delegateEdge : myDelegatedGraph.getAdjacentEdges(delegateIndex, filter)) {
        Integer compiledUpIndex = compiledNodeIndex(delegateEdge.getUpNodeIndex());
        Integer compiledDownIndex = compiledNodeIndex(delegateEdge.getDownNodeIndex());
        if (isVisibleEdge(compiledUpIndex, compiledDownIndex)) result.add(createEdge(delegateEdge, compiledUpIndex, compiledDownIndex));
      }

      result.addAll(myEdgeStorageWrapper.getAdjacentEdges(nodeIndex, filter));

      return result;
    }

    @NotNull
    @Override
    public GraphNode getGraphNode(int nodeIndex) {
      assertNotUnderModification();
      int delegateIndex = myNodesMap.getLongIndex(nodeIndex);
      GraphNode graphNode = myDelegatedGraph.getGraphNode(delegateIndex);
      return new GraphNode(nodeIndex, graphNode.getType());
    }

    @Override
    public int getNodeId(int nodeIndex) {
      assertNotUnderModification();
      int delegateIndex = myNodesMap.getLongIndex(nodeIndex);
      return myDelegatedGraph.getNodeId(delegateIndex);
    }

    @Override
    @Nullable
    public Integer getNodeIndex(int nodeId) {
      assertNotUnderModification();
      Integer delegateIndex = myDelegatedGraph.getNodeIndex(nodeId);
      if (delegateIndex == null) return null;
      if (myDelegateNodesVisibility.isVisible(delegateIndex)) {
        return myNodesMap.getShortIndex(delegateIndex);
      }
      else {
        return null;
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy