Input:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import routing.util.EnergyModel;
import routing.util.MessageTransferAcceptPolicy;
import routing.util.RoutingInfo;
import util.Tuple;
import core.Connection;
import core.DTNHost;
import core.Message;
import core.MessageListener;
import core.NetworkInterface;
import core.Settings;
import core.SimClock;

Context:
src/routing/ActiveRouter.java
/*
 * Copyright 2010 Aalto University, ComNet
 * Released under GPLv3. See LICENSE.txt for details.
 */
package routing;

/**
 * Superclass of active routers. Contains convenience methods (e.g.
 * {@link #getNextMessageToRemove(boolean)}) and watching of sending connections (see
 * {@link #update()}).
 */
public abstract class ActiveRouter extends MessageRouter {
	/** Delete delivered messages -setting id ({@value}). Boolean valued.
	 * If set to true and final recipient of a message rejects it because it
	 * already has it, the message is deleted from buffer. Default=false. */
	public static final String DELETE_DELIVERED_S = "deleteDelivered";
	/** should messages that final recipient marks as delivered be deleted
	 * from message buffer */
	protected boolean deleteDelivered;

	/** prefix of all response message IDs */
	public static final String RESPONSE_PREFIX = "R_";
	/** how often TTL check (discarding old messages) is performed */
	public static int TTL_CHECK_INTERVAL = 60;
	/** connection(s) that are currently used for sending */
	protected ArrayList<Connection> sendingConnections;
	/** sim time when the last TTL check was done */
	private double lastTtlCheck;

	private MessageTransferAcceptPolicy policy;
	private EnergyModel energy;

	/**
	 * Constructor. Creates a new message router based on the settings in
	 * the given Settings object.
	 * @param s The settings object
	 */
	public ActiveRouter(Settings s) {
		super(s);

		this.policy = new MessageTransferAcceptPolicy(s);

		this.deleteDelivered = s.getBoolean(DELETE_DELIVERED_S, false);

		if (s.contains(EnergyModel.INIT_ENERGY_S)) {
			this.energy = new EnergyModel(s);
		} else {
			this.energy = null; /* no energy model */
		}
	}

	/**
	 * Copy constructor.
	 * @param r The router prototype where setting values are copied from
	 */
	protected ActiveRouter(ActiveRouter r) {
		super(r);
		this.deleteDelivered = r.deleteDelivered;
		this.policy = r.policy;
		this.energy = (r.energy != null ? r.energy.replicate() : null);
	}

	@Override
	public void init(DTNHost host, List<MessageListener> mListeners) {
		super.init(host, mListeners);
		this.sendingConnections = new ArrayList<Connection>(1);
		this.lastTtlCheck = 0;
	}

	/**
	 * Called when a connection's state changes. If energy modeling is enabled,
	 * and a new connection is created to this node, reduces the energy for the
	 * device discovery (scan response) amount
	 * @param con The connection whose state changed
	 */
	@Override
	public void changedConnection(Connection con) {
		if (this.energy != null && con.isUp() && !con.isInitiator(getHost())) {
			this.energy.reduceDiscoveryEnergy();
		}
	}

