/* * 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.parser; import com.intellij.openapi.util.Pair; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; 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.GraphEdgeType; import com.intellij.vcs.log.graph.api.elements.GraphNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Map; import static com.intellij.vcs.log.graph.parser.CommitParser.nextSeparatorIndex; import static com.intellij.vcs.log.graph.parser.CommitParser.toLines; import static com.intellij.vcs.log.graph.parser.EdgeNodeCharConverter.parseGraphEdgeType; import static com.intellij.vcs.log.graph.parser.EdgeNodeCharConverter.parseGraphNodeType; public class LinearGraphParser { public static LinearGraph parse(@NotNull String in) { List<GraphNode> graphNodes = new ArrayList<>(); Map<GraphNode, List<String>> edges = ContainerUtil.newHashMap(); Map<Integer, Integer> nodeIdToNodeIndex = ContainerUtil.newHashMap(); for (String line : toLines(in)) { // parse input and create nodes Pair<Pair<Integer, GraphNode>, List<String>> graphNodePair = parseLine(line, graphNodes.size()); GraphNode graphNode = graphNodePair.first.second; edges.put(graphNode, graphNodePair.second); nodeIdToNodeIndex.put(graphNodePair.first.first, graphNodes.size()); graphNodes.add(graphNode); } MultiMap<Integer, GraphEdge> upEdges = MultiMap.create(); MultiMap<Integer, GraphEdge> downEdges = MultiMap.create(); for (GraphNode graphNode : graphNodes) { // create edges for (String strEdge : edges.get(graphNode)) { Pair<Integer, Character> pairEdge = parseNumberWithChar(strEdge); GraphEdgeType type = parseGraphEdgeType(pairEdge.second); GraphEdge edge; switch (type) { case USUAL: case DOTTED: Integer downNodeIndex = nodeIdToNodeIndex.get(pairEdge.first); assert downNodeIndex != null; edge = GraphEdge.createNormalEdge(graphNode.getNodeIndex(), downNodeIndex, type); break; case NOT_LOAD_COMMIT: case DOTTED_ARROW_DOWN: case DOTTED_ARROW_UP: edge = GraphEdge.createEdgeWithTargetId(graphNode.getNodeIndex(), pairEdge.first, type); break; default: throw new IllegalStateException("Unknown type: " + type); } if (edge.getUpNodeIndex() != null) downEdges.putValue(edge.getUpNodeIndex(), edge); if (edge.getDownNodeIndex() != null) upEdges.putValue(edge.getDownNodeIndex(), edge); } } return new TestLinearGraphWithElementsInfo(graphNodes, upEdges, downEdges); } /** * Example input line: * 0_U|-1_U 2_D */ public static Pair<Pair<Integer, GraphNode>, List<String>> parseLine(@NotNull String line, int lineNumber) { int separatorIndex = nextSeparatorIndex(line, 0); Pair<Integer, Character> pair = parseNumberWithChar(line.substring(0, separatorIndex)); GraphNode graphNode = new GraphNode(lineNumber, parseGraphNodeType(pair.second)); String[] edges = line.substring(separatorIndex + 2).split("\\s"); List<String> normalEdges = ContainerUtil.mapNotNull(edges, s -> { if (s.isEmpty()) return null; return s; }); return Pair.create(Pair.create(pair.first, graphNode), normalEdges); } private static Pair<Integer, Character> parseNumberWithChar(@NotNull String in) { return new Pair<>(Integer.decode(in.substring(0, in.length() - 2)), in.charAt(in.length() - 1)); } private static class TestLinearGraphWithElementsInfo implements LinearGraph { private final List<GraphNode> myGraphNodes; private final MultiMap<Integer, GraphEdge> myUpEdges; private final MultiMap<Integer, GraphEdge> myDownEdges; private TestLinearGraphWithElementsInfo(List<GraphNode> graphNodes, MultiMap<Integer, GraphEdge> upEdges, MultiMap<Integer, GraphEdge> downEdges) { myGraphNodes = graphNodes; myUpEdges = upEdges; myDownEdges = downEdges; } @Override public int nodesCount() { return myGraphNodes.size(); } @NotNull @Override public List<GraphEdge> getAdjacentEdges(int nodeIndex, @NotNull EdgeFilter filter) { List<GraphEdge> result = ContainerUtil.newArrayList(); for (GraphEdge upEdge : myUpEdges.get(nodeIndex)) { if (upEdge.getType().isNormalEdge() && filter.upNormal) result.add(upEdge); if (!upEdge.getType().isNormalEdge() && filter.special) result.add(upEdge); } for (GraphEdge downEdge : myDownEdges.get(nodeIndex)) { if (downEdge.getType().isNormalEdge() && filter.downNormal) result.add(downEdge); if (!downEdge.getType().isNormalEdge() && filter.special) result.add(downEdge); } return result; } @NotNull @Override public GraphNode getGraphNode(int nodeIndex) { return myGraphNodes.get(nodeIndex); } @Override public int getNodeId(int nodeIndex) { assert nodeIndex > 0 && nodeIndex < nodesCount() : "Bad nodeIndex: " + nodeIndex; return nodeIndex; } @Override @Nullable public Integer getNodeIndex(int nodeId) { if (nodeId >= 0 && nodeId < nodesCount()) return nodeId; return null; } } }