gt: 		if (!isFinalRecipient && outgoing!=null) {
context: /* 
   * Copyright 2010 Aalto University, ComNet
   * Released under GPLv3. See LICENSE.txt for details. 
   */
  package routing;
  
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Random;
  
  import routing.util.RoutingInfo;
  import util.Tuple;
  import core.Application;
  import core.Connection;
  import core.DTNHost;
  import core.Message;
  import core.MessageListener;
  import core.Settings;
  import core.SettingsError;
  import core.SimClock;
  import core.SimError;
  
  /**
   * Superclass for message routers.
   */
  public abstract class MessageRouter {
  	/** Message buffer size -setting id ({@value}). Integer value in bytes.*/
  	public static final String B_SIZE_S = "bufferSize";
  	/**
  	 * Message TTL -setting id ({@value}). Value is in minutes and must be
  	 * an integer. 
  	 */ 
  	public static final String MSG_TTL_S = "msgTtl";
  	/**
  	 * Message/fragment sending queue type -setting id ({@value}). 
  	 * This setting affects the order the messages and fragments are sent if the
  	 * routing protocol doesn't define any particular order (e.g, if more than 
  	 * one message can be sent directly to the final recipient). 
  	 * Valid values are<BR>
  	 * <UL>
  	 * <LI/> 1 : random (message order is randomized every time; default option)
  	 * <LI/> 2 : FIFO (most recently received messages are sent last)
  	 * </UL>
  	 */ 
  	public static final String SEND_QUEUE_MODE_S = "sendQueue";
  	
  	/** Setting value for random queue mode */
  	public static final int Q_MODE_RANDOM = 1;
  	/** Setting value for FIFO queue mode */
  	public static final int Q_MODE_FIFO = 2;
  	
  	/* Return values when asking to start a transmission:
  	 * RCV_OK (0) means that the host accepts the message and transfer started, 
  	 * values < 0 mean that the  receiving host will not accept this 
  	 * particular message (right now), 
  	 * values > 0 mean the host will not right now accept any message. 
  	 * Values in the range [-100, 100] are reserved for general return values
  	 * (and specified here), values beyond that are free for use in 
  	 * implementation specific cases */
  	/** Receive return value for OK */
  	public static final int RCV_OK = 0;
  	/** Receive return value for busy receiver */
  	public static final int TRY_LATER_BUSY = 1;
  	/** Receive return value for an old (already received) message */
  	public static final int DENIED_OLD = -1;
  	/** Receive return value for not enough space in the buffer for the msg */
  	public static final int DENIED_NO_SPACE = -2;
  	/** Receive return value for messages whose TTL has expired */
  	public static final int DENIED_TTL = -3;
  	/** Receive return value for a node low on some resource(s) */
  	public static final int DENIED_LOW_RESOURCES = -4;
  	/** Receive return value for a node low on some resource(s) */
  	public static final int DENIED_POLICY = -5;
  	/** Receive return value for unspecified reason */
  	public static final int DENIED_UNSPECIFIED = -99;
  	
  	private List<MessageListener> mListeners;
  	/** The messages being transferred with msgID_hostName keys */
  	private HashMap<String, Message> incomingMessages;
  	/** The messages this router is carrying */
  	private HashMap<String, Message> messages; 
  	/** The messages this router has received as the final recipient */
  	private HashMap<String, Message> deliveredMessages;
  	/** The messages that Applications on this router have blacklisted */
  	private HashMap<String, Object> blacklistedMessages;
  	/** Host where this router belongs to */
  	private DTNHost host;
  	/** size of the buffer */
  	private int bufferSize;
  	/** TTL for all messages */
  	protected int msgTtl;
  	/** Queue mode for sending messages */
  	private int sendQueueMode;
  
  	/** applications attached to the host */
  	private HashMap<String, Collection<Application>> applications = null;
  	
  	/**
  	 * Constructor. Creates a new message router based on the settings in
  	 * the given Settings object. Size of the message buffer is read from
  	 * {@link #B_SIZE_S} setting. Default value is Integer.MAX_VALUE.
  	 * @param s The settings object
  	 */
  	public MessageRouter(Settings s) {
  		this.bufferSize = Integer.MAX_VALUE; // defaults to rather large buffer	
  		this.msgTtl = Message.INFINITE_TTL;
  		this.applications = new HashMap<String, Collection<Application>>();
  		
  		if (s.contains(B_SIZE_S)) {
  			this.bufferSize = s.getInt(B_SIZE_S);
  		}
  		if (s.contains(MSG_TTL_S)) {
  			this.msgTtl = s.getInt(MSG_TTL_S);
  		}
  		if (s.contains(SEND_QUEUE_MODE_S)) {
  			this.sendQueueMode = s.getInt(SEND_QUEUE_MODE_S);
  			if (sendQueueMode < 1 || sendQueueMode > 2) {
  				throw new SettingsError("Invalid value for " + 
  						s.getFullPropertyName(SEND_QUEUE_MODE_S));
  			}
  		}
  		else {
  			sendQueueMode = Q_MODE_RANDOM;
  		}
  		
  	}
  	
  	/**
  	 * Initializes the router; i.e. sets the host this router is in and
  	 * message listeners that need to be informed about message related
  	 * events etc.
  	 * @param host The host this router is in
  	 * @param mListeners The message listeners
  	 */
  	public void init(DTNHost host, List<MessageListener> mListeners) {
  		this.incomingMessages = new HashMap<String, Message>();
  		this.messages = new HashMap<String, Message>();
  		this.deliveredMessages = new HashMap<String, Message>();
  		this.blacklistedMessages = new HashMap<String, Object>();
  		this.mListeners = mListeners;
  		this.host = host;
  	}
  	
  	/**
  	 * Copy-constructor.
  	 * @param r Router to copy the settings from.
  	 */
  	protected MessageRouter(MessageRouter r) {
  		this.bufferSize = r.bufferSize;
  		this.msgTtl = r.msgTtl;
  		this.sendQueueMode = r.sendQueueMode;
  
  		this.applications = new HashMap<String, Collection<Application>>();
  		for (Collection<Application> apps : r.applications.values()) {
  			for (Application app : apps) {
  				addApplication(app.replicate());
  			}
  		}
  	}
  	
  	/**
  	 * Updates router.
  	 * This method should be called (at least once) on every simulation
  	 * interval to update the status of transfer(s). 
  	 */
  	public void update(){
  		for (Collection<Application> apps : this.applications.values()) {
  			for (Application app : apps) {
  				app.update(this.host);
  			}
  		}
  	}
  	
  	/**
  	 * Informs the router about change in connections state.
  	 * @param con The connection that changed
  	 */
  	public abstract void changedConnection(Connection con);	
  	
  	/**
  	 * Returns a message by ID.
  	 * @param id ID of the message
  	 * @return The message
  	 */
  	protected Message getMessage(String id) {
  		return this.messages.get(id);
  	}
  	
  	/**
  	 * Checks if this router has a message with certain id buffered.
  	 * @param id Identifier of the message
  	 * @return True if the router has message with this id, false if not
  	 */
  	public boolean hasMessage(String id) {
  		return this.messages.containsKey(id);
  	}
  	
  	/**
  	 * Returns true if a full message with same ID as the given message has been
  	 * received by this host as the <strong>final</strong> recipient 
  	 * (at least once).
  	 * @param m message we're interested of
  	 * @return true if a message with the same ID has been received by 
  	 * this host as the final recipient.
  	 */
  	protected boolean isDeliveredMessage(Message m) {
  		return (this.deliveredMessages.containsKey(m.getId()));
  	}
  	
  	/** 
  	 * Returns <code>true</code> if the message has been blacklisted. Messages
  	 * get blacklisted when an application running on the node wants to drop it.
  	 * This ensures the peer doesn't try to constantly send the same message to
  	 * this node, just to get dropped by an application every time.
  	 * 
  	 * @param id	id of the message
  	 * @return <code>true</code> if blacklisted, <code>false</code> otherwise.
  	 */
  	protected boolean isBlacklistedMessage(String id) {
  		return this.blacklistedMessages.containsKey(id);
  	}
  	
  	/**
  	 * Returns a reference to the messages of this router in collection.
  	 * <b>Note:</b> If there's a chance that some message(s) from the collection
  	 * could be deleted (or added) while iterating through the collection, a
  	 * copy of the collection should be made to avoid concurrent modification
  	 * exceptions. 
  	 * @return a reference to the messages of this router in collection
  	 */
  	public Collection<Message> getMessageCollection() {
  		return this.messages.values();
  	}
  	
  	public Collection<Message> getIncomingMessageCollection() {
  		return this.incomingMessages.values();
  	}
  	
  	/**
  	 * Returns the number of messages this router has
  	 * @return How many messages this router has
  	 */
  	public int getNrofMessages() {
  		return this.messages.size();
  	}
  	
  	/**
  	 * Returns the size of the message buffer.
  	 * @return The size or Integer.MAX_VALUE if the size isn't defined.
  	 */
  	public int getBufferSize() {
  		return this.bufferSize;
  	}
  	
  	/**
  	 * Returns the amount of free space in the buffer. May return a negative
  	 * value if there are more messages in the buffer than should fit there
  	 * (because of creating new messages).
  	 * @return The amount of free space (Integer.MAX_VALUE if the buffer
  	 * size isn't defined)
  	 */
  	public int getFreeBufferSize() {
  		int occupancy = 0;
  		
  		if (this.getBufferSize() == Integer.MAX_VALUE) {
  			return Integer.MAX_VALUE;
  		}
  		
  		for (Message m : getMessageCollection()) {
  			occupancy += m.getSize();
  		}
  		
  		return this.getBufferSize() - occupancy;
  	}
  	
  	/**
  	 * Returns the host this router is in
  	 * @return The host object
  	 */
  	protected DTNHost getHost() {
  		return this.host;
  	}
  	
  	/**
  	 * Start sending a message to another host.
  	 * @param id Id of the message to send
  	 * @param to The host to send the message to
  	 */
  	public void sendMessage(String id, DTNHost to) {
  		Message m = getMessage(id);
  		Message m2;
  		if (m == null) throw new SimError("no message for id " +
  				id + " to send at " + this.host);
   
  		m2 = m.replicate();	// send a replicate of the message
  		to.receiveMessage(m2, this.host);
  	}
  	
  	/**
  	 * Requests for deliverable message from this router to be sent trough a
  	 * connection.
  	 * @param con The connection to send the messages trough
  	 * @return True if this router started a transfer, false if not
  	 */
  	public boolean requestDeliverableMessages(Connection con) {
  		return false; // default behavior is to not start -- subclasses override
  	}
  	
  	/**
  	 * Try to start receiving a message from another host.
  	 * @param m Message to put in the receiving buffer
  	 * @param from Who the message is from
  	 * @return Value zero if the node accepted the message (RCV_OK), value less
  	 * than zero if node rejected the message (e.g. DENIED_OLD), value bigger
  	 * than zero if the other node should try later (e.g. TRY_LATER_BUSY).
  	 */
  	public int receiveMessage(Message m, DTNHost from) {
  		Message newMessage = m.replicate();
  				
  		this.putToIncomingBuffer(newMessage, from);		
  		newMessage.addNodeOnPath(this.host);
  		
  		for (MessageListener ml : this.mListeners) {
  			ml.messageTransferStarted(newMessage, from, getHost());
  		}
  		
  		return RCV_OK; // superclass always accepts messages
  	}
  	
  	/**
  	 * This method should be called (on the receiving host) after a message
  	 * was successfully transferred. The transferred message is put to the
  	 * message buffer unless this host is the final recipient of the message.
  	 * @param id Id of the transferred message
  	 * @param from Host the message was from (previous hop)
  	 * @return The message that this host received
  	 */
  	public Message messageTransferred(String id, DTNHost from) {
  		Message incoming = removeFromIncomingBuffer(id, from);
  		boolean isFinalRecipient;
  		boolean isFirstDelivery; // is this first delivered instance of the msg
  		
  		
  		if (incoming == null) {
  			throw new SimError("No message with ID " + id + " in the incoming "+
  					"buffer of " + this.host);
  		}
  		
  		incoming.setReceiveTime(SimClock.getTime());
  		
  		// Pass the message to the application (if any) and get outgoing message
  		Message outgoing = incoming;
  		for (Application app : getApplications(incoming.getAppID())) {
  			// Note that the order of applications is significant
  			// since the next one gets the output of the previous.
  			outgoing = app.handle(outgoing, this.host);
  			if (outgoing == null) break; // Some app wanted to drop the message
  		}
  		
  		Message aMessage = (outgoing==null)?(incoming):(outgoing);
  		// If the application re-targets the message (changes 'to')
  		// then the message is not considered as 'delivered' to this host.
  		isFinalRecipient = aMessage.getTo() == this.host;
  		isFirstDelivery = isFinalRecipient &&
  		!isDeliveredMessage(aMessage);
  
  