/* * This file is part of the OSMembrane project. * More informations under www.osmembrane.de * * The project is licensed under the GNU GENERAL PUBLIC LICENSE 3.0. * for more details about the license see http://www.osmembrane.de/license/ * * Source: $HeadURL$ ($Revision$) * Last changed: $Date$ */ package de.osmembrane.model.pipeline; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.ObjectStreamException; import java.util.ArrayList; import java.util.List; import java.util.Observable; import javax.imageio.ImageIO; import de.osmembrane.model.Identifier; import de.osmembrane.model.ModelProxy; import de.osmembrane.model.pipeline.AbstractConnector.ConnectorPosition; import de.osmembrane.model.pipeline.ConnectorException.Type; import de.osmembrane.model.pipeline.PipelineObserverObject.ChangeType; import de.osmembrane.model.settings.SettingType; import de.osmembrane.model.xml.XMLFunction; import de.osmembrane.model.xml.XMLPipe; import de.osmembrane.model.xml.XMLTask; import de.osmembrane.tools.I18N; /** * This represents the implementation of a simple Function for the * XML-Functions. * * @author jakob_jarosch */ public class Function extends AbstractFunction { private static final long serialVersionUID = 2010123022380001L; transient private Pipeline pipeline; transient private AbstractFunctionGroup parent; private Identifier parentIdentifier; transient private XMLFunction xmlFunction; private Identifier xmlFunctionIdentifier; private Point2D coordinate = new Point2D.Double(); private List<Connector> inConnectors = new ArrayList<Connector>(); private List<Connector> outConnectors = new ArrayList<Connector>(); private List<Task> tasks = new ArrayList<Task>(); private Task activeTask; /** * State of the icon load sequence. */ private boolean triedLoadIcon = false; /** * Icon of the function. */ private BufferedImage icon = null; /** * Creates a new Function with given parent and XMLFunction. * * @param parent * parent of the Function * @param xmlFunction * XMLFunction belongs to this Function */ public Function(AbstractFunctionGroup parent, XMLFunction xmlFunction) { this.parent = parent; this.xmlFunction = xmlFunction; /* set the identifiers */ AbstractFunctionPrototype afp = ModelProxy.getInstance().getFunctions(); this.parentIdentifier = this.parent.getIdentifier(); this.xmlFunctionIdentifier = afp .getMatchingXMLFunctionIdentifier(this.xmlFunction); /* add task observers */ for (XMLTask xmlTask : xmlFunction.getTask()) { Task task = new Task(this, xmlTask); task.addObserver(this); tasks.add(task); } /* set the first task as default */ activeTask = getAvailableTasks()[0]; /* create the connectors */ createConnectors(); } @Override public AbstractFunctionGroup getParent() { return parent; } @Override protected void setPipeline(Pipeline pipeline) { this.pipeline = pipeline; } @Override public AbstractPipeline getPipeline() { return pipeline; } @Override public String getId() { return xmlFunction.getId(); } @Override public String getFriendlyName() { /* fallback if friendlyName is not available */ if (xmlFunction.getFriendlyName() == null) { return getId(); } return xmlFunction.getFriendlyName(); } @Override public String getDescription() { return I18N.getInstance().getDescription(xmlFunction); } @Override public BufferedImage getIcon() { if (triedLoadIcon == false) { try { if (xmlFunction.getIcon() != null) { icon = ImageIO.read(new File(xmlFunction.getIcon())); } } catch (IOException e) { icon = null; } triedLoadIcon = true; } return icon; } @Override public Task[] getAvailableTasks() { Task[] returnTasks = new Task[tasks.size()]; return tasks.toArray(returnTasks); } @Override public AbstractTask getActiveTask() { return activeTask; } @Override public void setActiveTask(AbstractTask newTask) { /* only allow a correct task to be set as active */ for (Task task : getAvailableTasks()) { /* should be the same instance of the task */ if (task == newTask) { /* * found the new active XMLTask, so copy the settings to the new * Task. */ for (AbstractParameter oldParam : activeTask.getParameters()) { for (AbstractParameter newParam : task.getParameters()) { if (oldParam.getName().equals(newParam.getName()) && oldParam.getType() .equals(newParam.getType())) { /* * Now check if the oldParam is the default value, * if that is so, don't copy. */ if (oldParam.getValue() != null && !oldParam.isDefaultValue()) { /* oldParam has a real non-default value. */ newParam.setValue(oldParam.getValue()); } } } } activeTask = task; changedNotifyObservers(new PipelineObserverObject( ChangeType.CHANGE_FUNCTION, this)); return; } } } @Override public boolean isComplete() { /* check the params */ for (AbstractParameter param : getActiveTask().getParameters()) { if (!param.isValid()) { return false; } } /* check the in connectors */ for (AbstractConnector connector : getInConnectors()) { if (connector.getConnections().length == 0) { return false; } } /* check the out connectors */ for (AbstractConnector connector : getOutConnectors()) { if (connector.getConnections().length == 0) { return false; } } /* everything seems to be ok, return true */ return true; } @Override public Point2D getCoordinate() { Integer rasterInt = (Integer) ModelProxy.getInstance().getSettings() .getValue(SettingType.PIPELINE_RASTER_SIZE); double raster = (double) rasterInt.intValue(); if (raster < 1.0) { return coordinate; } else { /* * filter the coordinates, use the modulo function, round to the * next raster-value. */ double x = coordinate.getX(); double y = coordinate.getY(); double xSub = x % (double) raster; double ySub = y % (double) raster; double xAdd = raster - xSub; double yAdd = raster - ySub; x = x + ((xSub < xAdd) ? -xSub : xAdd); y = y + ((ySub < yAdd) ? -ySub : yAdd); return new Point2D.Double(x, y); } } @Override public Point2D getUnrasteredCoordinate() { return coordinate; } @Override public void setCoordinate(Point2D coordinate) { this.coordinate = coordinate; changedNotifyObservers(new PipelineObserverObject( ChangeType.CHANGE_FUNCTION, this)); } @Override public Connector[] getInConnectors() { Connector[] inConnectors = new Connector[this.inConnectors.size()]; inConnectors = this.inConnectors.toArray(inConnectors); return inConnectors; } @Override public Connector[] getOutConnectors() { Connector[] outConnectors = new Connector[this.outConnectors.size()]; outConnectors = this.outConnectors.toArray(outConnectors); return outConnectors; } @Override public void addConnectionTo(AbstractFunction function) throws ConnectorException { boolean foundFullOne = false; for (AbstractConnector connectorOut : getOutConnectors()) { for (AbstractConnector connectorIn : function.getInConnectors()) { if (connectorOut.getType() == connectorIn.getType()) { /* found equal Connectors */ /* * check if already a connection between these two function * exists */ for (AbstractConnector con : connectorOut.getConnections()) { if (con == connectorIn) { throw new ConnectorException( Type.CONNECTION_ALREADY_EXISTS); } } if (!connectorOut.isFull() && !connectorIn.isFull()) { /* first, add connections */ connectorIn.addConnection(connectorOut); connectorOut.addConnection(connectorIn); /* now check loop freeness */ if (getPipeline().hasLoop()) { /* remove 'cause that is not ok */ connectorIn.removeConnection(connectorOut); connectorOut.removeConnection(connectorIn); throw new ConnectorException(Type.LOOP_CREATED); } changedNotifyObservers(new PipelineObserverObject( ChangeType.ADD_CONNECTION, connectorOut, connectorIn)); return; } else { foundFullOne = true; } } } } if (foundFullOne) { throw new ConnectorException(Type.FULL); } else { throw new ConnectorException(Type.NO_MATCH); } } @Override public boolean removeConnectionTo(AbstractFunction function) { for (AbstractConnector connectorOut : getOutConnectors()) { for (AbstractConnector connectorIn : function.getInConnectors()) { if (connectorOut.getType() == connectorIn.getType()) { /* found equal Connectors, remove connection */ boolean inRemove = connectorIn .removeConnection(connectorOut); boolean outRemove = connectorOut .removeConnection(connectorIn); if (inRemove && outRemove) { changedNotifyObservers(new PipelineObserverObject( ChangeType.DELETE_CONNECTION, connectorOut, connectorIn)); return true; } } } } return false; } @Override protected void unlinkConnectors() { for (AbstractConnector outConnector : getOutConnectors()) { outConnector.unlink(true); } for (AbstractConnector inConnector : getInConnectors()) { inConnector.unlink(false); } } /** * Creates the connectors for the active XMLTask. */ private void createConnectors() { /* In-Connectors */ for (XMLPipe pipe : activeTask.getInputPipe()) { inConnectors.add(new Connector(this, ConnectorPosition.IN, pipe)); } /* Out-Connectors */ for (XMLPipe pipe : activeTask.getOutputPipe()) { outConnectors.add(new Connector(this, ConnectorPosition.OUT, pipe)); } } @Override protected void changedNotifyObservers(PipelineObserverObject poo) { this.setChanged(); this.notifyObservers(poo); } @Override public void update(Observable arg0, Object arg1) { /* get Updates from a Task (changed anything) */ changedNotifyObservers(new PipelineObserverObject( ChangeType.CHANGE_FUNCTION, this)); } @Override public Function copy(CopyType type) { Function newFunction = new Function(this.parent, this.xmlFunction); newFunction.pipeline = this.pipeline; newFunction.parent = parent; if (type.copyPosition()) { newFunction.coordinate = this.coordinate; } /* copy the tasks */ newFunction.tasks.clear(); for (Task task : this.tasks) { Task newTask = task.copy(type, newFunction); newTask.addObserver(newFunction); newFunction.tasks.add(newTask); if (task == activeTask) { newFunction.activeTask = newTask; } } if (!type.copyValues()) { newFunction.activeTask = newFunction.getAvailableTasks()[0]; } return newFunction; } private Object readResolve() throws ObjectStreamException { AbstractFunctionPrototype afp = ModelProxy.getInstance().getFunctions(); this.parent = afp.getMatchingFunctionGroup(this.parentIdentifier); this.xmlFunction = afp .getMatchingXMLFunction(this.xmlFunctionIdentifier); /* create the observers */ for (Task task : tasks) { task.addObserver(this); } return this; } }