	@Override
	public boolean requestDeliverableMessages(Connection con) {
		if (isTransferring()) {
			return false;
		}

		DTNHost other = con.getOtherNode(getHost());
		/* do a copy to avoid concurrent modification exceptions
		 * (startTransfer may remove messages) */
		ArrayList<Message> temp =
			new ArrayList<Message>(this.getMessageCollection());
		for (Message m : temp) {
			if (other == m.getTo()) {
				if (startTransfer(m, con) == RCV_OK) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean createNewMessage(Message m) {
		makeRoomForNewMessage(m.getSize());
		return super.createNewMessage(m);
	}

	@Override
	public int receiveMessage(Message m, DTNHost from) {
		int recvCheck = checkReceiving(m, from);
		if (recvCheck != RCV_OK) {
			return recvCheck;
		}

		// seems OK, start receiving the message
		return super.receiveMessage(m, from);
	}

	@Override
	public Message messageTransferred(String id, DTNHost from) {
		Message m = super.messageTransferred(id, from);

		/**
		 *  N.B. With application support the following if-block
		 *  becomes obsolete, and the response size should be configured
		 *  to zero.
		 */
		// check if msg was for this host and a response was requested

src/core/NetworkInterface.java
abstract public class NetworkInterface implements ModuleCommunicationListener, Comparable<NetworkInterface> {
	/** transmit range -setting id ({@value})*/
	public static final String TRANSMIT_RANGE_S = "transmitRange";
	/** transmit speed -setting id ({@value})*/
	public static final String TRANSMIT_SPEED_S = "transmitSpeed";
	/** scanning interval -setting id ({@value})*/
	public static final String SCAN_INTERVAL_S = "scanInterval";

	/**
	 * Sub-namespace for the network related settings in the Group namespace
	 * ({@value})
	 */
	public static final String NET_SUB_NS = "net";

	/** Activeness offset jitter -setting id ({@value})
	 * The maximum amount of random offset for the offset */
	public static final String ACT_JITTER_S = "activenessOffsetJitter";

	/** {@link ModuleCommunicationBus} identifier for the "scanning interval"
    variable. */
	public static final String SCAN_INTERVAL_ID = "Network.scanInterval";
	/** {@link ModuleCommunicationBus} identifier for the "radio range"
	variable. Value type: double */
	public static final String RANGE_ID = "Network.radioRange";
	/** {@link ModuleCommunicationBus} identifier for the "transmission speed"
    variable. Value type: integer */
	public static final String SPEED_ID = "Network.speed";

	private static final int CON_UP = 1;
	private static final int CON_DOWN = 2;

	private static Random rng;
	protected DTNHost host = null;

	protected String interfacetype;
	protected List<Connection> connections; // connected hosts
	private List<ConnectionListener> cListeners = null; // list of listeners
	private int address; // network interface address
	protected double transmitRange;
	protected double oldTransmitRange;
	protected int transmitSpeed;
	/** scanning interval, or 0.0 if n/a */
	private double scanInterval;
	private double lastScanTime;

	/** activeness handler for the node group */
	private ActivenessHandler ah;
	/** maximum activeness jitter value for the node group */
	private int activenessJitterMax;
	/** this interface's activeness jitter value */
	private int activenessJitterValue;

	static {
		DTNSim.registerForReset(NetworkInterface.class.getCanonicalName());
		reset();
	}

	/**
	 * Resets the static fields of the class
	 */
	public static void reset() {
		rng = new Random(0);
	}

	/**
	 * For creating an empty class of a specific type
	 */
	public NetworkInterface(Settings s) {
		this.interfacetype = s.getNameSpace();
		this.connections = new ArrayList<Connection>();

		this.transmitRange = s.getDouble(TRANSMIT_RANGE_S);
		this.transmitSpeed = s.getInt(TRANSMIT_SPEED_S);
		ensurePositiveValue(transmitRange, TRANSMIT_RANGE_S);
		ensurePositiveValue(transmitSpeed, TRANSMIT_SPEED_S);
	}

	/**
	 * For creating an empty class of a specific type
	 */
	public NetworkInterface() {
		this.interfacetype = "Default";
		this.connections = new ArrayList<Connection>();
	}

	/**
	 * copy constructor
	 */
	public NetworkInterface(NetworkInterface ni) {
		this.connections = new ArrayList<Connection>();
		this.host = ni.host;
		this.cListeners = ni.cListeners;
		this.interfacetype = ni.interfacetype;
		this.transmitRange = ni.transmitRange;
		this.transmitSpeed = ni.transmitSpeed;
		this.scanInterval = ni.scanInterval;
		this.ah = ni.ah;

		if (ni.activenessJitterMax > 0) {
			this.activenessJitterValue = rng.nextInt(ni.activenessJitterMax);
		} else {
			this.activenessJitterValue = 0;
		}

		this.scanInterval = ni.scanInterval;
		/* draw lastScanTime of [0 -- scanInterval] */
		this.lastScanTime = rng.nextDouble() * this.scanInterval;
	}

	/**
	 * Replication function
	 */
	abstract public NetworkInterface replicate();

	/**
	 * For setting the host - needed when a prototype is copied for several
	 * hosts
	 * @param host The host where the network interface is
	 */
	public void setHost(DTNHost host) {
		this.host = host;
		ModuleCommunicationBus comBus = host.getComBus();

		if (!comBus.containsProperty(SCAN_INTERVAL_ID) &&
		    !comBus.containsProperty(RANGE_ID)) {
			/* add properties and subscriptions only for the 1st interface */
			/* TODO: support for multiple interfaces */
			comBus.addProperty(SCAN_INTERVAL_ID, this.scanInterval);
			comBus.addProperty(RANGE_ID, this.transmitRange);
			comBus.addProperty(SPEED_ID, this.transmitSpeed);
			comBus.subscribe(SCAN_INTERVAL_ID, this);
			comBus.subscribe(RANGE_ID, this);
			comBus.subscribe(SPEED_ID, this);
		}
	}

	/**
	 * Sets group-based settings for the network interface
	 * @param s The settings object using the right group namespace
	 */
	public void setGroupSettings(Settings s) {
		s.setSubNameSpace(NET_SUB_NS);
		ah = new ActivenessHandler(s);

		if (s.contains(SCAN_INTERVAL_S)) {
			this.scanInterval =  s.getDouble(SCAN_INTERVAL_S);
		} else {
			this.scanInterval = 0;
		}
		if (s.contains(ACT_JITTER_S)) {
			this.activenessJitterMax = s.getInt(ACT_JITTER_S);
		}

		s.restoreSubNameSpace();
	}

	/**
	 * For checking what interface type this interface is
	 */
	public String getInterfaceType() {
		return interfacetype;
	}

	/**
	 * For setting the connectionListeners
	 * @param cListeners List of connection listeners
	 */
	public void setClisteners(List<ConnectionListener> cListeners) {
		this.cListeners = cListeners;
	}

	/**
	 * Returns the transmit range of this network layer
	 * @return the transmit range
	 */
	public double getTransmitRange() {
		return this.transmitRange;
	}

	/**
	 * Returns the transmit speed of this network layer with respect to the
	 * another network interface
	 * @param ni The other network interface
	 * @return the transmit speed
	 */
	public int getTransmitSpeed(NetworkInterface ni) {
		return this.transmitSpeed;
	}

	/**
	 * Returns a list of currently connected connections
	 * @return a list of currently connected connections
	 */
	public List<Connection> getConnections() {
		return this.connections;
	}

	/**
	 * Returns true if the interface is on at the moment (false if not)
	 * @return true if the interface is on at the moment (false if not)
	 */
	public boolean isActive() {
		boolean active;

		if (ah == null) {
			return true; /* no handler: always active */
		}

		active = ah.isActive(this.activenessJitterValue);

		if (active && host.getComBus().getDouble(EnergyModel.ENERGY_VALUE_ID,
					1) <= 0) {
			/* TODO: better way to check battery level */
			/* no battery -> inactive */
			active = false;
		}

		if (active == false && this.transmitRange > 0) {
			/* not active -> make range 0 */
			this.oldTransmitRange = this.transmitRange;
			host.getComBus().updateProperty(RANGE_ID, 0.0);
		} else if (active == true && this.transmitRange == 0.0) {
			/* active, but range == 0 -> restore range  */
			host.getComBus().updateProperty(RANGE_ID,
					this.oldTransmitRange);
		}
		return active;
	}

	/**
	 * Checks if this interface is currently in the scanning mode
	 * @return True if the interface is scanning; false if not
	 */
	public boolean isScanning() {
		double simTime = SimClock.getTime();

		if (!isActive()) {
			return false;
		}

		if (scanInterval > 0.0) {
			if (simTime < lastScanTime) {
				return false; /* not time for the first scan */
			}
			else if (simTime > lastScanTime + scanInterval) {
				lastScanTime = simTime; /* time to start the next scan round */
				return true;
			}
			else if (simTime != lastScanTime ){
				return false; /* not in the scan round */
			}
		}
		/* interval == 0 or still in the same scan round as when
		   last time asked */
		return true;
	}

	/**
	 * Returns true if one of the connections of this interface is transferring
	 * data
	 * @return true if the interface transferring
	 */
	public boolean isTransferring() {
		for (Connection c : this.connections) {
			if (c.isTransferring()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Connects the interface to another interface.
	 *
	 * Overload this in a derived class.  Check the requirements for
	 * the connection to work in the derived class, then call
	 * connect(Connection, NetworkInterface) for the actual connection.
	 * @param anotherInterface The interface to connect to
	 */
	public abstract void connect(NetworkInterface anotherInterface);

	/**
	 * Connects this host to another host. The derived class should check
	 * that all pre-requisites for making a connection are satisfied before
	 * actually connecting.
	 * @param con The new connection object
	 * @param anotherInterface The interface to connect to
	 */
	protected void connect(Connection con, NetworkInterface anotherInterface) {
		this.connections.add(con);
		notifyConnectionListeners(CON_UP, anotherInterface.getHost());

		// set up bidirectional connection
		anotherInterface.getConnections().add(con);

		// inform routers about the connection
		this.host.connectionUp(con);
		anotherInterface.getHost().connectionUp(con);
	}

	/**
	 * Disconnect a connection between this and another host.
	 * @param other The other host's network interface to disconnect
	 * from this host
	 */
	public void disconnect(NetworkInterface other) {
		for (int i = 0; i < this.connections.size(); i++) {
			Connection con = this.connections.get(i);

			if (con.getOtherInterface(this) == other) {
				disconnect(con, other);
				connections.remove(i);
				return;
			}
		}
		// the connection didn't exist, do nothing
	}

	/**
	 * Disconnects this host from another host.  The derived class should
	 * make the decision whether to disconnect or not
	 * @param con The connection to tear down
	 */
	protected void disconnect(Connection con, NetworkInterface other) {
		// all connections should be up at this stage
		assert con.isUp() : "Connection " + con + " was down!";

		con.setUpState(false);
		notifyConnectionListeners(CON_DOWN, other.getHost());

		// tear down bidirectional connection
		if (!other.getConnections().remove(con)) {
			throw new SimError("No connection " + con + " found in " +
					other);
		}

		this.host.connectionDown(con);
		other.getHost().connectionDown(con);
	}

	/**
	 * Returns true if the given NetworkInterface is connected to this interface.
	 * @param other The other NetworkInterface to check
	 * @return True if the two hosts are connected
	 */
	public boolean isConnected(NetworkInterface other) {
		for (int i = 0; i < this.connections.size(); i++) {
			if (this.connections.get(i).getOtherInterface(this) == other) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Makes sure that a value is positive
	 * @param value Value to check
	 * @param settingName Name of the setting (for error's message)
	 * @throws SettingsError if the value was not positive
	 */
	protected void ensurePositiveValue(double value, String settingName) {
		if (value < 0) {
			throw new SettingsError("Negative value (" + value +
					") not accepted for setting " + settingName);
		}
	}

	/**
	 * Called when <other> interface enters the transmission range
	 * May be called only once even if connection is not immediately established
	 *  Callee is responsible to keep track of available connections
	 * Caller guarantees that:
	 *  - <other> =/= <this>
	 *  - There is no connection yet
	 * @param other The other interface
	 */
	public void linkUp(NetworkInterface other) {
		connect(other);
	}

	/**
	 * Called when <other> interface leaves the transmission range
	 * Caller guarantees that:
	 *  - <other> =/= <this>
	 *  - There was a connection
	 * @param other The other interface
	 */
	public void linkDown(NetworkInterface other) {
		disconnect(other);
	}

	/**
	 * Updates the state of current connections(ie recalculate transmission speeds etc.).
	 */
	public abstract void update();

	/**
	 * Notifies all the connection listeners about a change in connections.
	 * @param type Type of the change (e.g. {@link #CON_DOWN} )
	 * @param otherHost The other host on the other end of the connection.
	 */
	private void notifyConnectionListeners(int type, DTNHost otherHost) {
		if (this.cListeners == null) {
			return;
		}
		for (ConnectionListener cl : this.cListeners) {
			switch (type) {
			case CON_UP:
				cl.hostsConnected(this.host, otherHost);
				break;
			case CON_DOWN:
				cl.hostsDisconnected(this.host, otherHost);
				break;
			default:
				assert false : type;	// invalid type code
			}
		}
	}

	/**
	 * This method is called by the {@link ModuleCommunicationBus} when/if
	 * someone changes the scanning interval, transmit speed, or range
	 * @param key Identifier of the changed value
	 * @param newValue New value for the variable
	 */
	public void moduleValueChanged(String key, Object newValue) {
		if (key.equals(SCAN_INTERVAL_ID)) {
			this.scanInterval = (Double)newValue;
		}
		else if (key.equals(SPEED_ID)) {
			this.transmitSpeed = (Integer)newValue;
		}
		else if (key.equals(RANGE_ID)) {
			this.transmitRange = (Double)newValue;
		}
		else {
			throw new SimError("Unexpected combus ID " + key);
		}
	}

	/**
	 * Creates a connection to another host. This method does not do any checks
	 * on whether the other node is in range or active
	 * (cf. {@link #connect(NetworkInterface)}).
	 * @param anotherInterface The interface to create the connection to
	 */
	public abstract void createConnection(NetworkInterface anotherInterface);

	/**
	 * Returns the DTNHost of this interface
	 */
	public DTNHost getHost() {
		return host;
	}

	/**
	 * Returns the current location of the host of this interface.
	 * @return The location
	 */
	public Coord getLocation() {
		return host.getLocation();
	}

	/**
	 * Returns a string representation of the object.
	 * @return a string representation of the object.
	 */
	public String toString() {
		return this.address + " of " + this.host +
			". Connections: " +	this.connections;
	}

	@Override
	public int compareTo(NetworkInterface b) {
		return this.getHost().getID() - b.getHost().getID();
	}

}

src/core/SimClock.java
public class SimClock {
	private static double clockTime = 0.0;
	private static SimClock clock = null;

	private SimClock() {}

	static {
		DTNSim.registerForReset(SimClock.class.getCanonicalName());
		reset();
	}

	/**
	 * Get the instance of the class that can also change the time.
	 * @return The instance of this clock
	 */
	public static SimClock getInstance() {
		if (clock == null) {
			clock = new SimClock();
		}
		return clock;
	}

	/**
	 * Returns the current time (seconds since start)
	 * @return Time as a double
	 */
	public static double getTime() {
		return clockTime;
	}

	/**
	 * Returns the current time rounded to the nearest integer
	 * @return Time as integer
	 */
	public static int getIntTime() {
		return (int)Math.round(clockTime);
	}

	/**
	 * Returns a string presentation of the sim time shown with the given amount
	 * of decimals
	 * @param decimals The number of decimals to show
	 * @return The sim time
	 */
	public static String getFormattedTime(int decimals) {
		return String.format("%." + decimals + "f", clockTime);
	}

	/**
	 * Advances the time by n seconds
	 * @param time Nrof seconds to increase the time
	 */
	public void advance(double time) {
		clockTime += time;
	}

	/**
	 * Sets the time of the clock.
	 * @param time the time to set
	 */
	public void setTime(double time) {
		clockTime = time;
	}

	/**
	 * Returns the current simulation time in a string
	 * @return the current simulation time in a string
	 */
	public String toString() {
		return "SimTime: " + clockTime;
	}

	/**
	 * Resets the static fields of the class
	 */
	public static void reset() {
		clockTime = 0;
	}
}

src/core/MessageListener.java
public interface MessageListener {

	/**
	 * Method is called when a new message is created
	 * @param m Message that was created
	 */
	public void newMessage(Message m);

	/**
	 * Method is called when a message's transfer is started
	 * @param m The message that is going to be transferred
	 * @param from Node where the message is transferred from
	 * @param to Node where the message is transferred to
	 */
	public void messageTransferStarted(Message m, DTNHost from, DTNHost to);

	/**
	 * Method is called when a message is deleted
	 * @param m The message that was deleted
	 * @param where The host where the message was deleted
	 * @param dropped True if the message was dropped, false if removed
	 */
	public void messageDeleted(Message m, DTNHost where, boolean dropped);

	/**
	 * Method is called when a message's transfer was aborted before
	 * it finished
	 * @param m The message that was being transferred
	 * @param from Node where the message was being transferred from
	 * @param to Node where the message was being transferred to
	 */
	public void messageTransferAborted(Message m, DTNHost from, DTNHost to);

	/**
	 * Method is called when a message is successfully transferred from
	 * a node to another.
	 * @param m The message that was transferred
	 * @param from Node where the message was transferred from
	 * @param to Node where the message was transferred to
	 * @param firstDelivery Was the target node final destination of the message
	 * and received this message for the first time.
	 */
	public void messageTransferred(Message m, DTNHost from, DTNHost to,
			boolean firstDelivery);
}

src/core/Connection.java
public abstract class Connection {
	protected DTNHost toNode;
	protected NetworkInterface toInterface;
	protected DTNHost fromNode;
	protected NetworkInterface fromInterface;
	protected DTNHost msgFromNode;

	private boolean isUp;
	protected Message msgOnFly;
	/** how many bytes this connection has transferred */
	protected int bytesTransferred;

	/**
	 * Creates a new connection between nodes and sets the connection
	 * state to "up".
	 * @param fromNode The node that initiated the connection
	 * @param fromInterface The interface that initiated the connection
	 * @param toNode The node in the other side of the connection
	 * @param toInterface The interface in the other side of the connection
	 */
	public Connection(DTNHost fromNode, NetworkInterface fromInterface,
			DTNHost toNode, NetworkInterface toInterface) {
		this.fromNode = fromNode;
		this.fromInterface = fromInterface;
		this.toNode = toNode;
		this.toInterface = toInterface;
		this.isUp = true;
		this.bytesTransferred = 0;
	}

	/**
	 * Returns true if the connection is up
	 * @return state of the connection
	 */
	public boolean isUp() {
		return this.isUp;
	}

	/**
	 * Returns true if the connections is transferring a message
	 * @return true if the connections is transferring a message
	 */
	public boolean isTransferring() {
		return this.msgOnFly != null;
	}

	/**
	 * Returns true if the given node is the initiator of the connection, false
	 * otherwise
	 * @param node The node to check
	 * @return true if the given node is the initiator of the connection
	 */
	public boolean isInitiator(DTNHost node) {
		return node == this.fromNode;
	}

	/**
	 * Sets the state of the connection.
	 * @param state True if the connection is up, false if not
	 */
	public void setUpState(boolean state) {
		this.isUp = state;
	}

	/**
	 * Sets a message that this connection is currently transferring. If message
	 * passing is controlled by external events, this method is not needed
	 * (but then e.g. {@link #finalizeTransfer()} and
	 * {@link #isMessageTransferred()} will not work either). Only a one message
	 * at a time can be transferred using one connection.
	 * @param m The message
	 * @return The value returned by
	 * {@link MessageRouter#receiveMessage(Message, DTNHost)}
	 */
	public abstract int startTransfer(DTNHost from, Message m);

	/**
	 * Calculate the current transmission speed from the information
	 * given by the interfaces, and calculate the missing data amount.
	 */
	public void update() {};

	/**
     * Aborts the transfer of the currently transferred message.
     */
	public void abortTransfer() {
		assert msgOnFly != null : "No message to abort at " + msgFromNode;
		int bytesRemaining = getRemainingByteCount();

		this.bytesTransferred += msgOnFly.getSize() - bytesRemaining;

		getOtherNode(msgFromNode).messageAborted(this.msgOnFly.getId(),
				msgFromNode, bytesRemaining);
		clearMsgOnFly();
	}

	/**
	 * Returns the amount of bytes to be transferred before ongoing transfer
	 * is ready or 0 if there's no ongoing transfer or it has finished
	 * already
	 * @return the amount of bytes to be transferred
	 */
	public abstract int getRemainingByteCount();

	/**
	 * Clears the message that is currently being transferred.
	 * Calls to {@link #getMessage()} will return null after this.
	 */
	protected void clearMsgOnFly() {
		this.msgOnFly = null;
		this.msgFromNode = null;
	}

	/**
	 * Finalizes the transfer of the currently transferred message.
	 * The message that was being transferred can <STRONG>not</STRONG> be
	 * retrieved from this connections after calling this method (using
	 * {@link #getMessage()}).
	 */
	public void finalizeTransfer() {
		assert this.msgOnFly != null : "Nothing to finalize in " + this;
		assert msgFromNode != null : "msgFromNode is not set";

		this.bytesTransferred += msgOnFly.getSize();

		getOtherNode(msgFromNode).messageTransferred(this.msgOnFly.getId(),
				msgFromNode);
		clearMsgOnFly();
	}

	/**
	 * Returns true if the current message transfer is done
	 * @return True if the transfer is done, false if not
	 */
	public abstract boolean isMessageTransferred();

	/**
	 * Returns true if the connection is ready to transfer a message (connection
	 * is up and there is no message being transferred).
	 * @return true if the connection is ready to transfer a message
	 */
	public boolean isReadyForTransfer() {
		return this.isUp && this.msgOnFly == null;
	}

	/**
	 * Gets the message that this connection is currently transferring.
	 * @return The message or null if no message is being transferred
	 */
	public Message getMessage() {
		return this.msgOnFly;
	}

	/**
	 * Gets the current connection speed
	 */
	public abstract double getSpeed();

	/**
	 * Returns the total amount of bytes this connection has transferred so far
	 * (including all transfers).
	 */
	public int getTotalBytesTransferred() {
		if (this.msgOnFly == null) {
			return this.bytesTransferred;
		}
		else {
			if (isMessageTransferred()) {
				return this.bytesTransferred + this.msgOnFly.getSize();
			}
			else {
				return this.bytesTransferred +
				(msgOnFly.getSize() - getRemainingByteCount());
			}
		}
	}

	/**
	 * Returns the node in the other end of the connection
	 * @param node The node in this end of the connection
	 * @return The requested node
	 */
	public DTNHost getOtherNode(DTNHost node) {
		if (node == this.fromNode) {
			return this.toNode;
		}
		else {
			return this.fromNode;
		}
	}

	/**
	 * Returns the interface in the other end of the connection
	 * @param i The interface in this end of the connection
	 * @return The requested interface
	 */
	public NetworkInterface getOtherInterface(NetworkInterface i) {
		if (i == this.fromInterface) {
			return this.toInterface;
		}
		else {
			return this.fromInterface;
		}
	}

	/**
	 * Returns a String presentation of the connection.
	 */
	public String toString() {
		return fromNode + "<->" + toNode + " (" + getSpeed()/1000 + " kBps) is " +
		(isUp() ? "up":"down") +
		(isTransferring() ? " transferring " + this.msgOnFly  +
				" from " + this.msgFromNode : "");
	}

}

src/routing/util/EnergyModel.java
public class EnergyModel implements ModuleCommunicationListener {
	/** Initial units of energy -setting id ({@value}). Can be either a
	 * single value, or a range of two values. In the latter case, the used
	 * value is a uniformly distributed random value between the two values. */
	public static final String INIT_ENERGY_S = "initialEnergy";

	/** Energy usage per scanning (device discovery) -setting id ({@value}). */
	public static final String SCAN_ENERGY_S = "scanEnergy";

	/** Energy usage per scanning (device discovery) response -setting id
	 * ({@value}). */
	public static final String SCAN_RSP_ENERGY_S = "scanResponseEnergy";

	/** Energy usage per second when transferring data
	 * -setting id ({@value}). */
	public static final String TRANSMIT_ENERGY_S = "transmitEnergy";

	/** Energy update warmup period -setting id ({@value}). Defines the
	 * simulation time after which the energy level starts to decrease due to
	 * scanning, transmissions, etc. Default value = 0. If value of "-1" is
	 * defined, uses the value from the report warmup setting
	 * {@link report.Report#WARMUP_S} from the namespace
	 * {@value report.Report#REPORT_NS}. */
	public static final String WARMUP_S = "energyWarmup";

	/** {@link ModuleCommunicationBus} identifier for the "current amount of
	 * energy left" variable. Value type: double */
	public static final String ENERGY_VALUE_ID = "Energy.value";

	/** Initial energy levels from the settings */
	private final double[] initEnergy;
	private double warmupTime;
	/** current energy level */
	private double currentEnergy;
	/** energy usage per scan */
	private double scanEnergy;
	/** energy usage per transmitted byte */
	private double transmitEnergy;
	/** energy usage per device discovery response */
	private double scanResponseEnergy;
	/** sim time of the last energy updated */
	private double lastUpdate;
	private ModuleCommunicationBus comBus;
	private static Random rng = null;

	/**
	 * Constructor. Creates a new message router based on the settings in
	 * the given Settings object.
	 * @param s The settings object
	 */
	public EnergyModel(Settings s) {
		this.initEnergy = s.getCsvDoubles(INIT_ENERGY_S);

		if (this.initEnergy.length != 1 && this.initEnergy.length != 2) {
			throw new SettingsError(INIT_ENERGY_S + " setting must have " +
					"either a single value or two comma separated values");
		}

		this.scanEnergy = s.getDouble(SCAN_ENERGY_S);
		this.transmitEnergy = s.getDouble(TRANSMIT_ENERGY_S);
		this.scanResponseEnergy = s.getDouble(SCAN_RSP_ENERGY_S);

		if (s.contains(WARMUP_S)) {
			this.warmupTime = s.getInt(WARMUP_S);
			if (this.warmupTime == -1) {
				this.warmupTime = new Settings(report.Report.REPORT_NS).
					getInt(report.Report.WARMUP_S);
			}
		}
		else {
			this.warmupTime = 0;
		}
	}

	/**
	 * Copy constructor.
	 * @param proto The model prototype where setting values are copied from
	 */
	protected EnergyModel(EnergyModel proto) {
		this.initEnergy = proto.initEnergy;
		setEnergy(this.initEnergy);
		this.scanEnergy = proto.scanEnergy;
		this.transmitEnergy = proto.transmitEnergy;
		this.warmupTime  = proto.warmupTime;
		this.scanResponseEnergy = proto.scanResponseEnergy;
		this.comBus = null;
		this.lastUpdate = 0;
	}

	public EnergyModel replicate() {
		return new EnergyModel(this);
	}

	/**
	 * Sets the current energy level into the given range using uniform
	 * random distribution.
	 * @param range The min and max values of the range, or if only one value
	 * is given, that is used as the energy level
	 */
	protected void setEnergy(double range[]) {
		if (range.length == 1) {
			this.currentEnergy = range[0];
		}
		else {
			if (rng == null) {
				rng = new Random((int)(range[0] + range[1]));
			}
			this.currentEnergy = range[0] +
				rng.nextDouble() * (range[1] - range[0]);
		}
	}

	/**
	 * Returns the current energy level
	 * @return the current energy level
	 */
	public double getEnergy() {
		return this.currentEnergy;
	}

	/**
	 * Updates the current energy so that the given amount is reduced from it.
	 * If the energy level goes below zero, sets the level to zero.
	 * Does nothing if the warmup time has not passed.
	 * @param amount The amount of energy to reduce
	 */
	protected void reduceEnergy(double amount) {
		if (SimClock.getTime() < this.warmupTime) {
			return;
		}

		if (comBus == null) {
			return; /* model not initialized (via update) yet */
		}

		if (amount >= this.currentEnergy) {
			comBus.updateProperty(ENERGY_VALUE_ID, 0.0);
		} else {
			comBus.updateDouble(ENERGY_VALUE_ID, -amount);
		}

	}

	/**
	 * Reduces the energy reserve for the amount that is used when another
	 * host connects (does device discovery)
	 */
	public void reduceDiscoveryEnergy() {
		reduceEnergy(this.scanResponseEnergy);
	}

	/**
	 * Reduces the energy reserve for the amount that is used by sending data
	 * and scanning for the other nodes.
	 */
	public void update(NetworkInterface iface, ModuleCommunicationBus comBus) {
		double simTime = SimClock.getTime();
		double delta = simTime - this.lastUpdate;

		if (this.comBus == null) {
			this.comBus = comBus;
			this.comBus.addProperty(ENERGY_VALUE_ID, this.currentEnergy);
			this.comBus.subscribe(ENERGY_VALUE_ID, this);
		}

		if (simTime > this.lastUpdate && iface.isTransferring()) {
			/* sending or receiving data */
			reduceEnergy(delta * this.transmitEnergy);
		}
		this.lastUpdate = simTime;

		if (iface.isScanning()) {
			/* scanning at this update round */
			if (iface.getTransmitRange() > 0) {
				if (delta < 1) {
					reduceEnergy(this.scanEnergy * delta);
				} else {
					reduceEnergy(this.scanEnergy);
				}
			}
		}
	}

	/**
	 * Called by the combus if the energy value is changed
	 * @param key The energy ID
	 * @param newValue The new energy value
	 */
	public void moduleValueChanged(String key, Object newValue) {
		this.currentEnergy = (Double)newValue;
	}

}

src/core/DTNHost.java
public class DTNHost implements Comparable<DTNHost> {
	private static int count = 0;
	private int ID;

	private Coord destination;	// where is it going

	private MessageRouter router;
	private MovementEngine movementEngine;
	private MovementModel movementModel;
	private Path path;
	private double speed;
	public final String groupId;
	private String name;
	private List<MessageListener> msgListeners;
	private List<MovementListener> movListeners;
	private List<NetworkInterface> net;
	private ModuleCommunicationBus comBus;

	static {
		DTNSim.registerForReset(DTNHost.class.getCanonicalName());
		reset();
	}
	/**
	 * Creates a new DTNHost.
	 * @param msgLs Message listeners
	 * @param movLs Movement listeners
	 * @param groupId GroupID of this host
	 * @param interf List of NetworkInterfaces for the class
	 * @param comBus Module communication bus object
	 * @param mmProto Prototype of the movement model of this host
	 * @param mRouterProto Prototype of the message router of this host
	 */
	public DTNHost(List<MessageListener> msgLs,
			List<MovementListener> movLs,
			String groupId, List<NetworkInterface> interf,
			ModuleCommunicationBus comBus,
			MovementEngine me, MovementModel mmProto, MessageRouter mRouterProto) {
		this.comBus = comBus;
		this.ID = getNextID();
		this.groupId = groupId;
		this.name = groupId+ ID;
		this.net = new ArrayList<NetworkInterface>();

		for (NetworkInterface i : interf) {
			NetworkInterface ni = i.replicate();
			ni.setHost(this);
			net.add(ni);
		}

		// TODO - think about the names of the interfaces and the nodes
		//this.name = groupId + ((NetworkInterface)net.get(1)).getAddress();

		this.msgListeners = msgLs;
		this.movListeners = movLs;

		this.movementEngine = me;

		// create instances by replicating the prototypes
		this.movementModel = mmProto.replicate();
		this.movementModel.setComBus(comBus);
		this.movementModel.setHost(this);
		setRouter(mRouterProto.replicate());

		this.path = null;
	}

	/**
	 * Returns a new unique ID and increment the host count.
	 * @return The next ID.
	 */
	private synchronized static int getNextID() {
		return count++;
	}

	/**
	 * Reset the host and its interfaces
	 */
	public static void reset() {
		count = 0;
	}

	/**
	 * Returns true if this node is actively moving (false if not)
	 * @return true if this node is actively moving (false if not)
	 */
	public boolean isMovementActive() {
		return this.movementModel.isActive();
	}

	/**
	 * Returns true if this node's radio is active (false if not)
	 * @return true if this node's radio is active (false if not)
	 */
	public boolean isRadioActive() {
		// Radio is active if any of the network interfaces are active.
		for (final NetworkInterface i : this.net) {
			if (i.isActive()) return true;
		}
		return false;
	}

	/**
	 * Set a router for this host
	 * @param router The router to set
	 */
	private void setRouter(MessageRouter router) {
		router.init(this, msgListeners);
		this.router = router;
	}

	/**
	 * Returns the router of this host
	 * @return the router of this host
	 */
	public MessageRouter getRouter() {
		return this.router;
	}

	/**
	 * Returns the ID of this host.
	 */
	public int getID() {
		return this.ID;
	}

	/**
	 * Returns this hosts's ModuleCommunicationBus
	 * @return this hosts's ModuleCommunicationBus
	 */
	public ModuleCommunicationBus getComBus() {
		return this.comBus;
	}

    /**
	 * Informs the router of this host about state change in a connection
	 * object.
	 * @param con  The connection object whose state changed
	 */
	public void connectionUp(Connection con) {
		this.router.changedConnection(con);
	}

	public void connectionDown(Connection con) {
		this.router.changedConnection(con);
	}

	/**
	 * Returns a copy of the list of connections this host has with other hosts
	 * @return a copy of the list of connections this host has with other hosts
	 */
	public List<Connection> getConnections() {
		List<Connection> lc = new ArrayList<Connection>();

		for (NetworkInterface i : net) {
			lc.addAll(i.getConnections());
		}

		return lc;
	}

	/**
	 * Sets the host's location
	 * @param location The location to set
	 */
	public void setLocation(Coord location) {
		movementEngine.setLocation(ID, location);
	}

	/**
	 * Returns the current location of this host.
	 * @return The location
	 */
	public Coord getLocation() {
		return movementEngine.getLocation(ID);
	}

	/**
	 * Sets the Node's next destination and speed
	 * @param destination The destination to set
	 */
	public void setDestination(Coord destination, double speed) {
		if (destination == null) {
			this.destination = null;
			return;
		}
		this.destination = destination.clone();
		this.speed = speed;

		if (this.movListeners != null) {
			for (MovementListener l : this.movListeners) {
				l.newDestination(this, this.destination, this.speed);
			}
		}
	}

	/**
	 * Returns the current destination of this host.
	 * @return The destination
	 */
	public Coord getDestination() {
		return this.destination;
	}

	/**
	 * Returns the current speed of this host.
	 * @return The speed
	 */
	public double getSpeed() {
		return this.speed;
	}

	/**
	 * Sets the Path this node is currently traveling or null if no
	 * path is in use at the moment. (Node is waiting for new path or stationary)
	 * @return The path this node is traveling
	 */
	public void setPath(Path path) {
		this.path = path;
	}

	/**
	 * Returns the Path this node is currently traveling or null if no
	 * path is in use at the moment.
	 * @return The path this node is traveling
	 */
	public Path getPath() {
		return this.path;
	}

	/**
	 * Returns the movement model of this node
	 * @return The movement model of this node
	 */
	public MovementModel getMovementModel() {
		return this.movementModel;
	}

	/**
	 * Sets the Node's name overriding the default name (groupId + netAddress)
	 * @param name The name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Returns the messages in a collection.
	 * @return Messages in a collection
	 */
	public Collection<Message> getMessageCollection() {
		return this.router.getMessageCollection();
	}

	/**
	 * Returns the number of messages this node is carrying.
	 * @return How many messages the node is carrying currently.
	 */
	public int getNrofMessages() {
		return this.router.getNrofMessages();
	}

	/**
	 * Returns the buffer occupancy percentage. Occupancy is 0 for empty
	 * buffer but can be over 100 if a created message is bigger than buffer
	 * space that could be freed.
	 * @return Buffer occupancy percentage
	 */
	public double getBufferOccupancy() {
		long bSize = router.getBufferSize();
		long freeBuffer = router.getFreeBufferSize();
		return 100*((bSize-freeBuffer)/(bSize * 1.0));
	}

	/**
	 * Returns routing info of this host's router.
	 * @return The routing info.
	 */
	public RoutingInfo getRoutingInfo() {
		return this.router.getRoutingInfo();
	}

	/**
	 * Returns the interface objects of the node
	 */
	public List<NetworkInterface> getInterfaces() {
		return net;
	}

	/**
	 * Find the network interface based on the index
	 */
	public NetworkInterface getInterface(int interfaceNo) {
		NetworkInterface ni = null;
		try {
			ni = net.get(interfaceNo-1);
		} catch (IndexOutOfBoundsException ex) {
			throw new SimError("No such interface: "+interfaceNo +
					" at " + this);
		}
		return ni;
	}

	/**
	 * Find the network interface based on the interfacetype
	 */
	protected NetworkInterface getInterface(String interfacetype) {
		for (NetworkInterface ni : net) {
			if (ni.getInterfaceType().equals(interfacetype)) {
				return ni;
			}
		}
		return null;
	}

	/**
	 * Force a connection event
	 */
	public void forceConnection(DTNHost anotherHost, String interfaceId,
			boolean up) {
		NetworkInterface ni;
		NetworkInterface no;

		if (interfaceId != null) {
			ni = getInterface(interfaceId);
			no = anotherHost.getInterface(interfaceId);

			assert (ni != null) : "Tried to use a nonexisting interfacetype "+interfaceId;
			assert (no != null) : "Tried to use a nonexisting interfacetype "+interfaceId;
		} else {
			ni = getInterface(1);
			no = anotherHost.getInterface(1);

			assert (ni.getInterfaceType().equals(no.getInterfaceType())) :
				"Interface types do not match.  Please specify interface type explicitly";
		}

		if (up) {
			ni.createConnection(no);
		} else {
			ni.disconnect(no);
		}
	}

	/**
	 * for tests only --- do not use!!!
	 */
	public void connect(DTNHost h) {
		if (DEBUG) Debug.p("WARNING: using deprecated DTNHost.connect" +
			"(DTNHost) Use DTNHost.forceConnection(DTNHost,null,true) instead");
		forceConnection(h,null,true);
	}

	/**
	 * Updates node's network layer and router.
	 * @param simulateConnections Should network layer be updated too
	 */
	public void update(boolean simulateConnections) {
		if (!isRadioActive()) {
			// Make sure inactive nodes don't have connections
			tearDownAllConnections();
			return;
		}

		if (simulateConnections) {
			for (NetworkInterface i : net) {
				i.update();
			}
		}
		this.router.update();
	}

	/**
	 * Tears down all connections for this host.
	 */
	private void tearDownAllConnections() {
		for (NetworkInterface i : net) {
			// Get all connections for the interface
			List<Connection> conns = i.getConnections();
			if (conns.size() == 0) continue;

			// Destroy all connections
			List<NetworkInterface> removeList =
				new ArrayList<NetworkInterface>(conns.size());
			for (Connection con : conns) {
				removeList.add(con.getOtherInterface(i));
			}
			for (NetworkInterface inf : removeList) {
				i.disconnect(inf);
			}
		}
	}

	/**
	 * Sends a message from this host to another host
	 * @param id Identifier of the message
	 * @param to Host the message should be sent to
	 */
	public void sendMessage(String id, DTNHost to) {
		this.router.sendMessage(id, to);
	}

	/**
	 * Start receiving a message from another host
	 * @param m The message
	 * @param from Who the message is from
	 * @return The value returned by
	 * {@link MessageRouter#receiveMessage(Message, DTNHost)}
	 */
	public int receiveMessage(Message m, DTNHost from) {
		int retVal = this.router.receiveMessage(m, from);

		if (retVal == MessageRouter.RCV_OK) {
			m.addNodeOnPath(this);	// add this node on the messages path
		}

		return retVal;
	}

	/**
	 * Requests for deliverable message from this host to be sent trough a
	 * connection.
	 * @param con The connection to send the messages trough
	 * @return True if this host started a transfer, false if not
	 */
	public boolean requestDeliverableMessages(Connection con) {
		return this.router.requestDeliverableMessages(con);
	}

	/**
	 * Informs the host that a message was successfully transferred.
	 * @param id Identifier of the message
	 * @param from From who the message was from
	 */
	public void messageTransferred(String id, DTNHost from) {
		this.router.messageTransferred(id, from);
	}

	/**
	 * Informs the host that a message transfer was aborted.
	 * @param id Identifier of the message
	 * @param from From who the message was from
	 * @param bytesRemaining Nrof bytes that were left before the transfer
	 * would have been ready; or -1 if the number of bytes is not known
	 */
	public void messageAborted(String id, DTNHost from, int bytesRemaining) {
		this.router.messageAborted(id, from, bytesRemaining);
	}

	/**
	 * Creates a new message to this host's router
	 * @param m The message to create
	 */
	public void createNewMessage(Message m) {
		this.router.createNewMessage(m);
	}

	/**
	 * Deletes a message from this host
	 * @param id Identifier of the message
	 * @param drop True if the message is deleted because of "dropping"
	 * (e.g. buffer is full) or false if it was deleted for some other reason
	 * (e.g. the message got delivered to final destination). This effects the
	 * way the removing is reported to the message listeners.
	 */
	public void deleteMessage(String id, boolean drop) {
		this.router.deleteMessage(id, drop);
	}

	/**
	 * Informs movement listeners about the initial location.
	 * Called by movement engine after setting the initial location.
	 */
	public void notifyInitialLocation() {
		if (movListeners != null) {
			for (MovementListener l : movListeners) {
				l.initialLocation(this, getLocation());
			}
		}
	}

	/**
	 * Returns a string presentation of the host.
	 * @return Host's name
	 */
	public String toString() {
		return name;
	}

	/**
	 * Checks if a host is the same as this host by comparing the object
	 * reference
	 * @param otherHost The other host
	 * @return True if the hosts objects are the same object
	 */
	public boolean equals(DTNHost otherHost) {
		return this == otherHost;
	}

	/**
	 * Compares two DTNHosts by their IDs.
	 * @see Comparable#compareTo(Object)
	 */
	public int compareTo(DTNHost h) {
		return this.getID() - h.getID();
	}

}

src/core/Settings.java
public class Settings {
	/** properties object where the setting files are read into */
	protected static Properties props;
	/** file name of the default settings file ({@value}) */
	public static final String DEF_SETTINGS_FILE ="default_settings.txt";

	/**
	 * Setting to define the file name where all read settings are written
	 * ({@value}. If set to an empty string, standard output is used.
	 * By default setting are not written anywhere.
	 */
	public static final String SETTING_OUTPUT_S = "Settings.output";

	/** delimiter for requested values in strings ({@value})
	 * @see #valueFillString(String) */
	public static final String FILL_DELIMITER = "%%";

	/** Stream where all read settings are written to */
	private static PrintStream out = null;
	private static Set<String> writtenSettings = new HashSet<String>();

	/** run index for run-specific settings */
	private static int runIndex = 0;
	private String namespace = null; // namespace to look the settings from
	private String secondaryNamespace = null;
	private Stack<String> oldNamespaces;
	private Stack<String> secondaryNamespaces;

	/**
	 * Creates a setting object with a namespace. Namespace is the prefix
	 * of the all subsequent setting requests.
	 * @param namespace Namespace to use
	 */
	public Settings(String namespace) {
		this.oldNamespaces = new Stack<String>();
		this.secondaryNamespaces = new Stack<String>();
		setNameSpace(namespace);
	}

	/**
	 * Create a setting object without namespace. All setting requests must
	 * be prefixed with a valid namespace (e.g. "Report.nrofReports").
	 */
	public Settings() {
		this(null);
	}

	/**
	 * Sets the run index for the settings (only has effect on settings with
	 * run array). A run array can be defined with syntax<BR>
	 * <CODE>[settingFor1stRun ; settingFor2ndRun ; SettingFor3rdRun]</CODE>
	 * <BR>I.e. settings are put in brackets and delimited with semicolon.
	 * First run's setting is returned when index is 0, second when index is
	 * 1 etc. If run index is bigger than run array's length, indexing wraps
	 * around in run array (i.e. return value is the value at index
	 * <CODE>runIndex % arrayLength</CODE>).
	 * To disable whole run-index-thing, set index to value smaller than
	 * zero (e.g. -1). When disabled, run-arrays are returned as normal values,
	 * including the brackets.
	 * @param index The run index to use for subsequent settings calls, or
	 * -1 to disable run indexing
	 */
	public static void setRunIndex(int index) {
		runIndex = index;
		writtenSettings.clear();
	}

	/**
	 * Checks that the given integer array contains a valid range. I.e.,
	 * the length of the array must be two and
	 * <code>first_value <= second_value</code>.
	 * @param range The range array
	 * @param sname Name of the setting (for error messages)
	 * @throws SettingsError If the given array didn't qualify as a range
	 */
	public void assertValidRange(int range[], String sname)
		throws SettingsError {
		if (range.length != 2) {
			throw new SettingsError("Range setting " +
					getFullPropertyName(sname) +
					" should contain only two comma separated integer values");
		}
		if (range[0] > range[1]) {
			throw new SettingsError("Range setting's " +
					getFullPropertyName(sname) +
					" first value should be smaller or equal to second value");
		}
	}

	/**
	 * Makes sure that the given settings value is positive
	 * @param value Value to check
	 * @param settingName Name of the setting (for error's message)
	 * @throws SettingsError if the value was not positive
	 */
	public void ensurePositiveValue(double value, String settingName) {
		if (value < 0) {
			throw new SettingsError("Negative value (" + value +
					") not accepted for setting " + settingName);
		}
	}

	/**
	 * Sets the namespace to something else than the current namespace.
	 * This change can be reverted using {@link #restoreNameSpace()}
	 * @param namespace The new namespace
	 */
	public void setNameSpace(String namespace) {
		this.oldNamespaces.push(this.namespace);
		this.namespace = namespace;
	}

	/**
	 * Appends the given namespace to the the current namespace, <strong>
	 * for both the primary and secondary namespace </strong>.
	 * This change can be reverted using {@link #restoreNameSpace()} and
	 * {@link #restoreSecondaryNamespace()}.
	 * @param namespace The new namespace to append
	 */
	public void setSubNameSpace(String namespace) {
		this.oldNamespaces.push(this.namespace);
		this.namespace = this.namespace + "." + namespace;
		this.secondaryNamespaces.push(this.secondaryNamespace);
		this.secondaryNamespace = this.secondaryNamespace + "." + namespace;
	}

	/**
	 * Returns full (namespace prefixed) property name for a setting.
	 * @param setting The name of the setting
	 * @return The setting name prefixed with fully qualified name of the
	 * namespace where the requested setting would be retrieved from or null
	 * if that setting is not found from any of the current namespace(s)
	 */
	public String getFullPropertyName(String setting) {
		if (!contains(setting)) {
			return null;
		}

		if (props.getProperty(getFullPropertyName(setting, false)) != null) {
			return getFullPropertyName(setting, false);
		}

		// not found from primary, but Settings contains -> must be from 2ndary
		else return getFullPropertyName(setting, true);
	}

	/**
	 * Returns the namespace of the settings object
	 * @return the namespace of the settings object
	 */
	public String getNameSpace() {
		return this.namespace;
	}

	/**
	 * Returns the secondary namespace of the settings object
	 * @return the secondary namespace of the settings object
	 */
	public String getSecondaryNameSpace() {
		return this.secondaryNamespace;
	}

	/**
	 * Sets a secondary namespace where a setting is searched from if it
	 * isn't found from the primary namespace. Secondary namespace can
	 * be used e.g. as a "default" space where the settings are looked from
	 * if no specific setting is set.
	 * This change can be reverted using {@link #restoreSecondaryNamespace()}
	 * @param namespace The new secondary namespace or null if secondary
	 * namespace is not used (default behavior)
	 */
	public void setSecondaryNamespace(String namespace) {
		this.secondaryNamespaces.push(this.secondaryNamespace);
		this.secondaryNamespace = namespace;
	}

	/**
	 * Restores the namespace that was in use before a call to setNameSpace
	 * @see #setNameSpace(String)
	 */
	public void restoreNameSpace() {
		this.namespace = this.oldNamespaces.pop();
	}

	/**
	 * Restores the secondary namespace that was in use before a call to
	 * setSecondaryNameSpace
	 * @see #setSecondaryNamespace(String)
	 */
	public void restoreSecondaryNamespace() {
		this.secondaryNamespace = this.secondaryNamespaces.pop();
	}

	/**
	 * Reverts the change made with {@link #setSubNameSpace(String)}, i.e.,
	 * restores both the primary and secondary namespace.
	 */
	public void restoreSubNameSpace() {
		restoreNameSpace();
		restoreSecondaryNamespace();
	}

	/**
	 * Initializes the settings all Settings objects will use. This should be
	 * called before any setting requests. Subsequent calls replace all
	 * old settings and then Settings contains only the new settings.
	 * The file {@link #DEF_SETTINGS_FILE}, if exists, is always read.
	 * @param propFile Path to the property file where additional settings
	 * are read from or null if no additional settings files are needed.
	 * @throws SettingsError If loading the settings file(s) didn't succeed
	 */
	public static void init(String propFile) throws SettingsError {
		String outFile;
		try {
			if (new File(DEF_SETTINGS_FILE).exists()) {
				Properties defProperties = new Properties();
				defProperties.load(new FileInputStream(DEF_SETTINGS_FILE));
				props = new Properties(defProperties);
			}
			else {
				props = new Properties();
			}
			if (propFile != null) {
				props.load(new FileInputStream(propFile));
			}
		} catch (IOException e) {
			throw new SettingsError(e);
		}

		outFile = props.getProperty(SETTING_OUTPUT_S);
		if (outFile != null) {
			if (outFile.trim().length() == 0) {
				out = System.out;
			} else {
				try {
					out = new PrintStream(new File(outFile));
				} catch (FileNotFoundException e) {
					throw new SettingsError("Can't open Settings output file:" +
							e);
				}
			}
		}
	}

	/**
	 * Initializes the settings all Settings objects will use. This should be
	 * called before any setting requests. Subsequent calls replace all
	 * old settings and then Settings contains only the new settings.
	 *
	 * @param settingsStream
	 * 		InputStream where the properties are read.
	 * @throws SettingsError
	 * 		If loading the settings didn't succeed
	 */
	public static void initFromStream(final InputStream settingsStream)
	throws SettingsError {
		props = new Properties();
		try {
			props.load(settingsStream);
		} catch (IOException e) {
			throw new SettingsError(e);
		}

		String outFile = props.getProperty(SETTING_OUTPUT_S);
		if (outFile != null) {
			if (outFile.trim().length() == 0) {
				out = System.out;
			} else {
				try {
					out = new PrintStream(new File(outFile));
				} catch (FileNotFoundException e) {
					throw new SettingsError("Can't open Settings output file:" +
							e);
				}
			}
		}
	}

	/**
	 * Reads another settings file and adds the key-value pairs to the current
	 * settings overriding any values that already existed with the same keys.
	 * @param propFile Path to the property file
	 * @throws SettingsError If loading the settings file didn't succeed
	 * @see #init(String)
	 */
	public static void addSettings(String propFile) throws SettingsError {
		try {
			props.load(new FileInputStream(propFile));
		} catch (IOException e) {
			throw new SettingsError(e);
		}
	}

	/**
	 * Writes the given setting string to the settings output (if any)
	 * @param setting The string to write
	 */
	private static void outputSetting(String setting) {
		if (out != null && !writtenSettings.contains(setting)) {
			if (writtenSettings.size() == 0) {
				out.println("# Settings for run " + (runIndex + 1));
			}
			out.println(setting);
			writtenSettings.add(setting);
		}
	}

	/**
	 * Returns true if a setting with defined name (in the current namespace
	 * or secondary namespace if such is set) exists and has some value
	 * (not just white space)
	 * @param name Name of the setting to check
	 * @return True if the setting exists, false if not
	 */
	public boolean contains(String name) {
		try {
			String value = getSetting(name);
			if (value == null) {
				return false;
			}

			else return value.trim().length() > 0;
		}
		catch (SettingsError e) {
			return false; // didn't find the setting
		}
	}

	/**
	 * Returns full (namespace prefixed) property name for setting.
	 * @param name Name of the settings
	 * @param secondary If true, the secondary namespace is used.
	 * @return full (prefixed with current namespace) property name for setting
	 */
	private String getFullPropertyName(String name, boolean secondary) {
		String usedNamespace = (secondary ? secondaryNamespace : namespace);

		if (usedNamespace != null) {
			return usedNamespace + "." + name;
		}
		else {
			return name;
		}
	}

	/**
	 * Returns a String-valued setting. Setting is first looked from the
	 * namespace that is set (if any) and then from the secondary namespace
	 * (if any). All other getters use this method as their first step too
	 * (so all getters may throw SettingsError and look from both namespaces).
	 * @param name Name of the setting to get
	 * @return The contents of the setting in a String
	 * @throws SettingsError if the setting is not found from either one of
	 * the namespaces
	 */
	public String getSetting(String name) {
		String fullPropName;
		if (props == null) {
			init(null);
		}
		fullPropName = getFullPropertyName(name, false);
		String value = props.getProperty(fullPropName);

		if (value != null) { // found value, check if run setting can be parsed
			value = parseRunSetting(value.trim());
		}

		if ((value == null || value.length() == 0) &&
				this.secondaryNamespace != null) {
			// try secondary namespace if the value wasn't found from primary
			fullPropName = getFullPropertyName(name, true);
			value = props.getProperty(fullPropName);

			if (value != null) {
				value = parseRunSetting(value.trim());
			}
		}

		if (value == null || value.length() == 0) {
			throw new SettingsError("Can't find setting " +
					getPropertyNamesString(name));
		}

		outputSetting(fullPropName + " = " + value);
		return value;
	}

	/**
	 * Returns the given setting if it exists, or defaultValue if the setting
	 * does not exist
	 * @param name The name of the setting
	 * @param defaultValue The value to return if the given setting didn't exist
	 * @return The setting value or the default value if the setting didn't
	 * exist
	 */
	public String getSetting(String name, String defaultValue) {
		if (!contains(name)) {
			return defaultValue;
		} else {
			return getSetting(name);
		}
	}

	/**
	 * Parses run-specific settings from a String value
	 * @param value The String to parse
	 * @return The runIndex % arrayLength'th value of the run array
	 */
	private static String parseRunSetting(String value) {
		final String RUN_ARRAY_START = "[";
		final String RUN_ARRAY_END = "]";
		final String RUN_ARRAY_DELIM = ";";
		final int MIN_LENGTH = 3; // minimum run is one value. e.g. "[v]"

		if (!value.startsWith(RUN_ARRAY_START) ||
			!value.endsWith(RUN_ARRAY_END) ||
			runIndex < 0 ||
			value.length() < MIN_LENGTH) {
			return value; // standard format setting -> return
		}

		value = value.substring(1,value.length()-1); // remove brackets
		String[] valueArr = value.split(RUN_ARRAY_DELIM);
		int arrIndex = runIndex % valueArr.length;
		value = valueArr[arrIndex].trim();

		return value;
	}

	/**
	 * Returns the setting name appended to namespace name(s) on a String
	 * (for error messages)
	 * @param name Name of the setting
	 * @return the setting name appended to namespace name(s) on a String
	 */
	private String getPropertyNamesString(String name) {
		if (this.secondaryNamespace != null) {
			return "'"+ this.secondaryNamespace + "." + name + "' nor '" +
				this.namespace + "." + name + "'";
		}
		else if (this.namespace != null){
			return "'" + this.namespace + "." + name + "'";
		}
		else {
			return "'" + name + "'";
		}
	}

	/**
	 * Returns a double-valued setting
	 * @param name Name of the setting to get
	 * @return Value of the setting as a double
	 */
	public double getDouble(String name) {
		return parseDouble(getSetting(name), name);
	}

	/**
	 * Returns a double-valued setting, or the default value if the given
	 * setting does not exist
	 * @param name Name of the setting to get
	 * @param defaultValue The value to return if the setting doesn't exist
	 * @return Value of the setting as a double (or the default value)
	 */
	public double getDouble(String name, double defaultValue) {
		return parseDouble(getSetting(name, ""+defaultValue), name);
	}

	/**
	 * Parses a double value from a String valued setting. Supports
	 * kilo (k), mega (M) and giga (G) suffixes.
	 * @param value String value to parse
	 * @param setting The setting where this value was from (for error msgs)
	 * @return The value as a double
	 * @throws SettingsError if the value wasn't a numeric value
	 * (or the suffix wasn't recognized)
	 */
	private double parseDouble(String value, String setting) {
		double number;
		int multiplier = getMultiplier(value);

		if (multiplier > 1) { // take the suffix away before parsing
			value = value.replaceAll("[^\\d.]","");
			//replaceAll removes everything which is not a digit or point
		}

		try {
			number = Double.parseDouble(value) * multiplier;
		} catch (NumberFormatException e) {
			throw new SettingsError("Invalid numeric setting '" + value +
					"' for '" + setting +"'\n" + e.getMessage());
		}
		return number;
	}

	/**
	 * Parses a long value from a String valued setting. Supports
	 * kilo (k), mega (M) and giga (G) suffixes.
	 * @param value String value to parse
	 * @param setting The setting where this value was from (for error msgs)
	 * @return The value as a long
	 * @throws SettingsError if the value wasn't a numeric value
	 * (or the suffix wasn't recognized)
	 */
	private long parseLong(String value, String setting) {
		long number;
		int multiplier = getMultiplier(value);

		if (multiplier > 1) { // take the suffix away before parsing
			value = value.replaceAll("[^\\d.]","");
		}

		try {
			number = Long.parseLong(value) * multiplier;
		} catch (NumberFormatException e) {
			throw new SettingsError("Invalid numeric setting '" + value +
					"' for '" + setting +"'\n" + e.getMessage());
		}
		return number;
	}

	/**
	 * Parses the multiplier suffix from numeric setting
	 * @param value The setting value
	 * @return The muliplier as a number
	 */
	private int getMultiplier(String value) {
		value = value.trim();

		if (value.endsWith("k")) {
			return 1000;
		}
		else if (value.endsWith("M")) {
			return 1000000;
		}
		else if (value.endsWith("G")) {
			return 1000000000;
		}
		else if (value.endsWith("kiB")) {
			//2^10
			return 1024;
		}
		else if (value.endsWith("MiB")) {
			//2^20
			return 1048576;
		}
		else if (value.endsWith("GiB")) {
			//2^30
			return 1073741824;
		} else {
			// no multiplier
			return 1;
		}
	}

	/**
	 * Returns a CSV setting. Value part of the setting must be a list of
	 * comma separated values. Whitespace between values is trimmed away.
	 * @param name Name of the setting
	 * @return Array of values that were comma-separated
	 * @throws SettingsError if something went wrong with reading
	 */
	public String[] getCsvSetting(String name) {
		ArrayList<String> values = new ArrayList<String>();
		String csv = getSetting(name);
		Scanner s = new Scanner(csv);
		s.useDelimiter(",");

		while (s.hasNext()) {
			values.add(s.next().trim());
		}

		return values.toArray(new String[0]);
	}

	/**
	 * Returns a CSV setting containing expected amount of values.
	 * Value part of the setting must be a list of
	 * comma separated values. Whitespace between values is trimmed away.
	 * @param name Name of the setting
	 * @param expectedCount how many values are expected
	 * @return Array of values that were comma-separated
	 * @throws SettingsError if something went wrong with reading or didn't
	 * read the expected amount of values.
	 */
	public String[] getCsvSetting(String name, int expectedCount) {
		String[] values = getCsvSetting(name);

		if (values.length != expectedCount) {
			throw new SettingsError("Read unexpected amount (" + values.length +
					") of comma separated values for setting '"
					+ name + "' (expected " + expectedCount + ")");
		}

		return values;
	}

	/**
	 * Returns an array of CSV setting double values containing expected
	 * amount of values.
	 * @param name Name of the setting
	 * @param expectedCount how many values are expected
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String, int)
	 */
	public double[] getCsvDoubles(String name, int expectedCount) {
		return parseDoubles(getCsvSetting(name, expectedCount),name);
	}

	/**
	 * Returns an array of CSV setting double values.
	 * @param name Name of the setting
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String)
	 */
	public double[] getCsvDoubles(String name) {
		return parseDoubles(getCsvSetting(name), name);
	}

	/**
	 * Parses a double array out of a String array
	 * @param strings The array of strings containing double values
	 * @param name Name of the setting
	 * @return Array of double values parsed from the string values
	 */
	private double[] parseDoubles(String[] strings, String name) {
		double[] values = new double[strings.length];
		for (int i=0; i<values.length; i++) {
			values[i] = parseDouble(strings[i], name);
		}
		return values;
	}

	/**
	 * Returns an array of CSV setting integer values
	 * @param name Name of the setting
	 * @param expectedCount how many values are expected
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String, int)
	 */
	public int[] getCsvInts(String name, int expectedCount) {
		return convertToInts(getCsvDoubles(name, expectedCount), name);
	}

	/**
	 * Returns an array of CSV setting integer values
	 * @param name Name of the setting
	 * @return Array of values that were comma-separated
	 * @see #getCsvSetting(String, int)
	 */
	public int[] getCsvInts(String name) {
		return convertToInts(getCsvDoubles(name), name);
	}

	/**
	 * Returns comma-separated ranges (e.g., "3-5, 17-20, 15")
	 * @param name Name of the setting
	 * @return Array of ranges that were comma-separated in the setting
	 * @throws SettingsError if something went wrong with reading
	 */
	public Range[] getCsvRanges(String name) {
		String[] strRanges = getCsvSetting(name);
		Range[] ranges = new Range[strRanges.length];

		try {
			for (int i=0; i < strRanges.length; i++) {
				ranges[i] = new Range(strRanges[i]);
			}
		} catch (NumberFormatException nfe) {
			throw new SettingsError("Invalid numeric range value in " +
					name, nfe);
		}

		return ranges;
	}

	/**
	 * Returns an integer-valued setting
	 * @param name Name of the setting to get
	 * @return Value of the setting as an integer
	 */
	public int getInt(String name) {
		return convertToInt(getDouble(name), name);
	}

	/**
	 * Returns a long-valued setting
	 * @param name Name of the setting to get
	 * @return Value of the setting as an integer
	 */
	public long getLong(String name) {
		return parseLong(getSetting(name), name);
	}

	/**
	 * Returns an integer-valued setting, or the default value if the
	 * setting does not exist
	 * @param name Name of the setting to get
	 * @param defaultValue The value to return if the setting didn't exist
	 * @return Value of the setting as an integer
	 */
	public int getInt(String name, int defaultValue) {
		return convertToInt(getDouble(name, defaultValue), name);
	}

	/**
	 * Converts a double value that is supposedly equal to an integer value
	 * to an integer value.
	 * @param doubleValue The double value to convert
	 * @param name Name of the setting where this value is from (for
	 * SettingsError)
	 * @return The integer value
	 * @throws SettingsError if the double value was not equal to any integer
	 * value
	 */
	private int convertToInt(double doubleValue, String name) {
		int number = (int)doubleValue;

		if (number != doubleValue) {
			throw new SettingsError("Expected integer value for setting '" +
					name + "' " + " got '" + doubleValue + "'");
		}
		return number;
	}

	/**
	 * Converts an array of double values to int values using
	 * {@link #convertToInt(double, String)}.
	 * @param doubleValues The double valued array
	 * @param name Name of the setting where this value is from (for
	 * SettingsError)
	 * @return Array of integer values
	 * @see #convertToInt(double, String)
	 */
	private int[] convertToInts(double [] doubleValues, String name) {
		int[] values = new int[doubleValues.length];
		for (int i=0; i<values.length; i++) {
			values[i] = convertToInt(doubleValues[i], name);
		}
		return values;
	}

	/**
	 * Returns a boolean-valued setting
	 * @param name Name of the setting to get
	 * @return True if the settings value was either "true" (case ignored)
	 *  or "1", false is the settings value was either "false" (case ignored)
	 *  or "0".
	 *  @throws SettingsError if the value wasn't any recognized value
	 *  @see #getSetting(String)
	 */
	public boolean getBoolean(String name) {
		String stringValue = getSetting(name);
		boolean value;

		if (stringValue.equalsIgnoreCase("true") ||
				stringValue.equals("1")) {
			value = true;
		}
		else if (stringValue.equalsIgnoreCase("false") ||
				stringValue.equals("0")) {
			value = false;
		}
		else {
			throw new SettingsError("Not a boolean value: '"+stringValue+
					"' for setting " + name);
		}

		return value;
	}

	/**
	 * Returns the given boolean setting if it exists, or defaultValue if the
	 * setting does not exist
	 * @param name The name of the setting
	 * @param defaultValue The value to return if the given setting didn't exist
	 * @return The setting value or the default value if the setting didn't
	 * exist
	 */
	public boolean getBoolean(String name, boolean defaultValue) {
		if (!contains(name)) {
			return defaultValue;
		} else {
			return getBoolean(name);
		}
	}

	/**
	 * Returns an ArithmeticCondition setting. See {@link ArithmeticCondition}
	 * @param name Name of the setting to get
	 * @return ArithmeticCondition from the setting
	 * @throws SettingsError if the value wasn't a valid arithmetic expression
	 */
	public ArithmeticCondition getCondition(String name) {
		return new ArithmeticCondition(getSetting(name));
	}

	/**
	 * Creates (and dynamically loads the class of) an object that
	 * initializes itself using the settings in this Settings object
	 * (given as the only parameter to the constructor).
	 * @param className Name of the class of the object
	 * @return Initialized object
	 * @throws SettingsError if object couldn't be created
	 */
	public Object createIntializedObject(String className) {
		Class<?>[] argsClass = {Settings.class};
		Object[] args = {this};

		return loadObject(className, argsClass, args);
	}

	/**
	 * Creates (and dynamically loads the class of) an object using the
	 * constructor without any parameters.
	 * @param className Name of the class of the object
	 * @return Initialized object
	 * @throws SettingsError if object couldn't be created
	 */
	public Object createObject(String className) {
		return loadObject(className, null, null);
	}

	/**
	 * Dynamically loads and creates an object.
	 * @param className Name of the class of the object
	 * @param argsClass Class(es) of the argument(s) or null if no-argument
	 * constructor should be called
	 * @param args Argument(s)
	 * @return The new object
	 * @throws SettingsError if object couldn't be created
	 */
	private Object loadObject(String className, Class<?>[] argsClass,
			Object[] args) {
		Object o = null;
		Class<?> objClass = getClass(className);
		Constructor<?> constructor;

		try {
			if (argsClass != null) { // use a specific constructor
				constructor = objClass.getConstructor((Class[])argsClass);
				o = constructor.newInstance(args);
			}
			else { // call empty constructor
				o = objClass.newInstance();
			}
		} catch (SecurityException e) {
			e.printStackTrace();
			throw new SettingsError("Fatal exception " + e, e);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			throw new SettingsError("Fatal exception " + e, e);
		} catch (NoSuchMethodException e) {
			throw new SettingsError("Class '" + className +
					"' doesn't have a suitable constructor", e);
		} catch (InstantiationException e) {
			throw new SettingsError("Can't create an instance of '" +
					className + "'", e);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new SettingsError("Fatal exception " + e, e);
		} catch (InvocationTargetException e) {
			// this exception occurs if initialization of the object fails
			if (e.getCause() instanceof SettingsError) {
				throw (SettingsError)e.getCause();
			}
			else {
				e.printStackTrace();
				throw new SimError("Couldn't create settings-accepting object"+
					" for '" + className + "'\n" + "cause: " + e.getCause(), e);
			}
		}

		return o;
	}

	/**
	 * Returns a Class object for the name of class of throws SettingsError
	 * if such class wasn't found.
	 * @param name Full name of the class (including package name)
	 * @return A Class object of that class
	 * @throws SettingsError if such class wasn't found or couldn't be loaded
	 */
	private Class<?> getClass(String name) {
		String className = name;
		Class<?> c;

		try {
			c = Class.forName(className);
		} catch (ClassNotFoundException e) {
			throw new SettingsError("Couldn't find class '" + className + "'"+
					"\n" + e.getMessage(),e);
		}

		return c;
	}

	/**
	 * Fills a String formatted in a special way with values from Settings.
	 * String can contain (fully qualified) setting names surrounded by
	 * delimiters (see {@link #FILL_DELIMITER}). Values for those settings
	 * are retrieved and filled in the place of place holders.
	 * @param input The input string that may contain value requests
	 * @return A string filled with requested values (or the original string
	 * if no requests were found)
	 * @throws SettingsError if such settings were not found
	 */
	public String valueFillString(String input) {
		if (!input.contains(FILL_DELIMITER)) {
			return input;	// nothing to fill
		}

		Settings s = new Settings(); // don't use any namespace
		String result = "";
		Scanner scan = new Scanner(input);
		scan.useDelimiter(FILL_DELIMITER);

		if (input.startsWith(FILL_DELIMITER)) {
			result += s.getSetting(scan.next());
		}

		while(scan.hasNext()) {
			result += scan.next();
			if (!scan.hasNext()) {
				break;
			}
			result += s.getSetting(scan.next());
		}

		return result;
	}

	/**
	 * Returns a String representation of the stored settings
	 * @return a String representation of the stored settings
	 */
	public String toString() {
		return props.toString();
	}

}

src/routing/util/RoutingInfo.java
public class RoutingInfo {
	private String text;
	private List<RoutingInfo> moreInfo = null;

	/**
	 * Creates a routing info based on a text.
	 * @param infoText The text of the info
	 */
	public RoutingInfo(String infoText) {
		this.text = infoText;
	}

	/**
	 * Creates a routing info based on any object. Object's
	 * toString() method's output is used as the info text.
	 * @param o The object this info is based on
	 */
	public RoutingInfo(Object o) {
		this.text = o.toString();
	}

	/**
	 * Adds child info object for this routing info.
	 * @param info The info object to add.
	 */
	public void addMoreInfo(RoutingInfo info) {
		if (this.moreInfo == null) { // lazy creation
			this.moreInfo = new ArrayList<RoutingInfo>();
		}
		this.moreInfo.add(info);
	}

	/**
	 * Returns the child routing infos of this info.
	 * @return The children of this info or an empty list if this info
	 * doesn't have any children.
	 */
	public List<RoutingInfo> getMoreInfo() {
		if (this.moreInfo == null) {
			return new ArrayList<RoutingInfo>(0);
		}
		return this.moreInfo;
	}

	/**
	 * Returns the info text of this routing info.
	 * @return The info text
	 */
	public String toString() {
		return this.text;
	}

}

src/util/Tuple.java
public class Tuple<K,V>  {
	private K key;
	private V value;

	/**
	 * Creates a new tuple.
	 * @param key The key of the tuple
	 * @param value The value of the tuple
	 */
	public Tuple(K key, V value) {
		this.key = key;
		this.value = value;
	}

	/**
	 * Returns the key
	 * @return the key
	 */
	public K getKey() {
		return key;
	}

	/**
	 * Returns the value
	 * @return the value
	 */
	public V getValue() {
		return value;
	}

	/**
	 * Returns a string representation of the tuple
	 * @return a string representation of the tuple
	 */
	public String toString() {
		return key.toString() + ":" + value.toString();
	}
}

src/routing/util/MessageTransferAcceptPolicy.java
public class MessageTransferAcceptPolicy {

	/** Namespace for all "Message Transfer Accept policy" settings ({@value})*/
	public static final String MTA_POLICY_NS = "mtaPolicy";

	/** Number of Module Communication Bus Conditions -setting id ({@value}).
	 * Two comma separated values. Defines the number of receiving and number of
	 * sending conditions to read from the settings. */
	public static final String NROF_MCBCS_S = "nrofMCBACs";

	/** Module Communication Bus Arithmetic Condition for Receiving -setting id
	 * ({@value}). {@link ArithmeticCondition}. Defines one arithmetic condition
	 * to use for receiving messages. */
	public static final String MCBACR_S = "MCBRcondition";
	/** Module Communication Bus Arithmetic Condition for Sending -setting id
	 * ({@value}). {@link ArithmeticCondition}. */
	public static final String MCBACS_S = "MCBScondition";

	/** Module Communication Bus Condition Value for Receiving -setting id
	 * ({@value}). String. Defines the ID to use with the receiving
	 * condition. */
	public static final String MCBCVR_S = "MCBRvalue";
	/** Module Communication Bus Condition Value for Sending -setting id
	 * ({@value}). String. Defines the ID to use with the sending
	 * condition. */
	public static final String MCBCVS_S = "MCBSvalue";

	/** The valued used in to-policy to refer to this host ({@value}) */
	public static final int TO_ME_VALUE = -1;

	/** Simple-policy accept-to -setting id ({@value}). Integer list.
	 * Defines the addresses of the hosts accepted as the destination of a
	 * message when receiving messages. Special value {@link #TO_ME_VALUE}
	 * refers to this host. */
	public static final String TO_RPOLICY_S = "toReceivePolicy";

	/** Simple-policy accept-from -setting id ({@value}). Integer list.
	 * Defines the addresses of the hosts accepted as the source of a
	 * message when receiving messages. Special value {@link #TO_ME_VALUE}
	 * refers to this host. */
	public static final String FROM_RPOLICY_S = "fromReceivePolicy";

	/** Simple-policy accept-to -setting id ({@value}). Integer list.
	 * Defines the addresses of the hosts accepted as the destination of a
	 * message when sending messages. Special value {@link #TO_ME_VALUE} refers
	 * to this host (but doesn't usually make much sense here). */
	public static final String TO_SPOLICY_S = "toSendPolicy";

	/** <P> Simple-policy accept-from -setting id ({@value}). Integer list.
	 * Defines the addresses of the hosts accepted as the source of a
	 * message when sending messages. Special value {@link #TO_ME_VALUE} refers
	 * to this host. </P>
	 * <P> <B>Note:</B> if this setting is defined and the {@link #TO_ME_VALUE}
	 * is NOT listed, the hosts own messages are not sent anywhere. </P>*/
	public static final String FROM_SPOLICY_S = "fromSendPolicy";

	/** Hop count forwarding receive policy -setting id ({@value}).
	 * {@link ArithmeticCondition}. Defines condition for the message hop
	 * count; if the condition does not match, the message is rejected,
	 * unless it is destined to this node. */
	public static final String HOPCOUNT_RPOLICY_S = "hopCountReceivePolicy";
	/** Hop count forwarding send policy -setting id ({@value}).
	 * {@link ArithmeticCondition}. Defines condition for the message hop
	 * count; if the condition does not match, the message is not offered
	 * to other nodes, unless it would be delivered to the final destination. */
	public static final String HOPCOUNT_SPOLICY_S = "hopCountSendPolicy";

	private ArrayList<Tuple<String,ArithmeticCondition>> recvConditions = null;
	private ArrayList<Tuple<String,ArithmeticCondition>> sendConditions = null;

	private Range[] toSendPolicy = null;
	private Range[] fromSendPolicy = null;
	private Range[] toReceivePolicy = null;
	private Range[] fromReceivePolicy = null;
	private ArithmeticCondition hopCountSendPolicy = null;
	private ArithmeticCondition hopCountReceivePolicy = null;

	public MessageTransferAcceptPolicy(Settings nsSettings) {
		Settings s;

		if (! nsSettings.contains(MTA_POLICY_NS)) {
			return; /* no (or "default") policy */
		}

		s = new Settings(nsSettings.getSetting(MTA_POLICY_NS));
		addMCBCs(s);

		if (s.contains(TO_SPOLICY_S)) {
			this.toSendPolicy = s.getCsvRanges(TO_SPOLICY_S);
		}
		if (s.contains(FROM_SPOLICY_S)) {
			this.fromSendPolicy = s.getCsvRanges(FROM_SPOLICY_S);
		}
		if (s.contains(TO_RPOLICY_S)) {
			this.toReceivePolicy = s.getCsvRanges(TO_RPOLICY_S);
		}
		if (s.contains(FROM_RPOLICY_S)) {
			this.fromReceivePolicy = s.getCsvRanges(FROM_RPOLICY_S);
		}
		if (s.contains(HOPCOUNT_SPOLICY_S)) {
			hopCountSendPolicy = s.getCondition(HOPCOUNT_SPOLICY_S);
		}
		if (s.contains(HOPCOUNT_RPOLICY_S)) {
			hopCountReceivePolicy = s.getCondition(HOPCOUNT_RPOLICY_S);
		}
	}

	/**
	 * Adds MessageCommunicationBus Conditions
	 * @param s Settings where the conditions are read from
	 */
	private void addMCBCs(Settings s) {
		if (!s.contains(NROF_MCBCS_S)) {
			return; /* no transfer policy defined */
		}

		int[] nrof = s.getCsvInts(NROF_MCBCS_S);
		if (nrof[0] > 0) { /* create lists only if needed */
			this.recvConditions =
				new ArrayList<Tuple<String,ArithmeticCondition>>();
		}
		if (nrof[1] > 0) {
			this.sendConditions =
				new ArrayList<Tuple<String,ArithmeticCondition>>();
		}

		addConditions(s, MCBACR_S, MCBCVR_S, this.recvConditions,  nrof[0]);
		addConditions(s, MCBACS_S, MCBCVS_S, this.sendConditions,  nrof[1]);
	}

	/**
	 * Read conditions from the settings and add them to the given list
	 * @param s The settings object
	 * @param cPrefix Condition setting prefix
	 * @param vPrefix Value setting prefix
	 * @param list The list to add conditions
	 * @param nrof The number of settings to read
	 */
	private void addConditions(Settings s, String cPrefix, String vPrefix,
			ArrayList<Tuple<String,ArithmeticCondition>> list,
			int nrof) {
		for (int i=1; i<=nrof; i++) {
			ArithmeticCondition ac = s.getCondition(cPrefix + i);
			String mcbValue = s.getSetting(vPrefix + i);
			list.add(new Tuple<String, ArithmeticCondition>(mcbValue, ac));
		}
	}

	/**
	 * Checks all the Module Communication Bus conditions and returns false
	 * if at least one of them failed.
	 * @param mcb The module communication bus to use
	 * @param receiving Should check using the receiving conditions list;
	 * if false, the sending conditions list is used
	 * @return true if all conditions evaluated to true
	 */
	private boolean checkMcbConditions(ModuleCommunicationBus mcb,
			boolean receiving) {
		ArrayList<Tuple<String,ArithmeticCondition>> list =
			(receiving ? this.recvConditions : this.sendConditions);

		if (list == null) {
			return true;
		}

		for (Tuple<String,ArithmeticCondition> t : list) {
			if (!mcb.containsProperty(t.getKey())) {
				continue; /* no value in the bus; can't fail condition */
			}
			if (t.getValue().isTrueFor(mcb.getDouble(t.getKey(), 0))){
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks if the host's address is contained in the policy list
	 * (or {@value #TO_ME_VALUE} is contained and the address matches to
	 * thisHost parameter)
	 * @param host The hosts whose address to check
	 * @param policy The list of accepted addresses
	 * @param thisHost The address of this host
	 * @return True if the address was in the policy list, or the policy list
	 * was null
	 */
	private boolean checkSimplePolicy(DTNHost host, Range [] policy,
			int thisHost) {
		int address;

		if (policy == null) {
			return true;
		}

		address = host.getID();

		for (Range r : policy) {
			if (r.isInRange(TO_ME_VALUE) && address == thisHost) {
				return true;
			}
			 else if (r.isInRange(address)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Checks the given messages hop count against the given policy arithmetic
	 * condition
	 * @param m The message whose hop count is checked
	 * @param ac The policy arithmetic condition
	 * @return True if the condition is null or the hop count matches to the
	 * condition, false otherwise
	 */
	private boolean checkHopCountPolicy(Message m, ArithmeticCondition ac) {
		if (ac == null) {
			return true;
		} else {
			return ac.isTrueFor(m.getHopCount());
		}
	}

	/**
	 * Returns true if the given message, using the given connection, is OK
	 * to send from "from" to "to" host.
	 * @param from The sending host
	 * @param to The receiving host
	 * @param con The connection used by the hosts
	 * @param m The message to transfer
	 * @return True if the message is OK to transfer, false is not
	 */
	public boolean acceptSending(DTNHost from, DTNHost to, Connection con,
			Message m) {
		if (!checkMcbConditions(from.getComBus(), false)) {
			return false;
		}

		int myAddr = from.getID();
		if (! (checkSimplePolicy(m.getTo(), this.toSendPolicy, myAddr) &&
			checkSimplePolicy(m.getFrom(), this.fromSendPolicy,	myAddr)) ) {
			return false;
		}

		if (m.getTo() != to &&
				!checkHopCountPolicy(m, this.hopCountSendPolicy)){
			return false;
		}

		return true;
	}

	/**
	 * Returns true if the given message is OK to receive from "from" to
	 * "to" host.
	 * @param from The sending host
	 * @param to The receiving host
	 * @param m The message to transfer
	 * @return True if the message is OK to transfer, false is not
	 */
	public boolean acceptReceiving(DTNHost from, DTNHost to, Message m) {
		if (! checkMcbConditions(to.getComBus(), true)) {
			return false;
		}

		int myAddr = to.getID();
		if (! (checkSimplePolicy(m.getTo(), this.toReceivePolicy,myAddr) &&
			checkSimplePolicy(m.getFrom(), this.fromReceivePolicy, myAddr)) ) {
			return false;
		}

		if (m.getTo() != to &&
				!checkHopCountPolicy(m, this.hopCountReceivePolicy)) {
			return false;
		}

		return true;
	}

}

src/core/Message.java
public class Message implements Comparable<Message> {
	/** Value for infinite TTL of message */
	public static final int INFINITE_TTL = -1;
	private DTNHost from;
	private DTNHost to;
	/** Identifier of the message */
	private String id;
	/** Size of the message (bytes) */
	private int size;
	/** List of nodes this message has passed */
	private List<DTNHost> path;
	/** Next unique identifier to be given */
	private static int nextUniqueId;
	/** Unique ID of this message */
	private int uniqueId;
	/** The time this message was received */
	private double timeReceived;
	/** The time when this message was created */
	private double timeCreated;
	/** Initial TTL of the message */
	private int initTtl;

	/** if a response to this message is required, this is the size of the
	 * response message (or 0 if no response is requested) */
	private int responseSize;
	/** if this message is a response message, this is set to the request msg*/
	private Message requestMsg;

	/** Container for generic message properties. Note that all values
	 * stored in the properties should be immutable because only a shallow
	 * copy of the properties is made when replicating messages */
	private Map<String, Object> properties;

	/** Application ID of the application that created the message */
	private String	appID;

	static {
		reset();
		DTNSim.registerForReset(Message.class.getCanonicalName());
	}

	/**
	 * Creates a new Message.
	 * @param from Who the message is (originally) from
	 * @param to Who the message is (originally) to
	 * @param id Message identifier (must be unique for message but
	 * 	will be the same for all replicates of the message)
	 * @param size Size of the message (in bytes)
	 */
	public Message(DTNHost from, DTNHost to, String id, int size) {
		this.from = from;
		this.to = to;
		this.id = id;
		this.size = size;
		this.path = new ArrayList<DTNHost>();
		this.uniqueId = nextUniqueId;

		this.timeCreated = SimClock.getTime();
		this.timeReceived = this.timeCreated;
		this.initTtl = INFINITE_TTL;
		this.responseSize = 0;
		this.requestMsg = null;
		this.properties = null;
		this.appID = null;

		Message.nextUniqueId++;
		addNodeOnPath(from);
	}

	/**
	 * Returns the node this message is originally from
	 * @return the node this message is originally from
	 */
	public DTNHost getFrom() {
		return this.from;
	}

	/**
	 * Returns the node this message is originally to
	 * @return the node this message is originally to
	 */
	public DTNHost getTo() {
		return this.to;
	}

	/**
	 * Returns the ID of the message
	 * @return The message id
	 */
	public String getId() {
		return this.id;
	}

	/**
	 * Returns an ID that is unique per message instance
	 * (different for replicates too)
	 * @return The unique id
	 */
	public int getUniqueId() {
		return this.uniqueId;
	}

	/**
	 * Returns the size of the message (in bytes)
	 * @return the size of the message
	 */
	public int getSize() {
		return this.size;
	}

	/**
	 * Adds a new node on the list of nodes this message has passed
	 * @param node The node to add
	 */
	public void addNodeOnPath(DTNHost node) {
		this.path.add(node);
	}

	/**
	 * Returns a list of nodes this message has passed so far
	 * @return The list as vector
	 */
	public List<DTNHost> getHops() {
		return this.path;
	}

	/**
	 * Returns the amount of hops this message has passed
	 * @return the amount of hops this message has passed
	 */
	public int getHopCount() {
		return this.path.size() -1;
	}

	/**
	 * Returns the time to live (minutes) of the message or Integer.MAX_VALUE
	 * if the TTL is infinite. Returned value can be negative if the TTL has
	 * passed already.
	 * @return The TTL (minutes)
	 */
	public int getTtl() {
		if (this.initTtl == INFINITE_TTL) {
			return Integer.MAX_VALUE;
		}
		else {
			return (int)( ((this.initTtl * 60) -
					(SimClock.getTime()-this.timeCreated)) /60.0 );
		}
	}

	/**
	 * Sets the initial TTL (time-to-live) for this message. The initial
	 * TTL is the TTL when the original message was created. The current TTL
	 * is calculated based on the time of
	 * @param ttl The time-to-live to set
	 */
	public void setTtl(int ttl) {
		this.initTtl = ttl;
	}

	/**
	 * Sets the time when this message was received.
	 * @param time The time to set
	 */
	public void setReceiveTime(double time) {
		this.timeReceived = time;
	}

	/**
	 * Returns the time when this message was received
	 * @return The time
	 */
	public double getReceiveTime() {
		return this.timeReceived;
	}

	/**
	 * Returns the time when this message was created
	 * @return the time when this message was created
	 */
	public double getCreationTime() {
		return this.timeCreated;
	}

	/**
	 * If this message is a response to a request, sets the request message
	 * @param request The request message
	 */
	public void setRequest(Message request) {
		this.requestMsg = request;
	}

	/**
	 * Returns the message this message is response to or null if this is not
	 * a response message
	 * @return the message this message is response to
	 */
	public Message getRequest() {
		return this.requestMsg;
	}

	/**
	 * Returns true if this message is a response message
	 * @return true if this message is a response message
	 */
	public boolean isResponse() {
		return this.requestMsg != null;
	}

	/**
	 * Sets the requested response message's size. If size == 0, no response
	 * is requested (default)
	 * @param size Size of the response message
	 */
	public void setResponseSize(int size) {
		this.responseSize = size;
	}

	/**
	 * Returns the size of the requested response message or 0 if no response
	 * is requested.
	 * @return the size of the requested response message
	 */
	public int getResponseSize() {
		return responseSize;
	}

	/**
	 * Returns a string representation of the message
	 * @return a string representation of the message
	 */
	public String toString () {
		return id;
	}

	/**
	 * Deep copies message data from other message. If new fields are
	 * introduced to this class, most likely they should be copied here too
	 * (unless done in constructor).
	 * @param m The message where the data is copied
	 */
	protected void copyFrom(Message m) {
		this.path = new ArrayList<DTNHost>(m.path);
		this.timeCreated = m.timeCreated;
		this.responseSize = m.responseSize;
		this.requestMsg  = m.requestMsg;
		this.initTtl = m.initTtl;
		this.appID = m.appID;

		if (m.properties != null) {
			Set<String> keys = m.properties.keySet();
			for (String key : keys) {
				updateProperty(key, m.getProperty(key));
			}
		}
	}

	/**
	 * Adds a generic property for this message. The key can be any string but
	 * it should be such that no other class accidently uses the same value.
	 * The value can be any object but it's good idea to store only immutable
	 * objects because when message is replicated, only a shallow copy of the
	 * properties is made.
	 * @param key The key which is used to lookup the value
	 * @param value The value to store
	 * @throws SimError if the message already has a value for the given key
	 */
	public void addProperty(String key, Object value) throws SimError {
		if (this.properties != null && this.properties.containsKey(key)) {
			/* check to prevent accidental name space collisions */
			throw new SimError("Message " + this + " already contains value " +
					"for a key " + key);
		}

		this.updateProperty(key, value);
	}

	/**
	 * Returns an object that was stored to this message using the given
	 * key. If such object is not found, null is returned.
	 * @param key The key used to lookup the object
	 * @return The stored object or null if it isn't found
	 */
	public Object getProperty(String key) {
		if (this.properties == null) {
			return null;
		}
		return this.properties.get(key);
	}

	/**
	 * Updates a value for an existing property. For storing the value first
	 * time, {@link #addProperty(String, Object)} should be used which
	 * checks for name space clashes.
	 * @param key The key which is used to lookup the value
	 * @param value The new value to store
	 */
	public void updateProperty(String key, Object value) throws SimError {
		if (this.properties == null) {
			/* lazy creation to prevent performance overhead for classes
			   that don't use the property feature  */
			this.properties = new HashMap<String, Object>();
		}

		this.properties.put(key, value);
	}

	/**
	 * Returns a replicate of this message (identical except for the unique id)
	 * @return A replicate of the message
	 */
	public Message replicate() {
		Message m = new Message(from, to, id, size);
		m.copyFrom(this);
		return m;
	}

	/**
	 * Compares two messages by their ID (alphabetically).
	 * @see String#compareTo(String)
	 */
	public int compareTo(Message m) {
		return toString().compareTo(m.toString());
	}

	/**
	 * Resets all static fields to default values
	 */
	public static void reset() {
		nextUniqueId = 0;
	}

	/**
	 * @return the appID
	 */
	public String getAppID() {
		return appID;
	}

	/**
	 * @param appID the appID to set
	 */
	public void setAppID(String appID) {
		this.appID = appID;
	}

}

Answer: if (m.getTo() == getHost() && m.getResponseSize() > 0) {

<think>
In ActiveRouter.messageTransferred, after calling super.messageTransferred and the comment about checking if the message was for this host and a response was requested, the next logical line is the conditional that performs this check. The code checks whether the destination of the transferred message is the current host and whether a response size is set (>0). Hence the next line is the if statement that starts this check.
</think>
if (m.getTo() == getHost() && m.getResponseSize() > 0) {