/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012-2013, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.processing.chain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.geotoolkit.processing.chain.model.Chain;
import org.geotoolkit.processing.chain.model.Element;
import org.geotoolkit.processing.chain.model.ElementProcess;
import org.geotoolkit.processing.chain.model.FlowLink;
/**
* Utility methods to manipulate process chains.
*
* @author Johann Sorel (Geomatys)
*/
final class Flow {
private Flow() {}
/**
* Analyze the model and create a Node graph.
* Node objects can be :
* - Collection<Parameter> inputs
* - Collection<Parameter> outputs
* - Constant
* - Descriptor
*
* Links are translated with the getChildren method.
*/
static Collection<FlowNode> createFlow(final Chain chain){
final FlowNode inputNode = new FlowNode(ElementProcess.BEGIN,true,false);
final FlowNode outputNode = new FlowNode(ElementProcess.END,false,true);
final FlowNode inputParamNode = new FlowNode(chain.getInputs(),true,false);
final FlowNode outputParamNode = new FlowNode(chain.getOutputs(),false,true);
final Map<Object,FlowNode> nodes = new HashMap<Object,FlowNode>();
nodes.put(inputNode,inputNode);
nodes.put(outputNode,outputNode);
nodes.put(inputParamNode,inputParamNode);
nodes.put(outputParamNode,outputParamNode);
for(Object obj : chain.getElements()) {nodes.put(obj,new FlowNode(obj,false,false));}
for(FlowLink link : chain.getFlowLinks()){
Object source = link.getSource(chain);
Object target = link.getTarget(chain);
if(source.equals(ElementProcess.BEGIN)){
source = inputNode;
}
if(target.equals(ElementProcess.END)){
target = outputNode;
}
final FlowNode sourceNode = nodes.get(source);
final FlowNode targetNode = nodes.get(target);
sourceNode.children.add(targetNode);
sourceNode.links.add(link);
}
return nodes.values();
}
/**
* Sort Nodes by execution order.
* Each rank is a list of nodes which are necessarily
* executed after the previous rank and before the next.
*
* @param nodes
* @return List
*/
static List<List<FlowNode>> sortByRank(Collection<FlowNode> nodes){
nodes = new ArrayList<FlowNode>(nodes);
final List<List<FlowNode>> ranked = new ArrayList<List<FlowNode>>();
//extract in/out/begin/end nodes
FlowNode inNode = null;
FlowNode outNode = null;
FlowNode beginNode = null;
FlowNode endNode = null;
for(FlowNode n : nodes){
if (n.isInput) {
if (n.object instanceof Element) {
beginNode = n;
} else {
inNode = n;
}
} else if (n.isOutput) {
if (n.object instanceof Element) {
endNode = n;
} else {
outNode = n;
}
}
}
nodes.remove(beginNode);
nodes.remove(inNode);
nodes.remove(endNode);
nodes.remove(outNode);
while(!nodes.isEmpty()){
final List<FlowNode> step = new ArrayList<FlowNode>();
ranked.add(step);
loop:
for(FlowNode candidate : nodes){
//check amought remaining nodes if one of them is a parent of this node
for(FlowNode parent : nodes){
if(parent.children.contains(candidate)){
continue loop;
}
}
step.add(candidate);
}
nodes.removeAll(step);
}
//always place input and output parameters alone in first and last position
ranked.add(0, Arrays.asList(inNode, beginNode));
ranked.add(Arrays.asList(outNode, endNode));
return ranked;
}
}