// Copyright (c) 2006 - 2008, Markus Strauch. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. package net.sf.sdedit.text; import java.beans.IntrospectionException; import java.lang.reflect.Method; import java.util.Arrays; import net.sf.sdedit.diagram.MessageData; import net.sf.sdedit.error.SyntaxError; import net.sf.sdedit.util.Grep; /** * MessageData is data derived from strings representing messages exchanged * between objects. These strings have the following format: * * <pre> * {caller}[{level},{thread}]:>{answer}={callee}.{message} * </pre> * * {caller}, {callee} and {message} are strings and are mandatory. {answer}= is * a string and is optional. [{level}] is optional, where {level} is an integer * number. * <p> * When the <tt>parse</tt> method has finished, the parts of the string can be * fetched via <tt>get</tt> methods. If no level is given, level is 0. If no * answer is specified, answer is the empty string. * * @author Markus Strauch * */ public class TextBasedMessageData extends MessageData { private final static String LEVELS = "(" + "\\[(\\d*),(\\d+)\\]" + "|" + "\\[(\\d+)\\]" + "|" + "\\[(\\D.*)\\]" + ")"; private final static String PREFIX = "(\\(\\d*(,\\d+)?\\))?\\s*(.+)"; private static final String COLON = "(?<!\\\\):(?!>)"; private static final String SPAWN = "(?<!\\\\):>"; private static final String DOT = "(?<!\\\\)\\."; private static final String EQ = "(?<!\\\\)="; private static final String MSG = "(.*)()()()"; private static final String MSG_PROP = "(.*?)(\\{([^,=]+=[^,=]+)(,[^,=]+=[^,=]+)*\\})?"; private String string; static { for (Method method : TextBasedMessageData.class.getMethods()) { try { method.setAccessible(true); } catch (Throwable t) { System.out.println(t.getClass().getSimpleName()); } } } /** * Creates a new <tt>MessageParser</tt> for parsing a string. * * @param string * the string to be parsed * @throws SyntaxError * if the string is not a valid message */ public TextBasedMessageData(String string) throws SyntaxError { super(); this.string = string; parse(); } /** * Parses the string, after that the attributes corresponding to the parts * of the string (see {@linkplain TextBasedMessageData} can be fetched via * the <tt>get</tt> methods. * */ private void parse() throws SyntaxError { boolean success = false; if (string.indexOf(':') == -1) { throw new SyntaxError(null, "not a valid message - ':' missing"); } try { success = /* Level, thread, and answer */ Grep.parseAndSetProperties(this, PREFIX + LEVELS + COLON + "(.*)" + EQ + "(.+?)" + DOT + MSG_PROP, string, "noteId", "dummy", "caller", "dummy", "levelString", "threadString", "levelString", "callerMnemonic", "answer", "callee", "message","properties","dummy","dummy") /* Level, thread, no answer */ || Grep.parseAndSetProperties(this, PREFIX + LEVELS + COLON + "(.+?)" + DOT + MSG_PROP, string, "noteId", "dummy", "caller", "dummy", "levelString", "threadString", "levelString", "callerMnemonic", "callee", "message","properties","dummy","dummy") /* No level/thread, but answer */ || Grep.parseAndSetProperties(this, PREFIX + COLON + "(.*)" + EQ + "(.+?)" + DOT + MSG_PROP, string, "noteId", "dummy", "caller", "answer", "callee", "message","properties","dummy","dummy") /* No level/thread, no answer */ || Grep.parseAndSetProperties(this, PREFIX + COLON + "(.*?)" + DOT + MSG_PROP, string, "noteId", "dummy", "caller", "callee", "message","properties","dummy","dummy") /* primitive with level */ || Grep.parseAndSetProperties(this, PREFIX + LEVELS + COLON + MSG_PROP, string, "noteId", "dummy", "caller", "dummy", "levelString", "threadString", "levelString", "callerMnemonic", "message","properties","dummy","dummy") /* primitive without level */ || Grep.parseAndSetProperties(this, PREFIX + COLON + MSG_PROP, string, "noteId", "dummy", "caller", "message","properties","dummy","dummy") /* spawn with level */ || Grep.parseAndSetProperties(this, PREFIX + LEVELS + SPAWN + "(.+?)" + DOT + MSG_PROP, string, "noteId", "dummy", "spawner", "dummy", "levelString", "threadString", "levelString", "callerMnemonic", "callee", "message","properties","dummy","dummy") /* spawn without level */ || Grep.parseAndSetProperties(this, PREFIX + SPAWN + "(.+?)" + DOT + MSG_PROP, string, "noteId", "dummy", "spawner", "callee", "message","properties","dummy","dummy"); } catch (IntrospectionException e) { e.printStackTrace(); } if (!success) { throw new SyntaxError(null, "not a valid message"); } } @Override public void setMessage(String message) { if (message.endsWith("&")) { setReturnsInstantly(true); message = message.substring(0, message.length() - 1); } super.setMessage(message); } public void setCallee(String callee) { if (callee.length() >= 2 && callee.charAt(0) == '{' && callee.charAt(callee.length() - 1) == '}') { setCallees(callee.substring(1, callee.length() - 1).split(",")); } else { String[] parts = Grep.parse("(.*)\\[(\\D.*)\\]$", callee); if (parts == null) { super.setCallee(callee); } else { super.setCallee(parts[0]); super.setCalleeMnemonic(parts[1]); } } } /** * Sets the spawning object. * * @param spawner * the spawning object */ public void setSpawner(String spawner) { setSpawnMessage(true); setCaller(spawner); } /** * @param level * the level to set */ public void setLevelString(String level) { if (!level.equals("")) { // ignore mismatch setLevel(Integer.parseInt(level)); } } public void setThreadString(String thread) { if (!thread.equals("")) { setThread(Integer.parseInt(thread)); } } public void setDummy(String dummy) { } public void setNoteId(String noteId) { noteId = noteId.trim(); if (!noteId.equals("")) { noteId = noteId.substring(1, noteId.length() - 1); String[] ids = noteId.split(","); if (ids[0].length() > 0) { setNoteNumber(Integer.parseInt(ids[0])); } if (ids.length > 1) { setAnswerNoteNumber(Integer.parseInt(ids[1])); } } } }