1 /* 2 * $Id$ 3 * 4 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.util; 28 29 import java.io.PrintWriter; 30 import java.util.Enumeration; 31 import java.util.Properties; 32 33 /** 34 * Access to debugging settings which have been activated. 35 * Indexing is by string matching; the key is usually but not always a fully qualified 36 * classname. 37 * 38 * <p> 39 * The system properties have three classes of "value" data. The string may be 40 * equal to the string "true" (case insensitive), it may be parsable into an 41 * integer (by Integer.parseInt()), or it may be any other string. In the 42 * latter case where it is not TRUE and is not an int, it will be interpreted 43 * as a zero if a integer is requested, and FALSE if a boolean is requested. 44 * 45 * <p> 46 * The following definitions define how integer and boolean settings are 47 * interpreted: 48 * <dl> 49 * <dt>A lookup that results in a valid integer: 50 * <dd>Will return that integer for any of the getInt() methods 51 * <dd>Will return false for any getBoolean() method if the integer is less than or equal to zero 52 * <dd>Will return true for and getBoolean() method if the integer is greater than zero 53 * 54 * <dt>A lookup that results in "true" (any case combination): 55 * <dd>Will return one for any of the getInt() methods 56 * <dd>Will return true for any of the getBoolean() methods 57 * 58 * <dt>A lookup that results in neither of the above cases (not "true", nor an integer): 59 * <dd>Will return zero for any of the getInt() methods 60 * <dd>Will return false for any of the getBoolean() methods 61 * </dl> 62 * 63 * @see java.lang.Integer#parseInt 64 */ 65 66 public class Debug { 67 private Debug() { 68 // no instances 69 } 70 71 /** 72 * Print a debugging message. 73 * @param s the message to be printed. 74 */ 75 public static void print(String s) { 76 out.print(s); 77 out.flush(); 78 } 79 80 /** 81 * Print a debugging message and end the line. 82 * @param s the message to be printed. 83 */ 84 public static void println(String s) { 85 out.println(s); 86 out.flush(); 87 } 88 89 /** 90 * Check if overall debugging is enabled. 91 * If it is not enabled, methods will return null, false, and zero, 92 * as appropriate for the type. 93 * @return true if debugging is enabled, and false otherwise 94 */ 95 public static boolean isEnabled() { 96 return masterSwitch; 97 } 98 99 /** 100 * Do a raw lookup of a key, including matching of wildcard settings. 101 * Is the given string matched by any of the debug system properties. 102 * The matching is done according to import-style semantics, using the dot 103 * separator. In addition wildcards from the system properties are allowed 104 * <b>at the end</b> of the key specification. Unlike imports, the wildcard can 105 * match inner class names. 106 * 107 * @param key The name of the setting to be returned 108 * @return The setting, or null if not found. Null is returned if debugging is 109 * disabled entirely. 110 */ 111 public static String getSetting(String key) { 112 // important because there may be uninitialized objects 113 if (masterSwitch == false) 114 return null; 115 116 String match = dProps.getProperty(key); 117 118 if (match != null) 119 return match; 120 else { 121 match = wildProps.search(key); 122 // may be null 123 return match; 124 } 125 } 126 127 /** 128 * Find out the debugging setting for class c. 129 * Lookup is done by looking for fully qualified class name. 130 * 131 * @param c Class whose name should be used to lookup the setting, null results in 132 * a return value of zero. 133 * @return the debugging setting for the specified class 134 */ 135 public static boolean getBoolean(Class<?> c) { 136 init(false); 137 138 if (!masterSwitch) 139 return false; 140 141 String key = getName(c); 142 String setting = getSetting(key); 143 boolean state = convertToBool(setting); 144 145 return state; 146 } 147 148 /** 149 * Find out the debugging setting for class c. 150 * Lookup is done by looking for fully qualified class name with a dot and the 151 * given suffix appended. 152 * 153 * @param c Class whose name should be used to lookup the setting, null results in 154 * a return value of zero. 155 * @param suffix String to append to the classname, null will result in a lookup 156 * of just the classname. 157 * @return the debugging setting for the specified class 158 */ 159 public static boolean getBoolean(Class<?> c, String suffix) { 160 init(false); 161 162 if (!masterSwitch) 163 return false; 164 165 StringBuffer buf = new StringBuffer(getName(c)); 166 if (suffix != null && suffix.length() != 0) { 167 buf.append(Debug.SEPARATOR); 168 buf.append(suffix); 169 } 170 171 String key = buf.toString(); 172 String setting = getSetting(key); 173 boolean state = convertToBool(setting); 174 175 return state; 176 } 177 178 /** 179 * Get a named debugging setting. 180 * @param s the name of the desired debugging setting 181 * @return the value of the debugging setting 182 */ 183 public static boolean getBoolean(String s) { 184 init(false); 185 186 if (!masterSwitch || s == null) 187 return false; 188 189 String setting = getSetting(s); 190 boolean state = convertToBool(setting); 191 192 return state; 193 } 194 195 /** 196 * Find out the debugging setting for class c. 197 * Lookup is done by looking for fully qualified class name. 198 * 199 * @param c the class whose name should be used to lookup the setting 200 * @return the debugging setting for the given class, or 0 if no class 201 * was specified 202 */ 203 public static int getInt(Class<?> c) { 204 init(false); 205 206 if (!masterSwitch || c == null) 207 return 0; 208 209 String key = getName(c); 210 String setting = getSetting(key); 211 int state = convertToInt(setting); 212 213 return state; 214 } 215 216 /** 217 * Find out the debugging setting for class c. 218 * Lookup is done by looking for fully qualified class name with a dot and the 219 * given suffix appended. 220 * 221 * @param c a class whose name should be used to lookup the setting; 222 * null results in a return value of zero. 223 * @param suffix a string to append to the classname; 224 * null will result in a lookup of just the classname. 225 * @return the debugging setting for the class 226 */ 227 public static int getInt(Class<?> c, String suffix) { 228 init(false); 229 230 if (!masterSwitch || c == null) 231 return 0; 232 233 StringBuffer buf = new StringBuffer(getName(c)); 234 if (suffix != null && suffix.length() != 0) { 235 buf.append(Debug.SEPARATOR); 236 buf.append(suffix); 237 } 238 239 String key = buf.toString(); 240 String setting = getSetting(key); 241 int state = convertToInt(setting); 242 243 return state; 244 } 245 246 /** 247 * Get a named debugging setting. 248 * @param s the name of the desired debugging setting 249 * @return the value of the debugging setting 250 */ 251 public static int getInt(String s) { 252 init(false); 253 254 if (!masterSwitch) 255 return 0; 256 257 String setting = getSetting(s); 258 return convertToInt(setting); 259 260 /* 261 String setting = dProps.getProperty(s); 262 if (setting == null) 263 return 0; 264 else if (setting.equalsIgnoreCase("true")) 265 return 1; 266 else { 267 try { 268 return Integer.parseInt(setting); 269 } 270 catch (NumberFormatException e) { 271 e.printStackTrace(out); 272 return 0; 273 } 274 } 275 */ 276 } 277 278 /** 279 * Get the debugging stream, used for writing debug messages. 280 * @return the debugging stream, used to write debug messages 281 */ 282 public static PrintWriter getWriter() { 283 return out; 284 } 285 286 /** 287 * Set properties containing debugging settings. 288 * This is required if the security manager does not allow access to 289 * the system properties. 290 * @param props A table of properties containing debugging settings 291 */ 292 public static void setProperties(Properties props) { 293 givenProps = props; 294 } 295 296 /** 297 * Initialize (or re-initialize) debugging support. 298 * @param force Force reprocessing of System properties. 299 */ 300 public synchronized static void init(boolean force) { 301 if (dProps != null && force != true) 302 return; 303 304 Properties props; 305 306 try { 307 props = System.getProperties(); 308 } 309 catch (SecurityException e) { 310 // this is the backup source of settings 311 props = givenProps; 312 } 313 314 if (props == null) { 315 // we're stuck, must disable debugging 316 masterSwitch = false; 317 return; 318 } 319 320 Enumeration<?> keys = props.propertyNames(); 321 322 dProps = new Properties(); 323 wildProps = new WildcardProperties(); 324 325 while (keys.hasMoreElements()) { 326 String key = (String)(keys.nextElement()); 327 if (key.startsWith(DEBUG_PREFIX)) { 328 // this should be a setProperty() in JDK 1.2+ 329 if (key.equalsIgnoreCase(MASTER_KEY)) { 330 String val = props.getProperty(key); 331 // this will disable all debugging, all methods will return zero or false 332 if (val.equalsIgnoreCase(TRUE_STRING)) 333 masterSwitch = false; 334 } 335 else if (key.endsWith(WILD_SUFFIX)) { 336 wildProps.put(key.substring(DEBUG_PREFIX.length()), props.getProperty(key)); 337 } 338 else 339 dProps.put(key.substring(DEBUG_PREFIX.length()), props.getProperty(key)); 340 } 341 } // while 342 } 343 344 // -------- Private --------- 345 346 /** 347 * Convert a class object into a string appropriate for lookup. 348 * 349 * @param c Must not be null. 350 */ 351 private static String getName(Class<?> c) { 352 // null checking skipped 353 354 String name = c.getName(); 355 356 // compensate for inner classes for which getName() return the internal name 357 name = name.replace('$', '.'); 358 359 return name; 360 } 361 362 /* 363 * Take the user setting and interpret it. 364 * If the setting corresponds to the true string, true is returned. If it corresponds to 365 * an integer greater than zero, true is returned. Otherwise, false is returned. 366 */ 367 private static boolean convertToBool(String setting) { 368 if (setting == null) 369 return false; 370 371 if (setting.equalsIgnoreCase(TRUE_STRING)) 372 return true; 373 else { 374 try { 375 int num = Integer.parseInt(setting); 376 if (num > 0) 377 return true; 378 else 379 return false; 380 } 381 catch (NumberFormatException e) { 382 // not "true", not an integer 383 return false; 384 } 385 } 386 } 387 388 /** 389 * Take the user setting and interpret it. 390 * If it is an integer, that is returned. If it is true, 1 is returned. If it is 391 * anything else, zero is returned. 392 */ 393 private static int convertToInt(String setting) { 394 if (setting == null) 395 return 0; 396 397 if (setting.equalsIgnoreCase(TRUE_STRING)) 398 return 1; 399 else { 400 try { 401 int num = Integer.parseInt(setting); 402 return num; 403 } 404 catch (NumberFormatException e) { 405 // not "true", not an integer 406 return 0; 407 } 408 } 409 } 410 411 // ---- activate as needed ---- 412 private static void setEnabled(String debugKey, boolean state) { 413 } 414 415 private static void setEnabled(String debugKey, int level) { 416 } 417 418 private static void setOutput(PrintWriter w) { 419 out = w; 420 } 421 422 private static final String SEPARATOR = "."; 423 private static final String DEBUG_PREFIX = "debug" + SEPARATOR; 424 private static final String WILD_SUFFIX = "*"; 425 private static final String TRUE_STRING = "true"; 426 private static final String MASTER_KEY = "debug.disable"; 427 428 private static Properties givenProps; // settings used if System won't give 429 private static Properties dProps; // explicit props 430 private static WildcardProperties wildProps; // props which contain wildcards 431 432 private static PrintWriter out; 433 private static boolean defaultBool = false; 434 private static int defaultInt = 0; 435 436 /** 437 * True if debugging is enabled, and false otherwise. 438 */ 439 private static boolean masterSwitch = true; 440 441 static { 442 out = new PrintWriter(System.err); 443 } 444 445 /** 446 * Wildcards are compared against the given key minus one field. 447 * So a search(<tt>"foo.bar.baz"</tt>) does a search for <tt>"foo.bar.*"</tt>. 448 * <tt>"foo.*"</tt> will not be a match. 449 */ 450 private static class WildcardProperties extends Properties { 451 /** 452 * This is the only method in this class that accounts for wildcards. 453 */ 454 public String search(String key) { 455 String lowerKey = key.toLowerCase(); 456 String target = trimTarget(lowerKey); 457 458 Enumeration<?> keys = propertyNames(); 459 while (keys.hasMoreElements()) { 460 String k = (String)(keys.nextElement()); 461 String lowerK = k.toLowerCase(); 462 463 if (lowerK.startsWith(target)) { 464 // should strip target string and dot 465 String tail = lowerK.substring(target.length()); 466 String head = lowerK.substring(0, target.length()); 467 if (tail.equals(wildTail) || head.equals(lowerKey)) 468 return getProperty(k); 469 } 470 } // while 471 472 return null; 473 } 474 475 /** 476 * Remove the last element of the requested key to see if a wildcard fits into 477 * that position. Wildcards can only be valid up one level, so 478 * foo.* cannot match foo.bar.baz, but will match foo.bar. This method 479 * turns foo.bar into foo so it the search method can use "foo". 480 */ 481 String trimTarget(String t) { 482 int index = t.lastIndexOf(Debug.SEPARATOR); 483 if (index != -1) 484 return t.substring(0, index); 485 else 486 return t; 487 } 488 489 static final String wildTail = Debug.SEPARATOR + Debug.WILD_SUFFIX; 490 } 491 } 492