/*
* 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.vcs.log.graph.api.EdgeFilter;
import com.intellij.vcs.log.graph.api.LiteLinearGraph;
import com.intellij.vcs.log.graph.api.LiteLinearGraph.NodeFilter;
import com.intellij.vcs.log.graph.api.elements.GraphEdge;
import com.intellij.vcs.log.graph.utils.LinearGraphUtils;
import org.jetbrains.annotations.NotNull;
import static com.intellij.vcs.log.graph.api.elements.GraphEdgeType.*;
public class DottedFilterEdgesGenerator {
public static void update(@NotNull CollapsedGraph collapsedGraph, int upDelegateNodeIndex, int downDelegateNodeIndex) {
CollapsedGraph.Modification modification = collapsedGraph.startModification();
new DottedFilterEdgesGenerator(collapsedGraph, modification, upDelegateNodeIndex, downDelegateNodeIndex).update();
modification.apply();
}
@NotNull private final CollapsedGraph myCollapsedGraph;
@NotNull private final CollapsedGraph.Modification myModification;
@NotNull private final LiteLinearGraph myLiteDelegateGraph;
private final int myUpIndex;
private final int myDownIndex;
@NotNull private final ShiftNumber myNumbers;
private DottedFilterEdgesGenerator(@NotNull CollapsedGraph collapsedGraph,
@NotNull CollapsedGraph.Modification modification,
int upIndex,
int downIndex) {
myCollapsedGraph = collapsedGraph;
myModification = modification;
myLiteDelegateGraph = LinearGraphUtils.asLiteLinearGraph(collapsedGraph.getDelegatedGraph());
myUpIndex = upIndex;
myDownIndex = downIndex;
myNumbers = new ShiftNumber(upIndex, downIndex);
}
private boolean nodeIsVisible(int nodeIndex) {
return myCollapsedGraph.isNodeVisible(nodeIndex);
}
private void addDottedEdge(int nodeIndex1, int nodeIndex2) {
myModification.createEdge(new GraphEdge(nodeIndex1, nodeIndex2, null, DOTTED));
}
private void addDottedArrow(int nodeIndex, boolean isUp) {
myModification.createEdge(new GraphEdge(nodeIndex, null, null, isUp ? DOTTED_ARROW_UP : DOTTED_ARROW_DOWN));
}
// update specified range
private void update() {
downWalk();
cleanup();
upWalk();
}
private void cleanup() {
for (int currentNodeIndex = myUpIndex; currentNodeIndex <= myDownIndex; currentNodeIndex++) {
myNumbers.setNumber(currentNodeIndex, Integer.MAX_VALUE);
}
}
private boolean hasDottedEdges(int nodeIndex, boolean isUp) {
for (GraphEdge edge : myModification.getEdgesToAdd().getAdjacentEdges(nodeIndex, EdgeFilter.NORMAL_ALL)) {
if (edge.getType() == DOTTED) {
if (isUp && LinearGraphUtils.isEdgeUp(edge, nodeIndex)) return true;
if (!isUp && LinearGraphUtils.isEdgeDown(edge, nodeIndex)) return false;
}
}
return false;
}
private void addEdgeOrArrow(int currentNodeIndex, int anotherNodeIndex, boolean isUp) {
if (hasDottedEdges(currentNodeIndex, isUp)) {
if (nodeIsVisible(anotherNodeIndex)) {
addDottedEdge(currentNodeIndex, anotherNodeIndex);
}
else {
addDottedArrow(currentNodeIndex, isUp);
}
}
}
private void downWalk() {
for (int currentNodeIndex = myUpIndex; currentNodeIndex <= myDownIndex; currentNodeIndex++) {
if (nodeIsVisible(currentNodeIndex)) {
int nearlyUp = Integer.MIN_VALUE;
int maxAdjNumber = Integer.MIN_VALUE;
for (int upNode : myLiteDelegateGraph.getNodes(currentNodeIndex, NodeFilter.UP)) {
if (upNode < myUpIndex) {
addEdgeOrArrow(currentNodeIndex, upNode, true);
continue;
}
if (nodeIsVisible(upNode)) {
maxAdjNumber = Math.max(maxAdjNumber, myNumbers.getNumber(upNode));
}
else {
nearlyUp = Math.max(nearlyUp, myNumbers.getNumber(upNode));
}
}
if (nearlyUp == maxAdjNumber || nearlyUp == Integer.MIN_VALUE) {
myNumbers.setNumber(currentNodeIndex, maxAdjNumber);
}
else {
addDottedEdge(currentNodeIndex, nearlyUp);
myNumbers.setNumber(currentNodeIndex, nearlyUp);
}
}
else {
// node currentNodeIndex invisible
int nearlyUp = Integer.MIN_VALUE;
for (int upNode : myLiteDelegateGraph.getNodes(currentNodeIndex, NodeFilter.UP)) {
if (nodeIsVisible(upNode)) {
nearlyUp = Math.max(nearlyUp, upNode);
}
else {
if (upNode >= myUpIndex) nearlyUp = Math.max(nearlyUp, myNumbers.getNumber(upNode));
}
}
myNumbers.setNumber(currentNodeIndex, nearlyUp);
}
}
}
private void upWalk() {
for (int currentNodeIndex = myDownIndex; currentNodeIndex >= myUpIndex; currentNodeIndex--) {
if (nodeIsVisible(currentNodeIndex)) {
int nearlyDown = Integer.MAX_VALUE;
int minAdjNumber = Integer.MAX_VALUE;
for (int downNode : myLiteDelegateGraph.getNodes(currentNodeIndex, NodeFilter.DOWN)) {
if (downNode > myDownIndex) {
addEdgeOrArrow(currentNodeIndex, downNode, false);
continue;
}
if (nodeIsVisible(downNode)) {
minAdjNumber = Math.min(minAdjNumber, myNumbers.getNumber(downNode));
}
else {
nearlyDown = Math.min(nearlyDown, myNumbers.getNumber(downNode));
}
}
if (nearlyDown == minAdjNumber || nearlyDown == Integer.MAX_VALUE) {
myNumbers.setNumber(currentNodeIndex, minAdjNumber);
}
else {
addDottedEdge(currentNodeIndex, nearlyDown);
myNumbers.setNumber(currentNodeIndex, nearlyDown);
}
}
else {
// node currentNodeIndex invisible
int nearlyDown = Integer.MAX_VALUE;
for (int downNode : myLiteDelegateGraph.getNodes(currentNodeIndex, NodeFilter.DOWN)) {
if (nodeIsVisible(downNode)) {
nearlyDown = Math.min(nearlyDown, downNode);
}
else {
if (downNode <= myDownIndex) nearlyDown = Math.min(nearlyDown, myNumbers.getNumber(downNode));
}
}
myNumbers.setNumber(currentNodeIndex, nearlyDown);
}
}
}
static class ShiftNumber {
private final int startIndex;
private final int endIndex;
private final int[] numbers;
ShiftNumber(int startIndex, int endIndex) {
this.startIndex = startIndex;
this.endIndex = endIndex;
numbers = new int[endIndex - startIndex + 1];
}
private boolean inRange(int nodeIndex) {
return startIndex <= nodeIndex && nodeIndex <= endIndex;
}
protected int getNumber(int nodeIndex) {
if (inRange(nodeIndex)) return numbers[nodeIndex - startIndex];
return -1;
}
protected void setNumber(int nodeIndex, int value) {
if (inRange(nodeIndex)) {
numbers[nodeIndex - startIndex] = value;
}
}
}
}