/* * Copyright 2017 ThoughtWorks, Inc. * * 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.thoughtworks.go.server.valuestreammap; import com.thoughtworks.go.domain.valuestreammap.Node; import com.thoughtworks.go.domain.valuestreammap.NodeLevelMap; import com.thoughtworks.go.domain.valuestreammap.ValueStreamMap; import java.util.*; public class LevelAssignment { public NodeLevelMap apply(ValueStreamMap valueStreamMap) { Node rootNode = valueStreamMap.getCurrentPipeline() != null ? valueStreamMap.getCurrentPipeline() : valueStreamMap.getCurrentMaterial(); rootNode.setLevel(0); if(!rootNode.getParents().isEmpty()) { assignLevelsToDependencies(rootNode, new Upstream()); } if(!rootNode.getChildren().isEmpty()) { assignLevelsToDependencies(rootNode, new Downstream()); } return levelToNodesMap(valueStreamMap); } private void assignLevelsToDependencies(Node rootNode, LevelAssignmentDirection direction) { List<Node> topologicalOrder = new ArrayList<>(); getTopologicalOrder(rootNode, direction, new HashSet<>(), topologicalOrder); Collections.reverse(topologicalOrder); assignLevels(topologicalOrder, direction); } private void getTopologicalOrder(Node rootNode, LevelAssignmentDirection direction, Set<Node> visitedNodes, List<Node> topologicalOrder) { if (visitedNodes.contains(rootNode)) { return; } visitedNodes.add(rootNode); List<Node> relatedNodes = direction.getRelatedNodes(rootNode); if (!relatedNodes.isEmpty()) { for (Node relatedNode : relatedNodes) { getTopologicalOrder(relatedNode, direction, visitedNodes, topologicalOrder); } } topologicalOrder.add(rootNode); } private void assignLevels(List<Node> topologicalOrder, LevelAssignmentDirection direction) { for (Node currentNode : topologicalOrder) { int nextLevel = direction.getNextLevel(currentNode); List<Node> relatedNodes = direction.getRelatedNodes(currentNode); if (!relatedNodes.isEmpty()) { for (Node relatedNode : relatedNodes) { if (direction.canResetLevel(relatedNode, nextLevel)) { relatedNode.setLevel(nextLevel); } } } } } private NodeLevelMap levelToNodesMap(ValueStreamMap valueStreamMap) { NodeLevelMap nodeLevelMap = new NodeLevelMap(); Collection<Node> nodes = valueStreamMap.allNodes(); for (Node node : nodes) { nodeLevelMap.add(node); } return nodeLevelMap; } private interface LevelAssignmentDirection { List<Node> getRelatedNodes(Node node); int getNextLevel(Node node); boolean canResetLevel(Node node, int nextLevel); } private class Upstream implements LevelAssignmentDirection { @Override public List<Node> getRelatedNodes(Node node) { return node.getParents(); } @Override public int getNextLevel(Node node) { return node.getLevel() - 1; } @Override public boolean canResetLevel(Node node, int nextLevel) { return nextLevel < node.getLevel(); } } private class Downstream implements LevelAssignmentDirection { @Override public List<Node> getRelatedNodes(Node node) { return node.getChildren(); } @Override public int getNextLevel(Node node) { return node.getLevel() + 1; } @Override public boolean canResetLevel(Node node, int nextLevel) { return nextLevel > node.getLevel(); } } }