/**
* Copyright (C) 2008-2011 Daniel Senff
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package de.danielsenff.imageflow.models.unit;
import java.awt.Point;
import java.util.Collection;
import java.util.HashMap;
import java.util.Vector;
import visualap.Node;
import de.danielsenff.imageflow.models.NodeList;
import de.danielsenff.imageflow.models.connection.Connection;
import de.danielsenff.imageflow.models.connection.ConnectionList;
import de.danielsenff.imageflow.models.connection.Input;
import de.danielsenff.imageflow.models.connection.Output;
import de.danielsenff.imageflow.models.connection.Pin;
import de.danielsenff.imageflow.models.connection.ProxyInput;
import de.danielsenff.imageflow.models.connection.ProxyOutput;
/**
* Group unit is similar to the graph controller and contains own lists of Units and Connections.
* @author Daniel Senff
*
*/
public class GroupUnitElement extends UnitElement {
/**
* List of units included in this Group
*/
protected UnitList units;
private Vector<Connection> originalConnections;
private Collection<Connection> internalConnections;
private Collection<Connection> externalConnections;
/**
* @param point
* @param name
*/
public GroupUnitElement(final Point point, final String name) {
super(point, "name", "");
setLabel(name);
init();
}
/**
* @param origin
* @param unitName
* @param selections
* @param allUnits
* @param unitsImageJSyntax
*/
public GroupUnitElement(Point origin, String unitName,
final Collection<Node> selections, final UnitList allUnits) {
super(origin, unitName, "");
init();
try {
putUnits(selections, allUnits);
} catch (Exception e) {
e.printStackTrace();
}
}
private void init() {
this.units = new UnitList();
this.internalConnections = new Vector<Connection>();
this.externalConnections = new Vector<Connection>();
this.originalConnections = new Vector<Connection>();
}
/**
* Add a number of Units to this group.
* @param unitsToAdd
* @param allUnits
* @throws Exception
*/
public void putUnits(final Collection<Node> unitsToAdd, final UnitList allUnits) throws Exception {
findRelevantConnections(unitsToAdd, allUnits.getConnections());
if(selectedUnitsConsistentGroup(unitsToAdd, allUnits)) {
for (Node node : unitsToAdd) {
this.units.add(node);
}
/*
* determine position for group unit on workflow
*/
int x = (int) getUnit(0).getOrigin().getX();
int y = (int) getUnit(0).getOrigin().getY();
setOrigin(new Point(x, y));
dealWithConnections(allUnits.getConnections());
} else throw new Exception("group not albe");
/*
* remove original units from workflow
*/
for (Node node : this.units) {
/* this removes not only the node from the unitList
but also disconnects all connections to this unit.
Therefore all connections are stored before doing this. */
allUnits.remove(node);
}
// Reconnecting the connections within the group.
for (Connection connection : internalConnections) {
connection.connect();
}
/* uh, reconnect the external connections
if the target-unit is contained in the group
just not sure why not connecting from-units */
for (Connection connection : externalConnections) {
if(this.units.contains(connection.getToUnit())
//|| this.units.contains(connection.getFromUnit())
) {
connection.connect();
}
}
// proxy connections
for (Input input : getInputs()) {
((ProxyInput)input).getEmbeddedInput().setLocked(true);
}
for (Output output : getOutputs()) {
((ProxyOutput)output).getEmbeddedOutput().setLocked(true);
}
int lowestX = 3000, lowestY = 3000;
for (Node node : getNodes()) {
if(node.getOrigin().x < lowestX) {
lowestX = node.getOrigin().x;
}
if(node.getOrigin().y < lowestY) {
lowestY = node.getOrigin().y;
}
}
// offset from original
int deltaX = lowestX - 25;
int deltaY = lowestY - 25;
int x = 0, y = 0;
for (Node node : getNodes()) {
x = node.getOrigin().x;
y = node.getOrigin().y;
node.getOrigin().setLocation(x-deltaX, y-deltaY);
}
}
/**
* True if the selected Units result in a consistent group.
* @param allUnits
* @param unitsToAdd
* @return
*/
private boolean selectedUnitsConsistentGroup(Collection<Node> unitsToAdd, UnitList allUnits) {
/*
* for each input we test, if every unit in input graph is
*/
for (Connection connection : this.externalConnections) {
for (Node node : unitsToAdd) {
if(unitsToAdd.contains(connection.getToUnit())
&& connection.getInput().isConnectedInInputBranch(node))
return false;
}
}
return true;
}
/**
* @param unitsToAdd
* @param connection
* @return
*/
private boolean isToUnitIn(final Collection<Node> unitsToAdd, final Connection connection) {
UnitElement unit = (UnitElement)connection.getFromUnit();
if(unitsToAdd.contains(unit)
&& unitsToAdd.contains(connection.getToUnit())) {
// unit is in selection
// check if the preceding unit is also in selection
return true;
} else {
for (Input input : unit.getInputs()) {
if(input.isConnected() ) {
return isToUnitIn(unitsToAdd, input.getConnection());
}
}
}
return false;
}
/**
* @param allConnections
*/
public void dealWithConnections(final ConnectionList allConnections) {
for (Connection connection : externalConnections) {
this.originalConnections.add(connection);
}
/*
* create inputs and outputs based on external connections
*/
HashMap<Output, ProxyOutput> externalOutputs = new HashMap<Output, ProxyOutput>();
ProxyInput pInput;
Connection newConnection;
ProxyOutput pOutput;
for (Connection externalConnection : externalConnections) {
if(contains(externalConnection.getToUnit()))
{
pInput = new ProxyInput(externalConnection.getInput(), this, getInputsCount()+1);
addInput(pInput);
newConnection = new Connection(externalConnection.getOutput(), pInput);
allConnections.add(newConnection);
}
if(contains(externalConnection.getFromUnit())) {
if(externalOutputs.containsKey(externalConnection.getOutput())) {
// output already used, take existing proxy
pOutput = externalOutputs.get(externalConnection.getOutput());
} else {
// output not yet used, create new
pOutput = new ProxyOutput(externalConnection.getOutput(), this, getOutputsCount()+1);
addOutput(pOutput);
externalOutputs.put(externalConnection.getOutput(), pOutput);
}
newConnection = new Connection(pOutput, externalConnection.getInput());
allConnections.add(newConnection);
}
}
}
private final void findRelevantConnections(Collection<Node> unitsToAdd, final ConnectionList allConnections) {
for (Connection connection : allConnections) {
for (Node node : unitsToAdd) {
if (connection.isConnectedToUnit(node)
&& !externalConnections.contains(connection))
externalConnections.add(connection);
}
}
for (Connection connection : externalConnections) {
for (Node node : unitsToAdd) {
for (Node node2 : unitsToAdd) {
if(connection.getFromUnit().equals(node)
&& connection.getToUnit().equals(node2)) {
internalConnections.add(connection);
}
}
}
}
/*
* determine which connections "leave" the group
*/
for (Connection connection : internalConnections) {
externalConnections.remove(connection);
}
}
/**
* @return the externalConnections
*/
public Collection<Connection> getExternalConnections() {
return externalConnections;
}
/**
* Returns true if the node in question is contained in this GroupUnit.
* @param node
* @return
*/
public boolean contains(Node node) {
return this.units.contains(node);
}
/**
* Number of elements grouped in this GroupUnit.
* @return
*/
public int getGroupSize() {
return this.units.getSize();
}
/**
*
* @param i
* @return
*/
public Node getUnit(int i) {
return this.units.get(i);
}
/**
* Returns a Collection of Units embedded in this Group.
* @return
*/
public NodeList<Node> getNodes() {
return this.units;
}
/**
* @return the originalConnections
*/
public Vector<Connection> getOriginalConnections() {
return this.originalConnections;
}
/**
* @return the internalConnections
*/
public final Collection<Connection> getInternalConnections() {
return this.internalConnections;
}
@Override
public GroupUnitElement clone() {
GroupUnitElement groupClone = new GroupUnitElement(getOrigin(), getLabel());
groupClone = initClone(groupClone);
groupClone.setOriginalUnit(this);
return groupClone;
}
/**
* Initiate a clone the contents of this group into the given groupClone.
* @param groupClone
* @return
*/
protected GroupUnitElement initClone(final GroupUnitElement groupClone) {
/*
* clone included units
*/
HashMap<Pin, Pin> correspondingPins = new HashMap<Pin, Pin>();
UnitElement unit, unitClone;
Node c;
for (Node node : getNodes()) {
try {
c = node.clone();
groupClone.getNodes().add(c);
if(node instanceof UnitElement) {
unit = (UnitElement) node;
unitClone = (UnitElement) c;
for (int i = 0; i < unit.getInputsCount(); i++) {
correspondingPins.put(unit.getInput(i), unitClone.getInput(i));
}
for (int i = 0; i < unit.getOutputsCount(); i++) {
correspondingPins.put(unit.getOutput(i), unitClone.getOutput(i));
}
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
/*
* reconnect internal connections
* the internal connections have the original pins
* we have to create new connections with the respective pins
*/
Input cloneInput;
Output cloneOutput;
Connection newConnection;
for (Connection originalConnection : getInternalConnections()) {
cloneInput = (Input) correspondingPins.get(originalConnection.getInput());
cloneOutput = (Output) correspondingPins.get(originalConnection.getOutput());
newConnection = new Connection(cloneInput, cloneOutput);
groupClone.getInternalConnections().add(newConnection);
}
/*
* clone pins
*/
Input input, embeddedInputClone;
ProxyInput pInput;
for (int i = 0; i < getInputsCount(); i++) {
if(getInput(i) instanceof ProxyInput) {
input = ((ProxyInput)getInput(i)).getEmbeddedInput();
embeddedInputClone = (Input) correspondingPins.get(input);
pInput = new ProxyInput(embeddedInputClone, groupClone, i+1);
groupClone.addInput(pInput);
} else {
super.cloneInput(groupClone, i);
}
}
Output output, embeddedOutputClone;
ProxyOutput pOutput;
for (int i = 0; i < getOutputsCount(); i++) {
if(getOutput(i) instanceof ProxyOutput) {
output = ((ProxyOutput)getOutput(i)).getEmbeddedOutput();
embeddedOutputClone = (Output) correspondingPins.get(output);
pOutput = new ProxyOutput(embeddedOutputClone, groupClone, i+1);
groupClone.addOutput(pOutput);
} else
super.cloneOutput(groupClone, i);
}
return groupClone;
}
@Override
public String getHelpString() {
String string = "Grouped units: \n";
for (Node node : getNodes()) {
string += node.getLabel() + " \n ";
}
return string;
}
}