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.core.BasicClass;
    18  import org.as2lib.util.Stringifier;
    19  import org.as2lib.util.StringUtil;
    20  import org.as2lib.env.except.Throwable;
    21  import org.as2lib.env.except.StackTraceElement;
    22  import org.as2lib.env.reflect.ReflectUtil;
    23  
    24  /**
    25   * {@code ThrowableStringifier} stringifies instances of type {@link Throwable}.
    26   *
    27   * @author Simon Wacker
    28   */
    29  class org.as2lib.env.except.ThrowableStringifier extends BasicClass implements Stringifier {
    30  	
    31  	/** Show the stack trace. */
    32  	private var showStackTrace:Boolean;
    33  	
    34  	/** Show the cause. */
    35  	private var showCause:Boolean;
    36  	
    37  	/**
    38  	 * Constructs a new {@code ThrowableStringifier} instance.
    39  	 *
    40  	 * <p>You can switch different parts of the string representation on or off using
    41  	 * the declared arguments.
    42  	 *
    43  	 * <p>The stack trace and the cause are be default shown. That means if you want
    44  	 * them to be contained in the resulting string representation you do not have to
    45  	 * specify any arguments.
    46  	 *
    47  	 * <p>The settings apply only to the throwable to stringify. That means they do
    48  	 * not apply for its cause. The cause is responsible for stringifying itself.
    49  	 *
    50  	 * @param showStackTrace determines whether the string representation contains the
    51  	 * stack trace
    52  	 * @param showCause determines whether the string representation contains the
    53  	 * cause
    54  	 */
    55  	public function ThrowableStringifier(showStackTrace:Boolean, showCause:Boolean) {
    56  		this.showStackTrace = showStackTrace == null ? true : showStackTrace;
    57  		this.showCause = showCause == null ? true : showCause;
    58  	}
    59  	
    60  	/**
    61  	 * Returns a string representation of the passed-in {@link Throwable} instance.
    62  	 *
    63  	 * <p>Depending on the settings you made on instantiation the stack trace and
    64  	 * cause is contained in the resulting string or not.
    65  	 *
    66  	 * <p>Note that the cause is stringified by its own stringifier. That means the
    67  	 * setting show stack trace and show cause settings apply only for this throwable
    68  	 * and not for its causes. The cause is responsible for stringifying itself.
    69  	 *
    70  	 * <p>The throwable elements are also responsible for stringifying themselves.
    71  	 *
    72  	 * <p>The string representation is composed as follows:
    73  	 * <pre>
    74  	 *   theFullQualifiedNameOfTheThrowable: theMessage
    75  	 *     at theStringRepresentationOfTheStackTraceElement
    76  	 *     ..
    77  	 *   Caused by: theStringRepresentationOfTheCause
    78  	 * </pre>
    79  	 *
    80  	 * <p>Here is how a real string representation could look like:
    81  	 * <pre>
    82  	 *   org.as2lib.data.holder.IllegalLengthException: The argument length '-2' is not allowed to be negative.
    83  	 *     at org.as2lib.data.holder.MyDataHolder.setMaximumLength(Number)
    84  	 *   Caused by: org.as2lib.data.math.IllegalNumberException: The argument number '-2' is not allowed in a range from 0 to ∞.
    85  	 *     at org.as2lib.data.math.Range.setNumber(Number)
    86  	 * </pre>
    87  	 *
    88  	 * @param target the {@code Throwable} to stringify
    89  	 * @return the string representation of the passed-in {@code target} throwable
    90  	 * @see #stringifyStackTrace
    91  	 */
    92  	public function execute(target):String {
    93  		var throwable:Throwable = target;
    94  		var result:String = "";
    95  		var typeName:String = ReflectUtil.getTypeNameForInstance(throwable);
    96  		var indent:Number = typeName.length + 2;
    97  		result += typeName + ": " + StringUtil.addSpaceIndent(throwable.getMessage(), indent).substr(indent);
    98  		var stackTrace:Array = throwable.getStackTrace();
    99  		if (stackTrace && stackTrace.length > 0) {
   100  			result += "\n" + stringifyStackTrace(throwable.getStackTrace());
   101  		}
   102  		var cause = throwable.getCause();
   103  		if (cause) {
   104  			result += "\nCaused by: " + cause;
   105  		}
   106  		return result;
   107  	}
   108  
   109  	/**
   110  	 * Stringifies the passed-in {@code stackTrace} array that contains
   111  	 * {@link StackTraceElement} instances.
   112  	 *
   113  	 * <p>The individual {@code StackTraceElement} instances are responsible for
   114  	 * stringifying themselves.
   115  	 *
   116  	 * <p>The resulting string representation is composed as follows:
   117  	 * <pre>
   118  	 *     at theStringRepresentationOfTheFirstStackTraceElement
   119  	 *     at theStringRepresentationOfTheSecondStackTraceElement
   120  	 *     ..
   121  	 * </pre>
   122  	 *
   123  	 * <p>A real string representation could look like this:
   124  	 * <pre>
   125  	 *     at org.as2lib.data.math.Range.setNumber(Number)
   126  	 *     at org.as2lib.data.holder.MyDataHolder.setMaximumLength(Number)
   127  	 *     at com.simonwacker.MyApplication.initialize()
   128  	 * </pre>
   129  	 *
   130  	 * @param stackTrace the stack trace to stringify
   131  	 * @return the string representation of the passed-in {@code stackTrace}
   132  	 */
   133  	public function stringifyStackTrace(stackTrace:Array):String {
   134  		var result:String = "";
   135  		for (var i:Number = 0; i < stackTrace.length; i++) {
   136  			var element:StackTraceElement = stackTrace[i];
   137  			result += ("  at " 
   138  					   + element.toString());
   139  			if (i < stackTrace.length-1) {
   140  				result += "\n";
   141  			}
   142  		}
   143  		return result;
   144  	}
   145  	
   146  }