/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.operator; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Set; import java.util.Vector; import java.util.logging.Level; import com.rapidminer.Process; import com.rapidminer.operator.execution.UnitExecutionFactory; import com.rapidminer.operator.ports.InputPort; import com.rapidminer.operator.ports.InputPorts; import com.rapidminer.operator.ports.OutputPort; import com.rapidminer.operator.ports.OutputPorts; import com.rapidminer.operator.ports.Port; import com.rapidminer.operator.ports.PortException; import com.rapidminer.operator.ports.PortOwner; import com.rapidminer.operator.ports.impl.InputPortsImpl; import com.rapidminer.operator.ports.impl.OutputPortsImpl; import com.rapidminer.operator.ports.metadata.CompatibilityLevel; import com.rapidminer.operator.ports.metadata.OperatorLoopError; import com.rapidminer.tools.AbstractObservable; import com.rapidminer.tools.DelegatingObserver; import com.rapidminer.tools.LogService; import com.rapidminer.tools.Observer; import com.rapidminer.tools.Tools; /** A process is a collection of operators whose ports can be wired. A process * provides input and output ports to its contained operators. ExecutionUnit replaces * the legacy OperatorChain. * This class takes care of executing the operators in the correct order by sorting * them topologically with respect to their dependencies. * * @author Simon Fischer */ public class ExecutionUnit extends AbstractObservable<ExecutionUnit> { private final PortOwner portOwner = new PortOwner() { @Override public OperatorChain getPortHandler() { return getEnclosingOperator(); } @Override public String getName() { return ExecutionUnit.this.getName(); } @Override public Operator getOperator() { return getEnclosingOperator(); } @Override public ExecutionUnit getConnectionContext() { return ExecutionUnit.this; } }; private String name; private final OperatorChain enclosingOperator; private final InputPorts innerInputPorts = new InputPortsImpl(portOwner); private final OutputPorts innerOutputPorts = new OutputPortsImpl(portOwner); private Vector<Operator> operators = new Vector<Operator>(); private Vector<Operator> executionOrder; private final Observer<Port> delegatingPortObserver = new DelegatingObserver<Port,ExecutionUnit>(this, this); private final Observer<Operator> delegatingOperatorObserver = new DelegatingObserver<Operator,ExecutionUnit>(this, this); // private final Observer<Port> portObserver = new Observer<Port>() { // @Override public void update(Observable<Port> observable, Port arg) { clearAndUpdateMetaData(); } // }; // private final Observer<Operator> operatorObserver = new Observer<Operator>() { // @Override public void update(Observable<Operator> observable, Operator arg) { clearAndUpdateMetaData(); } // }; public ExecutionUnit(OperatorChain enclosingOperator, String name) { this.name = name; this.enclosingOperator = enclosingOperator; innerInputPorts.addObserver(delegatingPortObserver, false); innerOutputPorts.addObserver(delegatingPortObserver, false); //innerInputPorts.addObserver(portObserver, false); //innerOutputPorts.addObserver(portObserver, false); int index = 0; do { char c = name.charAt(index); if (!(Character.isUpperCase(c) || Character.isDigit(c))) { LogService.getRoot().warning("Process name does not follow naming conventions: "+name+" (in "+enclosingOperator.getOperatorDescription().getName()+")"); } index = name.indexOf(' ', index) + 1; } while (index != 0); } // private void clearAndUpdateMetaData() { // //transformMDNeighbourhood(); // if (getEnclosingOperator() != null) { // getEnclosingOperator().getRoot().clear(Port.CLEAR_METADATA | Port.CLEAR_META_DATA_ERRORS); // getEnclosingOperator().getRoot().transformMetaData(); // } else { // clear(Port.CLEAR_METADATA | Port.CLEAR_META_DATA_ERRORS); // transformMetaData(); // } // } public InputPorts getInnerSinks() { return innerInputPorts; } public OutputPorts getInnerSources() { return innerOutputPorts; } /** Same as {@link #addOperator(Operator, boolean)}. */ public int addOperator(Operator operator) { return addOperator(operator, true); } /** Adds the operator to this execution unit. * * @param registerWithProcess Typically true. If false, the operator will not be registered with its parent process. * @return the new index of the operator. */ public int addOperator(Operator operator, boolean registerWithProcess) { if (operator == null) { throw new NullPointerException("operator cannot be null!"); } if (operator instanceof ProcessRootOperator) { throw new IllegalArgumentException("'Process' operator cannot be added. It must always be the top-level operator!"); } operators.add(operator); registerOperator(operator, registerWithProcess); return operators.size() - 1; } /** * Adds the operator to this execution unit. The operator at this index and * all subsequent operators are shifted to the right. The operator is registered automatically. */ public void addOperator(Operator operator, int index) { if (operator == null) { throw new NullPointerException("operator cannot be null!"); } if (operator instanceof ProcessRootOperator) { throw new IllegalArgumentException("'Process' operator cannot be added. It must always be the top-level operator!"); } operators.add(index, operator); registerOperator(operator, true); } public int getIndexOfOperator(Operator operator) { return operators.indexOf(operator); } private void registerOperator(Operator operator, boolean registerWithProcess) { operator.setEnclosingProcess(this); Process process = getEnclosingOperator().getProcess(); if ((process != null) && registerWithProcess) { operator.registerOperator(process); } fireUpdate(this); operator.addObserver(delegatingOperatorObserver, false); operator.clear(Port.CLEAR_ALL); if (process != null) { process.fireOperatorAdded(operator); } } private void unregister(Operator operator) { operator.removeObserver(delegatingOperatorObserver); //operator.removeObserver(operatorObserver); } /** Removes the given operator. Don't call this method directly but call * {@link Operator#remove()}. */ protected void removeOperator(Operator operator) { if (!operators.contains(operator)) { throw new NoSuchElementException("Operator "+operator.getName() + " not contained in " + getName() + "!"); } int oldIndex = operators.indexOf(operator); int oldIndexAmongEnabled = getEnabledOperators().indexOf(operator); operators.remove(operator); unregister(operator); //operator.disconnectPorts(); //transformMDNeighbourhood(); Process process = getEnclosingOperator().getProcess(); if (process != null) { process.fireOperatorRemoved(operator, oldIndex, oldIndexAmongEnabled); } operator.setEnclosingProcess(null); fireUpdate(this); } public void clear(int clearFlags) { for (Operator operator : operators) { operator.clear(clearFlags); } getInnerSinks().clear(clearFlags); getInnerSources().clear(clearFlags); } /** Helper class to count the number of dependencies of an operator. */ private static class EdgeCounter { private final Map<Operator,Integer> numIncomingEdges = new LinkedHashMap<Operator,Integer>(); private EdgeCounter(Collection<Operator> operators) { for (Operator op : operators) { numIncomingEdges.put(op, 0); } } private void incNumEdges(Operator op) { Integer num = numIncomingEdges.get(op); if (num == null) { // this can only happen if we add edges to inner ports of the enclosing operator. return; } num = num + 1; numIncomingEdges.put(op, num); } private int decNumEdges(Operator op) { Integer num = numIncomingEdges.get(op); // this can only happen if we add edges to inner ports of the enclosing operator. if (num == null) { return -1; } num = num - 1; assert(num >= 0); numIncomingEdges.put(op, num); return num; } private LinkedList<Operator> getIndependentOperators() { LinkedList<Operator> independentOperators = new LinkedList<Operator>(); for (Map.Entry<Operator,Integer> entry : numIncomingEdges.entrySet()) { if (entry.getValue() == null || entry.getValue() == 0) { independentOperators.add(entry.getKey()); } } return independentOperators; } } /** Sorts the operators topologically, i.e. such that operator <var>i</var> * in the returned ordering has dependencies (i.e. connected {@link InputPort}s) only * from operators <var>0..i-1</var>. */ public Vector<Operator> topologicalSort() { final Map<Operator,Integer> originalIndices = new HashMap<Operator,Integer>(); for (int i = 0; i < operators.size(); i++) { originalIndices.put(operators.get(i), i); } EdgeCounter counter = new EdgeCounter(operators); for (Operator child : getOperators()) { for (OutputPort out : child.getOutputPorts().getAllPorts()) { InputPort dest = out.getDestination(); if (dest != null) { counter.incNumEdges(dest.getPorts().getOwner().getOperator()); } } } Vector<Operator> sorted = new Vector<Operator>(); PriorityQueue<Operator> independentOperators = new PriorityQueue<Operator>(Math.max(1, operators.size()), new Comparator<Operator>() { @Override public int compare(Operator o1, Operator o2) { return originalIndices.get(o1) - originalIndices.get(o2); } }); independentOperators.addAll(counter.getIndependentOperators()); while (!independentOperators.isEmpty()) { Operator first = independentOperators.poll(); sorted.add(first); for (OutputPort out : first.getOutputPorts().getAllPorts()) { InputPort dest = out.getDestination(); if (dest != null) { Operator destOp = dest.getPorts().getOwner().getOperator(); if (counter.decNumEdges(destOp) == 0) { //independentOperators.addFirst(destOp); independentOperators.add(destOp); } } } } return sorted; } protected void updateExecutionOrder() { this.executionOrder = topologicalSort(); if (!this.executionOrder.equals(operators)) { if (operators.size() != executionOrder.size()) { // we have a circle. without a check, operator vanishes. return; } this.operators = this.executionOrder; getEnclosingOperator().getProcess().fireExecutionOrderChanged(this); } for (Operator operator : this.operators) { operator.updateExecutionOrder(); } } public void transformMetaData() { List<Operator> sorted = topologicalSort(); for (Operator op : sorted) { op.transformMetaData(); } if (sorted.size() != operators.size()) { List<Operator> remainder = new LinkedList<Operator>(operators); remainder.removeAll(sorted); for (Operator nodeInCircle : remainder) { for (OutputPort outputPort : nodeInCircle.getOutputPorts().getAllPorts()) { InputPort destination = outputPort.getDestination(); if ((destination != null) && remainder.contains(destination.getPorts().getOwner().getOperator())) { if (destination.getSource() != null) { // (source can be null *during* a disconnect in which case // both the source and the destination fire an update // which leads to this inconsistent state) destination.addError(new OperatorLoopError(destination)); } outputPort.addError(new OperatorLoopError(outputPort)); } } } } getInnerSinks().checkPreconditions(); } /** Returns an unmodifiable view of the operators contained in this process. */ public List<Operator> getOperators() { return Collections.unmodifiableList(operators); // return operators; } /** Use this method only in cases where you are sure that you don't want a ConcurrentModificationException to occur * when the list of operators is modified. */ public Enumeration<Operator> getOperatorEnumeration() { return operators.elements(); } /** Returns an unmodifiable view of the operators contained in this process. */ public List<Operator> getEnabledOperators() { return new EnabledOperatorView(operators); } public String getName() { return name; } public void setName(String name) { this.name = name; } /** Returns the operator that contains this process as a subprocess. */ public OperatorChain getEnclosingOperator() { return enclosingOperator; } private void unwire(boolean recursive) { getInnerSources().disconnectAll(); for (Operator op : getOperators()) { unwire(op, recursive); } } private void unwire(Operator op, boolean recursive) throws PortException { op.getOutputPorts().disconnectAll(); if (recursive) { if (op instanceof OperatorChain) { for (ExecutionUnit subprocess : ((OperatorChain) op).getSubprocesses()) { subprocess.unwire(recursive); } } } } @SuppressWarnings("deprecation") private void autoWire(CompatibilityLevel level, InputPorts inputPorts, LinkedList<OutputPort> readyOutputs) throws PortException { boolean success = false; do { Set<InputPort> complete = new HashSet<InputPort>(); for (InputPort in : inputPorts.getAllPorts()) { success = false; if (!in.isConnected() && !complete.contains(in) && in.getPorts().getOwner().getOperator().shouldAutoConnect(in)) { Iterator<OutputPort> outIterator; // TODO: Simon: Does the same in both cases. Check again. if (in.simulatesStack()) { outIterator = readyOutputs.descendingIterator(); } else { outIterator = readyOutputs.descendingIterator(); } while (outIterator.hasNext()) { OutputPort outCandidate = outIterator.next(); // TODO: Remove shouldAutoConnect() in later versions Operator owner = outCandidate.getPorts().getOwner().getOperator(); if (owner.shouldAutoConnect(outCandidate)) { if (outCandidate.getMetaData() != null) { if (in.isInputCompatible(outCandidate.getMetaData(), level)) { readyOutputs.remove(outCandidate); outCandidate.connectTo(in); // we cannot continue with the remaining input ports // since connecting may have triggered the creation of new input ports // which would result in undefined behavior and a ConcurrentModificationException success = true; break; } } } } // no port found. complete.add(in); if (success) { break; } } } } while (success); } /** Transforms the meta data of the enclosing operator. Required in * {@link #autoWire()} after each Operator that has been wired. */ private void transformMDNeighbourhood() { //getEnclosingOperator().getTransformer().transformMetaData(); getEnclosingOperator().transformMetaData(); } /** Connects the ports automatically in a first-fit approach. Operators * are connected in their ordering within the {@link #operators} list. * Every input of every operator is connected to the first compatible output * of an operator "left" of this operator. This corresponds to the way, * IOObjects were consumed in the pre-5.0 version. Disabled operators * are skipped. * * <br/> * @param level If level is {@link CompatibilityLevel#VERSION_5}, an input * is considered compatible only if it satisfies all meta data * constraints. For {@link CompatibilityLevel#PRE_VERSION_5} we * only consider the classes. * * @param keepConnections if true, don't unwire old connections before rewiring. * */ public void autoWire(CompatibilityLevel level, boolean keepConnections, boolean recursive) throws PortException { if (!keepConnections) { unwire(recursive); } // store all outputs. Scan them to find matching inputs. LinkedList<OutputPort> readyOutputs = new LinkedList<OutputPort>(); addReadyOutputs(readyOutputs, getInnerSources()); List<Operator> enabled = new LinkedList<Operator>(); for (Operator op : getOperators()) { if (op.isEnabled()) { enabled.add(op); } } autoWire(level, enabled, readyOutputs, recursive, true); } /** * @param wireNew If true, OutputPorts of operators will be added to readyOutputs once they are wired. */ private void autoWire(CompatibilityLevel level, List<Operator> operators, LinkedList<OutputPort> readyOutputs, boolean recursive, boolean wireNew) throws PortException { transformMDNeighbourhood(); for (Operator op : operators) { try { readyOutputs = op.preAutoWire(readyOutputs); } catch (OperatorException e) { getEnclosingOperator().getLogger().log(Level.WARNING, "During auto-wiring: "+e, e); } autoWire(level, op.getInputPorts(), readyOutputs); transformMDNeighbourhood(); if (recursive) { if (op instanceof OperatorChain) { for (ExecutionUnit subprocess : ((OperatorChain)op).getSubprocesses()) { // we have already removed all connections, so keepConnections=true in recursive call subprocess.autoWire(level, true, recursive); } } } if (wireNew) { addReadyOutputs(readyOutputs, op.getOutputPorts()); } } autoWire(level, getInnerSinks(), readyOutputs); transformMDNeighbourhood(); } /** Automatically wires inputs and outputs of a single operator in this execution unit. * * @param inputs Wire inputs? * @param outputs Wire outputs?*/ public void autoWireSingle(Operator operator, CompatibilityLevel level, boolean inputs, boolean outputs) { // auto wire inputs if (inputs) { transformMDNeighbourhood(); // store all outputs. Scan them to find matching inputs. LinkedList<OutputPort> readyOutputs = new LinkedList<OutputPort>(); // add the ports, oldest first. Simulate pre-5.0-like stack by taking // the last out of this list when consuming input. addReadyOutputs(readyOutputs, getInnerSources()); boolean found = false; for (Operator other : operators) { if (other == operator) { found = true; break; } else { addReadyOutputs(readyOutputs, other.getOutputPorts()); } } if (!found) { throw new IllegalArgumentException("Operator "+operator.getName() + " does not belong to this subprocess "+getName()+"."); } getEnclosingOperator().getLogger().fine("Wiring: "+operator + "." + operator.getInputPorts().getAllPorts() + " to " + readyOutputs); autoWire(level, operator.getInputPorts(), readyOutputs); } // auto wire outputs if (outputs) { LinkedList<OutputPort> readyOutputs = new LinkedList<OutputPort>(); addReadyOutputs(readyOutputs, operator.getOutputPorts()); List<Operator> successors = new LinkedList<Operator>(); boolean foundMe = false; for (Operator other : getOperators()) { if (foundMe) { successors.add(other); } else if (other == operator) { foundMe = true; } } autoWire(level, successors, readyOutputs, false, false); } } private void addReadyOutputs(LinkedList<OutputPort> readyOutputs, OutputPorts ports) { // add the parameters in a stack-like fashion like in pre-5.0 //Iterator<OutputPort> i = ports.getAllPorts().iterator(); Iterator<OutputPort> i = new LinkedList<OutputPort>(ports.getAllPorts()).descendingIterator(); while (i.hasNext()) { OutputPort port = i.next(); if (!port.isConnected() && port.shouldAutoConnect()) { readyOutputs.addLast(port); } } } /** Returns a list of all available output ports within this process, including * inner sources and output ports of enclosed operators. */ public Collection<OutputPort> getAllOutputPorts() { Collection<OutputPort> outputPorts = new LinkedList<OutputPort>(); outputPorts.addAll(getInnerSources().getAllPorts()); for (Operator operator : operators) { outputPorts.addAll(operator.getOutputPorts().getAllPorts()); } return outputPorts; } public Operator getOperatorByName(String toOp) { for (Operator op : operators) { if (op.getName().equals(toOp)) { return op; } } return null; } public int getNumberOfOperators() { return operators.size(); } /** Clones operators contained in <code>original</code>, adds them * to this execution unit and wires them as they were originally. * * @param forParallelExecution Indicates whether this clone is supposed * to be executed in parallel. If yes, the clone will not be * registered with the parent process and will share its * {@link Operator#applyCount} with the original. */ public void cloneExecutionUnitFrom(ExecutionUnit original, boolean forParallelExecution) { // Clone operators Map<String,Operator> clonedOperatorsByName = new HashMap<String,Operator>(); for (Operator originalChild : original.operators) { Operator clonedOperator = originalChild.cloneOperator(originalChild.getName(), forParallelExecution); addOperator(clonedOperator, !forParallelExecution); clonedOperatorsByName.put(originalChild.getName(), clonedOperator); } // Restore connections cloneConnections(original.getInnerSources(), original, clonedOperatorsByName); for (Operator op : original.operators) { cloneConnections(op.getOutputPorts(), original, clonedOperatorsByName); } // Unlock original.getInnerSources().unlockPortExtenders(); original.getInnerSinks().unlockPortExtenders(); for (Operator op : this.operators) { op.getInputPorts().unlockPortExtenders(); op.getOutputPorts().unlockPortExtenders(); } // Other: this.expanded = original.expanded; } private void cloneConnections(OutputPorts originalPorts, ExecutionUnit originalExecutionUnit, Map<String,Operator> clonedOperatorsByName) { for (OutputPort originalSource : originalPorts.getAllPorts()) { if (originalSource.isConnected()) { OutputPort mySource; if (originalPorts.getOwner().getOperator() == originalExecutionUnit.getEnclosingOperator()) { // this is an inner source mySource = getInnerSources().getPortByName(originalSource.getName()); if (mySource== null) { throw new RuntimeException("Error during clone: Corresponding source for "+originalSource+" not found (no such inner source)."); } } else { // this is an output port Operator myOperator = clonedOperatorsByName.get(originalSource.getPorts().getOwner().getOperator().getName()); if (myOperator == null) { throw new RuntimeException("Error during clone: Corresponding source for "+originalSource +" not found (no such operator)."); } mySource = myOperator.getOutputPorts().getPortByName(originalSource.getName()); if (mySource == null) { throw new RuntimeException("Error during clone: Corresponding source for "+originalSource+" not found (no such output port)."); } } InputPort originalDestination = originalSource.getDestination(); InputPort myDestination; if (originalDestination.getPorts().getOwner().getOperator() == originalExecutionUnit.getEnclosingOperator()) { // this is an inner sink myDestination = getInnerSinks().getPortByName(originalDestination.getName()); if (myDestination == null) { throw new RuntimeException("Error during clone: Corresponding destination for "+originalDestination+" not found (no such inner sink)."); } } else { // this is an input port Operator myOperator = clonedOperatorsByName.get(originalDestination.getPorts().getOwner().getOperator().getName()); if (myOperator == null) { throw new RuntimeException("Error during clone: Corresponding destination for "+originalDestination +" not found (no such operator)."); } myDestination = myOperator.getInputPorts().getPortByName(originalDestination.getName()); if (myDestination == null) { throw new RuntimeException("Error during clone: Corresponding destination for "+originalDestination+" not found (no such input port)."); } } mySource.connectTo(myDestination); } } } /** Returns all nested operators. */ public Collection<Operator> getChildOperators() { List<Operator> children = new LinkedList<Operator>(); for (Operator operator : operators) { children.add(operator); } return children; } /** Recursively returns all nested operators. */ public List<Operator> getAllInnerOperators() { List<Operator> children = new LinkedList<Operator>(); for (Operator operator : operators) { children.add(operator); if (operator instanceof OperatorChain) { children.addAll(((OperatorChain)operator).getAllInnerOperators()); } } return children; } protected String createProcessTree(int indent, String selfPrefix, String childPrefix, Operator markOperator, String mark) { String tree = Tools.indent(indent) + " subprocess '"+getName()+"'"; Iterator<Operator> i = operators.iterator(); while (i.hasNext()) { tree += Tools.getLineSeparator() + i.next().createProcessTree(indent, childPrefix + "+- ", childPrefix + (i.hasNext() ? "| " : " "), markOperator, mark); } return tree; } /** Executes the inner operators. */ public void execute() throws OperatorException { // Logger logger = getEnclosingOperator().getLogger(); // if (logger.isLoggable(Level.FINE)) { // getEnclosingOperator().getLogger().fine("Executing subprocess "+getEnclosingOperator().getName()+"."+getName()+". Execution order is: "+executionOrder); // } // for (Operator operator : executionOrder) { // operator.execute(); // operator.freeMemory(); // } UnitExecutionFactory.getInstance().getExecutor(this).execute(this); } /** Frees memory used by inner sinks. */ public void freeMemory() { getInnerSources().freeMemory(); getInnerSinks().freeMemory(); } private boolean expanded = true; /** Sets the expansion mode which indicates if this operator is drawn expanded or not. */ public void setExpanded(boolean expanded) { this.expanded = expanded; } /** Returns true if this operator should be painted expanded. */ public boolean isExpanded() { return expanded; } public void processStarts() throws OperatorException { for (Operator operator : operators) { operator.processStarts(); } executionOrder = topologicalSort(); } public void processFinished() throws OperatorException { for (Operator operator : operators) { operator.processFinished(); } } /** Moves the operators from this process to another process, keeping all connections intact. * TODO: Test more rigorously. Do we register/unregister everything correctly? * @return the number of ports the connections of which could not be restored */ public int stealOperatorsFrom(ExecutionUnit otherUnit) { int failedReconnects = 0; // remember source and sink connections so we can reconnect them later. Map<String,InputPort> sourceMap = new HashMap<String,InputPort>(); Map<String,OutputPort> sinkMap = new HashMap<String,OutputPort>(); for (OutputPort source : otherUnit.getInnerSources().getAllPorts()) { if (source.isConnected()) { sourceMap.put(source.getName(), source.getDestination()); } } otherUnit.getInnerSources().disconnectAll(); for (InputPort sink : otherUnit.getInnerSinks().getAllPorts()) { if (sink.isConnected()) { sinkMap.put(sink.getName(), sink.getSource()); } } otherUnit.getInnerSinks().disconnectAll(); // Move operators Iterator<Operator> i = otherUnit.operators.iterator(); while (i.hasNext()) { Operator operator = i.next(); i.remove(); otherUnit.unregister(operator); Process otherProcess = operator.getProcess(); if (otherProcess != null) { operator.unregisterOperator(otherProcess); } this.operators.add(operator); operator.setEnclosingProcess(null); //operator.unregisterOperator(operator.getProcess()); registerOperator(operator, true); //operator.registerOperator(this.getEnclosingOperator().getProcess()); } // Rewire sources and sinks for (Map.Entry<String,InputPort> entry : sourceMap.entrySet()) { OutputPort mySource = getInnerSources().getPortByName(entry.getKey()); if (mySource != null) { mySource.connectTo(entry.getValue()); } else { failedReconnects++; } } getInnerSources().unlockPortExtenders(); for (Map.Entry<String,OutputPort> entry : sinkMap.entrySet()) { InputPort mySink = getInnerSinks().getPortByName(entry.getKey()); if (mySink != null) { entry.getValue().connectTo(mySink); } else { failedReconnects++; } } getInnerSinks().unlockPortExtenders(); fireUpdate(this); return failedReconnects; } /** Moves an operator to the given index. (If the old index is smaller * than the new one, the new one will automatically be reduced by one.) */ public void moveToIndex(Operator op, int newIndex) { int oldIndex = operators.indexOf(op); Process process = getEnclosingOperator().getProcess(); if (oldIndex != -1) { operators.remove(op); if (process != null) { int oldIndexAmongEnabled = getEnabledOperators().indexOf(op); process.fireOperatorRemoved(op, oldIndex, oldIndexAmongEnabled); } if (oldIndex < newIndex) { newIndex--; } operators.add(newIndex, op); if (process != null) { process.fireOperatorAdded(op); } fireUpdate(); updateExecutionOrder(); } } /** Re-arranges the execution order such that the specified operators immediately follow <code>insertAfter</code>. */ public void bringToFront(Collection<Operator> movedOperators, Operator insertAfter) { this.operators.removeAll(movedOperators); int index = this.operators.indexOf(insertAfter) + 1; for (Operator op : movedOperators) { this.operators.add(index++, op); } updateExecutionOrder(); fireUpdate(); } }