/* * 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.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import com.rapidminer.tools.Observable; import com.rapidminer.tools.Observer; /** This class observes a set of input and output ports and adds additional ports as needed to * several {@link Ports} collections. This behaviour is needed, e.g., for the ProcessBranch operator. * Here, we have an arbitrary number of input ports each of which is linked to two subprocesses * ("if" and "else"). * * @see SinglePortExtender * @see PortPairExtender * * @author Simon Fischer * TODO: Unchecked? */ @SuppressWarnings("unchecked") public class MultiPortPairExtender<S extends Port,M extends Port> implements PortExtender { private final String name; private final Ports<S> singlePorts; private final ArrayList<Ports<M>> multiPortsList; private int minNumber = 0; private final List<MultiPortPair> managedPairs = new LinkedList<MultiPortPair>(); private boolean isChanging = false; private int runningId = 0; private final Observer<Port> observer = new Observer<Port>() { @Override public void update(Observable<Port> observable, Port arg) { updatePorts(); } }; protected class MultiPortPair { protected S singlePort; protected ArrayList<M> multiPorts; public MultiPortPair(S singlePort, ArrayList<M> multiPorts) { this.singlePort = singlePort; this.multiPorts = multiPorts; } public boolean isDisconnected() { if (singlePort.isConnected() || singlePort.isLocked()) { return false; } for (Port port : multiPorts) { if (port.isConnected() || port.isLocked()) { return false; } } return true; } } public MultiPortPairExtender(String name, Ports<S> singlePorts, Ports<M>[] multiPortsList) { this.name = name; this.singlePorts = singlePorts; this.multiPortsList = new ArrayList<Ports<M>>(Arrays.asList(multiPortsList)); singlePorts.registerPortExtender(this); for (Ports ports : multiPortsList) { ports.registerPortExtender(this); } } public void addMultiPorts(Ports<M> multiPorts, int index) { multiPortsList.add(index, multiPorts); int id = 1; for (MultiPortPair pair : managedPairs) { pair.multiPorts.add(index, multiPorts.createPassThroughPort(name + " " + id)); id++; } multiPorts.addObserver(observer, false); updatePorts(); } public void removeMultiPorts(int index) { Ports<M> oldPorts = multiPortsList.remove(index); oldPorts.removeObserver(observer); for (MultiPortPair pair : managedPairs) { //M oldPort = pair.multiPorts.remove(index); //oldPorts.removePort(oldPort); } updatePorts(); } private void updatePorts() { if (!isChanging) { isChanging = true; boolean first = true; MultiPortPair foundDisconnected = null; Iterator<MultiPortPair> i = managedPairs.iterator(); while (i.hasNext()) { MultiPortPair pair = i.next(); if (pair.isDisconnected()) { // we don't remove the first disconnected port. if (first) { first = false; foundDisconnected = pair; } else { if (minNumber == 0) { deletePorts(pair); i.remove(); } } } } if ((foundDisconnected == null) || (managedPairs.size() < minNumber)) { do { managedPairs.add(createPort()); } while (managedPairs.size() < minNumber); } else { if (minNumber == 0) { managedPairs.remove(foundDisconnected); managedPairs.add(foundDisconnected); singlePorts.pushDown(foundDisconnected.singlePort); for (int j = 0; j < multiPortsList.size(); j++) { multiPortsList.get(j).pushDown(foundDisconnected.multiPorts.get(j)); } } } fixNames(); isChanging = false; } } /** Creates an initial port and starts to listen. */ public void start() { managedPairs.add(createPort()); fixNames(); singlePorts.addObserver(observer, false); for (Ports<M> ports : multiPortsList) { ports.addObserver(observer, false); } } private MultiPortPair createPort() { runningId++; S singlePort = singlePorts.createPassThroughPort(name+" "+runningId); ArrayList<M> newMultiPorts = new ArrayList<M>(multiPortsList.size()); for (Ports<M> ports : multiPortsList) { M out = ports.createPassThroughPort(name+" "+runningId); newMultiPorts.add(out); } return new MultiPortPair(singlePort, newMultiPorts); } private void deletePorts(MultiPortPair pair) { singlePorts.removePort(pair.singlePort); for (Port multiPort : pair.multiPorts) { if (multiPort instanceof OutputPort) { if (multiPort.isConnected()) { ((OutputPort)multiPort).disconnect(); } } ((Ports<M>)multiPort.getPorts()).removePort((M)multiPort); } } private void fixNames() { runningId = 0; for (MultiPortPair pair : managedPairs) { runningId++; singlePorts.renamePort(pair.singlePort, name+"_tmp_"+runningId); for (Port port : pair.multiPorts) { ((Ports<M>)port.getPorts()).renamePort((M)port, name+"_tmp_"+runningId); } } runningId = 0; for (MultiPortPair pair : managedPairs) { runningId++; singlePorts.renamePort(pair.singlePort, name+" "+runningId); for (Port port : pair.multiPorts) { ((Ports<M>)port.getPorts()).renamePort((M)port, name+" "+runningId); } } } protected List<MultiPortPair> getManagedPairs() { return managedPairs; } @Override public String getNamePrefix() { return name + " "; } @Override public void ensureMinimumNumberOfPorts(int minNumber) { this.minNumber = minNumber; updatePorts(); } }