// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/graphicLoader/netmap/NetMapGraphicLoader.java,v $
// $RCSfile: NetMapGraphicLoader.java,v $
// $Revision: 1.9 $
// $Date: 2005/08/09 19:09:55 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.graphicLoader.netmap;
import java.awt.Component;
import java.net.InetAddress;
import java.util.Enumeration;
import java.util.Properties;
import com.bbn.openmap.Layer;
import com.bbn.openmap.graphicLoader.MMLGraphicLoader;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMLine;
import com.bbn.openmap.plugin.PlugIn;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PropUtils;
/**
* The NetMapGraphicLoader is a component that can listen to a NetMapConnector,
* receive and interpret NetMapEvents, and draw the resulting network on the
* map. The NetMapConnector does all the heavy work, the NetMapGraphicLoader
* serves as an interface to get OMGraphics on the map.
* <P>
*
* The easiest way to use this class is to create it with the NetMapConnector
* and add it to the LayerHandler or MapHandler. If the NetMapConnector is going
* to be created by another object for application design reasons, just create a
* NetMapConnectionHandler and add it to the MapHandler, and then add the
* NetMapConnector to the MapHandler, too. The NetMapConnectionHandler will
* create a NetMapGraphicLoader for the NetMapConnector. Make sure a
* GraphicLoaderConnector is also in the MapHandler, too, because it will create
* a GraphicLoaderPlugIn/PlugInLayer for the NetMapGraphicLoader.
*/
public class NetMapGraphicLoader extends MMLGraphicLoader implements
NetMapListener, NetMapConstants {
/** The list that gets sent to the GraphicLoaderPlugIn. */
protected OMGraphicList omList = null;
/**
* The component that provides controls to the NetMapReader, which in turn
* reads the server stream.
*/
private NetMapConnector connector = null;
/**
* The cached list of nodes, created from the event properties.
*/
private NodeCache nodeList = new NodeCache();
/**
* The cached list of links between nodes, created from the event
* properties.
*/
private LineCache lineList = new LineCache();
protected boolean DEBUG = false;
protected String localhostIP = null;
/**
* Constructor for the NetMapGraphicLoader, you still have to set the
* NetMapConnector.
*/
public NetMapGraphicLoader() {
DEBUG = Debug.debugging("netmap");
this.nodeList = new NodeCache();
this.lineList = new LineCache();
try {
localhostIP = InetAddress.getLocalHost().getHostAddress();
if (DEBUG) {
Debug.output("NetMapGraphicLoader running on: " + localhostIP);
}
} catch (java.net.UnknownHostException uhe) {
localhostIP = null;
}
}
/**
* Constructor for the NetMapGraphicLoader that sets the NetMapConnector.
*
* @param nmc the NetMapConnector to listen to.
*/
public NetMapGraphicLoader(NetMapConnector nmc) {
this();
setNetMapConnector(nmc);
}
/**
* Set the NetMapConnector to listen to. This method will add the
* NetMapGraphicLoader to the NetMapConnector as a NetMapListener. If there
* is already a NetMapConnector set in this NetMapGraphicLoader, then this
* method will disconnect from the current NetMapConnector, and reconnect
* with the new one if it isn't null.
*/
public void setNetMapConnector(NetMapConnector nmc) {
if (connector != null) {
connector.removeNetMapListener(this);
}
connector = nmc;
if (connector != null) {
connector.addNetMapListener(this);
}
}
/**
* Get the current NetMapConnector.
*/
public NetMapConnector getNetMapConnector() {
return connector;
}
/**
* NetMapListener method, called by the NetMapConnector.
*/
public void catchEvent(NetMapEvent nme) {
// for now, print to debug, later, create OMGraphics out of
// it.
if (DEBUG) {
Debug.output(nme.getProperties().toString());
}
processEventProperties(nme.getProperties());
}
private void setNodePositionFromEventProps(Node node, Properties eventProps) {
String geo = eventProps.getProperty(LAT_FIELD);
if (geo != null) {
try {
node.setLat(Float.parseFloat(geo));
node.posLat = geo;
} catch (Exception e) {
Debug.error("NetMapGraphicLoader: " + geo
+ " is not a valid latitude value.");
}
}
geo = eventProps.getProperty(LON_FIELD);
if (geo != null) {
try {
node.setLon(Float.parseFloat(geo));
node.posLon = geo;
} catch (Exception e) {
Debug.error("NetMapGraphicLoader: " + geo
+ " is not a valid longitude value.");
}
}
}
/**
* Process a NetMapEvent Properties object, which means that a Properties
* object, representing an event from the NetMap server, is evaluated and
* used to modify the NodeCache and LineCache accordingly.
*
* @param eventProps the properties from a NetMapEvent.
*/
protected void processEventProperties(Properties eventProps) {
int status;
Node node;
Line line;
String cmd = eventProps.getProperty(COMMAND_FIELD);
// Used for many (if not all commands, might as well do this
// here.
int index = PropUtils.intFromProperties(eventProps,
INDEX_FIELD,
ERROR_VALUE_INT);
if (cmd.equals(NODE_OBJECT)) {
int shape = PropUtils.intFromProperties(eventProps,
SHAPE_FIELD,
ERROR_VALUE_INT);
if (index == ERROR_VALUE_INT) {
Debug.error("NMGL: error parsing object index for node.");
return;
}
node = nodeList.get(index);
if (shape == 11) {
String icon = eventProps.getProperty(ICON_FIELD);
if (DEBUG)
Debug.output("NetMapReader: jimage " + icon);
}
if (shape == NODE_DELETE) { // Delete
// While we're at it, we might as well delete all the
// "Line" entries that terminate on this node as
// well...
if (node != null) {
lineList.del(node);
nodeList.del(node);
}
} else if (shape == NODE_MOVE && node != null) { // move
setNodePositionFromEventProps(node, eventProps);
lineList.move(node);
node.setTime(Double.parseDouble(eventProps.getProperty(TIME_FIELD,
"0")));
} else {
// Define a new entry if "shape" is anything else,
// including NODE_MOVE without a valid node.
/*
* int posX = LayerUtils.intFromProperties(eventProps,
* POSX_FIELD, ERROR_VALUE_INT);
*
* int posY = LayerUtils.intFromProperties(eventProps,
* POSY_FIELD, ERROR_VALUE_INT);
*
* int width = LayerUtils.intFromProperties(eventProps,
* WIDTH_FIELD, ERROR_VALUE_INT);
*
* int height = LayerUtils.intFromProperties(eventProps,
* HEIGHT_FIELD, ERROR_VALUE_INT);
*/
status = PropUtils.intFromProperties(eventProps,
STATUS_FIELD,
0);
int menu = PropUtils.intFromProperties(eventProps,
MENU_FIELD,
0);
/*
* int joffset = LayerUtils.intFromProperties(eventProps,
* JOFFSET_FIELD, ERROR_VALUE_INT);
*/
String label = eventProps.getProperty(LABEL_FIELD);
if (label == null) {
// This label misdirection is temporary...
label = eventProps.getProperty(INDEX_FIELD);
}
String ip = eventProps.getProperty(IP_FIELD);
/*
* float elevation = LayerUtils.floatFromProperties(eventProps,
* ELEVATION_FIELD, 0f);
*/
boolean isLocalhost = false;
if (ip != null && localhostIP != null) {
isLocalhost = localhostIP.equals(ip);
if (DEBUG) {
Debug.output("NetMapGraphicLoader displaying a node running on the localhost: "
+ localhostIP);
}
}
if (DEBUG) {
Debug.output("Creating node (" + label + ")");
}
try {
if (shape != ERROR_VALUE_INT) {
node = nodeList.add(label, index, shape, menu, status);
node.setLocalhost(isLocalhost);
}
} catch (Exception e) {
Debug.error("NMGL: error creating node");
}
setNodePositionFromEventProps(node, eventProps);
}
} else if (cmd.equals(NODE_OBJECT_STATUS)) {
if (index == ERROR_VALUE_INT) {
Debug.error("NMGL: error parsing object index for status update.");
return;
}
node = nodeList.get(index);
if (node != null) {
status = PropUtils.intFromProperties(eventProps,
STATUS_FIELD,
ERROR_VALUE_INT);
if (status != ERROR_VALUE_INT) {
node.setStatus(status);
}
}
} else if (cmd.equals(LINK_OBJECT_STATUS)) {
if (index == ERROR_VALUE_INT) {
Debug.error("NMGL: error parsing line index for status update.");
return;
}
line = lineList.get(index);
if (line != null) {
status = PropUtils.intFromProperties(eventProps,
STATUS_FIELD,
ERROR_VALUE_INT);
if (status != ERROR_VALUE_INT) {
line.setStatus(status);
}
}
} else if (cmd.equals(LINK_OBJECT)) {
if (index == ERROR_VALUE_INT) {
Debug.error("NMGL: error parsing line index for link.");
return;
}
line = lineList.get(index);
int shape = PropUtils.intFromProperties(eventProps,
SHAPE_FIELD,
ERROR_VALUE_INT);
if (shape == NODE_DELETE) {
lineList.del(index);
} else {
status = PropUtils.intFromProperties(eventProps,
STATUS_FIELD,
0);
int node1 = PropUtils.intFromProperties(eventProps,
LINK_NODE1_FIELD,
ERROR_VALUE_INT);
int node2 = PropUtils.intFromProperties(eventProps,
LINK_NODE2_FIELD,
ERROR_VALUE_INT);
if (node1 == ERROR_VALUE_INT || node2 == ERROR_VALUE_INT) {
Debug.error("NMGL: error parsing node indexes for link");
return;
}
Node n1 = nodeList.get(node1);
Node n2 = nodeList.get(node2);
if (n1 != null && n2 != null) {
lineList.add(String.valueOf(index),
index,
shape,
status,
n1,
n2);
} else {
if (DEBUG) {
Debug.output("NetMapGraphicLoader: can't create lobj, nodes are undefined");
}
}
}
} else if (cmd.equals(REFRESH) || cmd.equals(UPDATE)) {
// manageGraphics();
} else if (cmd.equals(CLEAR)) {
if (nodeList != null) {
nodeList.flush();
}
if (lineList != null) {
lineList.flush();
}
// manageGraphics();
} else {
if (DEBUG) {
Debug.output("NMGL: received unused event: "
+ eventProps.toString());
}
}
manageGraphics();
}
/**
* Internal method used to create a single OMGraphicList from the NodeCache
* and the LineCache.
*/
protected OMGraphicList getOMList() {
/*
* By creating a new list, we avoid ConcurrentModificationExceptions
* within the PlugInLayer, and within the GraphicLoaderPlugIn during
* generate().
*/
omList = new OMGraphicList();
if (nodeList != null) {
Enumeration list = nodeList.elements();
while ((list != null) && list.hasMoreElements()) {
Node point = (Node) list.nextElement();
point.setMatted(point.isLocalhost());
omList.add(point);
}
}
if (lineList != null) {
Enumeration list = lineList.elements();
while ((list != null) && list.hasMoreElements()) {
Line line = (Line) list.nextElement();
if (line == null)
continue;
line.setPos();
omList.add((OMLine) line);
}
}
if (DEBUG) {
int size = omList.size();
Debug.output("NMGL.getOMList(): created list with " + size
+ (size == 1 ? " graphic." : " graphics."));
}
return omList;
}
/**
* Called by the GraphicLoaderPlugIn, the GUI is provided from the
* NetMapConnector.
*/
public Component getGUI() {
if (connector != null) {
return connector.getGUI();
} else
return null;
}
/**
* Needed to fill in a GUI with a receiver's name, to enable the user to
* send a graphic to a specific object. Should be a pretty name, suitable to
* let a user know what it is.
*/
public String getName() {
return "NetMap";
}
/**
* The inherited AbstractGraphicLoader method, that sends the current
* OMGrapicList to the receiver.
*/
public void manageGraphics() {
// receiver is inherited from
// com.bbn.openmap.graphicLoader.MMLGraphicLoader/AbstractGraphicLoader.
if (receiver != null) {
if (DEBUG) {
Debug.output("NetMapConnector.update: Updating graphics.");
}
receiver.setList(getOMList());
} else {
if (DEBUG) {
Debug.output("NetMapConnector.update: no receiver to notify.");
}
}
}
protected boolean toolTipUp = false;
/**
* Invoked when the mouse button has been moved on a component (with no
* buttons down).
*
* @param e MouseEvent
* @return true if the listener was able to process the event.
*/
public boolean mouseMoved(java.awt.event.MouseEvent e) {
if (receiver instanceof PlugIn && omList != null) {
OMGraphic graphic = omList.getContains(e.getX(), e.getY());
String label = null;
if (graphic instanceof Node) {
label = ((Node) graphic).getLabel();
// } else if (graphic instanceof Line) {
// label = ((Line)graphic).getLabel();
}
if (receiver instanceof PlugIn) {
Component comp = ((PlugIn) receiver).getComponent();
if (comp instanceof Layer) {
if (graphic != null && label != null) {
((Layer) comp).fireRequestToolTip("Node " + label);
toolTipUp = true;
} else if (toolTipUp) {
((Layer) comp).fireHideToolTip();
toolTipUp = false;
}
return true;
}
}
}
return false;
}
}