You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

595 lines
19 KiB
Java

// TimeSequenceDiagram.java
package simulator; // simulator package
import java.awt.*; // import Java AWT classes
import java.util.*; // import Java utility classes
import support.*; // import Jasper support classes
/**
This is the class for a time sequence diagram.
@author Iain A. Robin, Kenneth J. Turner
@version 1.0 (1st September 1999, IAR): initial version <br/>
<br/> 1.4 (9th March 2006, KJT): updated for JDK 1.5, use of Swing
graphics, minor tidying
<br/> 1.5 (27th July 2010, KJT): minor tidying; vertical space before
DELIVER; vertical space before COMMENT not just for first
event; addition of TRAVERSE for events that span columns
*/
public class TimeSequenceDiagram extends Canvas {
/** Vertical extent of primitive */
private final static int primitiveHeight = 10;
/** Width of primitive arrow */
private final static int arrowWidth = 30;
/** Top/left insert of diagram */
private final static int inset = 15;
/** Page left-hand margin */
private final static int margin = 15; //
/** Maximum printing height */
private final static int maxHeight = 800;
/** Max symbols to print */
private final static int maxSymbols = 64;
/** Diagram/page header font */
public final static Font headerFont = new Font("Sans serif", Font.BOLD, 14);
/** Page footer font */
public final static Font footerFont = new Font("Sans serif", Font.BOLD, 14);
/** Diagram label font */
public final static Font labelFont = new Font("Sans serif", Font.PLAIN, 13);
/** Mid green comments */
public final static Color commentColour = new Color(0, 128, 0);
/** Red last symbol */
public final static Color lastColour = Color.red;
/** Tan timeouts */
public final static Color timeoutColour = new Color(128, 64, 48);
/** Tan timeouts */
private String[] columnHeaders;
/** Header text height */
private int headerTextHeight;
/** Column width in pixels */
private int columnWidth;
/** Number of columns */
private int columns;
/** Y-coordinate for symbol area top */
private int topOfBlock;
/** Default symbol height */
private int defaultSymbolHeight;
/** Vertical scroll position */
private int verticalPosition;
/** List of symbols */
private Vector<TSDSymbol> symbols;
/** List of protocol entities */
private Vector<ProtocolEntity> entities;
/** Printing starting */
private boolean starting;
/** Printing finished */
private boolean stopped;
/** Y-coordinate for initial symbol */
private int initialHeight;
/** Medium column number */
private int mediumColumn;
/** Last column number */
private int lastColumn;
/** Y-coordinate for start of SEND arrow */
private int start = 0;
/** Current printing top */
private int currentHeight;
/** Current page number */
private int page;
/**
Constructor for the TimeSequenceDiagram object
@param protocol Parameter
*/
public TimeSequenceDiagram(Protocol protocol) {
setBackground(Color.white);
entities = protocol.getEntities();
mediumColumn = protocol.getMediumColumn();
columns = entities.size();
columnHeaders = new String[columns];
for (int i = 0; i < columns; i++)
columnHeaders[i] = ((ProtocolEntity) entities.elementAt(i)).getName();
symbols = new Vector<TSDSymbol>();
verticalPosition = 0;
lastColumn = -1;
starting = true;
}
/**
Adds a feature to the CommentSymbol attribute of the TimeSequenceDiagram
object.
@param comment comment text
@param lToR true = left-to-right
@param sourceCol source column
*/
private void addCommentSymbol(String comment, boolean lToR, int sourceCol) {
CommentSymbol st =
new CommentSymbol(null, comment, lToR, sourceCol, topOfBlock);
addSymbol(st);
}
// determine types of protocol events
// and add appropriate symbols to diagram
/**
Add events to time sequence diagram.
@param events event list
*/
public void addEvents(Vector<ProtocolEvent> events) {
int eventNo = 0;
int source = 0;
int destination = 0;
for (Enumeration e = events.elements(); e.hasMoreElements(); ) {
ProtocolEvent event = (ProtocolEvent) e.nextElement();
PDU pdu = null;
if (event != null) {
int eventType = event.getType();
pdu = event.getPDU();
if (pdu != null) {
source = entities.indexOf(pdu.getSource());
destination = entities.indexOf(pdu.getDestination());
boolean lToR = destination > source;
int col = lToR ? source + 1 : source - 1;
String label = pdu.getLabel();
int transmissionHeight = defaultSymbolHeight;
switch (eventType) {
case ProtocolEvent.SEND:
addSpace(source);
addServicePrimitive(
event, lToR, col, topOfBlock, label, ServicePrimitive.TAIL);
break;
case ProtocolEvent.TRANSMIT:
addSpace(source);
addServicePrimitive(
event, lToR, col, topOfBlock, label, ServicePrimitive.TAIL);
break;
case ProtocolEvent.RECEIVE:
start = getTransmissionStart(pdu);
if (start != topOfBlock)
transmissionHeight = Math.max(
defaultSymbolHeight, topOfBlock - start + primitiveHeight);
if (destination == lastColumn)
transmissionHeight += 7;
addSuccessfulTransmission(
event, lToR, mediumColumn, start, transmissionHeight);
addServicePrimitive(
event, lToR, destination, start + transmissionHeight,
label, ServicePrimitive.HEAD);
lastColumn = destination;
break;
case ProtocolEvent.DELIVER:
addSpace(source);
addServicePrimitive(
event, lToR, destination, topOfBlock, label,
ServicePrimitive.HEAD);
break;
case ProtocolEvent.FRAGMENT:
if (eventNo == 0)
start = getTransmissionStart(pdu);
if (start != topOfBlock)
transmissionHeight = Math.max(
defaultSymbolHeight, topOfBlock - start + primitiveHeight);
if (destination == lastColumn)
transmissionHeight += 7;
addSuccessfulTransmission(
event, lToR, mediumColumn, start, transmissionHeight);
addServicePrimitive(
event, lToR, destination, start + transmissionHeight,
label, ServicePrimitive.HEAD);
lastColumn = destination;
break;
case ProtocolEvent.LOSE:
if (eventNo == 0)
start = getTransmissionStart(pdu);
if (start != topOfBlock)
transmissionHeight = Math.max(
defaultSymbolHeight, topOfBlock - start + primitiveHeight);
if (destination == lastColumn)
transmissionHeight += 7;
addFailedTransmission(
event, lToR, mediumColumn, start, transmissionHeight);
lastColumn = destination;
break;
case ProtocolEvent.TIMEOUT:
if (eventNo == 0)
addSpace(source);
addSenderTimeout(event, lToR, source);
addServicePrimitive(
event, lToR, col, topOfBlock, label, ServicePrimitive.TAIL);
break;
case ProtocolEvent.TRAVERSE:
source++; // incr. source column
int columns = destination - source; // set number of columns
start = getTransmissionStart(pdu); // get y for start
if (start != topOfBlock) // not top of block?
transmissionHeight = Math.max( // set trans. height
defaultSymbolHeight, topOfBlock - start + primitiveHeight);
if (columns > 1)
transmissionHeight =
columns*(transmissionHeight + primitiveHeight);
if (destination == lastColumn) // last column?
transmissionHeight += 7; // allow extra space
addTraverseTransmission( // add traverse trans.
event, lToR, source, columns, start, transmissionHeight);
addServicePrimitive( // add service prim.
event, lToR, destination, start + transmissionHeight,
label, ServicePrimitive.HEAD);
lastColumn = destination; // set last column
break;
}
}
if (eventType == ProtocolEvent.COMMENT) {
// if (eventNo == 0)
addSpace(source);
int col = entities.indexOf(event.getTarget());
addCommentSymbol(event.getComment(), col < mediumColumn, col);
}
}
eventNo++;
}
}
/**
Adds a failed transmission to a time sequence diagram.
@param event protocol event
@param lToR true = left-to-right
@param mediumColumn medium column
@param top top of service primitive
@param height height of successful transmission
*/
private void addFailedTransmission(
ProtocolEvent event, boolean lToR, int mediumColumn, int top, int height) {
FailedTransmission t =
new FailedTransmission(event, lToR, mediumColumn, top, height);
addSymbol(t);
}
/**
Adds a feature to the SenderTimeout attribute of the TimeSequenceDiagram
object.
@param event protocol event
@param lToR true = left-to-right
@param sourceCol source column
*/
private void addSenderTimeout(
ProtocolEvent event, boolean lToR, int sourceCol) {
CommentSymbol st =
new CommentSymbol(event, "timeout", lToR, sourceCol, topOfBlock);
addSymbol(st);
}
/**
Adds a feature to the ServicePrimitive attribute of the TimeSequenceDiagram
object.
@param event protocol event
@param lToR true = left-to-right
@param destination destination column
@param top top of service primitive
@param label label for service primitive
@param pos position for label
*/
private void addServicePrimitive(
ProtocolEvent event, boolean lToR, int destination, int top,
String label, int pos) {
ServicePrimitive sp =
new ServicePrimitive(event, lToR, destination, top, primitiveHeight);
sp.setLabel(label, pos);
addSymbol(sp);
}
/**
Adds a feature to the Space attribute of the TimeSequenceDiagram object
@param col column
*/
private void addSpace(int col) {
addSymbol(new SpaceFiller(col, topOfBlock));
}
/**
Adds a successful transmission to a time sequence diagram.
@param event protocol event
@param lToR true = left-to-right
@param mediumColumn medium column
@param top top of service primitive
@param height height of successful transmission
*/
private void addSuccessfulTransmission(
ProtocolEvent event, boolean lToR, int mediumColumn, int top, int height) {
SuccessfulTransmission t =
new SuccessfulTransmission(event, lToR, mediumColumn, top, height);
addSymbol(t);
}
/**
Adds a feature to the Symbol attribute of the TimeSequenceDiagram object
@param symbol The feature to be added to the Symbol attribute
*/
private void addSymbol(TSDSymbol symbol) {
int height = getSize().height;
int symbolHeight = symbol.getHeight();
int top = symbol.getTop();
topOfBlock = top + symbolHeight;
symbols.addElement(symbol);
if (topOfBlock + symbolHeight + 2 * inset >= height) {
int width = getSize().width;
setSize(width, topOfBlock + symbolHeight + 2 * inset);
}
verticalPosition = getSize().height - initialHeight;
repaint();
}
/**
Adds a traverse transmission to a time sequence diagram.
@param event protocol event
@param lToR true = left-to-right
@param source source column
@param columns columns
@param top top of service primitive
@param height height of traverse transmission
*/
private void addTraverseTransmission(
ProtocolEvent event, boolean lToR, int source, int columns, int top,
int height) {
TraverseTransmission t =
new TraverseTransmission(event, lToR, source, columns, top, height);
addSymbol(t);
}
/**
Remove everything from the time sequence diagram.
*/
public void clear() {
symbols.removeAllElements();
verticalPosition = 0;
setSize(getSize().width, initialHeight);
topOfBlock = 3 * inset + headerTextHeight;
repaint();
}
/**
Draw the columns of the time sequence diagram.
@param g graphics object
@param margin column margin
@param width column width
@param height column height
*/
private void drawColumns(Graphics g, int margin, int width, int height) {
int headerWidth;
int columnCentre;
g.setFont(headerFont); // column headings
FontMetrics fm = getFontMetrics(headerFont);
headerTextHeight = fm.getHeight();
for (int c = 0; c < columns; c++) {
columnCentre = margin + inset + (2 * c + 1) * columnWidth / 2;
headerWidth = fm.stringWidth(columnHeaders[c]);
g.drawString(
columnHeaders[c], columnCentre - headerWidth / 2,
inset + fm.getAscent());
}
g.drawLine(
margin + inset, inset + headerTextHeight,
margin + width - inset, inset + headerTextHeight);
for (int c = 1; c < columns; c++) // column separators
g.drawLine(
margin + inset + c * columnWidth, inset,
margin + inset + c * columnWidth, height - inset);
}
/**
Gets the transmissionStart attribute of the TimeSequenceDiagram object.
@param pdu PDU associated with the TRANSMIT event
@return Y-coordinate of the end of the SEND event arrow from
which the corresponding TRANSMIT event symbol will start
*/
private int getTransmissionStart(PDU pdu) {
int y = topOfBlock;
for (Enumeration e = symbols.elements(); e.hasMoreElements(); ) {
TSDSymbol symbol = (TSDSymbol) e.nextElement();
ProtocolEvent event = symbol.getEvent();
if (event != null) {
PDU pduSent = event.getPDU();
if (pdu != null && pduSent.matches(pdu))
y = symbol.getTop() + symbol.getHeight();
}
}
return(y);
}
/**
Get the verticalPosition attribute of the TimeSequenceDiagram object.
@return vertical position
*/
public int getVerticalPosition() {
return(verticalPosition);
}
/**
Paint the time sequene diagram.
@param g graphics object
*/
public void paint(Graphics g) {
TSDSymbol symbol;
int width = getSize().width;
int height = getSize().height;
columnWidth = (width - 2 * inset) / columns;
TSDSymbol.setParameters(inset, columnWidth, arrowWidth);
defaultSymbolHeight =
(columnWidth - arrowWidth) * primitiveHeight / arrowWidth;
drawColumns(g, 0, width, height);
if (starting) {
initialHeight = height;
topOfBlock = 3 * inset + headerTextHeight;
starting = false;
}
for (Enumeration e = symbols.elements(); e.hasMoreElements(); ) {
symbol = (TSDSymbol) e.nextElement();
if (!e.hasMoreElements()) // colour last symbol
g.setColor(lastColour);
symbol.draw(g);
}
}
/**
Print the time sequence diagram.
@param g graphcs object
*/
public void print(Graphics g) {
TSDSymbol symbol = null; // current symbol
TSDSymbol symbolBuff[] = // symbols to print
new TSDSymbol[maxSymbols];
int nextSymbol = 0; // next symbol position
int sym; // symbol count
int width = getSize().width; // diagram width
int height = getSize().height; // diagram height
int offset; // offset from page top
int nextHeight; // next printing top
int nullHeight = 0; // height at last null
if (starting) { // first page?
starting = false; // note not first page
stopped = false; // printing not finished
offset = 0; // set first page offset
currentHeight = 0; // set symbol height
page = 0; // set page number
System.out.print("Printing " + // start page listing
ProtocolSimulator.protocolType + " page");
}
else // not first page
offset = // set later page offset
3 * inset + headerTextHeight - currentHeight;
nextHeight = currentHeight + maxHeight; // next printing top
columnWidth = (width - 2 * inset) / columns; // set column width
TSDSymbol.setParameters( // set symbol details
margin + inset, columnWidth, arrowWidth);
defaultSymbolHeight = // set transm. height
(columnWidth - arrowWidth) * primitiveHeight / arrowWidth;
drawColumns(g, margin, width, height); // draw column headers
System.err.print(" " + ++page); // print page number
for (Enumeration e = symbols.elements(); // go through symbols
e.hasMoreElements(); ) {
symbol = (TSDSymbol) e.nextElement(); // get next symbol
if (symbol.getClass() == SpaceFiller.class) { // symbol gap?
if (symbol.top + symbol.height >= nextHeight) { // past end of page?
printFooter(g); // print page footer
currentHeight = nullHeight; // set next page top
return; // stop rendering
}
else { // still within page
nullHeight = symbol.top; // save last null height
for (sym = 0; sym < nextSymbol; sym++) { // go through symbols
symbol = symbolBuff[sym]; // get stored symbol
symbol.top += offset; // move symbol up page
symbol.draw(g); // draw symbol
symbol.top -= offset; // move symbol back
}
nextSymbol = 0; // back to buffer start
}
}
else if (symbol.top >= currentHeight) { // inside current page?
if (nextSymbol < maxSymbols) // room for symbol?
symbolBuff[nextSymbol++] = symbol; // store symbol
else { // no room for symbol
System.err.println( // report error
"Cannot print more than " + maxSymbols + " symbols");
System.exit(1); // leave programme
}
}
}
if (symbol != null && // non-null symbol?
symbol.top >= nextHeight) // past end of page?
currentHeight = nullHeight; // set next page top
else { // still within page
for (sym = 0; sym < nextSymbol; sym++) { // go through symbols
symbol = symbolBuff[sym]; // get stored symbol
symbol.top += offset; // move symbol up page
symbol.draw(g); // draw symbol
symbol.top -= offset; // move symbol back
}
System.err.println(); // end page listing
stopped = true; // printing finished
}
printFooter(g); // print page footer
}
/**
Print footer of time sequence diagram.
@param g graphics object
*/
public void printFooter(Graphics g) { // print page footer
String footer = // set page footer
ProtocolSimulator.protocolType + " Page " + page;
FontMetrics fm = g.getFontMetrics(footerFont); // get font metrics
int diagWidth = getSize().width; // diagram width
int footerWidth = fm.stringWidth(footer); // get footer width
g.drawString(ProtocolSimulator.protocolType + // print page footer
" Page " + page,
margin + (diagWidth - footerWidth) / 2, inset + maxHeight);
}
/**
Note printing as started.
*/
public void printStart() { // start printing
starting = true; // set not started
}
/**
Check if printing has stopped.
@return true/false if printing has/has not stopped
*/
public boolean printStopped() { // return print status
return((stopped)); // get printing stopped
}
}