/**
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* All rights reserved. This program and the accompanying materials are made available under
* the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html.
*
*/
/*******************************************************************************
* Copyright (c) 2007 Ecliptical Software Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ecliptical Software Inc. - initial API and implementation
*******************************************************************************/
package gov.redhawk.sca.util;
import gov.redhawk.sca.util.internal.Messages;
import java.io.PrintStream;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Date;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
/**
* Helper class used for debug tracing (defaults to System.out if not specified).
* To use, create a static final variable initialized with the corresponding debug option (tag).
* Wherever tracing is needed, check the {@link #enabled} variable first for best performance,
* then use {@link #trace(String)}, {@link #message(String)}, etc. to print debug messages.
* E.g.,
*
* <pre>
* private static final Debug debug = new Debug("myPluginID", "myTag");
* ...
* if (debug.enabled) {
* debug.trace(...);
* }
* </pre>
* <br>
* Then create a ".options" properties file in the plug-in's root folder so that
* <tt>Eclipse > Run Configuration > myApp > Tracing</tt> lists the available options.
*
* For example:<pre>
* myPluginID/debug=false
* myPluginID/debug/myTag=false
* </pre>
*
* @since 1.1
*/
public final class Debug {
public static final String DEBUG = "/debug"; //$NON-NLS-1$
private static final boolean SHOW_TAG;
private static final boolean SHOW_DATE;
private static final MessageFormat OUT_FORMAT;
static {
final String valueShowTag = Platform.getDebugOption("gov.redhawk.sca.util/debug/showTag"); //$NON-NLS-1$
final String valueShowDate = Platform.getDebugOption("gov.redhawk.sca.util/debug/showDate"); //$NON-NLS-1$
SHOW_TAG = Boolean.valueOf(valueShowTag);
SHOW_DATE = Boolean.valueOf(valueShowDate);
if (Debug.SHOW_TAG) {
if (Debug.SHOW_DATE) {
OUT_FORMAT = new MessageFormat(Messages.Debug__DATE_TAG_MSG_PATTERN);
} else {
OUT_FORMAT = new MessageFormat(Messages.Debug__TAG_MSG_PATTERN);
}
} else if (Debug.SHOW_DATE) {
OUT_FORMAT = new MessageFormat(Messages.Debug__DATE_MSG_PATTERN);
} else {
OUT_FORMAT = new MessageFormat(Messages.Debug__MSG_PATTERN);
}
}
//CHECKSTYLE:OFF Turned off since we gain performance by having this public
/** True if debugging output should occur */
public final boolean enabled;
/** The tag for this debug tracing. */
public final String tag;
//CHECKSTYLE:ON
/** The out stream to print messages to. */
private final PrintStream out;
/** The id of the plugin that this tracing is for */
private final String pluginId;
/**
* Use the specified tag
*
* @param tag the tag
* @param plugin the plugin
*/
public Debug(final Plugin plugin, final String tag) {
this((plugin == null) ? null : plugin.getBundle().getSymbolicName(), tag); // SUPPRESS CHECKSTYLE AvoidInLine
}
/**
* Uses the root DEBUG tag.
*
* @param plugin the plugin
*/
public Debug(final Plugin plugin) {
this(plugin, null);
}
/**
* Uses the root DEBUG tag.
*
* @param pluginId the plugin id
*/
public Debug(final String pluginId) {
this(pluginId, null);
}
/**
* Defaults output to System.out
*
* @param pluginId the plugin id
* @param tag the tag
*/
public Debug(final String pluginId, final String tag) {
this(pluginId, tag, null);
}
/**
* Customize the output behavior of DEBUG
*
* @param pluginId the plugin id
* @param tag the tag
* @param out The print stream to write messaging to
*/
public Debug(final String pluginId, String tag, final PrintStream out) {
Assert.isNotNull(pluginId, Messages.DEBUG__NULL_PLUGIN_ID);
this.pluginId = pluginId;
if (out == null) {
this.out = System.out;
} else {
this.out = out;
}
final String debugTag = this.pluginId + Debug.DEBUG;
if (tag == null) {
tag = debugTag;
} else if (!tag.startsWith(debugTag)) {
tag = debugTag + "/" + tag; //$NON-NLS-1$
}
this.tag = tag;
String value = Platform.getDebugOption(debugTag);
final boolean debug = Boolean.parseBoolean(value); // parseBoolean(.) defaults to false
value = Platform.getDebugOption(this.tag);
final boolean tagEnabled = Boolean.parseBoolean(value);
this.enabled = debug && tagEnabled;
}
/**
* @return the pluginId that this Debugger is associated with
*/
public String getPluginId() {
return this.pluginId;
}
/**
* @return the tag that this debugger is enabled for
*/
public String getTag() {
return this.tag;
}
/**
* Entering method.
*
* @param args the arguments (if any) of the method
*/
public void enteringMethod(final Object... args) {
if (this.enabled) {
final StackTraceElement stack = getCaller();
final String fileName = stack.getFileName();
final String lineNumber = getLineNumberString(stack);
String argStr = ""; //$NON-NLS-1$;
if (args != null && args.length > 0) {
argStr = Arrays.toString(args);
}
if (fileName == null) {
internalMessage(Messages.Debug__ENTERING_TYPE
+ MessageFormat.format(Messages.DEBUG__ENTERING_METHOD_NO_FILE, stack.getClassName(), stack.getMethodName(), argStr));
} else {
internalMessage(Messages.Debug__ENTERING_TYPE
+ MessageFormat.format(Messages.DEBUG__ENTERING_METHOD, stack.getClassName(), stack.getMethodName(), fileName, lineNumber, argStr));
}
}
}
private String getLineNumberString(final StackTraceElement element) {
if (element != null && element.getLineNumber() > 0) {
return Integer.toString(element.getLineNumber());
} else {
return Messages.DEBUG__UNKNOWN_LINE_NUMBER;
}
}
/**
* Gets the first stack trace element called before this class was called
*/
private StackTraceElement getCaller() {
final StackTraceElement[] trace = Thread.currentThread().getStackTrace();
for (final StackTraceElement element : trace) {
if (!getClass().getName().equals(element.getClassName()) && !Thread.class.getName().equals(element.getClassName())) {
return element;
}
}
return null;
}
public void exitingMethod(final Object retVal) {
exitingMethodWithResult(retVal);
}
/**
* Exiting method.
*
* @param retVal the return value of the method
* @return Returns passed in value
* @since 3.4
*/
public <T> T exitingMethodWithResult(final T retVal) {
if (this.enabled) {
final StackTraceElement stack = getCaller();
final String fileName = stack.getFileName();
final String lineNumber = getLineNumberString(stack);
if (fileName == null) {
internalMessage(Messages.Debug__EXITING_TYPE
+ MessageFormat.format(Messages.DEBUG__EXITING_METHOD_WITH_RETURN_VALUE_NO_FILE, stack.getClassName(), stack.getMethodName(), retVal));
} else {
internalMessage(Messages.Debug__EXITING_TYPE
+ MessageFormat.format(Messages.DEBUG__EXITING_METHOD_WITH_RETURN_VALUE,
stack.getClassName(),
stack.getMethodName(),
fileName,
lineNumber,
retVal));
}
}
return retVal;
}
/**
* Leaving method.
*/
public void exitingMethod() {
if (this.enabled) {
final StackTraceElement stack = getCaller();
final String fileName = stack.getFileName();
final String lineNumber = getLineNumberString(stack);
if (fileName == null) {
internalMessage(Messages.Debug__EXITING_TYPE
+ MessageFormat.format(Messages.DEBUG__EXITING_METHOD_NO_FILE, stack.getClassName(), stack.getMethodName()));
} else {
internalMessage(Messages.Debug__EXITING_TYPE
+ MessageFormat.format(Messages.DEBUG__EXITING_METHOD, stack.getClassName(), stack.getMethodName(), fileName, lineNumber));
}
}
}
/**
* Print a debug message for throwing an exception.
*
* The return value is equal to the parameter argument. This allow chaining in the throw statement.
* <code>
* throw Debug.throwing(new Exception());
* </code>
*
* @param e the exception that is to be thrown
* @return Returns the exception that is the argument.
* @since 3.0
*/
public < T extends Throwable > T throwing(final T e) {
if (this.enabled) {
internalMessage(Messages.Debug__THROWN_TYPE + MessageFormat.format(Messages.DEBUG__THROWN, e));
//CHECKSTYLE:OFF
e.printStackTrace(this.out);
//CHECKSTYLE:ON
}
return e;
}
/**
* Traces the catching of the specified throwable.
*
* @param throwable The throwable that is being caught.
*/
public void catching(final Throwable e) {
if (this.enabled) {
internalMessage(Messages.Debug__CATCHING_TYPE + MessageFormat.format(Messages.DEBUG__CATCHING, e));
//CHECKSTYLE:OFF
e.printStackTrace(this.out);
//CHECKSTYLE:ON
}
}
/**
* Traces the catching of the specified throwable.
*
* @param throwable The throwable that is being caught.
* @since 3.0
*/
public void catching(final String msg, final Throwable e) {
if (this.enabled) {
internalMessage(Messages.Debug__CATCHING_TYPE + " " + msg + " " + MessageFormat.format(Messages.DEBUG__CATCHING, e));
//CHECKSTYLE:OFF
e.printStackTrace(this.out);
//CHECKSTYLE:ON
}
}
/**
* Gets the Class and method from the stack trace of the caller.
*
* @param valueDescription The description of the value which is changing.
* @param oldValue The old value.
* @param newValue The new value.
*/
public void changing(final String valueDescription, final Object oldValue, final Object newValue) {
if (this.enabled) {
trace(Messages.Debug__CHANGING_TYPE + MessageFormat.format(Messages.DEBUG__CHANGING, valueDescription, oldValue, newValue));
}
}
/**
* Trace.
*
* @param format the format
* @param args the args
*/
public void trace(final String format, final Object... args) {
if (this.enabled) {
final String msg = MessageFormat.format(format, args);
final StackTraceElement stack = getCaller();
final String fileName = stack.getFileName();
final String lineNumber = getLineNumberString(stack);
if (fileName != null) {
internalMessage("TRACE: " + MessageFormat.format(Messages.Debug__TRACE_FILE_PATTERN, //$NON-NLS-1$
stack.getClassName(),
stack.getMethodName(),
stack.getFileName(),
lineNumber,
msg));
} else {
internalMessage("TRACE: " + MessageFormat.format(Messages.Debug__TRACE_NO_FILE_PATTERN, //$NON-NLS-1$
stack.getClassName(),
stack.getMethodName(),
msg));
}
}
}
/**
* Trace.
*
* @param msg the msg
*/
public void trace(final String msg) {
if (this.enabled) {
final StackTraceElement stack = getCaller();
final String fileName = stack.getFileName();
final String lineNumber = getLineNumberString(stack);
if (fileName != null) {
internalMessage(Messages.Debug__TRACE_TYPE
+ MessageFormat.format(Messages.Debug__TRACE_FILE_PATTERN,
stack.getClassName(),
stack.getMethodName(),
stack.getFileName(),
lineNumber,
msg));
} else {
internalMessage(Messages.Debug__TRACE_TYPE
+ MessageFormat.format(Messages.Debug__TRACE_NO_FILE_PATTERN, stack.getClassName(), stack.getMethodName(), msg));
}
}
}
/**
* Message.
*
* @param msg the msg
*/
public void message(final String msg) {
if (this.enabled) {
internalMessage(Messages.Debug__MESSAGE_TYPE + msg);
}
}
/**
* Message.
*
* @param msg the msg
*/
public void message(final String pattern, final Object... args) {
if (this.enabled) {
internalMessage(Messages.Debug__MESSAGE_TYPE + MessageFormat.format(pattern, args));
}
}
/**
* Message.
*
* @param msg the msg
*/
private void internalMessage(final String msg) {
final Object[] arguments;
if (Debug.SHOW_TAG) {
if (Debug.SHOW_DATE) {
arguments = new Object[] {
new Date(), this.tag, msg
};
} else {
arguments = new Object[] {
this.tag, msg
};
}
} else if (Debug.SHOW_DATE) {
arguments = new Object[] {
new Date(), msg
};
} else {
arguments = new Object[] {
msg
};
}
final StringBuffer result = new StringBuffer();
Debug.OUT_FORMAT.format(arguments, result, null);
this.out.println(result.toString());
}
}