/* * Copyright 2003-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 jetbrains.mps.generator.impl.plan; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SNodeUtil; import org.jetbrains.mps.openapi.model.SReference; import jetbrains.mps.util.GraphUtil; import org.jetbrains.mps.openapi.model.SNode; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Uses linear algorithms. * <p/> * Evgeny Gryaznov, Jan 11, 2010 */ public class ConnectedComponentPartitioner { private static Component[] EMPTY_COMPONENTS = new Component[0]; private int myCount; private SNode[] myRoots; private int[][] myDependencies; public ConnectedComponentPartitioner(List<SNode> roots) { myCount = roots.size(); myRoots = roots.toArray(new SNode[this.myCount]); myDependencies = buildDependencies(); } private int[][] buildDependencies() { int[] dependsOn = new int[myCount]; int[][] result = new int[myCount][]; Map<SNode, Integer> rootIndex = new HashMap<SNode, Integer>(); for (int i = 0; i < myRoots.length; i++) { rootIndex.put(myRoots[i], i); } for (int index = 0; index < myCount; index++) { SNode root = myRoots[index]; for (SNode node : SNodeUtil.getDescendants(root, null, true)) { buildNodeDependencies(node, dependsOn, rootIndex); } dependsOn[index] = 0; result[index] = GraphUtil.setToList(dependsOn); } return result; } private void buildNodeDependencies(SNode node, int[] dependsOn, Map<SNode, Integer> rootIndex) { for (SReference ref : node.getReferences()) { SModel sm = node.getModel(); assert sm != null; if (!sm.getReference().equals(ref.getTargetSModelReference())) continue; SNode targetNode = ref.getTargetNode(); if (targetNode != null) { Integer targetIndex = rootIndex.get(targetNode.getContainingRoot()); if (targetIndex != null) { dependsOn[targetIndex] = 1; } } } } public List<SNode[]> partition() { int[][] partitions = GraphUtil.components(GraphUtil.removeOrientation(myDependencies)); List<SNode[]> result = new ArrayList<SNode[]>(partitions.length + 1); for (int i = 0; i < partitions.length; i++) { SNode[] proots = new SNode[partitions[i].length]; for (int e = 0; e < proots.length; e++) { proots[e] = myRoots[partitions[i][e]]; } result.add(proots); } result.add(new SNode[0]); return result; } public Component[] partitionStrong() { int[][] partitions = GraphUtil.tarjan(myDependencies); // temporary byte[] dependencies = new byte[partitions.length]; int[] rootToComponent = new int[myRoots.length]; Arrays.fill(rootToComponent, -1); Component[] result = new Component[partitions.length]; for (int i = 0; i < partitions.length; i++) { // create roots array SNode[] roots = new SNode[partitions[i].length]; for (int e = 0; e < roots.length; e++) { int rootIndex = partitions[i][e]; roots[e] = myRoots[rootIndex]; rootToComponent[rootIndex] = i; } // calc dependencies int count = 0; Arrays.fill(dependencies, (byte) 0); for (int e = 0; e < roots.length; e++) { int rootIndex = partitions[i][e]; for (int dependsOn : myDependencies[rootIndex]) { int componentIndex = rootToComponent[dependsOn]; assert componentIndex >= 0; if (componentIndex < i && dependencies[componentIndex] == 0) { dependencies[componentIndex] = 1; count++; } } } Component[] dep = count > 0 ? new Component[count] : EMPTY_COMPONENTS; for (int e = 0; count > 0 && e < i; e++) { if (dependencies[e] == 1) { dep[--count] = result[e]; } } result[i] = new Component(roots, dep); } return result; } @Override public String toString() { int[][] strongPartitions = GraphUtil.tarjan(myDependencies); int[][] partitions = GraphUtil.components(GraphUtil.removeOrientation(myDependencies)); return printPartitions(partitions) + "\n\nStrong:\n" + printPartitions(strongPartitions); } private String printPartitions(int[][] partitions) { StringBuffer sb = new StringBuffer(); sb.append(myRoots.length).append(" roots, ").append(partitions.length).append(" components\n"); for (int i = 0; i < partitions.length; i++) { sb.append("#").append(i).append("(").append(partitions[i].length).append("): "); for (int e = 0; e < partitions[i].length; e++) { sb.append(" ").append(myRoots[partitions[i][e]]); } sb.append('\n'); } return sb.toString(); } public static class Component { private final SNode[] roots; private final Component[] dependsOn; private boolean isDirty; public Component(SNode[] roots, Component[] dependsOn) { this.roots = roots; this.dependsOn = dependsOn; } public SNode[] getRoots() { return roots; } public Component[] getDependsOn() { return dependsOn; } public boolean isDirty() { return isDirty; } public void setDirty(boolean dirty) { isDirty = dirty; } } }