1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.env.except.IllegalArgumentException;
    18  import org.as2lib.env.reflect.MethodInfo;
    19  import org.as2lib.env.reflect.ConstructorInfo;
    20  import org.as2lib.env.reflect.ClassInfo;
    21  import org.as2lib.test.speed.TestResult;
    22  import org.as2lib.test.speed.AbstractTestResult;
    23  
    24  /**
    25   * {@code MethodInvocation} reflects a profiled method invocation.
    26   * 
    27   * @author Simon Wacker
    28   */
    29  class org.as2lib.test.speed.MethodInvocation extends AbstractTestResult implements TestResult {
    30  	
    31  	/** Designates an unknown type in the type signature. */
    32  	public static var UNKNOWN:String = "[unknown]";
    33  	
    34  	/** Designates type {@code Void} in the type signature. */
    35  	public static var VOID:String = "Void";
    36  	
    37  	/** Invoked method. */
    38  	private var method:MethodInfo;
    39  	
    40  	/** Caller of this method invocation. */
    41  	private var caller:MethodInvocation;
    42  	
    43  	/** Time needed for this method invocation. */
    44  	private var time:Number;
    45  	
    46  	/** Arguments used for this method invocation. */
    47  	private var args:Array;
    48  	
    49  	/** Return value of this method invocation. */
    50  	private var returnValue;
    51  	
    52  	/** Exception thrown during this method invocation. */
    53  	private var exception;
    54  	
    55  	/** The previous method invocation. */
    56  	private var previousMethodInvocation:MethodInvocation;
    57  	
    58  	/**
    59  	 * Constructs a new {@code MethodInvocation} instance.
    60  	 * 
    61  	 * @param method the invoked method
    62  	 * @throws IllegalArgumentException if {@code method} is {@code null} or
    63  	 * {@code undefined}
    64  	 */
    65  	public function MethodInvocation(method:MethodInfo) {
    66  		if (!method) {
    67  			throw new IllegalArgumentException("Argument 'method' [" + method + "] must not be 'null' nor 'undefined'.", this, arguments);
    68  		}
    69  		this.method = method;
    70  	}
    71  	
    72  	/**
    73  	 * Returns the invoked method.
    74  	 * 
    75  	 * @return the invoked method
    76  	 */
    77  	public function getMethod(Void):MethodInfo {
    78  		return this.method;
    79  	}
    80  	
    81  	/**
    82  	 * Returns the name of this method invocation. This is the method's name plus the
    83  	 * signature of this method invocation.
    84  	 * 
    85  	 * @return the name of this method invocation
    86  	 * @see #getMethodName
    87  	 * @see #getMethodSignature
    88  	 */
    89  	public function getName(Void):String {
    90  		return (getMethodName() + getSignature());
    91  	}
    92  	
    93  	/**
    94  	 * Returns the full name of the invoked method.
    95  	 * 
    96  	 * @return the full name of the invoked method
    97  	 */
    98  	public function getMethodName(Void):String {
    99  		return this.method.getFullName();
   100  	}
   101  	
   102  	/**
   103  	 * Returns the signature of this method invocation.
   104  	 * 
   105  	 * <p>If any information needed to generate the signature is not defined,
   106  	 * {@link #UNKNOWN} is used as placeholder.
   107  	 * 
   108  	 * @return this method invocation's signature
   109  	 */
   110  	public function getSignature(Void):String {
   111  		var result:String = "(";
   112  		if (this.args.length > 0) {
   113  			for (var i:Number = 0; i < this.args.length; i++) {
   114  				if (i != 0) {
   115  					result += ", ";
   116  				}
   117  				result += getFullTypeName(args[i]);
   118  			}
   119  		} else {
   120  			result += "Void";
   121  		}
   122  		if (this.method instanceof ConstructorInfo) {
   123  			result += ")";
   124  		} else {
   125  			result += "):";
   126  			if (this.returnValue === undefined) {
   127  				result += VOID;
   128  			} else {
   129  				result += getFullTypeName(this.returnValue);
   130  			}
   131  		}
   132  		if (!wasSuccessful()) {
   133  			result += " throws ";
   134  			result += getFullTypeName(this.exception);
   135  		}
   136  		return result;
   137  	}
   138  	
   139  	/**
   140  	 * Returns the fully qualified type name for the passed-in {@code instance}.
   141  	 * 
   142  	 * @param instance the instance to return the type name for
   143  	 * @return the fully qualified type name for the passed-in {@code instance}.
   144  	 */
   145  	private function getFullTypeName(instance):String {
   146  		if (instance == null) return UNKNOWN;
   147  		var typeName:String = ClassInfo.forInstance(instance).getFullName();
   148  		if (typeName == null) {
   149  			return UNKNOWN;
   150  		} else {
   151  			return typeName;
   152  		}
   153  	}
   154  	
   155  	/**
   156  	 * Returns the time in milliseconds needed for this method invocation.
   157  	 * 
   158  	 * @return the time in milliseconds needed for this method invocation
   159  	 */
   160  	public function getTime(Void):Number {
   161  		return this.time;
   162  	}
   163  	
   164  	/**
   165  	 * Sets the time in milliseconds needed for this method invocation.
   166  	 * 
   167  	 * @param time the time in milliseconds needed for this method invocation
   168  	 */
   169  	public function setTime(time:Number):Void {
   170  		this.time = time;
   171  	}
   172  	
   173  	/**
   174  	 * Returns the arguments used for this method invocation.
   175  	 * 
   176  	 * @return the arguments used for this method invocation
   177  	 */
   178  	public function getArguments(Void):Array {
   179  		return this.args;
   180  	}
   181  	
   182  	/**
   183  	 * Sets the arguments used for this method invocation.
   184  	 * 
   185  	 * @param args the arguments used for this method invocation
   186  	 */
   187  	public function setArguments(args:Array):Void {
   188  		this.args = args;
   189  	}
   190  	
   191  	/**
   192  	 * Returns this method invocation's return value.
   193  	 * 
   194  	 * @return this method invocation's return value
   195  	 */
   196  	public function getReturnValue(Void) {
   197  		return this.returnValue;
   198  	}
   199  	
   200  	/**
   201  	 * Sets the return value of this method invocation.
   202  	 * 
   203  	 * @param returnValue the return value of this method invocation
   204  	 */
   205  	public function setReturnValue(returnValue):Void {
   206  		this.exception = undefined;
   207  		this.returnValue = returnValue;
   208  	}
   209  	
   210  	/**
   211  	 * Returns the exception thrown during this method invocation.
   212  	 * 
   213  	 * @return the exception thrown during this method invocation
   214  	 */
   215  	public function getException(Void) {
   216  		return this.exception;
   217  	}
   218  	
   219  	/**
   220  	 * Sets the exception thrown during this method invocation.
   221  	 * 
   222  	 * @param exception the exception thrown during this method invocation
   223  	 */
   224  	public function setException(exception):Void {
   225  		this.returnValue = undefined;
   226  		this.exception = exception;
   227  	}
   228  	
   229  	/**
   230  	 * Returns whether this method invocation was successful. Successful means that it
   231  	 * returned a proper return value and did not throw an exception.
   232  	 * 
   233  	 * @return {@code true} if this method invocation was successful else {@code false}
   234  	 */
   235  	public function wasSuccessful(Void):Boolean {
   236  		return (this.exception === undefined);
   237  	}
   238  	
   239  	/**
   240  	 * Returns the method invocation that called the method that resulted in this
   241  	 * method invocation.
   242  	 * 
   243  	 * @return the method invocation that called the method that resulted in this
   244  	 * method.
   245  	 */
   246  	public function getCaller(Void):MethodInvocation {
   247  		return this.caller;
   248  	}
   249  	
   250  	/**
   251  	 * Sets the method invocation that called the method that resulted in this method
   252  	 * invocation.
   253  	 * 
   254  	 * @param caller the method invocation that called the method that resulted in this
   255  	 * method invocation.
   256  	 */
   257  	public function setCaller(caller:MethodInvocation):Void {
   258  		this.caller = caller;
   259  	}
   260  	
   261  	/**
   262  	 * Returns the previous method invocation.
   263  	 * 
   264  	 * @return the previous method invocation
   265  	 */
   266  	public function getPreviousMethodInvocation(Void):MethodInvocation {
   267  		return this.previousMethodInvocation;
   268  	}
   269  	
   270  	/**
   271  	 * Sets the previous method invocation.
   272  	 * 
   273  	 * @param previousMethodInvocation the previous method invocation
   274  	 */
   275  	public function setPreviousMethodInvocation(previousMethodInvocation:MethodInvocation):Void {
   276  		this.previousMethodInvocation = previousMethodInvocation;
   277  	}
   278  	
   279  	/**
   280  	 * Checks whether this method invocation was invoked before the passed-in
   281  	 * {@code methodInvocation}.
   282  	 * 
   283  	 * @param methodInvocation the method invocation to make the check upon
   284  	 * @return {@code true} if this method invocation was invoked previously to the
   285  	 * passed-in {@code methodInvocation} else {@code false}
   286  	 */
   287  	public function isPreviousMethodInvocation(methodInvocation:MethodInvocation):Boolean {
   288  		if (methodInvocation == this) return false;
   289  		var previousMethodInvocation:MethodInvocation = methodInvocation.getPreviousMethodInvocation();
   290  		while (previousMethodInvocation) {
   291  			if (previousMethodInvocation == this) return true;
   292  			previousMethodInvocation = previousMethodInvocation.getPreviousMethodInvocation();
   293  		}
   294  		return false;
   295  	}
   296  	
   297  	/**
   298  	 * Returns the string representation of this method invocation.
   299  	 * 
   300  	 * @param rootTestResult test result that holds the total values needed for
   301  	 * percentage calculations
   302  	 * @return the string representation of this method invocation
   303  	 */
   304  	public function toString():String {
   305  		var rootTestResult:TestResult = arguments[0];
   306  		if (!rootTestResult) rootTestResult = getThis();
   307  		var result:String = getTimePercentage(rootTestResult.getTime()) + "%";
   308  		result += ", " + getThis().getTime() + " ms";
   309  		result += " - " + getName();
   310  		return result;
   311  	}
   312  	
   313  }