/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.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.ports;
import com.rapidminer.tools.Observable;
import com.rapidminer.tools.Observer;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* A port extender automatically creates an additional port if ports are connected. It guarantees
* that there is always exactly one unconnected port. A port extender works by adding itself as an
* observer of a {@link Ports} object and creating or deleting ports as necessary on any update.
*
* @author Simon Fischer
* @see PortPairExtender
* @see MultiPortPairExtender
* @param <T>
* The type of port that is created.
*/
public class SinglePortExtender<T extends Port> implements PortExtender {
private final Ports<T> ports;
private final String name;
/** The number of ports that are guaranteed to exist. */
protected int minNumber = 0;
private boolean isChanging = false;
/**
* List of all Ports which are managed by this Class. Caution: to prevent failures in the GUI
* mind that this list must be synchronized with the Ports-field!
*/
protected final List<T> managedPorts = new LinkedList<>();
private int runningId = 0;
private final Observer<Port> observer = new Observer<Port>() {
@Override
public void update(Observable<Port> observable, Port arg) {
updatePorts();
}
};
/**
*
* @param name
* The name prefix for the generated ports. An underscore and a number will be added.
* @param ports
* The port to which ports are added.
*/
public SinglePortExtender(String name, Ports<T> ports) {
this.ports = ports;
this.name = name;
ports.registerPortExtender(this);
}
/**
* Deletes all unused (= not connected and not locked) ports, always keeping at least one free
* port available. <br/>
* Override only in special cases, e.g. when needing to control the number of ports depending on
* parameters or other properties of the operator.
*/
protected void updatePorts() {
if (!isChanging) {
isChanging = true;
boolean first = true;
T foundDisconnected = null;
Iterator<T> i = managedPorts.iterator();
while (i.hasNext()) {
T port = i.next();
if (!port.isConnected() && !port.isLocked()) {
// we don't remove the first disconnected port.
if (first) {
foundDisconnected = port;
first = false;
} else {
if (minNumber == 0) { // we don't remove if guaranteeing ports
deletePort(port);
i.remove();
}
}
}
}
if ((foundDisconnected == null) || (managedPorts.size() < minNumber)) {
do {
managedPorts.add(createPort());
} while (managedPorts.size() < minNumber);
} else {
if (minNumber == 0) {
managedPorts.remove(foundDisconnected);
managedPorts.add(foundDisconnected);
ports.pushDown(foundDisconnected);
}
}
fixNames();
isChanging = false;
}
}
protected void deletePort(T port) {
if (port instanceof OutputPort) {
if (port.isConnected()) {
((OutputPort) port).disconnect();
}
}
ports.removePort(port);
}
protected T createPort() {
runningId++;
T port = ports.createPort(name + " " + runningId);
return port;
}
protected void fixNames() {
runningId = 0;
for (T port : managedPorts) {
runningId++;
ports.renamePort(port, name + "_tmp_" + runningId);
}
runningId = 0;
for (T port : managedPorts) {
runningId++;
ports.renamePort(port, name + " " + runningId);
}
}
/** Creates an initial port and starts to listen. */
public void start() {
managedPorts.add(createPort());
fixNames();
ports.addObserver(observer, false);
}
/** Returns an unmodifiable view of the ports created by this port extender. */
public List<T> getManagedPorts() {
return Collections.unmodifiableList(managedPorts);
}
@Override
public String getNamePrefix() {
return name + " ";
}
@Override
public void ensureMinimumNumberOfPorts(int minNumber) {
this.minNumber = minNumber;
updatePorts();
}
protected Ports<T> getPorts() {
return ports;
}
}