// Medium.java
package support; // protocol support package
import java.util.Enumeration; // import Java enumerations
import java.util.HashSet; // import Java hash sets
import java.util.StringTokenizer; // import Java string tokenisers
import java.util.Vector; // import Java vectors
/**
This is the class for a protocol medium.
@author Iain A. Robin, Kenneth J. Turner
@version 1.0 (1st September 1999, IAR): initial version
1.4 (9th March 2006, KJT): updated for JDK 1.5, use of Swing
graphics, minor tidying
1.5 (27th July 2010, KJT): minor tidying; "isEmpty" check added
*/
public class Medium implements ProtocolEntity {
/** Control delivery and loss */
public final static int CONTROL_DELIVERY_LOSS = 0;
/** Control delivery (but not loss) */
public final static int CONTROL_DELIVERY = 1;
/** Control neither delivery nor loss - immediately deliver on reception */
public final static int CONTROL_NOTHING = 2;
/** PDUs in the medium */
protected Vector pdus;
/** Destination events */
protected Vector destinationEvents;
/** Medium control type */
private int mediumType = CONTROL_DELIVERY_LOSS;
/**
Constructor for a medium.
*/
public Medium() {
initialise();
}
/**
Get the name attribute of a medium.
@return The name value
*/
public String getName() {
return("Medium");
}
/**
Identify and return PDU currently on the medium with type and parameters
matching those in the given description.
@param description service description
@return corresponding PDU (or null if not found)
*/
protected PDU getMatchingPDU(String description) {
PDU pdu;
int seq = -1;
// extract name of source entity for this PDU:
int sourceStart = description.indexOf('[') + 1;
int sourceEnd = description.indexOf(']');
String sourceName = description.substring(sourceStart, sourceEnd);
// get type and parameters of PDU from action string
// PDU type starts after first space following "Deliver" or "Lose"
int typeStart = description.indexOf(' ') + 1;
int typeEnd = description.indexOf('(');
if (typeEnd < 0)
typeEnd = sourceStart - 2;
String type = description.substring(typeStart, typeEnd);
String[] params = getParams(description);
boolean noParams = params == null;
int paramCount = noParams ? 0 : params.length;
if (paramCount == 1)
seq = Integer.parseInt(params[0]); // sequence number
// try to match PDU type and seq with a PDU currently on channel
for (Enumeration enumeration = pdus.elements();
enumeration.hasMoreElements(); ) {
pdu = (PDU) enumeration.nextElement();
boolean matches = false;
if (pdu != null && pdu.type.equals(type)
&& pdu.getSource().getName().equals(sourceName)) {
if (noParams)
matches = true;
else if (seq >= 0)
matches = pdu.seq == seq;
}
if (matches)
return (pdu);
}
return (null); // no match found
}
/**
Extract comma-separated parameter strings within round brackets in string
argument.
@param description description
@return comma-separated parameters
*/
protected String[] getParams(String description) {
int openBracket = description.indexOf("(");
if (openBracket >= 0) { // check for opening bracket
int closeBracket = description.indexOf(")");
String s1 = description.substring(openBracket + 1, closeBracket);
StringTokenizer st = new StringTokenizer(s1, ",");
String[] result = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
result[i] = st.nextToken();
return (result);
}
return (null);
}
/**
Return a list of strings describing actions (services) that can be carried
out in the current state.
@return service descriptions
*/
public Vector getServices() {
Vector list = new Vector(); // initialise service list
HashSet pdusSent = // initialise list of sources
new HashSet();
for (Enumeration enumeration = pdus.elements(); // go through PDUs
enumeration.hasMoreElements(); ) {
PDU pdu = (PDU) enumeration.nextElement();// get next PDU
if (pdu != null) { // non-null PDU?
String name = pdu.getSource().getName();// get PDU source name
String description = // get PDU id and name
pdu.getID() + " [" + name + "]";
if (!pdusSent.contains(name)) { // no PDU with this source?
pdusSent.add(name); // add source to list
list.addElement( // add delivery service
"Deliver " + description + " - no loss");
}
if (mediumType == CONTROL_DELIVERY_LOSS)// delivery and loss controlled?
list.addElement( // add loss service
"Lose " + description + " - congestion/error");
}
}
return(list); // return service list
}
/**
Initialise the medium.
*/
public void initialise() {
pdus = new Vector();
destinationEvents = new Vector();
}
/**
Check if the medium is empty (i.e. has no PDUs in either direction).
@return true/false if the medium has no/has PDUs
*/
public boolean isEmpty() {
return(pdus == null || pdus.size() == 0); // return empty check
}
/**
Check if the medium is empty (i.e. has no PDUs with the given source name).
@param entityName entity name
@return true/false if the medium has no/has PDUs
*/
public boolean isEmpty(String entityName) {
for (Enumeration enumeration = pdus.elements(); // go through PDUs
enumeration.hasMoreElements(); ) {
PDU pdu = (PDU) enumeration.nextElement();// get next PDU
String name = pdu.getSource().getName(); // get PDU source name
if (name.equals(entityName)) // PDU with given source?
return(false); // immediately return false
}
return(true); // return true as no PDUs
}
/**
Carry out the action specified in the given string, and return a list of
protocol events resulting from this action
@param service service to perform
@return resulting protocol events
*/
public Vector performService(String service) {
Vector events = new Vector();
destinationEvents.removeAllElements();
PDU pdu = getMatchingPDU(service);
if (pdu != null) {
if (service.startsWith("Deliver")) {
transmitPDU(pdu, pdu.getDestination());
pdus.removeElement(pdu);
events.addElement(new ProtocolEvent(ProtocolEvent.RECEIVE, pdu));
}
else if (service.startsWith("Lose")) {
destinationEvents.removeAllElements();
pdus.removeElement(pdu);
events.addElement(new ProtocolEvent(ProtocolEvent.LOSE, pdu));
}
}
// tack on any events triggered by delivery of a message
// to its destination protocol entity
for (Enumeration enumeration = destinationEvents.elements();
enumeration.hasMoreElements(); )
events.addElement((ProtocolEvent) enumeration.nextElement());
return(events);
}
/**
Passively accept an incoming PDU and add to the list of those currently on
channel.
@param pdu PDU
@return resulting protocol events
*/
public Vector receivePDU(PDU pdu) {
Vector events = new Vector();
pdus.addElement(pdu);
if (mediumType == CONTROL_NOTHING) {
String s = "Deliver " + pdu.getID() +
" [" + pdu.getSource().getName() + "] - no loss";
events.addAll(performService(s));
transmitPDU(pdu, pdu.getDestination());
}
return(events); // return empty list of events
}
/**
Set user control over delivery and loss.
@param mediumType medium control type
*/
public void setMediumType(int mediumType) {
this.mediumType = mediumType;
}
/**
Deliver specified PDU to destination protocol entity, and get any protocol
events triggered by this delivery.
@param pdu PDU
@param destination PDU destination
*/
public void transmitPDU(PDU pdu, ProtocolEntity destination) {
destinationEvents = destination.receivePDU(pdu);
}
}