package de.tu_dresden.inf.ggp06_2.connection; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; import de.tu_dresden.inf.ggp06_2.parser.Parser; import de.tu_dresden.inf.ggp06_2.resolver.ExpressionList; /** * This class handles all message related transformations. * @author ingo * */ public class Message { /* Stores the logger for this class */ public static Logger logger = Logger.getLogger(Message.class); /* Message strings */ static final String STR_START = "START"; static final String STR_STOP = "STOP"; static final String STR_PLAY = "PLAY"; /* Message types */ static final int ERROR = -1; static final int EMPTY = 0; static final int START = 1; static final int PLAY = 2; static final int STOP = 3; /* Regular Expressions */ static final Pattern RE_COMMAND = Pattern.compile("\\(([^\\s]*)\\s"); /* Message content fields */ int type; String matchId; String role; ExpressionList gameDescription; ExpressionList moves; int startClock; int playClock; /** * Constructs a message object from a message string. * @param message */ public Message(String message) { this.parseMessage(message); } /** * This method parses the message string into a message object. * @param message */ public void parseMessage(String message) { //logger.debug( "Start parsing the message: " + message ); // resets the message this.reset(); // no message there if ( message == null ) return; // retrieve the command String command = getCommand( message ); // remove closing brackets message = message.substring( 1, message.length() - 1 ); // split message in its parts String[] messageParts = message.split( " " ); int length = messageParts.length; // create temp string for move parsing String tmpString = ""; /* START Message handling */ if ( command.equals( Message.STR_START ) ) { this.type = Message.START; this.matchId = messageParts[1]; this.role = messageParts[2]; this.startClock = Integer.parseInt( messageParts[length-2] ); this.playClock = Integer.parseInt( messageParts[length-1] ); // all other groups are related to the game description for (int i = 3; i < length - 2; i++) tmpString += " " + messageParts[i]; this.gameDescription = Message.parseString( tmpString ); /* PLAY Message handling */ } else if ( command.equals( Message.STR_PLAY ) ) { this.type = Message.PLAY; this.matchId = messageParts[1]; // all other groups are related to the move section for (int i = 2; i < messageParts.length; i++) tmpString += " " + messageParts[i]; // GDL Parser doesn't recognize NOOP so we give him a predicate tmpString = tmpString.replace( "NOOP", "(NOOP)" ); this.moves = Message.parseString( tmpString ); /* STOP Message handling */ } else if ( command.equals( Message.STR_STOP ) ) { this.type = Message.STOP; this.matchId = messageParts[1]; // STOP Message does not have to have a move list try { // all other groups are related to the move section for (int i = 2; i < messageParts.length; i++) tmpString += " " + messageParts[i]; // GDL Parser doesn't recognize NOOP so we give him a predicate tmpString = tmpString.replace( "NOOP", "(NOOP)" ); this.moves = Message.parseString( tmpString ); } catch ( Exception ex ) { this.moves = new ExpressionList(); } } else { this.reset(); //logger.error( "Command not found: " + command ); this.type = Message.ERROR; } //logger.debug( "Finished parsing the message." ); } /** * This method extracts the message command string. * * @param message * @return */ private String getCommand(String message) { String command = null; try { Matcher m = RE_COMMAND.matcher(message); if ( m.lookingAt() ) { command = m.group(1); command = command.toUpperCase(); } } catch (Exception ex) { //logger.error( "Pattern to extract command did not match!", ex ); this.type = Message.ERROR; } return command; } /** * This method parses a gdl string into a ExpressionList. * If a error occurs while parsing the result will be an empty list. * @param gdl * @return */ public static ExpressionList parseString( String gdl ) { // the first answer in a match if ( gdl.contains( "NIL" ) ) return null; // remove additional outer brackets -> used for move parsing // TODO: Create a better check with a regular expression or so if (gdl.startsWith(" ((") ) if ( gdl.endsWith("))") ) gdl = gdl.substring( 2, gdl.length() - 1 ); else if ( gdl.endsWith(") )" ) ) gdl = gdl.substring( 2, gdl.length() - 2 ); // parse stream into game description return Parser.parseGDL( gdl ); } /** * This method handles a clean reset of the message object. Essentially it * sets all content fields to a default empty state. */ private void reset() { this.type = Message.EMPTY; this.matchId = ""; this.role = ""; this.gameDescription = new ExpressionList(); this.moves = new ExpressionList(); this.startClock = 0; this.playClock = 0; } @Override public String toString() { return "Type: " + this.type + "\nMatchID: " + this.matchId; } /** * @return the gameDescription */ public ExpressionList getGameDescription() { return gameDescription; } /** * @param gameDescription the gameDescription to set */ public void setGameDescription(ExpressionList gameDescription) { this.gameDescription = gameDescription; } /** * @return the matchId */ public String getMatchId() { return matchId; } /** * @param matchId the matchId to set */ public void setMatchId(String matchId) { this.matchId = matchId; } /** * @return the moves */ public ExpressionList getMoves() { return moves; } /** * @param moves the moves to set */ public void setMoves(ExpressionList moves) { this.moves = moves; } /** * @return the playClock */ public int getPlayClock() { return playClock; } /** * @param playClock the playClock to set */ public void setPlayClock(int playClock) { this.playClock = playClock; } /** * @return the role */ public String getRole() { return role; } /** * @param role the role to set */ public void setRole(String role) { this.role = role; } /** * @return the startClock */ public int getStartClock() { return startClock; } /** * @param startClock the startClock to set */ public void setStartClock(int startClock) { this.startClock = startClock; } /** * @return the type */ public int getType() { return type; } /** * @param type the type to set */ public void setType(int type) { this.type = type; } }