001 /*
002 * Copyright 2006 Mat Gessel <mat.gessel@gmail.com>
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package asquare.gwt.debug.client;
017
018 import asquare.gwt.debug.client.impl.DebugImpl;
019
020 import com.google.gwt.core.client.GWT;
021 import com.google.gwt.core.client.JavaScriptObject;
022 import com.google.gwt.user.client.Event;
023
024 /**
025 * <p>
026 * Facade for debugging methods. Creates methods for printing debug statements
027 * from JSNI and native JavaScript. You can enable/disable debug methods by
028 * pressing the <code>Esc</code> key twice sequentially.
029 * <p>
030 * Uses a {@link asquare.gwt.debug.client.DebugEventListener DebugEventListener}
031 * to listen for the enabler key. <em>See
032 * {@link asquare.gwt.debug.client.DebugEventListener DebugEventListener} for
033 * potential conflicts with PopupPanel and DialogBox.</em>
034 * <p>
035 * A stub version of this class is provided which will remove this class
036 * definition from the final deliverable if placed ahead of this implementation
037 * in the classpath. The stub class will prevent debug statements from being
038 * displayed, however, the string will still be created by the browser's
039 * interpreter. To entirely remove debugging statements from the deliverable you
040 * will need to do something like this:
041 *
042 * <pre>
043 * if (Debug.ENABLED)
044 * {
045 * Debug.println("Foo.bar(" + value1 + ',' + value2 + ')');
046 * }
047 * </pre>
048 * <code>Debug.ENABLED</code> is <code>false</code> in the stub version.
049 * </p>
050 * <P>
051 * The compiler may optimize out the class if you <strong>only</strong>
052 * reference this class through JSNI. Consider calling
053 * {@link #init() Debug.init()} in your entry point or test case if you are
054 * getting this message: <br/>
055 * <code>com.google.gwt.core.client.JavaScriptException: JavaScript TypeError exception: Object doesn't support this property or method</code>.
056 *
057 * @see DebugConsole
058 */
059 public class Debug
060 {
061 public static final boolean ENABLED = true;
062
063 /**
064 * The default debug enabler key (<code>Esc</code>).
065 */
066 public static final char DEFAULT_ENABLE_KEY = (char) 27;
067
068 private static boolean s_runtimeEnabled = false;
069
070 private static boolean s_initRunning = false;
071
072 private static boolean s_initialized = false;
073
074 private static DebugEventListener s_eventTracer = null;
075
076 private static Enabler s_enabler = null;
077
078 private static char s_enableKey = DEFAULT_ENABLE_KEY;
079
080 private static DebugImpl s_impl = (DebugImpl) GWT.create(DebugImpl.class);
081
082 static
083 {
084 init();
085 }
086
087 /**
088 * This is called automatically when the class (script) is loaded. If you
089 * only reference this class via JavaScript you need to call init() (or
090 * another method) to ensure this class is not optimized out by the
091 * compiler.
092 */
093 public static void init()
094 {
095 if (ENABLED && ! s_initialized && ! s_initRunning)
096 {
097 s_initRunning = true;
098
099 jsInit();
100
101 (s_enabler = new Enabler()).install();
102
103 s_initialized = true;
104 s_initRunning = false;
105 }
106 }
107
108 /**
109 * Sets up a "native" JS API so you don't have to type the contorted JNI
110 * syntax to print debug statements from JS.
111 */
112 private static native void jsInit() /*-{
113 if ($wnd.Debug === undefined)
114 {
115 $wnd.Debug = new Object();
116 }
117
118 if (typeof Debug == "undefined")
119 {
120 Debug = $wnd.Debug;
121 }
122
123 Debug.enable = $wnd.Debug.enable = function()
124 {
125 @asquare.gwt.debug.client.Debug::enable()();
126 };
127
128 Debug.enableSilently = $wnd.Debug.enableSilently = function()
129 {
130 @asquare.gwt.debug.client.Debug::enableSilently()();
131 };
132
133 Debug.disable = $wnd.Debug.disable = function()
134 {
135 @asquare.gwt.debug.client.Debug::disable()();
136 };
137
138 Debug.print = $wnd.Debug.print = function(object)
139 {
140 @asquare.gwt.debug.client.Debug::print(Ljava/lang/String;)("" + object);
141 };
142
143 Debug.println = $wnd.Debug.println = function(object)
144 {
145 @asquare.gwt.debug.client.Debug::println(Ljava/lang/String;)("" + object);
146 };
147
148 Debug.prettyPrint = $wnd.Debug.prettyPrint = function(object)
149 {
150 if (typeof object == "undefined")
151 {
152 Debug.println("undefined");
153 }
154 else if (object == null)
155 {
156 Debug.println("null");
157 }
158 else if (object instanceof Array)
159 {
160 Debug.println("[Array length=" + object.length + "]");
161 }
162 else if (object.nodeName)
163 {
164 Debug.println("[object " + object.nodeName + "]");
165 }
166 else
167 {
168 Debug.println(object);
169 }
170 };
171
172 Debug.dump = $wnd.Debug.dump = function(object)
173 {
174 Debug.prettyPrint(object);
175 if (object instanceof Array)
176 {
177 for (var i = 0; i < object.length; i++)
178 {
179 Debug.println(" [" + i + "]" + object[i]);
180 }
181 }
182 else
183 {
184 for (var member in object)
185 {
186 Debug.print(" +" + member + "=");
187 try
188 {
189 Debug.prettyPrint(object[member]);
190 }
191 catch(e)
192 {
193 Debug.println("(Exception caught: " + e + ")");
194 }
195 }
196 }
197 };
198 }-*/;
199
200 /**
201 * Get the key which enables & disables debugging.
202 *
203 * @return a keycode representing the enabler key
204 */
205 public static char getEnableKey()
206 {
207 return s_enableKey;
208 }
209
210 /**
211 * Set the key which enables & disables debugging.
212 *
213 * @param enableKey a keycode representing the enabler key
214 */
215 public static void setEnableKey(char enableKey)
216 {
217 s_enableKey = enableKey;
218 if (s_enabler != null)
219 {
220 s_enabler.setEnableKey(s_enableKey);
221 }
222 }
223
224 /**
225 * Enable debugging, but does not show the in-browser console.
226 */
227 public static void enableSilently()
228 {
229 if (ENABLED)
230 {
231 s_runtimeEnabled = true;
232 s_enabler.setEnabled(true);
233 }
234 }
235
236 /**
237 * Enables debugging and prints a message stating so.
238 */
239 public static void enable()
240 {
241 if (ENABLED && ! s_runtimeEnabled)
242 {
243 s_runtimeEnabled = true;
244 s_enabler.setEnabled(true);
245 Debug.println("Debug enabled");
246 }
247 }
248
249 /**
250 * Disables debugging, prints a message stating so and hides the in-browser console.
251 */
252 public static void disable()
253 {
254 if (ENABLED)
255 {
256 Debug.println("Debug disabled");
257 s_enabler.setEnabled(false);
258 s_runtimeEnabled = false;
259 }
260 }
261
262 /**
263 * Is debugging enabled at compile time and runtime.
264 *
265 * @return true if enabled
266 */
267 public static boolean isEnabled()
268 {
269 return ENABLED && s_runtimeEnabled;
270 }
271
272 /**
273 * Prints a debugging message to the {@link DebugConsole} widget. The
274 * message is mirrored to the browser's native console, if supported. In
275 * hosted mode the message is mirrored to {@link System#out} and the GWT
276 * Shell.
277 *
278 * @param message
279 * @see DebugConsole
280 */
281 public static void print(String message)
282 {
283 if (ENABLED && s_runtimeEnabled)
284 {
285 System.out.print(message);
286 GWT.log(message, null);
287 printToBrowserConsole(message);
288 DebugConsole.getInstance().print(message);
289 }
290 }
291
292 /**
293 * Prints a debugging message to the {@link DebugConsole} widget, follwed by
294 * "\r\n". The message is mirrored to the browser's native console, if
295 * supported. In hosted mode the message is mirrored to {@link System#out}
296 * and the GWT Shell.
297 *
298 * @param message
299 * @see DebugConsole
300 */
301 public static void println(String message)
302 {
303 if (ENABLED && s_runtimeEnabled)
304 {
305 System.out.println(message);
306 GWT.log(message, null);
307 printToBrowserConsole(message);
308 DebugConsole.getInstance().println(message);
309 }
310 }
311
312 /**
313 * Prints a message to the native browser console, if available. Otherwise
314 * does nothing.
315 *
316 * @param message
317 */
318 public static void printToBrowserConsole(String message)
319 {
320 s_impl.printToBrowserConsole(message);
321 }
322
323 /**
324 * Prints a description of a JavaScript object. Not very useful for Java
325 * objects.
326 *
327 * @see #prettyPrint(JavaScriptObject)
328 */
329 public static native void prettyPrint(Object o) /*-{
330 if (@asquare.gwt.debug.client.Debug::isEnabled()())
331 {
332 Debug.prettyPrint(o);
333 }
334 }-*/;
335
336 /**
337 * Prints a description of a JavaScript object. Not very useful for Java
338 * objects.
339 */
340 public static native void prettyPrint(JavaScriptObject o) /*-{
341 if (@asquare.gwt.debug.client.Debug::isEnabled()())
342 {
343 Debug.prettyPrint(o);
344 }
345 }-*/;
346
347 /**
348 * Displays members of native JavaScript objects. Not very useful for Java
349 * objects.
350 *
351 * @see #dump(JavaScriptObject)
352 */
353 public static native void dump(Object o) /*-{
354 if (@asquare.gwt.debug.client.Debug::isEnabled()())
355 {
356 Debug.dump(o);
357 }
358 }-*/;
359
360 /**
361 * Displays members of native JavaScript objects. This is implemented in
362 * JavaScript where Java objects are opaque. GWT doesn't support reflection
363 * so we can't see objects members in Java.
364 */
365 public static native void dump(JavaScriptObject o) /*-{
366 if (@asquare.gwt.debug.client.Debug::isEnabled()())
367 {
368 Debug.dump(o);
369 }
370 }-*/;
371
372 /**
373 * Installs an experimental event tracer. Press the specified key twice to
374 * start tracing. A message will be printed to the console for each event
375 * matching eventMask.
376 * <p>
377 * Some events are not tracing in Firefox at this time, even though the
378 * events trigger listeners in GWT widgets.
379 * </p>
380 *
381 * @param enableKey a keyCode or (char) 0 for the default ('e')
382 * @param eventMask a bitwise event mask, 0 for no events or -1 for the
383 * default
384 * @see DebugEventListener
385 * @see Event
386 */
387 public static void installEventTracer(char enableKey, int eventMask)
388 {
389 if (ENABLED && s_eventTracer == null)
390 {
391 s_eventTracer = new DebugEventListener(enableKey, eventMask, null);
392 s_eventTracer.install();
393 }
394 }
395
396 /**
397 * @see #installEventTracer(char, int)
398 */
399 public static void uninstallEventTracer()
400 {
401 if (s_eventTracer != null)
402 {
403 s_eventTracer.uninstall();
404 s_eventTracer = null;
405 }
406 }
407
408 /**
409 * Listens for the enable key and enables/disables debugging.
410 */
411 private static final class Enabler extends DebugEventListener
412 {
413 Enabler()
414 {
415 super(Debug.DEFAULT_ENABLE_KEY, 0, "Debug enabler");
416 }
417
418 protected void doDisabled()
419 {
420 Debug.disable();
421 }
422
423 protected void doEnabled()
424 {
425 Debug.enable();
426 }
427
428 protected void doEvent(Event event)
429 {
430 // NOOP
431 }
432 }
433 }