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.Throwable; 18 import org.as2lib.env.except.IllegalArgumentException; 19 import org.as2lib.env.except.IllegalStateException; 20 import org.as2lib.env.except.StackTraceElement; 21 import org.as2lib.env.except.ThrowableStringifier; 22 import org.as2lib.util.Stringifier; 23 import org.as2lib.env.log.Logger; 24 import org.as2lib.env.log.LogManager; 25 26 /** 27 * {@code AbstractThrowable} is an abstract class that contains sourced out 28 * functionalities used by the classes {@link Exception} and 29 * {@link FatalException}. 30 * 31 * <p>It is thought to be an abstract implementation of the {@link Throwable} 32 * interface. Because of that sub-classes must implement the {@code Throwable} 33 * interface if they are themselves not abstract. 34 * 35 * <p>This class extends the {@code Error} class. Thus you can use sub-classes of 36 * it as throwable type in catch-blocks in Flex. 37 * 38 * @author Simon Wacker 39 * @see org.as2lib.env.except.Throwable 40 */ 41 class org.as2lib.env.except.AbstractThrowable extends Error { 42 43 /** Stringifier used to stringify throwables. */ 44 private static var stringifier:Stringifier; 45 46 /** Logger used to output throwables. */ 47 private static var logger:Logger; 48 49 /** 50 * Returns the stringifier to stringify throwables. 51 * 52 * <p>The returned stringifier is either the default 53 * {@link ThrowableStringifier} if no custom stringifier was set or if the 54 * stringifier was set to {@code null}. 55 * 56 * @return the current stringifier 57 */ 58 public static function getStringifier(Void):Stringifier { 59 if (!stringifier) stringifier = new ThrowableStringifier(); 60 return stringifier; 61 } 62 63 /** 64 * Sets the stringifier to stringify throwables. 65 * 66 * <p>If {@code throwableStringifier} is {@code null} the static 67 * {@link #getStringifier} method will return the default stringifier. 68 * 69 * @param throwableStringifier the stringifier to stringify throwables 70 */ 71 public static function setStringifier(throwableStringifier:Stringifier):Void { 72 stringifier = throwableStringifier; 73 } 74 75 /** 76 * Returns the logger used to log throwables. 77 * 78 * @return the logger used to log throwables 79 */ 80 private static function getLogger(Void):Logger { 81 if (!logger) { 82 logger = LogManager.getLogger("org.as2lib.env.except.Throwable"); 83 } 84 return logger; 85 } 86 87 /** The saved stack of method calls. */ 88 private var stackTrace:Array; 89 90 /** The throwable that caused this throwable to be thrown. */ 91 private var cause; 92 93 /** The message describing what went wrong. */ 94 private var message:String; 95 96 /** The error code to obtain localized client messages. */ 97 private var errorCode:String; 98 99 /** 100 * Constructs a new {@code AbstractThrowable} instance. 101 * 102 * <p>All arguments are allowed to be {@code null} or {@code undefined}. But 103 * if one is, the string representation returned by the {@code toString} 104 * method will not be complete. 105 * 106 * <p>The {@code args} array should be the internal arguments array of the 107 * method that throws the throwable. The internal arguments array exists in 108 * every method and contains its parameters, the callee method and the caller 109 * method. You can refernce it in every method using the name 110 * {@code "arguments"}. 111 * 112 * @param message the message that describes the problem in detail 113 * @param thrower the object that declares the method that throws this 114 * throwable 115 * @param args the arguments of the throwing method 116 */ 117 private function AbstractThrowable(message:String, thrower, args:Array) { 118 this.message = message; 119 stackTrace = new Array(); 120 addStackTraceElement(thrower, args.callee, args); 121 // TODO: Implement findMethod to display the next line correctly. 122 // addStackTraceElement(undefined, args.caller, new Array()); 123 } 124 125 /** 126 * Adds a stack trace element to the stack trace. 127 * 128 * <p>The new stack trace element is added to the end of the stack trace. 129 * 130 * <p>At some parts in your application you may want to add stack trace elements 131 * manually. This can help you to get a clearer image of what went where wrong and 132 * why. You can use this method to do so. 133 * 134 * @param thrower the object that threw, rethrew or forwarded (let pass) the 135 * throwable 136 * @param method the method that threw, rethrew or forwarded (let pass) the 137 * throwable 138 * @param args the arguments the method was invoked with when throwing, rethrowing 139 * or forwarding (leting pass) the throwable 140 */ 141 public function addStackTraceElement(thrower, method:Function, args:Array):Void { 142 stackTrace.push(new StackTraceElement(thrower, method, args)); 143 } 144 145 /** 146 * Returns an array that contains {@link StackTraceElement} instances of the 147 * methods invoked before this throwable was thrown. 148 * 149 * <p>The last element is always the one that contains the actual method that 150 * threw the throwable. 151 * 152 * <p>The stack trace helps you a lot because it says you where the throwing of 153 * the throwable took place and also what arguments caused the throwing. 154 * 155 * <p>The returned stack trace is never {@code null} or {@code undefined}. If 156 * no stack trace element has been set an empty array is returned. 157 * 158 * @return a stack containing the invoked methods until the throwable was thrown 159 */ 160 public function getStackTrace(Void):Array { 161 return stackTrace; 162 } 163 164 /** 165 * Returns the initialized cause. 166 * 167 * <p>The cause is the throwable that caused this throwable to be thrown. 168 * 169 * @return the initialized cause 170 * @see #initCause 171 */ 172 public function getCause(Void) { 173 return cause; 174 } 175 176 /** 177 * Initializes the cause of this throwable. 178 * 179 * <p>The cause can only be initialized once. You normally initialize a cause 180 * if you throw a throwable due to the throwing of another throwable. Thereby 181 * you do not lose the information the cause offers. 182 * 183 * <p>This method returns this throwable to have an easy way to initialize the 184 * cause. Following is how you could use the cause mechanism. 185 * 186 * <code> 187 * try { 188 * myInstance.invokeMethodThatThrowsAThrowable(); 189 * } catch (e:org.as2lib.env.except.Throwable) { 190 * throw new MyThrowable("myMessage", this, arguments).initCause(e); 191 * } 192 * </code> 193 * 194 * @param cause the throwable that caused the throwing of this throwable 195 * @return this throwable itself 196 * @throws org.as2lib.env.except.IllegalArgumentException if the passed-in 197 * {@code newCause} is {@code null} or {@code undefined} 198 * @throws org.as2lib.env.except.IllegalStateException if the cause has 199 * already been initialized 200 * @see #getCause 201 */ 202 public function initCause(newCause):Throwable { 203 if (!newCause) throw new IllegalArgumentException("Cause must not be null or undefined.", this, arguments); 204 if (cause) throw new IllegalStateException("The cause [" + cause + "] has already been initialized.", this, arguments); 205 cause = newCause; 206 return Throwable(this); 207 } 208 209 /** 210 * Returns the message that describes in detail what went wrong. 211 * 212 * <p>The message should be understandable, even for non-programmers. It should 213 * contain detailed information about what went wrong. And maybe also how the user 214 * that sees this message can solve the problem. 215 * 216 * <p>If the throwable was thrown for example because of a wrong collaborator or 217 * an illegal string or something similar, provide the string representation of it 218 * in the error message. It is recommended to put these between []-characters. 219 * 220 * @return the message that describes the problem in detail 221 */ 222 public function getMessage(Void):String { 223 return message; 224 } 225 226 /** 227 * Initializes the error code for this throwable. 228 * 229 * <p>The initialization works only once. Any further initialization results in an 230 * exception. 231 * 232 * <p>Take a look at {@link #getErrorCode} to see what error codes are good for. 233 * 234 * @param errorCode the error code to get localized client messages by 235 * @return this throwable 236 * @see #getErrorCode 237 */ 238 public function initErrorCode(errorCode:String):Throwable { 239 this.errorCode = errorCode; 240 return Throwable(this); 241 } 242 243 /** 244 * Returns the initialized error code. 245 * 246 * <p>Error codes can be used to obtain localized messages appropriate for users; 247 * while the {@link #getMessage} method returns messages inteded for developers to 248 * get hands on the exception and fix bugs more easily. 249 * The localized messages can for example be obtained through a global message 250 * source and property files. 251 * 252 * @return the error code to obtain an error message for users 253 */ 254 public function getErrorCode(Void):String { 255 return errorCode; 256 } 257 258 /** 259 * Returns the string representation of this throwable. 260 * 261 * <p>The string representation is obtained via the stringifier returned by 262 * the static {@link #getStringifier} method. 263 * 264 * <p>If you want to change the string representation either set a new 265 * stringifier via the static {@link #setStringifier} method or if you want 266 * the string representation only change for one throwable and its 267 * sub-classes overwrite this method. 268 * 269 * @return the string representation of this throwable 270 */ 271 private function doToString(Void):String { 272 return getStringifier().execute(this); 273 } 274 275 }