Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
patterntesting.runtime.log.SequenceGrapher Maven / Gradle / Ivy
Go to download
PatternTesting Runtime (patterntesting-rt) is the runtime component for
the PatternTesting framework. It provides the annotations and base classes
for the PatternTesting testing framework (e.g. patterntesting-check,
patterntesting-concurrent or patterntesting-exception) but can be also
used standalone for classpath monitoring or profiling.
It uses AOP and AspectJ to perform this feat.
/*
* $Id: SequenceGrapher.java,v 1.30 2014/03/22 21:32:48 oboehm Exp $
*
* Copyright (c) 2013 by Oliver Boehm
*
* 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 orimplied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* (c)reated 06.09.2013 by oliver ([email protected] )
*/
package patterntesting.runtime.log;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.*;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.ConstructorSignature;
import org.slf4j.*;
import patterntesting.annotation.check.runtime.NullArgsAllowed;
import patterntesting.runtime.exception.NotFoundException;
import patterntesting.runtime.util.*;
import patterntesting.runtime.util.regex.TypePattern;
/**
* This class supports the creation of simple sequence diagrams as desribed
* in the user manual of UML Graph .
*
* @author oliver
* @since 1.3.1 (06.09.2013)
*/
public class SequenceGrapher extends AbstractLogger {
private static final Logger log = LoggerFactory.getLogger(SequenceGrapher.class);
private final Writer writer;
private final List statements = new ArrayList();
private final Map objnames = new HashMap();
private final Map placeHolderNames = new HashMap();
private final Map varnames = new HashMap();
private final Stack callerNames = new Stack();
private TypePattern[] excludeFilter = new TypePattern[0];
private String active = "";
private int objectNumber = 0;
/**
* Instantiates a new SequenceGrapher. The generated sequence diagram is
* stored in a temporary file.
*/
public SequenceGrapher() {
this(createTempLogFile("seq-diagram", ".pic"));
}
/**
* Instantiates a new SequenceGrapher. The generated sequence diagram is
* stored in the given log file.
*
* @param logFile the log file
*/
public SequenceGrapher(final File logFile) {
this(logFile, "seq-head.template");
}
/**
* Instantiates a new sequence grapher. The header for the generated diagram
* is read from the given resource:
*
* "seq-head.template"
(default): default header with with
* needed "sequence.pic"
and comments inside
* "seq-head-small.template"
: a sort header with copy
* instruction for the needed "sequence.pic"
and without any
* comments.
*
*
* @param logFile the log file
* @param resource the resource ("seq-head.template" or "seq-head-small")
*/
public SequenceGrapher(final File logFile, final String resource) {
this(getStreamFor(logFile), resource);
log.info("Sequence diagram will be written to \"{}\" with header from \"{}\".", logFile,
resource);
}
/**
* Instantiates a new object logger to the given stream.
*
* @param ostream the ostream
*/
public SequenceGrapher(final OutputStream ostream) {
this(ostream, "seq-head.template");
}
/**
* /**
* Instantiates a new sequence grapher. The header for the generated diagram
* is read from the given resource:
*
* "seq-head.template"
(default): default header with with
* needed "sequence.pic"
and comments inside
* "seq-head-small.template"
: a sort header with copy
* instruction for the needed "sequence.pic"
and without any
* comments.
*
*
* @param ostream the ostream
* @param resource the resource ("seq-head.template" or "seq-head-small")
*/
public SequenceGrapher(final OutputStream ostream, final String resource) {
super(ostream);
this.writer = new BufferedWriter(new OutputStreamWriter(ostream,
Charset.forName("ISO-8859-1")));
this.writeTemplate(resource);
}
private void writeTemplate(final String resource) {
InputStream istream = this.getClass().getResourceAsStream(resource);
if (istream == null) {
log.warn("Resource \"{}\" not found - content will be missing in generated diagram.",
resource);
} else {
try {
String head = IOUtils.toString(istream, "ISO-8859-1");
this.writer.write(head);
} catch (IOException ioe) {
log.warn("Content of \"" + resource + "\" will be missing in generated diagram.",
ioe);
} finally {
IOUtils.closeQuietly(istream);
}
}
}
/**
* Sets the exclude filter. Classes which matches the filter will not appear
* in the generated sequence diagram.
*
* @param pattern the new exclude filter
* @since 1.4.1
*/
public void setExcludeFilter(final String[] pattern) {
this.excludeFilter = new TypePattern[pattern.length];
for (int i = 0; i < pattern.length; i++) {
this.excludeFilter[i] = new TypePattern(pattern[i]);
}
}
/**
* This method is called at shutdown to close the open writer.
*
* @see java.lang.Thread#run()
*/
@Override
public void run() {
this.closeQuietly();
super.run();
}
/**
* Closes the stream with the logged objects.
*/
@Override
public void close() {
this.closeQuietly();
super.close();
}
private void closeQuietly() {
this.sortOutEmptyCreateMessages();
this.writeDefines();
this.writeMessages();
this.completeObjects();
this.writeTemplate("seq-tail.template");
IOUtils.closeQuietly(this.writer);
}
/**
* Here we prepare the cached statements to the file. If a create message is
* found with no other activities this creation will be sorted out to keep the
* generated sequence diagram simple.
*/
private void sortOutEmptyCreateMessages() {
List emptyCreateMessages = new ArrayList();
for (DrawStatement stmt : this.statements) {
if ((stmt.getType() == DrawType.CREATE_MESSAGE) && !hasActivities(stmt)) {
log.debug("{} will be ignored because it is a single statement.", stmt);
emptyCreateMessages.add(stmt);
removeValue(this.placeHolderNames, stmt.getTarget());
removeValue(this.varnames, stmt.getTarget());
}
}
this.statements.removeAll(emptyCreateMessages);
}
private static void removeValue(final Map map, final String name) {
for (Map.Entry entry : map.entrySet()) {
if (name.equals(entry.getValue())) {
map.remove(entry.getKey());
break;
}
}
}
private void writeDefines() {
SortedMap sortedObjnames = new TreeMap(
new VarnameComparator());
for (Entry entry : objnames.entrySet()) {
sortedObjnames.put(entry.getValue(), entry.getKey());
}
for (Entry entry : sortedObjnames.entrySet()) {
Class> clazz = entry.getValue().getClass();
if (entry.getValue() instanceof Class>) {
clazz = (Class>) entry.getValue();
}
this.writeLine(getBoxwidStatementFor(clazz.getSimpleName()));
this.writeLine("object(" + entry.getKey() + ",\":" + clazz.getSimpleName() + "\");");
}
Collection objectNames = new TreeSet(new VarnameComparator());
objectNames.addAll(this.placeHolderNames.values());
for (String name : objectNames) {
this.writeLine("placeholder_object(" + name + ");");
}
}
private static String getBoxwidStatementFor(final String name) {
return "boxwid = " + getBoxwidFor(name) + ";";
}
private static String getBoxwidFor(final String name) {
int length = name.length() + 1;
if (length > 16) {
return "1.5";
} else if (length > 10) {
return "1.0";
} else {
return "0.75";
}
}
private void writeMessages() {
this.writeLine("step();");
this.writeLine("");
this.writeComment("Message sequences");
if (this.statements.isEmpty()) {
log.debug("No draw statemtents are logged.");
return;
}
DrawStatement previous = this.statements.get(0);
previous.writeStatement(this.writer);
for (int i = 1; i < this.statements.size(); i++) {
DrawStatement stmt = this.statements.get(i);
if (previous.hasMessageToLeft() && stmt.hasMessageToRight()) {
this.writeLine("step();");
}
if (stmt.hasMessage()) {
previous = stmt;
}
stmt.writeStatement(this.writer);
}
}
/**
* If the target of the given statement is part of any other
* statement 'true' will be returned.
*
* @param drawStatement the draw statement
* @return true, if successful
*/
private boolean hasActivities(final DrawStatement drawStatement) {
String target = drawStatement.getTarget();
for (DrawStatement stmt : this.statements) {
if (drawStatement.equals(stmt)) {
continue;
}
if (stmt.hasActor(target)) {
return true;
}
}
return false;
}
private void completeObjects() {
this.writeLine("");
this.writeComment("Complete the lifelines");
this.writeLine("step();");
Collection names = new TreeSet(this.varnames.values());
for (String name : names) {
this.writeLine("complete(" + name + ");");
}
}
/**
* Logs the creation of an object in the created sequence diagram.
*
* @param call the call
* @param result the created object
*/
public void createMessage(final JoinPoint call, final Object result) {
this.addComment(JoinPointHelper.getAsLongString(call) + " = " + result);
Object creator = call.getThis();
if (creator == null) {
String classname = JoinPointHelper.getCallerOf(call).getClassName();
try {
creator = Class.forName(classname);
} catch (ClassNotFoundException ex) {
throw new NotFoundException(classname, ex);
}
}
this.createMessage(creator, result);
}
/**
* Logs the creation of an object in the created sequence diagram.
*
* @param creator the creator
* @param createdObject the created object
*/
public void createMessage(final Object creator, final Object createdObject) {
if (this.matches(creator) || this.matches(createdObject)) {
log.debug("{} --creates--> {} is not logged because of exclude filter.", creator, createdObject);
return;
}
String name = this.varnames.get(createdObject);
String typeName = this.addPlaceHolder(createdObject);
if (name != null) {
log.trace("Creation of {} is already logged.", createdObject);
this.objnames.remove(createdObject);
return;
}
name = this.getVarnameFor(creator);
Class> type = createdObject.getClass();
this.addCreateMessage(name, type, typeName);
}
private boolean matches(final Object creator) {
for (int i = 0; i < this.excludeFilter.length; i++) {
if (this.excludeFilter[i].matches(creator)) {
return true;
}
}
return false;
}
private String addPlaceHolder(final Object obj) {
String name = this.placeHolderNames.get(obj);
if (name == null) {
name = this.objnames.get(obj);
if (name == null) {
name = this.addVarnameFor(obj);
this.placeHolderNames.put(obj, name);
} else {
//this.objnames.remove(obj);
this.placeHolderNames.put(obj, name);
}
}
return name;
}
private String addObject(final Object obj) {
String name = this.addVarnameFor(obj);
objnames.put(obj, name);
return name;
}
@NullArgsAllowed
private String getVarnameFor(final Object obj) {
if (obj == null) {
return getActorName();
}
String name = this.varnames.get(obj);
if (name == null) {
if (obj instanceof Class>) {
name = this.getVarnameFor((Class>) obj);
} else {
name = this.varnames.get(obj.getClass());
}
}
if (name == null) {
name = addObject(obj);
}
return name;
}
private String getVarnameFor(final Class> clazz) {
String name = this.varnames.get(clazz);
if (name == null) {
for (Entry entry : this.varnames.entrySet()) {
if (clazz.equals(entry.getKey().getClass())) {
return entry.getValue();
}
}
name = addObject(clazz);
}
return name;
}
private String getActorName() {
String name = this.varnames.get("Actor");
if (name == null) {
name = addVarnameFor("Actor");
this.writeLine("actor(" + name + ",\"\");");
}
return name;
}
private String addVarnameFor(final Object obj) {
if (obj instanceof Class>) {
return addVarnameFor((Class>) obj);
}
String name = toName(obj.getClass());
return addVarname(name, obj);
}
private String addVarnameFor(final Class> clazz) {
String name = toName(clazz);
return addVarname(name, clazz);
}
private String addVarname(final String name, final Object obj) {
if (this.varnames.containsKey(obj)) {
log.trace("{} already in map of var names.", obj);
} else {
this.varnames.put(obj, name);
this.objectNumber++;
}
return this.varnames.get(obj);
}
private String toName(final Class> clazz) {
return clazz.getSimpleName().substring(0, 1).toUpperCase()
+ Integer.toString(this.objectNumber, Character.MAX_RADIX);
}
/**
* This is the opposite to the toName() method and returns the number part
* of a variable name.
*
* @param name the name
* @return the string
*/
private static String toNumber(final String name) {
return name.substring(1);
}
// /**
// * A message points to left, if the given senderName was created after
// * the given targetName. This information can be derived from the name.
// *
// * @param senderName the sender name
// * @param targetName the target name
// * @return true, if successful
// */
// private static boolean messagePointsToLeft(final String senderName, final String targetName) {
// return toNumber(senderName).compareTo(toNumber(targetName)) > 0;
// }
private void setActive(final String name) {
this.statements.add(new DrawStatement(DrawType.ACTIVE, name));
this.active = name;
}
private boolean isActive(final String name) {
return this.active.equals(name);
}
/**
* Trys to log the call of the given excecution joinpoint. For this reason
* we must find the caller which is a little bit tricky. We use the
* classname of the mapped variable names to guess which could be the
* caller.
*
* @param execution the execution joinpoint
*/
public void execute(final JoinPoint execution) {
this.addComment(JoinPointHelper.getAsLongString(execution));
String senderName = getCallerNameOf(execution);
String targetName = getTargetName(execution);
if (execution.getSignature() instanceof ConstructorSignature) {
this.addCreateMessage(senderName, execution.getThis().getClass(), targetName);
} else {
this.message(senderName, targetName, execution.getSignature().getName(),
execution.getArgs());
}
}
private String getTargetName(final JoinPoint execution) {
Object thisObject = execution.getThis();
if (thisObject == null) {
StackTraceElement element = StackTraceScanner.find(execution.getSignature());
try {
return this.getVarnameFor(Class.forName(element.getClassName()));
} catch (ClassNotFoundException ex) {
log.debug("Classname of " + element + " not found.", ex);
return this.getVarnameFor(null);
}
} else {
return this.getVarnameFor(thisObject);
}
}
private String getCallerNameOf(final JoinPoint execution) {
StackTraceElement caller = JoinPointHelper.getCallerOf(execution);
String classname = caller.getClassName();
for (Entry entry : varnames.entrySet()) {
if (classname.equals(entry.getKey().getClass().getName())) {
log.trace("Caller of {} is {}.", execution, entry);
return entry.getValue();
}
}
log.trace("Caller of {} not found in {}.", execution, varnames);
try {
return this.addObject(Class.forName(caller.getClassName()));
} catch (ClassNotFoundException ex) {
log.info("cannot get class for {} because of {}.", caller, ex.getMessage());
return getActorName();
}
}
/**
* Logs the call of a method to the generated sequence diagram.
*
* @param call the call
*/
public void message(final JoinPoint call) {
this.message(call.getThis(), call);
}
/**
* Logs the call of a method to the generated sequence diagram.
*
* @param caller the caller
* @param call the call
*/
public void message(final Object caller, final JoinPoint call) {
this.addComment(JoinPointHelper.getAsLongString(call));
this.message(caller, call.getTarget(), call.getSignature().getName(), call.getArgs());
}
/**
* Logs the call of a method to the generated sequence diagram.
*
* @param sender the sender
* @param target the target
* @param methodName the method name
* @param args the args
*/
public void message(final Object sender, final Object target, final String methodName,
final Object[] args) {
if (this.matches(sender) || this.matches(target)) {
log.debug("{} -----------> {} is not logged because of exclude filter.", sender, target);
return;
}
String senderName = this.getVarnameFor(sender);
String targetName = this.getVarnameFor(target);
this.message(senderName, targetName, methodName, args);
}
private void message(final String senderName, final String targetName, final String methodName,
final Object[] args) {
this.addMessage(senderName, targetName, methodName, args);
}
private static String getArgsAsString(final Object... args) {
return StringEscapeUtils.escapeJava(Converter.toShortString(JoinPointHelper
.getArgsAsString(args)));
}
/**
* Logs the return arrow from the last call to the generated sequence
* diagram.
*
* @param call the call
*/
public void returnMessage(final JoinPoint call) {
this.returnMessage(call, "");
}
/**
* Logs the return arrow from the last call to the generated sequence
* diagram.
*
* @param call the call
* @param returnValue the return value
*/
public void returnMessage(final JoinPoint call, final Object returnValue) {
this.addComment(call.toLongString() + " = " + returnValue);
//writeComment(call.toLongString() + " = " + returnValue, cache);
this.returnMessage(call.getTarget(), returnValue);
}
/**
* Return message.
*
* @param returnee the returnee
* @param returnValue the return value
*/
public void returnMessage(final Object returnee, final Object returnValue) {
if (this.matches(returnee)) {
log.debug("{} <--{}-- is not logged because of exclude filter.", returnee, returnValue);
return;
}
this.addReturnMessage(returnee, returnValue);
}
private static String toEscapedString(final Object returnValue) {
if (returnValue == null) {
return "null";
}
return StringEscapeUtils.escapeJava(returnValue.toString());
}
private void writeComment(final String comment) {
this.writeLine("# " + comment);
}
private void writeLine(final String line) {
writeLine(line, this.writer);
}
private static void writeLine(final String line, final Writer lineWriter) {
try {
lineWriter.write(line);
lineWriter.write("\n");
} catch (IOException ioe) {
log.debug("Writing to {} failed because of {}.", lineWriter, ioe.getMessage());
log.info(line);
}
}
private void addComment(final String comment) {
DrawStatement stmt = new DrawStatement(DrawType.COMMENT, comment);
this.statements.add(stmt);
}
private void addCreateMessage(final String senderName, final Class> type, final String typeName) {
if (!isActive(senderName)) {
this.setActive(senderName);
}
DrawStatement stmt = new DrawStatement(senderName, type, typeName);
this.statements.add(stmt);
}
private void addMessage(final String senderName, final String targetName, final String methodName, final Object[] args) {
this.callerNames.push(senderName);
DrawStatement stmt = new DrawStatement(senderName, targetName, methodName, args);
this.statements.add(stmt);
}
private void addReturnMessage(final Object returnee, final Object returnValue) {
String receiverName = this.callerNames.pop();
String returneeName = this.getVarnameFor(returnee);
DrawStatement stmt = new DrawStatement(receiverName, returneeName, returnValue);
this.statements.add(stmt);
}
/**
* The Class VarnameComparator compares two variable names. The name is of
* the form "A999...", e.g. first character is a (uppercase) letter and the
* next characters are a number (to base {@link Character#MAX_RADIX}). Only
* the number are used for comparison to get the creation order.
*/
private static final class VarnameComparator implements Comparator, Serializable {
private static final long serialVersionUID = 20140104L;
/**
* Compares two variable names.
*
* @param x1 the x1
* @param x2 the x2
* @return a negativ valule if x1 < x2, "0" for x1 == x2, otherwise
* positive value
* @see java.util.Comparator#compare(Object, Object)
*/
public int compare(final String x1, final String x2) {
return toNumber(x1) - toNumber(x2);
}
private static int toNumber(final String varname) {
String numberPart = varname.substring(1);
return Integer.parseInt(numberPart, Character.MAX_RADIX);
}
}
//-------------------------------------------------------------------------
/**
* The different types of drawings.
*/
private enum DrawType {
UNKNOWN,
COMMENT,
ACTIVE,
CREATE_MESSAGE,
MESSAGE,
RETURN_MESSAGE;
}
/**
* Internal class for caching the different draw statements.
*
* @author oliver
* @since 1.4.1 (17.01.2014)
*/
private static class DrawStatement {
private final DrawType type;
private final String sender;
private final String target;
private final String methodName;
private final Object[] args;
private final String comment;
/**
* Instantiates a new draw statement.
*
* @param type the type
* @param comment the comment or active argument
*/
public DrawStatement(final DrawType type, final String comment) {
this.type = type;
this.sender = comment;
this.target = null;
this.methodName = null;
this.args = null;
this.comment = comment;
}
/**
* Instantiates a new draw statement.
*
* @param senderName the sender name
* @param createdClass the created class
* @param typeName the type name
*/
public DrawStatement(final String senderName, final Class> createdClass, final String typeName) {
this.type = DrawType.CREATE_MESSAGE;
this.sender = senderName;
this.target = typeName;
this.methodName = null;
this.args = createObjectArray(createdClass);
this.comment = null;
}
/**
* Instantiates a new draw statement.
*
* @param sender the sender
* @param target the target
* @param name the name
* @param args the args
*/
public DrawStatement(final String sender, final String target, final String name,
final Object[] args) {
this.type = DrawType.MESSAGE;
this.sender = sender;
this.target = target;
this.methodName = name;
this.args = args;
this.comment = null;
}
/**
* Instantiates a new draw statement.
*
* @param receiverName the receiver name
* @param returnee the returnee
* @param returnValue the return value
*/
public DrawStatement(final String receiverName, final String returnee, final Object returnValue) {
this.type = DrawType.RETURN_MESSAGE;
this.sender = receiverName;
this.target = returnee;
this.methodName = null;
this.args = createObjectArray(returnValue);
this.comment = null;
}
private Object[] createObjectArray(final Object...objects) {
return objects;
}
/**
* Gets the type.
*
* @return the type
*/
public DrawType getType() {
return this.type;
}
/**
* Gets the target.
*
* @return the target
*/
public String getTarget() {
return this.target;
}
/**
* If the given name is part of any stored actor in this statement
* 'true' will be returned.
*
* @param name the name
* @return true, if is involved
*/
public boolean hasActor(final String name) {
switch (this.type) {
case CREATE_MESSAGE:
case MESSAGE:
return name.equals(this.target);
case RETURN_MESSAGE:
return name.equals(this.sender) || name.equals(this.target);
default:
return false;
}
}
/**
* Checks for message.
*
* @return true, if successful
*/
public boolean hasMessage() {
switch (this.type) {
case CREATE_MESSAGE:
case MESSAGE:
case RETURN_MESSAGE:
return true;
default:
return false;
}
}
/**
* A message points to left, if the given senderName was created after
* the given targetName. This information can be derived from the name.
*
* @return true, if successful
*/
public boolean hasMessageToLeft() {
switch (this.type) {
case CREATE_MESSAGE:
case MESSAGE:
return toNumber(this.sender).compareTo(toNumber(this.target)) > 0;
case RETURN_MESSAGE:
return toNumber(this.target).compareTo(toNumber(this.sender)) > 0;
default:
return false;
}
}
/**
* Checks for message to right.
*
* @return true, if successful
*/
public boolean hasMessageToRight() {
switch (this.type) {
case CREATE_MESSAGE:
case MESSAGE:
case RETURN_MESSAGE:
return !this.hasMessageToLeft();
default:
return false;
}
}
/**
* Write statement.
*
* @param writer the writer
*/
public void writeStatement(final Writer writer) {
switch (type) {
case COMMENT:
this.writeComment(writer);
break;
case ACTIVE:
this.writeActive(writer);
break;
case CREATE_MESSAGE:
this.writeCreateMessage(writer);
break;
case MESSAGE:
this.writeMessage(writer);
break;
case RETURN_MESSAGE:
this.writeReturnMessage(writer);
break;
default:
log.warn("{} statement is ignored.", type);
break;
}
}
/**
* Write comment.
*
* @param writer the writer
*/
public void writeComment(final Writer writer) {
writeLine(this.toString(), writer);
}
/**
* Write active.
*
* @param writer the writer
*/
public void writeActive(final Writer writer) {
writeLine(this.toString(), writer);
}
/**
* Write create message.
*
* @param writer the writer
*/
public void writeCreateMessage(final Writer writer) {
String classname = getTargetType();
writeLine(getBoxwidStatementFor(classname), writer);
writeLine(this.toString(), writer);
}
private String getTargetType() {
Class> targetType = (Class>) this.args[0];
return targetType.getSimpleName();
}
/**
* Write message.
*
* @param writer the writer
*/
public void writeMessage(final Writer writer) {
if (this.hasMessageToLeft()) {
writeLine("step();", writer);
}
writeLine(this.toString(), writer);
writeLine("active(" + this.target + ");", writer);
}
/**
* Write return message.
*
* @param writer the writer
*/
public void writeReturnMessage(final Writer writer) {
if (this.hasMessageToLeft()) {
writeLine("step();", writer);
}
writeLine(this.toString(), writer);
writeLine("inactive(" + this.target + ");", writer);
}
/**
* Hash code.
*
* @return the int
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return this.type.hashCode() + ((this.sender == null) ? 0 : this.sender.hashCode());
}
/**
* Equals.
*
* @param obj the obj
* @return true, if successful
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof DrawStatement)) {
return false;
}
DrawStatement other = (DrawStatement) obj;
return (this.type == other.type)
&& StringUtils.equals(this.sender, other.sender)
&& StringUtils.equals(this.target, other.target)
&& StringUtils.equals(this.methodName, other.methodName)
&& StringUtils.equals(this.comment, other.comment)
&& Arrays.equals(this.args, other.args);
}
/**
* To string.
*
* @return the string
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
switch (type) {
case COMMENT:
return "# " + this.comment;
case ACTIVE:
return "active(" + this.sender + ");";
case CREATE_MESSAGE:
String classname = this.getTargetType();
return "create_message(" + this.sender + "," + this.target + ",\":" + classname + "\");";
case MESSAGE:
return "message(" + this.sender + "," + this.target + ",\"" + this.methodName
+ getArgsAsString(this.args) + "\");";
case RETURN_MESSAGE:
return "return_message(" + this.target + "," + this.sender + ",\""
+ toEscapedString(Converter.toShortString(this.args[0])) + "\");";
default:
return "# " + this.type + " statement";
}
}
}
}