/*
* 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;
}
}
}