/* * 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.ports; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import com.rapidminer.tools.Observable; import com.rapidminer.tools.Observer; /** 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. */ private int minNumber = 0; private boolean isChanging = false; private final List<T> managedPorts = new LinkedList<T>(); 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); } private 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; } } private 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; } private 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; } }