// ServicesEdge.java
//
//    senger@ebi.ac.uk
//    October 2003
//

package org.biomoby.client;

import org.biomoby.shared.MobyService;

/**
 * An instance of this class is a container for a pair of Moby service
 * definitions, and for an information how they can be connected. You
 * can imagine that instances of this class represent edges in a graph
 * of services.  <p>
 * 
 * The instances are suitable for creation of graphs, but also, for
 * example, for building automatic clients that exchange data between
 * several services.  <p>
 *
 * @see ServiceConnections
 * @author <A HREF="mailto:senger@ebi.ac.uk">Martin Senger</A>
 * @version $Id: ServicesEdge.java,v 1.7 2005/11/20 12:30:51 senger Exp $
 */

public class ServicesEdge {


    // types of connections

    private static final int MIN_CONNECTION_TYPE = 0;

    /** Indicates that there is no information how the services are connected. */
    public static final int NO_CONNECTION = 0;
    public static final int SIMPLE_CONNECTION = 1;
    public static final int HEAD_COLLECTION_CONNECTION = 2;
    public static final int TAIL_COLLECTION_CONNECTION = 3;
    public static final int BOTH_COLLECTIONS_CONNECTION = 4;
    public static final int NO_OUTPUT = 5;
    private static final int MAX_CONNECTION_TYPE = 5;

    /** A divider used in {@link #getConnector connectors} to separate
     * namespace and data type name. */
    public static final String NS_DIVIDER = "/";

    // private members
    MobyService sourceService = null;
    MobyService targetService = null;
    String connector = "";
    int connectionType = NO_CONNECTION;
    boolean isWeakConnection = false;

    public String getId() {
	StringBuffer buf = new StringBuffer();
// 	buf.append (sourceService);
	buf.append (sourceService == null ? "null" : sourceService.getUniqueName());
	buf.append ("|");
// 	buf.append (targetService);
	buf.append (targetService == null ? "null" : targetService.getUniqueName());
	buf.append ("|");
	buf.append (connector);
	buf.append ("|");
	buf.append ("" + connectionType);
	buf.append ("|");
	buf.append ("" + isWeakConnection);
	return new String (buf);
    }

    public String toString() {
	StringBuffer buf = new StringBuffer();
	buf.append ( (sourceService == null ? "null" : sourceService.getName()) );
	buf.append (" ---( ");
	buf.append (connector);
	buf.append (" )--> ");
	buf.append ( (targetService == null ? "null" : targetService.getName()) );
	return new String (buf);
    }

    public String extractNamespace() {
	int pos = connector.indexOf (NS_DIVIDER);
	return (pos > -1 ? connector.substring (0, pos) : "");
    }

    /*************************************************************************
     * Constructs an instance with a source service and a connection
     * type.  It prints a warning on the STDERR if the connection type
     * is unknown.
     *************************************************************************/
    public ServicesEdge (MobyService sourceService, int connectionType) {
	this.sourceService = sourceService;
	setConnectionType (connectionType);
    }

    /*************************************************************************
     * Constructs an instance with a source service and a
     * connector. This is usually used for services not connected with
     * other services.
     *************************************************************************/
    public ServicesEdge (MobyService sourceService, String connector) {
	this.sourceService = sourceService;
	setConnector (connector);
	connectionType = SIMPLE_CONNECTION;
    }

    /*************************************************************************
     * Constructs an instance with both source and target services,
     * and a connector. This is usually a normal case - the connection
     * type is set to SIMPLE but can be overwritten
     * by {@link #setConnectionType} later.
     *************************************************************************/
    public ServicesEdge (MobyService sourceService, MobyService targetService,
			 String connector) {
	this.sourceService = sourceService;
	this.targetService = targetService;
	setConnector (connector);
	connectionType = SIMPLE_CONNECTION;
    }

    /*************************************************************************
     * Retrieves the source service.
     *
     * @return the source service
     *
     *************************************************************************/
    public MobyService getSourceService() {
	return sourceService;
    }

    /*************************************************************************
     * Retrieves the target service.
     *
     * @return the target service
     *
     *************************************************************************/
    public MobyService getTargetService() {
	return targetService;
    }

    /*************************************************************************
     * Sets connector.
     *
     * @param connector is a string representing namespace and data
     * type linking together source and target services
     *
     *************************************************************************/
    public void setConnector (String connector) {
	if (connector == null)
	    this.connector = "";
	else
	    this.connector = connector;
    }

    /*************************************************************************
     * Retrieves connector.
     *
     * @see #setConnector
     * @return connector
     *
     *************************************************************************/
    public String getConnector() {
	return connector;
    }

    /*************************************************************************
     * Sets connection type.
     *
     * @param connectionType is a new connection type. It prints a
     * warning on the STDERR if the connection type is unknown.
     *
     *************************************************************************/
    public void setConnectionType (int connectionType) {
	if (! checkConnectionType (connectionType)) {
	    System.err.println ("Unknown connection type: " + connectionType +
				". Replaced by NO_CONNECTION type.");
	} else {
	    this.connectionType = connectionType;
	}
    }

    /*************************************************************************
     * Retrieves connection type.
     *
     * @return connection type
     *
     *************************************************************************/
    public int getConnectionType() {
	return connectionType;
    }

    /*************************************************************************
     * Sets quality of this connection.
     *
     * @param weak value TRUE suggests to consider the connection weak
     * @see #isWeakConnection
     *
     *************************************************************************/
    public void setWeakConnection (boolean weak) {
	isWeakConnection = weak;
    }

    /*************************************************************************
     * Returns quality of this connection.
     * <p>
     *
     * What is a "weak connection"? It indicates that one or both
     * involved services have more than one input or output but the
     * other service in the pair can provide less (usually just one)
     * inputs or outputs. This may not be wrong (it depends on the
     * service itself how can deal with missing inputs) but it is
     * worth to mentioned it somewhere.
     *
     * @return true if the connection between two services is considered weak
     *
     *************************************************************************/
    public boolean isWeakConnection() {
	return isWeakConnection;
    }

    /*************************************************************************
     * Checks if the given connection type is known.
     *************************************************************************/
    private boolean checkConnectionType (int conType) {
	return (conType >= MIN_CONNECTION_TYPE &&
		conType <= MAX_CONNECTION_TYPE);
    }

}

