/* * (C) Copyright 2013 Arnaud Bailly (arnaud.oqube@gmail.com), * Yves Roos (yroos@lifl.fr) and others. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package rationals.ioautomata; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Messages { private static final Pattern messagesPattern = Pattern.compile("(?:\\s*(\\w+)\\s*(->|<-)|\\^)\\s*(\\w+)\\.(\\S+)\\s*"); private static final Pattern lettersPattern = Pattern.compile("(\\?|!|\\^)(\\s*(\\w+)\\s*(->|<-))?\\s*(\\w+)\\.(\\S+)\\s*"); /** * Builds a {@link Message} object from a string description. * * @param message description of the message to build, of the form <code>a -> b.m</code> or <code>a <- b.m</code>. * The direction of the "arrow" distinguish whether this message is an input or an output. The left * identifier denotes the object whose viewpoint this message reflects. <code>a -> b.m</code> means * "in a, observe output of message m to object b". * * @return a {@link Message} instance * * @throws IllegalArgumentException if message doos not match expected pattern. */ public static Message message(String message) { Matcher matcher = messagesPattern.matcher(message); if (matcher.matches()) { return new Message(matcher); } throw rejectMessage(message); } private static IllegalArgumentException rejectMessage(String message) { return new IllegalArgumentException(message + " does not match expected pattern " + messagesPattern); } private static IllegalArgumentException rejectLetter(String message) { return new IllegalArgumentException(message + " does not match expected pattern for a letter " + lettersPattern); } public static IOTransition.IOLetter letter(String message) { Matcher matcher = messagesPattern.matcher(message); if (matcher.matches()) { Message built = new Message(matcher); if (matcher.group(2) == null) { return new IOTransition.IOLetter(built, IOAlphabetType.INTERNAL); } if (matcher.group(2).equals("->")) { return new IOTransition.IOLetter(built, IOAlphabetType.OUTPUT); } else { return new IOTransition.IOLetter(built, IOAlphabetType.INPUT); } } throw rejectMessage(message); } /** * Builds a full {@link rationals.ioautomata.IOTransition.IOLetter} from its textual description. * * @param message textual description of the letter to build. Can be one of: * * <ol> * <li>a caret (<tt>^</tt>) denoting an internal message followed by a message "call" <code> * b.m</code>,</li> * <li>a send or receive signal, resp. (<tt>!</tt>) and (<tt>?</tt>), denoting an output, resp. * input, message, followed by send/receiver/content triple in the form <code>a -> b.m</code>. * Note that the direction the arrow is pointing at is taken into account to distinguish sender * and emitter of the message.</li> * </ol> * * @return a full {@link rationals.ioautomata.IOTransition.IOLetter} with a {@link Message} as label. */ public static IOTransition.IOLetter fullLetter(String message) { Matcher matcher = lettersPattern.matcher(message); if (matcher.matches()) { if (matcher.group(1).equals("^")) { if (matcher.group(2) == null) { return new IOTransition.IOLetter(new Message(matcher.group(5), matcher.group(5), matcher.group(6)), IOAlphabetType.INTERNAL); } else { if (matcher.group(4).equals("->")) { return new IOTransition.IOLetter(new Message(matcher.group(3), matcher.group(5), matcher.group(6)), IOAlphabetType.INTERNAL); } else { return new IOTransition.IOLetter(new Message(matcher.group(5), matcher.group(3), matcher.group(6)), IOAlphabetType.INTERNAL); } } } if (matcher.group(1).equals("!")) { if (matcher.group(4).equals("->")) { return new IOTransition.IOLetter(new Message(matcher.group(3), matcher.group(5), matcher.group(6)), IOAlphabetType.OUTPUT); } else { return new IOTransition.IOLetter(new Message(matcher.group(5), matcher.group(3), matcher.group(6)), IOAlphabetType.OUTPUT); } } else { if (matcher.group(4).equals("->")) { return new IOTransition.IOLetter(new Message(matcher.group(3), matcher.group(5), matcher.group(6)), IOAlphabetType.INPUT); } else { return new IOTransition.IOLetter(new Message(matcher.group(5), matcher.group(3), matcher.group(6)), IOAlphabetType.INPUT); } } } throw rejectLetter(message); } public static class Message { private final String from; private final String to; private final String content; Message(Matcher matcher) { String first = matcher.group(1); String second = matcher.group(3); String content = matcher.group(4); if (matcher.group(2) == null) { from = to = matcher.group(3); } else { if (matcher.group(2).equals("->")) { from = first; to = second; } else { from = second; to = first; } } this.content = content; } public Message(String from, String to, String content) { this.from = from; this.to = to; this.content = content; } @Override public boolean equals(Object o) { if (this == o) return true; if ((o == null) || (getClass() != o.getClass())) return false; Message message = (Message) o; if (!content.equals(message.content)) return false; if (!from.equals(message.from)) return false; return to.equals(message.to); } @Override public int hashCode() { int result = from.hashCode(); result = (31 * result) + to.hashCode(); result = (31 * result) + content.hashCode(); return result; } @Override public String toString() { if (!from.equals(to)) return from + " -> " + to + '.' + content; else return from + '.' + content; } } }