/*
* Software Engineering Tools.
*
* $Id: Test59.java 15 2008-08-04 22:00:07Z hboutemy $
*
* Copyright (c) 1997-2001 Joseph Kiniry
* Copyright (c) 2000-2001 KindSoftware, LLC
* Copyright (c) 1997-1999 California Institute of Technology
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Joseph Kiniry, KindSoftware, nor the
* California Institute of Technology, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KIND SOFTWARE OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package idebughc;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Hashtable;
/**
* <p> A collection of shared algorithms for the this package. </p>
*
* @version $Revision: 15 $ $Date: 2008-08-05 00:00:07 +0200 (Di, 05 Aug 2008) $
* @author Joseph R. Kiniry <joe@kindsoftware.com>
* @see Debug
* @see Context
*/
class Utilities
{
// Attributes
/**
* <p> The Debug object associated with this object. </p>
*/
private Debug debug;
// Inherited Methods
// Constructors
/**
* Construct a new Utilities class.
*/
Utilities(Debug d)
{
this.debug = d;
}
// Public Methods
/**
* <p> Prints the stack trace of the current thread to the current
* debugging output stream. </p>
*
* @concurrency GUARDED
*/
public static synchronized void printStackTrace()
{
Throwable throwable = new Throwable();
throwable.printStackTrace();
}
/**
* <p> Dumps the current stack and stops the current thread. This is
* only safe to do when the current thread is not in a critical section
* (an enclosing context that is synchronized in any way). If the thread
* is in a critical section, or the thread's context is unknown,
* dumpStackSafe() should be used instead. </p>
*
* @concurrency GUARDED
* @see Thread#dumpStack
* @see #dumpStackSafe
* @deprecated Use <code>dumpStackSafe()</code> instead given that this
* method is often unsafe to call and uses the deprecated method
* <code>Thread.stop()</code>.
*/
public static synchronized void dumpStack()
{
Thread currentThread = Thread.currentThread();
currentThread.dumpStack();
currentThread.stop();
}
/**
* <p> Dumps the current stack and but <em>doesn't</em> shut down the
* current thread. This method can be called from any thread
* context. </p>
*
* @concurrency GUARDED
* @see Thread#dumpStack
*/
public static synchronized void dumpStackSafe()
{
Thread currentThread = Thread.currentThread();
currentThread.dumpStack();
}
// Protected Methods
// Package Methods
/**
* <p> Adds a class to a hashtable of class-specific enabled debugging.
* If the current class debugging context is "*", adding a class has no
* effect. If adding the context "*", the database is cleared and the
* "*" in inserted. </p>
*
* @concurrency GUARDED
* @param hashtable the hashtable to which the class is added.
* @param className the name of the class to add to the set of classes
* that have debugging enabled.
*/
static synchronized void addClassToHashtable(Hashtable hashtable,
String className)
{
/** require [hashtable_non_null] (hashtable != null);
[className_non_null] (className != null);
[className_length_positive] (className.length() > 0); **/
// If we are adding "*", the tabled should be cleared and the "*"
// should be inserted.
if (className.equals("*"))
{
hashtable.clear();
hashtable.put("*", new Boolean(true));
} else
// See if an entry for the passed class exists.
if (hashtable.containsKey(className))
return;
else
// If a "*" is in the table, then don't bother adding at all, just
// return true.
if (hashtable.containsKey("*"))
return;
else
// Add a new entry for the passed class.
hashtable.put(className, null);
/** ensure [class_in_hashtable]
(hashtable.containsKey(className)); **/
return;
}
/**
* <p> Removes a class from a database of debugging-enabled classes.
* Note that a class of "*" means that all classes will be removed
* and debugging disabled. There is no way to "undo" such a
* command. Adding classes after the removal of "*" works as you
* would expect. </p>
*
* @concurrency GUARDED
* @param hashtable the hashtable from which the class is removed.
* @param className the name of the class to remove.
*/
static synchronized void removeClassFromHashtable(Hashtable hashtable,
String className)
{
/** require [hashtable_non_null] (hashtable != null);
[className_non_null] (className != null);
[className_length_positive] (className.length() > 0); **/
// If we are removing the class "*", just clear the hashtable.
if (className.equals("*"))
{
hashtable.clear();
} else
// If entry is in the hashtable, remove it.
if (hashtable.containsKey(className))
hashtable.remove(className);
/** ensure [class_not_in_hashtable]
(!hashtable.containsKey(className)); **/
return;
}
/**
* <p> Tests to see if the current debug context warrants output. </p>
*
* @concurrency GUARDED
* @param level The debugging level of this message.
*/
synchronized boolean levelTest(int level)
{
// Get the current thread.
Thread currentThread = Thread.currentThread();
// Check to see if global-debugging is enabled.
if (debug.isOn())
{
// Global debugging is enabled, so check the current global
// debugging level and, if it is greater than or equal to the
// passed debugging level, print out the message.
if ((level >= debug.getLevel()) && sourceClassValid())
return true;
else
return false;
}
// Global debugging is not enabled, so check per-thread debugging.
// Check to see if this thread has a debugging context.
Context debugContext = debug.getContext(currentThread);
// If there is no context, we should not give the ok to print.
if (debugContext == null)
return false;
// Check to see if this thread has debugging enabled.
if (debugContext.isOn() == false)
return false;
// Now, see the current per-thread debugging level is >= the
// passed debugging level. If this condition holds, print the
// message.
if ((level >= debugContext.getLevel()) && sourceClassValid())
return true;
return false;
}
/**
* <p> Tests to see if the current debug context warrants output. </p>
*
* @concurrency GUARDED
* @returns a boolean indicating if the context warrants output.
* @param category is the category of this message.
*/
synchronized boolean categoryTest(String category)
{
/** require [category_non_null] (category != null);
[category_length_positive] (category.length() > 0); **/
int categoryLevel = 0;
// Get the current thread.
Thread currentThread = Thread.currentThread();
// Check to see if global-debugging is enabled.
if (debug.isOn())
{
// Get a reference to the global category hashtable.
Hashtable categoryHashtable =
(Hashtable)(debug.threadHashtable.get("GLOBAL_CATEGORIES"));
// If this category is not defined in the global hashtable,
// we break out of the global checks and start the per-thread
// checks.
if (categoryHashtable.containsKey(category))
{
// Get the debugging level of this defined global category.
categoryLevel =
((Integer)(categoryHashtable.get(category))).intValue();
// Global debugging is enabled, the category is defined in the
// global database, so check the current global debugging level
// and, if it is greater than or equal to the debugging level of
// the passed category, print out the message.
if ((categoryLevel >= debug.getLevel()) && sourceClassValid())
return true;
else return false;
}
}
// Global debugging is not enabled, so check per-thread debugging.
// Check to see if this thread has a debugging context.
Context debugContext = debug.getContext(currentThread);
// If there is no context, we should not give the ok to print.
if (debugContext == null)
return false;
// Check to see if this thread has debugging enabled.
if (debugContext.isOn() == false)
return false;
// Check to see if this category is defined for the current thread.
if (!debugContext.categoryHashtable.containsKey(category))
return false;
// The current thread has context, debugging is enabled, the
// category is defined, so get the per-thread debugging level of
// this defined per-thread category.
categoryLevel =
((Integer)(debugContext.categoryHashtable.get(category))).intValue();
// Now, see the current per-thread debugging level is >= the
// per-thread category debugging level of the passed category. If
// this condition holds, print the message.
if ((categoryLevel >= debugContext.getLevel()) && sourceClassValid())
return true;
return false;
}
/**
* <p> Tests to see whether the object performing the debugging action is
* permitted to print in the current debugging context. </p>
*
* @concurrency GUARDED
* @return a true if the object performing the debugging action
* is permitted to print in the current debugging context.
*/
synchronized boolean sourceClassValid()
{
int index, startIndex, parenIndex;
Throwable throwable;
StringWriter stringWriter;
PrintWriter printWriter;
StringBuffer stringBuffer;
String string, matchString, className;
Hashtable classHashtable;
// Create a new Throwable object so that we can get a snapshot of
// the current execution stack. Snapshot the stack into a
// StringBuffer that we can parse.
throwable = new Throwable();
stringWriter = new StringWriter();
printWriter = new PrintWriter(stringWriter);
throwable.printStackTrace(printWriter);
// Now stringWriter contains a textual snapshot of the current
// execution stack. We need to pull lines out of it until we find
// the last line containing the string "at idebug.". The very
// next line is the stack level of the object that called the
// IDebug method in question. We need to strip out its name, then
// compare it with the database of classes that have debugging
// enabled.
stringBuffer = stringWriter.getBuffer();
string = stringBuffer.toString();
// Match to the last occurance of a idebug stack frame.
matchString = new String("idebug.");
index = string.lastIndexOf(matchString);
// Bump the index past the matched string.
startIndex = index + matchString.length() + 1;
// Match forward to the beginning of the classname for the next
// stack frame (the object that called a idebug method).
index = string.indexOf("at ", startIndex);
// Bump up the index past the "at ".
index += 3;
// Grab out the class name of the next stack frame.
parenIndex = string.indexOf("(", index);
// So, everything between index and parenIndex is the class name
// that we are interested in.
className = string.substring(index, parenIndex);
// Strip off the last part past the last ".", it is a method name.
index = className.lastIndexOf(".");
className = className.substring(0, index);
// Now, we have the name of the class that called the debugging
// routine. It is stored in className.
// See if global debugging is enabled.
if (debug.isOn())
{
// It is, so see if this class is included in the list of
// classes that have debugging enabled.
classHashtable =
(Hashtable)(debug.threadHashtable.get("GLOBAL_CLASSES"));
// If "*" is in the hashtable, then we are testing for reductive
// specification of classes. I.e. if the class is not in the
// table _and_ "*" is in the table, then we _should_ return a
// true. If "*" is _not_ in the table and the class _is_, then
// we _should_ return a true. We do not return a false here
// because there is still the possibility that per-thread
// context will specify that output should appear.
if ((classHashtable.containsKey("*")) &&
(!classHashtable.containsKey(className)))
return true;
else
if (classHashtable.containsKey(className))
return true;
}
// Either global debugging isn't enabled or the global debugging
// context didn't specify that output should appear. So, now we
// check the per-thread context.
Thread currentThread = Thread.currentThread();
// If there is no per-thread context for the current thread,
// return a false.
if (!debug.threadHashtable.containsKey(currentThread))
return false;
// The table has the key, so get the record for this thread.
Context debugContext =
(Context)(debug.threadHashtable.get(currentThread));
// Is debugging turned on at all for this thread? If not, return
// a false.
if (!debugContext.isOn())
return false;
// Debugging is enabled for this thread, so perform the same check
// as above to see if the calling class should output debugging
// information. This time, if we fail, we fail.
classHashtable = debugContext.classHashtable;
if (classHashtable.containsKey("*"))
{
if (classHashtable.containsKey(className))
return false;
else
return true;
}
else
{
if (classHashtable.containsKey(className))
return true;
else
return false;
}
}
// Private Methods
// Inner Classes
} // end of class Utilities
/*
* Local Variables:
* Mode: Java
* fill-column: 75
* End:
*/