/** * 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.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import com.rapidminer.operator.IOContainer; import com.rapidminer.operator.IOObject; import com.rapidminer.operator.ports.InputPort; import com.rapidminer.operator.ports.OutputPort; import com.rapidminer.operator.ports.Port; import com.rapidminer.operator.ports.PortException; import com.rapidminer.operator.ports.PortExtender; import com.rapidminer.operator.ports.PortOwner; import com.rapidminer.operator.ports.Ports; import com.rapidminer.tools.AbstractObservable; import com.rapidminer.tools.I18N; import com.rapidminer.tools.LogService; import com.rapidminer.tools.Observable; import com.rapidminer.tools.Observer; /** * @author Simon Fischer */ public abstract class AbstractPorts<T extends Port> extends AbstractObservable<Port> implements Ports<T> { private final List<T> portList = Collections.synchronizedList(new ArrayList<>()); private final Map<String, T> portMap = new HashMap<>(); private String[] portNames; private boolean portNamesValid = false; private final PortOwner owner; private final Observer<Port> delegatingObserver = new Observer<Port>() { @Override public void update(Observable<Port> observable, Port arg) { fireUpdate(arg); } }; public AbstractPorts(PortOwner owner) { this.owner = owner; portNamesValid = false; } private void updatePortNames() { if (!portNamesValid) { portNames = new String[portList.size()]; int i = 0; synchronized (portList) { for (Port port : portList) { portNames[i++] = port.getName(); } } portNamesValid = true; } } @Override public void addPort(T port) throws PortException { if (portMap.containsKey(port.getName())) { throw new PortException("Port name already used: " + port.getName()); } assert port.getPorts() == this; portList.add(port); portMap.put(port.getName(), port); portNamesValid = false; port.addObserver(delegatingObserver, false); fireUpdate(port); } @Override public void removePort(T port) throws PortException { if (!portList.contains(port) || port.getPorts() != this) { throw new PortException("Cannot remove " + port + "."); } else { if (port.isConnected()) { if (port instanceof OutputPort) { ((OutputPort) port).disconnect(); } else { ((InputPort) port).getSource().disconnect(); } } portList.remove(port); portMap.remove(port.getName()); port.removeObserver(delegatingObserver); fireUpdate(); } } @Override public void removeAll() { // don't iterate to avoid concurrent modification while (getNumberOfPorts() != 0) { removePort(getPortByIndex(0)); } } @Override public int getNumberOfPorts() { return portList.size(); } @Override public T getPortByIndex(int index) { return portList.get(index); } @Override public T getPortByName(String name) { T port = portMap.get(name); if (port != null) { return port; } else { // LogService.getRoot().fine("Port '"+name+"' does not exist. Checking for extenders."); LogService.getRoot().log(Level.FINE, "com.rapidminer.operator.ports.impl.AbstractPorts.port_does_not_exist", name); if (portExtenders != null) { for (PortExtender extender : portExtenders) { String prefix = extender.getNamePrefix(); if (name.startsWith(prefix)) { // LogService.getRoot().fine("Found extender with prefix '"+prefix+"'. // Trying to extend."); LogService.getRoot().log(Level.FINE, "com.rapidminer.operator.ports.impl.AbstractPorts.found_extender", prefix); try { int index = Integer.parseInt(name.substring(prefix.length())); extender.ensureMinimumNumberOfPorts(index); // numbering starts at 1 T secondTry = portMap.get(name); if (secondTry == null) { // LogService.getRoot().warning("Port extender "+prefix+" did not // extend to size "+index+"."); LogService.getRoot().log(Level.WARNING, "com.rapidminer.operator.ports.impl.AbstractPorts.port_extender_did_not_extend", new Object[] { prefix, index }); } else { // LogService.getRoot().fine("Port was created. Ports are now: // "+getAllPorts()); LogService.getRoot().log(Level.FINE, "com.rapidminer.operator.ports.impl.AbstractPorts.ports_created", getAllPorts()); } return secondTry; } catch (NumberFormatException e) { // LogService.getRoot().log(Level.WARNING, // "Cannot extend "+prefix+": "+e, e); LogService.getRoot().log(Level.WARNING, I18N.getMessage(LogService.getRoot().getResourceBundle(), "com.rapidminer.operator.ports.impl.AbstractPorts.extending_error", prefix, e), e); return null; } } } } // no extender found return null; } } @Override public String[] getPortNames() { updatePortNames(); return portNames; } @Override public List<T> getAllPorts() { synchronized (portList) { return Collections.unmodifiableList(new ArrayList<>(portList)); } } @SuppressWarnings("deprecation") @Override public String getMetaDataDescription() { StringBuilder b = new StringBuilder(); synchronized (portList) { for (Port port : portList) { b.append(port.getName()); b.append(": "); b.append(port.getMetaData()); b.append("; "); } } return b.toString(); } @Override public PortOwner getOwner() { return owner; } @Override public boolean containsPort(T port) { return portList.contains(port); } @Override public void renamePort(T port, String newName) { if (portMap.containsKey(newName)) { throw new PortException("Port name already used: " + port.getName()); } portMap.remove(port.getName()); ((AbstractPort) port).setName(newName); portMap.put(newName, port); } @Override public void clear(int clearFlags) { for (T port : getAllPorts()) { port.clear(clearFlags); } } @Override public IOContainer createIOContainer(boolean onlyConnected, boolean omitEmptyResults) { Collection<IOObject> output = new LinkedList<>(); for (Port port : getAllPorts()) { if (!onlyConnected || port.isConnected()) { IOObject data = port.getAnyDataOrNull(); if (omitEmptyResults) { if (data != null) { output.add(data); } } else { output.add(data); } } } return new IOContainer(output); } @Override public IOContainer createIOContainer(boolean onlyConnected) { return createIOContainer(onlyConnected, true); } @Override public void pushDown(T port) { if (!portList.contains(port)) { throw new PortException("Cannot push down " + port.getName() + ": port does not belong to " + this); } portList.remove(port); portList.add(port); } private List<PortExtender> portExtenders; @Override public void registerPortExtender(PortExtender extender) { if (portExtenders == null) { portExtenders = new LinkedList<>(); } portExtenders.add(extender); } @Override public void unlockPortExtenders() { if (portExtenders != null) { for (PortExtender extender : portExtenders) { extender.ensureMinimumNumberOfPorts(0); } } } @Override public void freeMemory() { for (Port inputPort : getAllPorts()) { inputPort.freeMemory(); } } @Override public int getNumberOfConnectedPorts() { int count = 0; for (Port port : getAllPorts()) { if (port.isConnected()) { count++; } } return count; } @Override public String toString() { StringBuilder b = new StringBuilder(); boolean first = true; for (String port : getPortNames()) { if (first) { first = false; } else { b.append(", "); } b.append(port); } return b.toString(); } }