use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class DialogComponentColumnFilter method updateComponent.
/**
* {@inheritDoc}
*/
@Override
protected void updateComponent() {
// update component only if content is out of sync
final SettingsModelFilterString filterModel = (SettingsModelFilterString) getModel();
final Set<String> compIncl = m_columnFilter.getIncludedColumnSet();
final Set<String> compExcl = m_columnFilter.getExcludedColumnSet();
final boolean compKeepAll = m_columnFilter.isKeepAllSelected();
final Set<String> modelIncl = new LinkedHashSet<String>(filterModel.getIncludeList());
final Set<String> modelExcl = new LinkedHashSet<String>(filterModel.getExcludeList());
final boolean modelKeepAll = filterModel.isKeepAllSelected();
boolean update = (compIncl.size() != modelIncl.size()) || (compExcl.size() != modelExcl.size() || compKeepAll != modelKeepAll);
if (!update) {
// update if the current spec and the spec we last updated with
// are different
final PortObjectSpec currPOSpec = getLastTableSpec(m_inPortIndex);
if (currPOSpec == null) {
update = false;
} else {
if (!(currPOSpec instanceof DataTableSpec)) {
throw new RuntimeException("Wrong type of PortObject for" + " ColumnFilterPanel, expecting DataTableSpec!");
}
final DataTableSpec currSpec = (DataTableSpec) currPOSpec;
update = (!currSpec.equalStructure(m_specInFilter));
}
}
if (!update) {
// one way check, because size is equal
update = !modelIncl.containsAll(compIncl);
}
if (!update) {
// one way check, because size is equal
update = !modelExcl.containsAll(compExcl);
}
if (update) {
m_specInFilter = (DataTableSpec) getLastTableSpec(m_inPortIndex);
if (m_specInFilter == null) {
// the component doesn't take a null spec. Create an empty one
m_specInFilter = new DataTableSpec();
}
m_columnFilter.update(m_specInFilter, filterModel.getIncludeList(), filterModel.getExcludeList(), m_inclUnknown);
m_columnFilter.setKeepAllSelected(modelKeepAll);
}
// also update the enable status
setEnabledComponents(filterModel.isEnabled());
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class NodeModel method configureModel.
/**
* This function is called when something changes that could affect the
* output <code>DataTableSpec</code> elements. E.g. after a reset,
* execute, (dis-)connect, and object creation (model instantiation).
* <p>
* The function calls <code>#configure()</code> to receive the updated
* output DataTableSpecs, if the model is not executed yet. After execution
* the DataTableSpecs are simply taken from the output DataTables.
*
* @param inSpecs An array of input <code>DataTableSpec</code> elements,
* either the array or each of its elements can be
* <code>null</code>.
* @return An array where each element indicates if the outport has changed.
* @throws InvalidSettingsException if the current settings don't go along
* with the table specs
*/
final PortObjectSpec[] configureModel(final PortObjectSpec[] inSpecs) throws InvalidSettingsException {
assert inSpecs.length == getNrInPorts();
setWarningMessage(null);
PortObjectSpec[] copyInSpecs = new PortObjectSpec[getNrInPorts()];
PortObjectSpec[] newOutSpecs;
System.arraycopy(inSpecs, 0, copyInSpecs, 0, inSpecs.length);
// Rather empty ones than null
for (int i = 0; i < copyInSpecs.length; i++) {
if (copyInSpecs[i] == null && BufferedDataTable.TYPE.equals(m_inPortTypes[i]) && !m_inPortTypes[i].isOptional()) {
// only mimic empty table for real table connections
copyInSpecs[i] = new DataTableSpec();
}
// only weak port compatibility check during connect
// (model reader can be connected to any model port)
// complain if actual types are incompatible.
Class<? extends PortObjectSpec> expected = m_inPortTypes[i].getPortObjectSpecClass();
if (// i.e. skip only "optional and not connected"
copyInSpecs[i] != null && !expected.isAssignableFrom(copyInSpecs[i].getClass()) && !(copyInSpecs[i] instanceof InactiveBranchPortObjectSpec)) {
StringBuilder b = new StringBuilder("Incompatible port spec");
if (copyInSpecs.length > 1) {
b.append(" at port ").append(i);
}
b.append(", expected: ").append(expected.getSimpleName());
b.append(", actual: ").append(copyInSpecs[i].getClass().getSimpleName());
throw new InvalidSettingsException(b.toString());
}
}
// CALL CONFIGURE
newOutSpecs = configure(copyInSpecs);
if (newOutSpecs == null) {
newOutSpecs = new PortObjectSpec[getNrOutPorts()];
}
// check output object spec length
if (newOutSpecs.length != getNrOutPorts()) {
m_logger.error("Output spec-array length invalid: " + newOutSpecs.length + " <> " + getNrOutPorts());
newOutSpecs = new PortObjectSpec[getNrOutPorts()];
}
return newOutSpecs;
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class SubNodeContainer method setVirtualOutputIntoOutport.
/**
* Copies data from virtual output node into m_outputs, notifies state listeners if not
* processing call from parent.
* @param newState State of the internal WFM to decide whether to publish ports and/or specs.
*/
@SuppressWarnings("null")
private boolean setVirtualOutputIntoOutport(final InternalNodeContainerState newState) {
// retrieve results and copy to outports
final VirtualSubNodeOutputNodeModel virtualOutNodeModel = getVirtualOutNodeModel();
final boolean isInactive = getVirtualOutNode().isInactive();
final VirtualSubNodeExchange outputExchange = virtualOutNodeModel.getOutputExchange();
// put objects into output if state of WFM is executed
boolean publishObjects = newState.isExecuted();
// publishObjects implies that output node has data or is inactive
assert !publishObjects || (isInactive || (outputExchange != null && outputExchange.getPortObjects() != null)) : String.format("output node must have data or be inactive, status: %s, inactive: %b, exhange is null: %b, " + "exchange content is null: %s", newState, isInactive, outputExchange == null, outputExchange == null ? "<invalid>" : String.valueOf(outputExchange.getPortObjects() == null));
boolean publishSpecs = (isInactive || outputExchange != null) && (newState.isConfigured() || newState.isExecuted() || newState.isExecutionInProgress());
boolean changed = false;
for (int i = 1; i < m_outputs.length; i++) {
// not publish spec: null output
// inactive output node: inactive branch port object
// otherwise: use data from output node
final PortObjectSpec spec = publishSpecs ? (isInactive ? InactiveBranchPortObjectSpec.INSTANCE : outputExchange.getPortSpecs()[i - 1]) : null;
changed = m_outputs[i].setSpec(spec) || changed;
final PortObject object = publishObjects ? (isInactive ? InactiveBranchPortObject.INSTANCE : outputExchange.getPortObjects()[i - 1]) : null;
changed = m_outputs[i].setObject(object) || changed;
}
final PortObjectSpec spec = publishSpecs ? (isInactive ? InactiveBranchPortObjectSpec.INSTANCE : FlowVariablePortObjectSpec.INSTANCE) : null;
changed = m_outputs[0].setSpec(spec) || changed;
final PortObject object = publishObjects ? (isInactive ? InactiveBranchPortObject.INSTANCE : FlowVariablePortObject.INSTANCE) : null;
changed = m_outputs[0].setObject(object) || changed;
final FlowObjectStack outgoingFlowObjectStack = getOutgoingFlowObjectStack();
// TODO API to remove variables from stack, then remove variables no longer in output node and update "changed"
if (publishObjects && !isInactive) {
for (FlowVariable f : outputExchange.getFlowVariables()) {
outgoingFlowObjectStack.push(f.cloneAndUnsetOwner());
}
} else {
// outgoing stack may be null if reset is called twice in a row (or once but no configure was called)
if (outgoingFlowObjectStack != null) {
while (!outgoingFlowObjectStack.isEmpty()) {
outgoingFlowObjectStack.pop(FlowObject.class);
}
}
}
if (changed && !m_isPerformingActionCalledFromParent) {
// updates port views
notifyStateChangeListeners(new NodeStateEvent(this));
}
return changed;
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class WorkflowManager method configureSingleNodeContainer.
/**
* Configure a SingleNodeContainer.
*
* @param snc node to be configured
* @param keepNodeMessage Whether to keep previously set node messages (important during load sometimes)
* @return true if the configuration did change something.
*/
private boolean configureSingleNodeContainer(final SingleNodeContainer snc, final boolean keepNodeMessage) {
boolean configurationChanged = false;
try (WorkflowLock lock = lock()) {
NodeMessage oldMessage = keepNodeMessage ? snc.getNodeMessage() : NodeMessage.NONE;
final int inCount = snc.getNrInPorts();
NodeID sncID = snc.getID();
NodeOutPort[] predPorts = assemblePredecessorOutPorts(sncID);
final PortObjectSpec[] inSpecs = new PortObjectSpec[inCount];
final FlowObjectStack[] sos = new FlowObjectStack[inCount];
final HiLiteHandler[] hiliteHdls = new HiLiteHandler[inCount];
// check for presence of input specs and collects inport
// TableSpecs, FlowObjectStacks and HiLiteHandlers
boolean allSpecsExists = true;
for (int i = 0; i < predPorts.length; i++) {
if (predPorts[i] != null) {
inSpecs[i] = predPorts[i].getPortObjectSpec();
sos[i] = predPorts[i].getFlowObjectStack();
hiliteHdls[i] = predPorts[i].getHiLiteHandler();
allSpecsExists &= inSpecs[i] != null;
} else if (snc.getInPort(i).getPortType().isOptional()) {
// optional input, which is not connected ... ignore
} else {
allSpecsExists = false;
}
}
if (!allSpecsExists) {
// (NodeMessage did not change -- can exit here)
return false;
}
if (!canConfigureNodes()) {
String message;
if (snc.getParent().isComponentProjectWFM()) {
message = "No example input data stored with component";
} else {
message = "Component does not have input data, execute upstream nodes first";
}
snc.setNodeMessage(NodeMessage.merge(oldMessage, NodeMessage.newWarning(message)));
return false;
}
boolean doConfigure = false;
// which might attempt to configure an already queued node again
switch(snc.getInternalState()) {
case IDLE:
case CONFIGURED:
case UNCONFIGURED_MARKEDFOREXEC:
case CONFIGURED_MARKEDFOREXEC:
doConfigure = true;
break;
case EXECUTINGREMOTELY:
// grid/server) -- also these nodes will be configured() on load
if (snc.findJobManager().canDisconnect(getExecutionJob())) {
// see AP-9290 -- in order to properly check we whether we are in 'loading' state we would need
// additional AP -- decided that this workaround will be OK as most job manager are not remote
doConfigure = true;
}
break;
case EXECUTED:
case EXECUTED_MARKEDFOREXEC:
// should not happen but could if reset has worked on slightly
// different nodes than configure, for instance.
// FIXME: report errors again, once configure follows only ports, not nodes.
LOGGER.debug("configure found " + snc.getInternalState() + " node: " + snc.getNameWithID());
break;
case PREEXECUTE:
case POSTEXECUTE:
case EXECUTING:
// should not happen but could if reset has worked on slightly
// different nodes than configure, for instance.
LOGGER.debug("configure found " + snc.getInternalState() + " node: " + snc.getNameWithID());
break;
case CONFIGURED_QUEUED:
case EXECUTED_QUEUED:
// should not happen but could if reset has worked on slightly
// different nodes than configure, for instance.
LOGGER.debug("configure found " + snc.getInternalState() + " node: " + snc.getNameWithID());
break;
default:
LOGGER.error("configure found weird state (" + snc.getInternalState() + "): " + snc.getNameWithID());
}
if (doConfigure) {
// the stack that previously would have been propagated,
// used to track changes
FlowObjectStack oldFOS = snc.createOutFlowObjectStack();
// create new FlowObjectStack
boolean flowStackConflict = false;
FlowObjectStack scsc;
try {
scsc = createAndSetFlowObjectStackFor(snc, sos);
} catch (IllegalFlowObjectStackException e) {
LOGGER.warn("Unable to merge flow object stacks: " + e.getMessage(), e);
scsc = new FlowObjectStack(sncID);
flowStackConflict = true;
}
snc.setCredentialsStore(m_credentialsStore);
// update backwards reference for scopes (e.g. loops)
if (snc.isModelCompatibleTo(ScopeEndNode.class)) {
// if this is an END to a scope (e.g. loop), make sure it knows its head
// (for both: active and inactive loops/scopes)
Node sncNode = ((NativeNodeContainer) snc).getNode();
FlowScopeContext fsc = scsc.peek(FlowScopeContext.class);
if (fsc == null) {
// no head found - ignore during configure!
sncNode.setScopeStartNode(null);
} else {
// scope/loop seems to be correctly wired - set head
NodeContainer headNode = m_workflow.getNode(fsc.getOwner());
if (headNode == null) {
// odd: head is not in the same workflow,
// ignore as well during configure
sncNode.setScopeStartNode(null);
} else {
// head found, let the end node know about it
// but only if the start and end nodes have a compatible flow scope context
Class<? extends FlowScopeContext> flowScopeContextClass = ((ScopeEndNode<?>) sncNode.getNodeModel()).getFlowScopeContextClass();
if (flowScopeContextClass.isAssignableFrom(fsc.getClass())) {
sncNode.setScopeStartNode(((NativeNodeContainer) headNode).getNode());
}
}
}
}
// TODO think about it... happens magically
for (int i = 0; i < inCount; i++) {
snc.setInHiLiteHandler(i, hiliteHdls[i]);
}
// remember HiLiteHandler on OUTPORTS of all nodes!
HiLiteHandler[] oldHdl = new HiLiteHandler[snc.getNrOutPorts()];
for (int i = 0; i < oldHdl.length; i++) {
oldHdl[i] = snc.getOutPort(i).getHiLiteHandler();
}
// configure node itself
boolean outputSpecsChanged = false;
if (flowStackConflict) {
// can't be configured due to stack clash.
// make sure execution from here on is canceled
disableNodeForExecution(sncID);
// (ought to be red with this type of error!)
if (!snc.getInternalState().equals(IDLE)) {
// if not already idle make sure it is!
invokeResetOnSingleNodeContainer(snc);
}
// report the problem
snc.setNodeMessage(NodeMessage.merge(oldMessage, NodeMessage.newError("Can't merge FlowVariable Stacks! (likely a loop problem.)")));
// different outputs - empty ports!
outputSpecsChanged = true;
} else {
outputSpecsChanged = snc.configure(inSpecs, keepNodeMessage);
}
// NOTE:
// no need to clean stacks of LoopEnd nodes - done automagically
// inside the getFlowObjectStack of the ports of LoopEnd
// Nodes.
// check if FlowObjectStacks have changed
boolean stackChanged = false;
FlowObjectStack newFOS = snc.createOutFlowObjectStack();
stackChanged = !newFOS.equals(oldFOS);
// check if HiLiteHandlers have changed
boolean hiLiteHdlsChanged = false;
for (int i = 0; i < oldHdl.length; i++) {
HiLiteHandler hdl = snc.getOutPort(i).getHiLiteHandler();
hiLiteHdlsChanged |= (hdl != oldHdl[i]);
}
configurationChanged = (outputSpecsChanged || stackChanged || hiLiteHdlsChanged);
// and finally check if we can queue this node!
if (snc.getInternalState().equals(UNCONFIGURED_MARKEDFOREXEC) || snc.getInternalState().equals(CONFIGURED_MARKEDFOREXEC)) {
queueIfQueuable(snc);
}
}
}
return configurationChanged;
// we have a problem here. Subsequent metanodes with through connections
// need to be configured no matter what - they can change their state
// because 3 nodes before in the pipeline the execute state changed...
// return configurationChanged == configurationChanged;
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class Normalizer3NodeModel method configure.
/**
* All {@link org.knime.core.data.def.IntCell} columns are converted to {@link org.knime.core.data.def.DoubleCell}
* columns.
*
* {@inheritDoc}
*/
@Override
protected PortObjectSpec[] configure(final PortObjectSpec[] inSpecs) throws InvalidSettingsException {
DataTableSpec spec = (DataTableSpec) inSpecs[0];
// extract selected numeric columns
String[] columns = getIncludedComlumns(spec);
DataTableSpec modelSpec = FilterColumnTable.createFilterTableSpec(spec, columns);
return new PortObjectSpec[] { Normalizer2.generateNewSpec(spec, columns), modelSpec };
}
Aggregations