package org.pileus.spades;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Message
{
/* Enumerations */
- enum Type {
+ static enum Type {
OTHER, // Unknown message type
+ JOIN, // Join channel
+ PART, // Leave channel
PRIVMSG, // Private message
TOPIC, // Display current topic
NAMES, // Display user names
AUTHFAIL, // Authentication failed
};
- enum How {
+ static enum How {
OTHER, // Unknown message type
CHANNEL, // Normal message to a channel
MENTION, // User was mentioned in message text
DIRECT, // Message directed towards user
- PRIVMSG // Private message to user only
+ PRIVMSG, // Private message to user only
+ SENT // Message was sent by the user
+ };
+
+ /* Structures */
+ static class Color {
+ public int code;
+ public String hex;
+ public String name;
+ public int color;
+
+ public Color(int code, String hex, String name)
+ {
+ this.code = code;
+ this.hex = hex;
+ this.name = name;
+ this.color = Os.getColor(hex);
+ }
+ };
+
+ static class Format implements Cloneable {
+ public boolean bold;
+ public boolean italic;
+ public boolean strike;
+ public boolean underline;
+ public boolean reverse;
+ public Color fg;
+ public Color bg;
+ public String txt;
+
+ public Format clone()
+ {
+ try {
+ return (Format)super.clone();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public String toString()
+ {
+ return (fg!=null ? fg.hex : "xxxxxx") + ":" +
+ (bg!=null ? bg.hex : "xxxxxx") + ":" +
+ (bold ? "b" : "-" ) +
+ (italic ? "i" : "-" ) +
+ (strike ? "s" : "-" ) +
+ (underline ? "u" : "-" ) +
+ (reverse ? "r" : "-" ) + ":" +
+ "[" + txt + "]";
+ }
+ };
+
+ /* Colors */
+ private static final Color colors[] = {
+ new Color(0x0, "FFFFFF", "White"),
+ new Color(0x1, "000000", "Black"),
+ new Color(0x2, "000080", "Navy Blue"),
+ new Color(0x3, "008000", "Green"),
+ new Color(0x4, "FF0000", "Red"),
+ new Color(0x5, "804040", "Brown"),
+ new Color(0x6, "8000FF", "Purple"),
+ new Color(0x7, "808000", "Olive"),
+ new Color(0x8, "FFFF00", "Yellow"),
+ new Color(0x9, "00FF00", "Lime Green"),
+ new Color(0xA, "008080", "Teal"),
+ new Color(0xB, "00FFFF", "Aqua Light"),
+ new Color(0xC, "0000FF", "Royal Blue"),
+ new Color(0xD, "FF00FF", "Hot Pink"),
+ new Color(0xE, "808080", "Dark Gray"),
+ new Color(0xF, "C0C0C0", "Light Gray"),
};
/* Constants */
- private final String reMsg = "(:([^ ]+) +)?(([A-Z0-9]+) +)(([^ ]+)[= ]+)?(([^: ]+) *)?(:(.*))?";
- private final String reFrom = "([^! ]+)!.*";
- private final String reTo = "(([^ :,]*)[:,] *)?(.*)";
+ private static final String reMsg = "(:([^ ]+) +)?(([A-Z0-9]+) +)(([^ ]+)[= ]+)?(([^: ]+) *)?(:(.*))?";
+ private static final String reFrom = "([^! ]+)!.*";
+ private static final String reTo = "(([^ :,]*)[:,] *)?(.*)";
+ private static final String reCmd = "/([a-z]+)( +(.*))?";
+
+ private static final Pattern ptMsg = Pattern.compile(reMsg);
+ private static final Pattern ptFrom = Pattern.compile(reFrom);
+ private static final Pattern ptTo = Pattern.compile(reTo);
+ private static final Pattern ptCmd = Pattern.compile(reCmd);
- private static Pattern ptMsg = null;
- private static Pattern ptFrom = null;
- private static Pattern ptTo = null;
+ private static final String cReNum = "1[0-5]|0?[0-9]";
+ private static final String cReFmt = "[\\002\\011\\017\\023\\025\\026\\037]";
+ private static final String cReClr = "[\\003\\013]("+cReNum+")?(,("+cReNum+"))?";
+ private static final String cRegex = cReFmt + "|" + cReClr + "|$";
+ private static final Pattern cPtrn = Pattern.compile(cRegex);
/* Public data */
public Date time = null;
public String to = "";
public String txt = "";
- /* Private methods */
- private String notnull(String string)
+ public List<Format> parts = new ArrayList<Format>();
+
+ /* Static methods */
+ private static Color getColor(String code)
{
- return string == null ? "" : string;
+ if (code == null)
+ return null;
+ int i = Integer.parseInt(code);
+ if (i >= 0 && i < Message.colors.length)
+ return Message.colors[i];
+ return null;
}
/* Public Methods */
public Message(String dst, String from, String msg)
{
+ this.time = new Date();
+ this.how = How.SENT;
+ this.from = from;
+
+ if (this.parseSlash(msg))
+ return;
+
+ this.type = Type.PRIVMSG;
this.cmd = "PRIVMSG";
this.dst = dst;
- this.from = from;
this.msg = msg;
this.line = this.cmd + " " + this.dst + " :" + this.msg;
}
public Message(String line, String name)
{
- // Initialize regexes
- if (ptMsg == null) ptMsg = Pattern.compile(reMsg);
- if (ptFrom == null) ptFrom = Pattern.compile(reFrom);
- if (ptTo == null) ptTo = Pattern.compile(reTo);
-
- // Set time stamp
this.time = new Date();
+ this.parseText(line);
+ this.parseTypes(name);
+ this.parseColors(this.msg);
+ }
+
+ public void debug()
+ {
+ Os.debug("---------------------");
+ Os.debug("line = [" + line + "]");
+ Os.debug("src = " + this.src);
+ Os.debug("cmd = " + this.cmd);
+ Os.debug("dst = " + this.dst);
+ Os.debug("arg = " + this.arg);
+ Os.debug("msg = " + this.msg);
+ Os.debug("from = " + this.from);
+ Os.debug("to = " + this.to);
+ Os.debug("txt = " + this.txt);
+ Os.debug("---------------------");
+ }
+
+ public String toString()
+ {
+ return this.from + ": " + this.txt;
+ }
+
+ /* Private methods */
+ private String notnull(String string)
+ {
+ return string == null ? "" : string;
+ }
+
+ private boolean parseSlash(String msg)
+ {
+ if (msg.charAt(0) != '/')
+ return false;
+
+ // Split up line
+ Matcher mr = ptCmd.matcher(msg);
+ if (!mr.matches())
+ return false;
+
+ String cmd = notnull(mr.group(1));
+ String arg = notnull(mr.group(3));
+
+ // Parse commands
+ if (cmd.matches("j(oin)?")) {
+ Os.debug("Message: /join");
+ this.type = Type.JOIN;
+ this.cmd = "JOIN";
+ this.msg = arg;
+ this.line = this.cmd + " :" + arg;
+ }
+
+ if (cmd.matches("p(art)?")) {
+ Os.debug("Message: /part");
+ this.type = Type.PART;
+ this.cmd = "PART";
+ this.msg = arg;
+ this.line = this.cmd + " :" + arg;
+ }
+
+ // Print warning if command is not recognized
+ if (this.line == null)
+ Os.debug("Message: unknown command");
+
+ return true;
+ }
+
+ private void parseText(String line)
+ {
// Cleanup line
- line = line.replaceAll("\\s+", " ");
- line = line.replaceAll("^ | $", "");
+ line = line.replaceAll("\\s+", " ");
+ line = line.replaceAll("^ | $", "");
this.line = line;
// Split line into parts
this.txt = notnull(this.msg);
else
this.txt = notnull(mrTo.group(3));
+ }
+ private void parseTypes(String name)
+ {
// Parse commands names
if (this.cmd.equals("PRIVMSG")) this.type = Type.PRIVMSG;
else if (this.cmd.equals("332")) this.type = Type.TOPIC;
else if (this.type == Type.PRIVMSG) this.how = How.CHANNEL;
}
- public void debug()
+ private void parseColors(String msg)
{
- Os.debug("---------------------");
- Os.debug("line = [" + line + "]");
- Os.debug("src = " + this.src);
- Os.debug("cmd = " + this.cmd);
- Os.debug("dst = " + this.dst);
- Os.debug("arg = " + this.arg);
- Os.debug("msg = " + this.msg);
- Os.debug("from = " + this.from);
- Os.debug("to = " + this.to);
- Os.debug("txt = " + this.txt);
- Os.debug("---------------------");
- }
+ // Setup regex matching
+ Matcher match = Message.cPtrn.matcher(msg);
- public String toString()
- {
- return this.from + ": " + this.txt;
+ // Initialize state variables
+ int pos = 0;
+ Format fmt = new Format();
+ ArrayList<Format> list = new ArrayList<Format>();
+
+ // Parse the string
+ while (match.find()) {
+ // Push current string
+ fmt.txt = msg.substring(pos, match.start());
+ if (fmt.txt.length() > 0)
+ list.add(fmt.clone());
+ pos = match.end();
+
+ // Abort at end of string
+ if (match.hitEnd())
+ break;
+
+ // Update format for next string
+ switch (match.group().charAt(0)) {
+ // Format attributes
+ case 002: fmt.bold ^= true; break;
+ case 011: fmt.italic ^= true; break;
+ case 023: fmt.strike ^= true; break;
+ case 025: fmt.underline ^= true; break;
+ case 037: fmt.underline ^= true; break;
+ case 026: fmt.reverse ^= true; break;
+
+ // Reset
+ case 017:
+ fmt = new Format();
+ break;
+
+ // Colors
+ case 003:
+ String fg = match.group(1);
+ String bg = match.group(3);
+ fmt.fg = Message.getColor(fg);
+ fmt.bg = Message.getColor(bg);
+ break;
+ }
+ }
+
+ // Cleanup extra space
+ list.trimToSize();
+ this.parts = list;
+ this.msg = msg.replaceAll(cRegex, "");
+ this.to = msg.replaceAll(cRegex, "");
+ this.txt = msg.replaceAll(cRegex, "");
}
}