use of org.knime.core.node.workflow.action.ExpandSubnodeResult in project knime-core by knime.
the class Bug6029_ExpandSubnode method testExecuteAfterExpandAndCollapse.
@Test
public void testExecuteAfterExpandAndCollapse() throws Exception {
WorkflowManager mgr = getManager();
ExpandSubnodeResult expandSubWorkflowResult = mgr.expandSubWorkflow(m_subnode8);
assertThat("Expected to be dirty after expand", mgr.isDirty(), is(true));
assertThat("Subnode must not longer exist", mgr.getNodeContainer(m_subnode8, SubNodeContainer.class, false), is(nullValue()));
ConnectionContainer flowVarConn = findInConnection(m_javaSnippet_After_Expand_3, 0);
assertNotNull("didn't find connection after expand", flowVarConn);
assertThat("Source should be string input node", flowVarConn.getSource(), is(m_stringInput5));
executeAllAndWait();
checkState(mgr, InternalNodeContainerState.EXECUTED);
checkStateOfMany(InternalNodeContainerState.EXECUTED, m_tableDiffer7, m_javaSnippet_After_Expand_3);
assertThat("Should be expandable but is not", expandSubWorkflowResult.canUndo(), is(Boolean.TRUE));
expandSubWorkflowResult.undo();
checkState(mgr, InternalNodeContainerState.CONFIGURED);
assertThat("Subnode must have been re-created", mgr.getNodeContainer(m_subnode8, SubNodeContainer.class, false), is(not(nullValue())));
assertThat("Java Snippet must have been removed/collapsed", mgr.getNodeContainer(m_javaSnippet_After_Expand_3, NativeNodeContainer.class, false), is(nullValue()));
executeAllAndWait();
checkState(mgr, InternalNodeContainerState.EXECUTED);
checkStateOfMany(InternalNodeContainerState.EXECUTED, m_tableDiffer7, m_subnode8);
}
use of org.knime.core.node.workflow.action.ExpandSubnodeResult in project knime-core by knime.
the class WorkflowManager method expandSubWorkflow.
/**
* Expand the selected subnode into a set of nodes in this WFM and remove the old metanode.
*
* @param nodeID ID of the node containing the sub workflow
* @return copied content containing nodes and annotations
* @throws IllegalStateException if expand cannot be done
* @since 2.12
* @noreference This method is not intended to be referenced by clients.
*/
public ExpandSubnodeResult expandSubWorkflow(final NodeID nodeID) throws IllegalStateException {
try (WorkflowLock lock = lock()) {
WorkflowCopyContent.Builder cnt = WorkflowCopyContent.builder();
cnt.setNodeIDs(nodeID);
cnt.setIncludeInOutConnections(true);
WorkflowPersistor undoCopyPersistor = copy(true, cnt.build());
final NodeContainer node = getNodeContainer(nodeID);
final WorkflowManager subWFM;
HashSet<NodeID> virtualNodes = new HashSet<NodeID>();
if (node instanceof WorkflowManager) {
CheckUtils.checkState(canExpandMetaNode(nodeID) == null, canExpandMetaNode(nodeID));
subWFM = (WorkflowManager) node;
} else if (node instanceof SubNodeContainer) {
CheckUtils.checkState(canExpandSubNode(nodeID) == null, canExpandSubNode(nodeID));
SubNodeContainer snc = (SubNodeContainer) node;
virtualNodes.add(snc.getVirtualInNodeID());
virtualNodes.add(snc.getVirtualOutNodeID());
subWFM = snc.getWorkflowManager();
} else {
throw new IllegalStateException("Not a sub- or metanode: " + node);
}
// retrieve all nodes from metanode
Collection<NodeContainer> ncs = subWFM.getNodeContainers();
NodeID[] orgIDs = new NodeID[ncs.size()];
int i = 0;
for (NodeContainer nc : ncs) {
orgIDs[i] = nc.getID();
i++;
}
// retrieve all workflow annotations
Collection<WorkflowAnnotation> annos = subWFM.getWorkflowAnnotations();
WorkflowAnnotation[] orgAnnos = annos.toArray(new WorkflowAnnotation[annos.size()]);
// copy the nodes from the sub workflow manager:
WorkflowCopyContent.Builder orgContent = WorkflowCopyContent.builder();
orgContent.setNodeIDs(orgIDs);
orgContent.setAnnotation(orgAnnos);
WorkflowCopyContent newContent = this.copyFromAndPasteHere(subWFM, orgContent.build());
NodeID[] newIDs = newContent.getNodeIDs();
Annotation[] newAnnos = newContent.getAnnotations();
// create map and set of quick lookup/search
Map<NodeID, NodeID> oldIDsHash = new HashMap<NodeID, NodeID>();
HashSet<NodeID> newIDsHashSet = new HashSet<NodeID>();
for (i = 0; i < orgIDs.length; i++) {
oldIDsHash.put(orgIDs[i], newIDs[i]);
newIDsHashSet.add(newIDs[i]);
}
if (node instanceof WorkflowManager) {
// connect connections TO the sub workflow:
for (ConnectionContainer cc : m_workflow.getConnectionsByDest(subWFM.getID())) {
int destPortIndex = cc.getDestPort();
for (ConnectionContainer subCC : subWFM.m_workflow.getConnectionsBySource(subWFM.getID())) {
if (subCC.getSourcePort() == destPortIndex) {
if (subCC.getDest().equals(subWFM.getID())) {
// THROUGH connection - skip here, handled below!
} else {
// reconnect
NodeID newID = oldIDsHash.get(subCC.getDest());
this.addConnection(cc.getSource(), cc.getSourcePort(), newID, subCC.getDestPort());
}
}
}
}
// connect connection FROM the sub workflow
for (ConnectionContainer cc : getOutgoingConnectionsFor(subWFM.getID())) {
int sourcePortIndex = cc.getSourcePort();
ConnectionContainer subCC = subWFM.getIncomingConnectionFor(subWFM.getID(), sourcePortIndex);
if (subCC != null) {
if (subCC.getSource().equals(subWFM.getID())) {
// THROUGH connection
ConnectionContainer incomingCC = this.getIncomingConnectionFor(subWFM.getID(), subCC.getSourcePort());
// delete existing connection from Metanode to
// Node (done automatically) and reconnect
this.addConnection(incomingCC.getSource(), incomingCC.getSourcePort(), cc.getDest(), cc.getDestPort());
} else {
// delete existing connection from Metanode to Node (automatically) and reconnect
NodeID newID = oldIDsHash.get(subCC.getSource());
this.addConnection(newID, subCC.getSourcePort(), cc.getDest(), cc.getDestPort());
}
}
}
} else if (node instanceof SubNodeContainer) {
// connect connections TO the sub workflow:
for (ConnectionContainer outerConnection : m_workflow.getConnectionsByDest(nodeID)) {
for (ConnectionContainer innerConnection : subWFM.m_workflow.getConnectionsBySource(((SubNodeContainer) node).getVirtualInNodeID())) {
if (outerConnection.getDestPort() == innerConnection.getSourcePort()) {
addConnection(outerConnection.getSource(), outerConnection.getSourcePort(), oldIDsHash.get(innerConnection.getDest()), innerConnection.getDestPort());
}
}
}
// connect connections FROM the sub workflow:
List<ConnectionContainer> cons = new ArrayList<ConnectionContainer>();
cons.addAll(m_workflow.getConnectionsBySource(nodeID));
for (ConnectionContainer outerConnection : cons) {
for (ConnectionContainer innerConnection : subWFM.m_workflow.getConnectionsByDest(((SubNodeContainer) node).getVirtualOutNodeID())) {
if (outerConnection.getSourcePort() == innerConnection.getDestPort()) {
addConnection(oldIDsHash.get(innerConnection.getSource()), innerConnection.getSourcePort(), outerConnection.getDest(), outerConnection.getDestPort());
}
}
}
}
// move nodes so that their center lies on the position of
// the old metanode!
// ATTENTION: if you change this make sure it is (correctly)
// revertable by collapseToMetaNodes (undo-redo!)
int xmin = Integer.MAX_VALUE;
int ymin = Integer.MAX_VALUE;
int xmax = Integer.MIN_VALUE;
int ymax = Integer.MIN_VALUE;
for (i = 0; i < newIDs.length; i++) {
NodeContainer nc = getNodeContainer(newIDs[i]);
NodeUIInformation uii = nc.getUIInformation();
if (uii != null) {
int[] bounds = uii.getBounds();
if (bounds.length >= 2) {
xmin = Math.min(bounds[0], xmin);
ymin = Math.min(bounds[1], ymin);
xmax = Math.max(bounds[0], xmax);
ymax = Math.max(bounds[1], ymax);
}
}
}
NodeUIInformation uii = node.getUIInformation();
if (uii != null) {
int[] metaBounds = uii.getBounds();
int xShift = metaBounds[0] - (xmin + xmax) / 2;
int yShift = metaBounds[1] - (ymin + ymax) / 2;
for (i = 0; i < newIDs.length; i++) {
NodeContainer nc = getNodeContainer(newIDs[i]);
uii = nc.getUIInformation();
if (uii != null) {
NodeUIInformation newUii = NodeUIInformation.builder(uii).translate(new int[] { xShift, yShift }).build();
nc.setUIInformation(newUii);
}
}
for (Annotation anno : newAnnos) {
anno.shiftPosition(xShift, yShift);
}
// move bendpoints of connections between moved nodes
for (ConnectionContainer cc : this.getConnectionContainers()) {
if ((newIDsHashSet.contains(cc.getSource())) && (newIDsHashSet.contains(cc.getDest()))) {
ConnectionUIInformation cuii = cc.getUIInfo();
if (cuii != null) {
ConnectionUIInformation newUI = ConnectionUIInformation.builder(cuii).translate(new int[] { xShift, yShift }).build();
cc.setUIInfo(newUI);
}
}
}
}
// remove virtual nodes
for (NodeID id : virtualNodes) {
removeNode(oldIDsHash.get(id));
}
// and finally remove old sub workflow
this.removeNode(nodeID);
return new ExpandSubnodeResult(this, newContent, undoCopyPersistor);
}
}
Aggregations