package com.jaivox.agent; import java.awt.Point; import java.util.*; import com.jaivox.util.Log; /** * MessageData parses messages between agents. The messages are in a * simplified Json format. * For example, a message may be like * {action: respond, from: sphinx, to: coordinator, session: 1234, * passcode: xyz, message: \"recognized words\"} */ public class MessageData { static String markers = " \t\r\n~`!@#$%^&*()+-=|\\;\"\'<>,.?/"; static String standardFields = "action from to session message"; /** * The key value pairs for the message, for example a message like * {action: respond, etc. has a key "action" with value "respond". */ Hashtable <String, String> kv; boolean Valid = false; /** * Create an empty MessageData. You can fill the keys and values in a * hashtable kv that can be later used to generate a string message. */ public MessageData () { kv = new Hashtable <String, String> (); } /** * Creates the key-value pairs from a message in a simple * Json-like format. Note we allow _ in keys. Also, : is not a marker. * This is due to the fact that : is used to indicate keys. There * is a way to allow : to be a separator of tokens and still recognize * keys, but that makes the parsing a bit too messy. @param message string in a simple Json format */ public MessageData (String message) { try { kv = new Hashtable <String, String> (); int start = message.indexOf ("{"); int end = message.lastIndexOf ("}"); // start = -1 is no problem if (end == -1) end = message.length (); String msg = fixColon (message, start+1, end); // Log.info ("MessageData:"+msg); // msg should not contain {}[] if (msg.indexOf ("{") != -1 || msg.indexOf ("[") != -1 || msg.indexOf ("}") != -1 || msg.indexOf ("]") != -1) { Log.info ("MessageData:"+message+" contains illegal embedded {}{}"); return; } StringTokenizer st = new StringTokenizer (msg, markers); Vector <Point> tokens = new Vector <Point> (); int last = 0; while (st.hasMoreTokens ()) { String token = st.nextToken (); if (token.endsWith (":")) { // find where it starts and ends int tstart = msg.indexOf (token, last); int tend = tstart + token.length (); Point p = new Point (tstart, tend); tokens.add (p); // Log.fine ("token "+token+" from "+tstart+" to "+tend); last = tend+1; } } int n = tokens.size (); int m = msg.length (); for (int i=0; i<n; i++) { Point p = tokens.elementAt (i); int tstart = p.x; int tend = p.y; int next = m; if (i < n-1) { Point q = tokens.elementAt (i+1); next = q.x; } String key = msg.substring (tstart, tend-1); String val = msg.substring (tend, next).trim (); // exactly what to trim? we will remove a comma since // that is usually added to values if (val.endsWith (",")) val = val.substring (0, val.length () -1); kv.put (key, val); } Valid = true; } catch (Exception e) { Log.severe (e.toString ()); e.printStackTrace (); } } String fixColon (String line, int start, int end) { String part = line.substring (start, end); StringBuffer sb = new StringBuffer (); int n = part.length (); for (int i=0; i<n; i++) { char c = part.charAt (i); sb.append (c); if (c == ':') { if (i < n-1) { char d = part.charAt (i+1); if (!Character.isWhitespace (d)) { sb.append (' '); continue; } } } } String changed = new String (sb); return changed; } /** * Creates a MessageData object from the given values. The message key * will be associated with "No Message", this can be changed later. @param action @param from @param to @param session */ public void createKeyValues (String action, String from, String to, String session) { setValue ("action", action); setValue ("from", from); setValue ("to", to); setValue ("session", session); setValue ("message", "\"No Message\""); } /** * Creates a MessageData object from the given values @param action @param from @param to @param session @param msg */ public void createKeyValuesAndMsg (String action, String from, String to, String session, String msg) { setValue ("action", action); setValue ("from", from); setValue ("to", to); setValue ("session", session); setValue ("message", msg); } /** * Creates a string based on information in this MessageData. The * resulting string can be sent via socket connections. @return message in the form of a string */ public String createMessage () { StringBuffer sb = new StringBuffer (); // fill some standard values sb.append ("{"); String act = getValue ("action"); if (act != null) { sb.append ("action: "); sb.append (act); sb.append (", "); } String from = getValue ("from"); if (from != null) { sb.append ("from: "); sb.append (from); sb.append (", "); } String to = getValue ("to"); if (to != null) { sb.append ("to: "); sb.append (to); sb.append (", "); } String session = getValue ("session"); if (session != null) { sb.append ("session: "); sb.append (session); sb.append (", "); } // add any other keyvalue pairs Set <String> keys = kv.keySet (); for (Iterator <String> it = keys.iterator (); it.hasNext (); ) { String key = it.next (); String val = kv.get (key); if (standardFields.indexOf (key) != -1) continue; sb.append (key); sb.append (": "); sb.append (val); sb.append (", "); } String message = getValue ("message"); if (message != null) { sb.append ("message: "); sb.append (message); } sb.append (" }"); String all = new String (sb); return all; } void showKeyValues () { Set <String> keys = kv.keySet (); for (Iterator <String> it = keys.iterator (); it.hasNext (); ) { String key = it.next (); String val = kv.get (key); System.out.println (key+" = "+val); } } /** * Get the value for a key in the message. For example, if you want to know * the name of the agent that sent the message, you can get the value of * the key "from". @param key @return */ public String getValue (String key) { return kv.get (key); } /** * Set the value for a key. This can be used for example to set the message * text in a MessageData object that has been created already. @param key @param value */ public void setValue (String key, String value) { kv.put (key, value); } /** * Check whether the message is valid. This is a way to handle messages * that may contain parse errors. @return */ public boolean isValid () { return Valid; } }