/*
* Copyright (C) 2012 Jason Gedge <http://www.gedge.ca>
*
* This file is part of the OpGraph project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.gedge.opgraph;
import java.util.Collection;
import ca.gedge.opgraph.dag.SimpleDirectedEdge;
import ca.gedge.opgraph.exceptions.ItemMissingException;
import ca.gedge.opgraph.extensions.Extendable;
import ca.gedge.opgraph.extensions.ExtendableSupport;
import ca.gedge.opgraph.validators.TypeValidator;
/**
* A link between nodes in an {@link OpGraph}.
*/
public final class OpLink
extends SimpleDirectedEdge<OpNode>
implements Extendable
{
/** The connected output field in the source node's value map */
private final OutputField sourceField;
/** The connected input field in the destination node's value map */
private final InputField destinationField;
/**
* Create a link with the given source/destination nodes.
*
* @param source source node
* @param sourceFieldKey the key of the field connected at the source
* @param destination destination node
* @param destinationFieldKey the key of the field connected at the destination
*
* @throws ItemMissingException if the given source field key is not in the
* source node's output fields, or similarly
* for the destination field and destination node's input fields.
* @throws NullPointerException if either node is <code>null</code>
*/
public OpLink(OpNode source,
String sourceFieldKey,
OpNode destination,
String destinationFieldKey)
throws ItemMissingException
{
super(source, destination);
sourceField = source.getOutputFieldWithKey(sourceFieldKey);
if(sourceField == null)
throw new ItemMissingException(sourceField);
destinationField = destination.getInputFieldWithKey(destinationFieldKey);
if(destinationField == null)
throw new ItemMissingException(destinationField);
}
/**
* Create an node with the given source/destination nodes.
*
* @param source source node
* @param sourceField the field connected at the source
* @param destination destination node
* @param destinationField the field connected at the destination
*
* @throws ItemMissingException if the given source field is not in the source
* node's output fields, or similarly for the
* destination field and destination node's input fields.
* @throws NullPointerException if any parameter is <code>null</code>
*/
public OpLink(OpNode source,
OutputField sourceField,
OpNode destination,
InputField destinationField)
throws ItemMissingException
{
super(source, destination);
if(sourceField == null || destinationField == null)
throw new NullPointerException("Source/destination fields cannot be null");
if(!source.getOutputFields().contains(sourceField))
throw new ItemMissingException(sourceField);
if(!destination.getInputFields().contains(destinationField))
throw new ItemMissingException(destinationField);
this.sourceField = sourceField;
this.destinationField = destinationField;
}
/**
* Gets the input field.
*
* @return the input field
*/
public OutputField getSourceField() {
return sourceField;
}
/**
* Gets the output field.
*
* @return the output field
*/
public InputField getDestinationField() {
return destinationField;
}
/**
* Gets whether or not this link is valid. A link is valid whenever the
* output type of the source field is accepted by the validator defined
* in the input field.
*
* @return <code>true</code> if this link is valid, <code>false</code> otherwise
*/
public boolean isValid() {
final TypeValidator validator = destinationField.getValidator();
return (validator == null || validator.isAcceptable(sourceField.getOutputType()));
}
//
// Overrides
//
@Override
public String toString() {
return String.format("{%s:%s --> %s:%s}",
source.getName(), sourceField.getKey(),
destination.getName(), destinationField.getKey());
}
@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
boolean ret = false;
if(obj != null && (obj instanceof OpLink)) {
final OpLink link = (OpLink)obj;
ret = source.equals(link.source)
&& destination.equals(link.destination)
&& sourceField.equals(link.sourceField)
&& destinationField.equals(link.destinationField);
}
return ret;
}
@Override
public int hashCode() {
return (31 * source.hashCode()
+ 19 * destination.hashCode()
+ 61 * sourceField.hashCode()
+ 67 * destinationField.hashCode());
}
//
// Extendable
//
private ExtendableSupport extendableSupport = new ExtendableSupport(OpGraph.class);
@Override
public <T> T getExtension(Class<T> type) {
return extendableSupport.getExtension(type);
}
@Override
public Collection<Class<?>> getExtensionClasses() {
return extendableSupport.getExtensionClasses();
}
@Override
public <T> T putExtension(Class<T> type, T extension) {
return extendableSupport.putExtension(type, extension);
}
}