001 /*
002 * Copyright (c) Holger Pfaff - http://pfaff.ws
003 *
004 * This software maybe used for any purpose provided the
005 * above copyright notice is retained. It is supplied as is.
006 * No warranty expressed or implied - Use at your own risk.
007 */
008
009 import java.io.PrintWriter;
010 import java.io.StringWriter;
011 import java.text.DateFormat;
012 import java.text.SimpleDateFormat;
013 import java.util.Date;
014 import java.util.Vector;
015 import java.util.TimeZone;
016 import java.util.Enumeration;
017 import java.util.StringTokenizer;
018
019 /**
020 * (#)Debug.java
021 * @author Holger Pfaff
022 * @version 3.2 19-Mar-2004<br><br>
023 *
024 * Standalone class with static methods to aid with debugging.
025 * Debugging output can be controlled with different methods:<br><br>
026 *
027 * 1) By specifying the class with should be debugged<br>
028 * 2) By specifying an optional "level" with should be debugged<br>
029 * 3) By specifying the "warning" flag<br>
030 *
031 * The format can be controlled via some flags<br><br>
032 *
033 * 1) Thread (p): Shows the name of the current Thread<br>
034 * 2) Location (l): Shows the location within the source file<br>
035 * 3) Time (t): Shows the current time including millisec.<br>
036 * 4) Delta (d): Shows the time delta from the last debugging output<br>
037 * 5) Warning (w): Shows warning messages (see above)<br>
038 * 6) Method (m): Shows the name of the current method<br>
039 * 7) Class (c): Shows the class name<br><br>
040 *
041 * Location printing only works correct if there is no JIT Compiler active.
042 *
043 */
044
045 public class Debug extends Object {
046
047 /**
048 * Stream to write to
049 */
050 private static PrintWriter _writer = new PrintWriter(System.err);
051
052 /**
053 * format to use for timestamps
054 */
055 private static DateFormat _datef = new SimpleDateFormat("HH:mm:ss.SSS ");
056
057 /**
058 * this is where we get our time from ...
059 */
060 private static Date _date = new Date();
061
062 /**
063 * show thread id ? (p)
064 */
065 private static boolean _thread = false;
066
067 /**
068 * show warnings ? (w)
069 */
070 private static boolean _warn = false;
071
072 /**
073 * show location in source file ? (l)
074 */
075 private static boolean _loc = false;
076
077 /**
078 * show methodname ? (m)
079 */
080 private static boolean _meth = false;
081
082 /**
083 * show an absolute timestamp ? (t)
084 */
085 private static boolean _time = false;
086
087 /**
088 * show a delta timestamp ? (d)
089 */
090 private static boolean _delta = false;
091
092 /**
093 * show class name ? (c)
094 */
095 private static boolean _cname = false;
096
097 /**
098 * store levels to debug
099 */
100 private static Vector _level = new Vector();
101
102 /**
103 * store classes to debug
104 */
105 private static Vector _class = new Vector();
106
107 /**
108 * empty string array - used internally for error conditions
109 */
110 private static String[] _nloc = new String[0];
111
112 /**
113 * Configure debugging from command line arg.
114 * Format shoud be: <flags>:<level>,...:<class>,...
115 * e.g. plt:*:Main
116 * shows thread name, locaton & time for any level (*) and class Main
117 *
118 * @param a the command line arg
119 */
120 public static void setArgs(String a) {
121 StringTokenizer t = new StringTokenizer(a, ":");
122 if(t.hasMoreTokens()) setFlags(t.nextToken(), true);
123 if(t.hasMoreTokens()) setLevel(t.nextToken(), true);
124 if(t.hasMoreTokens()) setClass(t.nextToken(), true);
125 }
126
127 /**
128 * Set debugging flags
129 *
130 * @param f flaf to set
131 * @param b set or re-set ?
132 */
133 public static void setFlags(String f, boolean b) {
134 Compiler.disable();
135 for(int i = 0; i < f.length(); ++i) {
136 switch(f.charAt(i)) {
137 case 'p': _thread = b; break;
138 case 'w': _warn = b; break;
139 case 'm': _meth = b; break;
140 case 'l': _loc = b; break;
141 case 't': _time = b; break;
142 case 'c': _cname = b; break;
143 case 'd': _delta = b; _datef.setTimeZone(TimeZone.getTimeZone("GMT")); break;
144 case '*': setFlags("pwltmc", b); break;
145 }
146 }
147 }
148
149 /**
150 * Set/unset debugging levels,classes
151 *
152 * @param v vector to use - _levels, _classes
153 * @param s level or class to set/unset
154 * @param a set or unset?
155 */
156 private static void set(Vector v, String s, boolean a) {
157 if(s.indexOf(",") > -1) {
158 for(Enumeration e = new StringTokenizer(s, ",") ; e.hasMoreElements() ;) {
159 set(v, (String) e.nextElement(), a);
160 }
161 } else {
162 if(a) {
163 if(v.contains(s) == false) v.addElement(s);
164 } else {
165 v.removeElement(s);
166 }
167 }
168 }
169
170 /**
171 * Set/unset debugging levels
172 *
173 * @param l level to set/unset
174 * @param a set or unset?
175 */
176 public static void setLevel(String l, boolean a) {
177 set(_level, l, a);
178 }
179
180 /**
181 * Set/unset debugging classes
182 *
183 * @param c class to set/unset
184 * @param a set or unset?
185 */
186 public static void setClass(String c, boolean a) {
187 set(_class, c, a);
188 }
189
190 /**
191 * Set/unset debugging classes
192 *
193 * @param c class to set/unset
194 * @param a set or unset?
195 */
196 public static void setClass(Class c, boolean a) {
197 set(_class, c.getName(), a);
198 }
199
200 /**
201 * Return classname of Object. o ist eiter:<br>
202 * - a Class object<br>
203 * - a String containing the class name<br>
204 * - or the Object itself<br>
205 *
206 * @param o object to return classname of
207 * @return classname of Object as string
208 */
209 public static String getClassName(Object o) {
210 if(o instanceof Class) {
211 return ((Class) o).getName();
212 } else if(o instanceof String) {
213 return o.toString();
214 } else {
215 return o.getClass().getName();
216 }
217 }
218
219 /**
220 * Report an error
221 *
222 * @param m usually the error message or Exception
223 */
224 public static void error(Object m) {
225 println("###ERROR: ", m);;
226 }
227
228 /**
229 * Report a warning
230 *
231 * @param m usually the warning message or Exception
232 */
233 public static void warn(Object m) {
234 if(_warn) println("###WARN: ", m);
235 }
236
237 /**
238 * Debug object o if object o is to debug - no additional message
239 *
240 * @param o Object to debug
241 */
242 public static void debug(Object o) {
243 debug(o, "<>");
244 }
245
246 /**
247 * Debug object o if object o is to debug on all levels - add message m
248 *
249 * @param o Object to debug
250 *
251 * @param m the message
252 */
253 public static void debug(Object o, Object m) {
254 debug("*", o, m);
255 }
256
257 /**
258 * Debug object o if object o and level l is to debug - add message m
259 *
260 * @param l level
261 * @param o Object to debug
262 * @param m the message
263 */
264 public static void debug(Object l, Object o, Object m) {
265 if(isLevel(l) && isClass(o)) println(cname(o), m);
266 }
267
268 /**
269 * Check if level l was requested to debug.
270 *
271 * @param l level
272 */
273 public static boolean isLevel(Object l) {
274 if(_level.contains("*")) return true;
275 if(l == null) return false;
276 return _level.contains(l);
277 }
278
279 /**
280 * Check if class o was requested to debug.
281 *
282 * @param o class
283 */
284 public static boolean isClass(Object o) {
285 if(_class.contains("*")) return true;
286 if(o == null) return false;
287 if(o instanceof String) return _class.contains(o.toString());
288 if(o instanceof Class == false) o = o.getClass();
289 for(Class c = (Class) o; c != null; c = c.getSuperclass()) {
290 if(_class.contains(c.getName())) return true;
291 }
292 return false;
293 }
294
295
296 /**
297 * form a debug string
298 *
299 * @param p prefix (e.g. timestamp)
300 * @param m the message
301 *
302 * @return a nice formatted string
303 */
304 public static String toString(String p, Object m) {
305 String[] l = _meth || _loc ? stackline(0) : _nloc;
306 return time() + thread() + stackline2meth(l) + p + msg(m) + stackline2loc(l);
307 }
308
309 /**
310 * print a debug string
311 *
312 * @param p prefix (e.g. timestamp)
313 * @param m the message
314 */
315 public static void println(String p, Object m) {
316 _writer.println(toString(p, m));
317 _writer.flush();
318 }
319
320 /**
321 * put out a timestamp if requested
322 */
323 public static String time() {
324 if(_time || _delta) {
325 Date d = _date;
326 _date = new Date();
327 d.setTime(_delta ? _date.getTime() - d.getTime() : _date.getTime());
328 return _datef.format(d);
329 } else {
330 return "";
331 }
332 }
333
334 /**
335 * put out a classname if requested
336 *
337 * @param o object to put out classname for
338 */
339 public static String cname(Object o) {
340 return _cname ? "(" + o.getClass().getName() + ") " : "";
341 }
342
343 /**
344 * put out a threadname if requested
345 */
346 public static String thread() {
347 return _thread ? "[" + Thread.currentThread().getName() + "] " : "";
348 }
349
350 /**
351 * format the error msg
352 *
353 * @param o object to format
354 */
355 public static String msg(Object o){
356 return o == null ? "<null>" : (o instanceof Exception ? msg((Exception) o) : o.toString());
357 }
358
359 /**
360 * format the error msg
361 *
362 * @param x Exception to format
363 */
364 public static String msg(Exception x) {
365 String m = x.getMessage();
366 String n = x.getClass().getName();
367 n = n.substring(n.lastIndexOf('.') + 1);
368 if(m == null || m.length() == 0) {
369 return n;
370 } else {
371 return n + ": " + m;
372 }
373 }
374
375 /**
376 * return stack trace as string
377 *
378 * @param x Exception to format
379 */
380 public static String stack(Exception X) {
381 try {
382 StringWriter b = new StringWriter();
383 PrintWriter w = new PrintWriter(b);
384 X.printStackTrace(w);
385 w.close();
386 b.close();
387 return b.toString().replace('\r', ' ');
388 } catch(Exception x) {
389 return "";
390 }
391 }
392
393 /**
394 * return specific stackline - 0 is where exception occured
395 *
396 * @param 0 position in stack to return
397 */
398 public static String[] stackline(int s){
399 StringTokenizer t = new StringTokenizer(stack(new Exception()), "\n\r");
400 for(int i = 0; t.hasMoreTokens();) {
401 String l = t.nextToken().trim(); // get next line & remove blanks
402 if(l.startsWith("java") || l.startsWith("at Debug")) {
403 continue; // remove first line & self
404 }
405 if(i++ == s) { // reached desired depth ?
406 StringTokenizer st = new StringTokenizer(l, " ()");
407 String[] sl = new String[st.countTokens()];
408 for(int j = 0; j < sl.length; ++j) sl[j] = st.nextToken();
409 return sl;
410 }
411 }
412 return _nloc; // return empty array on failure
413 }
414
415 /**
416 * extract methodname from stackline
417 */
418 public static String stackline2meth(String[] l) {
419 return _meth && l.length > 1 ? "{" + l[1] + "} " : "";
420 }
421
422 /**
423 * extract location in source from stackline
424 */
425 public static String stackline2loc(String[] l) {
426 return _loc && l.length > 2 ? " (at " + l[2] + ")" : "";
427 }
428 }