package org.incava.qualog;
import java.io.*;
import java.util.*;
/**
* <p>Writes the logging output, applying filters and decorations. The
* <code>Qualog</code> class offers a much cleaner and more thorough interface
* than this class.</p>
*
* @see org.incava.qualog.Qualog
*/
public class QlWriter
{
public static final int NO_OUTPUT = 0;
public static final int QUIET = 1;
public static final int VERBOSE = 2;
public int fileWidth = 25;
public boolean columns = true;
public int lineWidth = 5;
public int functionWidth = 25;
public int classWidth = 35;
public boolean showFiles = true;
public boolean showClasses = true;
public PrintWriter out = new PrintWriter(System.out, true);
public List packagesSkipped = new ArrayList(Arrays.asList(
new String[] {
"org.incava.qualog",
}));
public List classesSkipped = new ArrayList(Arrays.asList(
new String[] {
"tr.Ace"
}));
public List methodsSkipped = new ArrayList(Arrays.asList(
new String[] {
}));
private int outputType = NO_OUTPUT;
private Map packageColors = new HashMap();
private Map classColors = new HashMap();
private Map methodColors = new HashMap();
private Map fileColors = new HashMap();
private Map levelColors = new HashMap();
private StackTraceElement prevStackElement = null;
private Thread prevThread = null;
private String prevDisplayedClass = null;
private String prevDisplayedMethod = null;
private QlLevel level = Qualog.LEVEL9;
private List filters = new ArrayList();
private boolean useColor = true;
/**
* Adds a filter to be applied for output.
*
* @see org.incava.qualog.QlFilter
*/
public void addFilter(QlFilter filter)
{
filters.add(filter);
}
public void setDisabled(Class cls)
{
addFilter(new QlClassFilter(cls, null));
}
public void setClassColor(String className, ANSIColor color)
{
classColors.put(className, color);
}
public void setPackageColor(String pkg, ANSIColor color)
{
}
public void setMethodColor(String className, String methodName, ANSIColor color)
{
methodColors.put(className + "#" + methodName, color);
}
public void clearClassColor(String className)
{
classColors.remove(className);
}
public void setFileColor(String fileName, ANSIColor color)
{
fileColors.put(fileName, color);
}
public void set(boolean columns, int fileWidth, int lineWidth, int classWidth, int functionWidth)
{
this.columns = columns;
this.fileWidth = fileWidth;
this.lineWidth = lineWidth;
this.classWidth = classWidth;
this.functionWidth = functionWidth;
}
/**
* Sets the output type and level. Either verbose or quiet can be enabled.
*/
public void setOutput(int type, QlLevel level)
{
this.outputType = type;
this.level = level;
}
public boolean verbose()
{
return outputType == VERBOSE;
}
public void setColumns(boolean cols)
{
columns = cols;
}
public void addClassSkipped(Class cls)
{
addClassSkipped(cls.getName());
}
public void addClassSkipped(String clsName)
{
classesSkipped.add(clsName);
}
/**
* Resets parameters to their defaults.
*/
public void clear()
{
packageColors = new HashMap();
classColors = new HashMap();
methodColors = new HashMap();
fileColors = new HashMap();
levelColors = new HashMap();
prevStackElement = null;
prevThread = null;
prevDisplayedClass = null;
prevDisplayedMethod = null;
level = Qualog.LEVEL9;
filters = new ArrayList();
}
public void reset()
{
prevThread = Thread.currentThread();
prevStackElement = null;
}
public boolean stack(QlLevel level,
ANSIColor[] msgColors,
String name,
Object obj,
ANSIColor fileColor,
ANSIColor classColor,
ANSIColor methodColor,
int numFrames)
{
if (isLoggable(level)) {
String nm = name == null ? "" : name;
if (obj == null) {
String msg = nm + ": " + "null";
return stack(level, msgColors, msg, fileColor, classColor, methodColor, numFrames);
}
else if (obj instanceof Collection) {
Collection c = (Collection)obj;
return QlCollection.stack(level, msgColors, nm, c, fileColor, classColor, methodColor, numFrames);
}
else if (obj instanceof Iterator) {
Iterator it = (Iterator)obj;
return QlIterator.stack(level, msgColors, nm, it, fileColor, classColor, methodColor, numFrames);
}
else if (obj instanceof Enumeration) {
Enumeration en = (Enumeration)obj;
return QlEnumeration.stack(level, msgColors, nm, en, fileColor, classColor, methodColor, numFrames);
}
else if (obj instanceof Object[]) {
Object[] ary = (Object[])obj;
return QlObjectArray.stack(level, msgColors, nm, ary, fileColor, classColor, methodColor, numFrames);
}
else if (obj instanceof Map) {
Map m = (Map)obj;
return QlMap.stack(level, msgColors, nm, m, fileColor, classColor, methodColor, numFrames);
}
else if (obj.getClass().isArray()) {
String[] strs = null;
if (obj instanceof byte[]) {
byte[] ary = (byte[])obj;
strs = new String[ary.length];
for (int ai = 0; ai < ary.length; ++ai) {
strs[ai] = String.valueOf(ary[ai]);
}
}
else if (obj instanceof char[]) {
char[] ary = (char[])obj;
strs = new String[ary.length];
for (int ai = 0; ai < ary.length; ++ai) {
strs[ai] = String.valueOf(ary[ai]);
}
}
else if (obj instanceof double[]) {
double[] ary = (double[])obj;
strs = new String[ary.length];
for (int ai = 0; ai < ary.length; ++ai) {
strs[ai] = String.valueOf(ary[ai]);
}
}
else if (obj instanceof float[]) {
float[] ary = (float[])obj;
strs = new String[ary.length];
for (int ai = 0; ai < ary.length; ++ai) {
strs[ai] = String.valueOf(ary[ai]);
}
}
else if (obj instanceof int[]) {
int[] ary = (int[])obj;
strs = new String[ary.length];
for (int ai = 0; ai < ary.length; ++ai) {
strs[ai] = String.valueOf(ary[ai]);
}
}
else if (obj instanceof long[]) {
long[] ary = (long[])obj;
strs = new String[ary.length];
for (int ai = 0; ai < ary.length; ++ai) {
strs[ai] = String.valueOf(ary[ai]);
}
}
return QlObjectArray.stack(level, msgColors, nm, strs, fileColor, classColor, methodColor, numFrames);
}
else {
String msg = nm + ": " + objectToString(obj);
return stack(level, msgColors, msg, fileColor, classColor, methodColor, numFrames);
}
}
else {
return true;
}
}
public boolean isSkipped(StackTraceElement ste)
{
String className = ste.getClassName();
if (classesSkipped.contains(className) || methodsSkipped.contains(ste.getMethodName())) {
return true;
}
else {
Iterator pit = packagesSkipped.iterator();
while (pit.hasNext()) {
String pkgName = (String)pit.next();
if (className.startsWith(pkgName)) {
return true;
}
}
}
return false;
}
public boolean isLoggable(QlLevel level)
{
return outputType != NO_OUTPUT && this.level != null && this.level.compareTo(level) >= 0;
}
/**
* Returns the index in the stack where logging (stacks) should be
* displayed. Returns -1 if the end of the stack is reached and no logging
* should occur.
*/
public synchronized int findStackStart(StackTraceElement[] stack)
{
for (int fi = 0; fi < stack.length; ++fi) {
if (!isSkipped(stack[fi])) {
return fi;
}
}
return stack.length;
}
public synchronized boolean stack(QlLevel lvl,
ANSIColor[] msgColor,
String msg,
ANSIColor fileColor,
ANSIColor classColor,
ANSIColor methodColor,
int numFrames)
{
if (isLoggable(lvl)) {
if (outputType == QUIET) {
numFrames = 1;
}
StackTraceElement[] stack = getStack(numFrames);
// when we're switching threads, reset to a null state.
if (!Thread.currentThread().equals(prevThread)) {
reset();
}
int fi = findStackStart(stack);
for (int framesShown = 0; fi < stack.length && framesShown < numFrames; ++fi, ++framesShown) {
StackTraceElement stackElement = stack[fi];
String className = stackElement.getClassName();
String methodName = stackElement.getMethodName();
boolean filtered = false;
if (framesShown == 0) {
Iterator fit = filters.iterator();
while (fit.hasNext()) {
QlFilter filter = (QlFilter)fit.next();
int lineNum = stackElement.getLineNumber();
String fileName = stackElement.getFileName();
if (filter.isMatch(fileName, lineNum, className, methodName)) {
QlLevel flevel = filter.getLevel();
filtered = filtered || flevel != null && level.compareTo(flevel) < 0;
}
}
}
if (filtered) {
return true;
}
StringBuffer buf = new StringBuffer();
if (outputType == VERBOSE) {
if (showFiles) {
outputFileName(buf, fileColor, stackElement);
}
if (showClasses) {
outputClassAndMethod(buf, classColor, methodColor, stackElement);
}
}
outputMessage(buf, framesShown, msgColor, msg, stackElement);
out.println(buf.toString());
// System.err.println("buf: " + buf.toString());
prevStackElement = stackElement;
}
}
return true;
}
void setUseColor(boolean useColor)
{
this.useColor = useColor;
}
protected void outputFileName(StringBuffer buf, ANSIColor fileColor, StackTraceElement stackElement)
{
String fileName = stackElement.getFileName();
buf.append("[");
if (fileName == null) {
fileName = "";
}
if (prevStackElement != null &&
prevStackElement.getFileName() != null &&
prevStackElement.getFileName().equals(fileName)) {
int width = columns ? Math.min(fileWidth, fileName.length()) : fileName.length();
fileName = repeat(width, ' ');
}
String lnStr = stackElement.getLineNumber() >= 0 ? String.valueOf(stackElement.getLineNumber()) : "";
ANSIColor col = fileColor;
if (col == null) {
col = (ANSIColor)fileColors.get(fileName);
}
if (columns) {
if (col == null) {
appendPadded(buf, fileName, fileWidth);
buf.append(' ');
buf.append(repeat(lineWidth - lnStr.length(), ' ')).append(lnStr);
}
else {
buf.append(col);
buf.append(fileName);
buf.append(Qualog.NONE);
repeat(buf, fileWidth - fileName.length(), ' ');
repeat(buf, 1 + lineWidth - lnStr.length(), ' ');
buf.append(col).append(lnStr).append(Qualog.NONE);
}
}
else if (col == null) {
appendPadded(buf, fileName + ":" + lnStr, fileWidth);
}
else {
buf.append(col);
buf.append(fileName);
buf.append(':');
buf.append(lnStr);
buf.append(Qualog.NONE);
repeat(buf, fileWidth - fileName.length() - 1 - lnStr.length(), ' ');
}
buf.append("] ");
}
protected void outputClassAndMethod(StringBuffer buf,
ANSIColor classColor,
ANSIColor methodColor,
StackTraceElement stackElement)
{
buf.append("{");
String className = stackElement.getClassName();
if (classColor == null) {
classColor = (ANSIColor)classColors.get(className);
}
boolean sameClass = prevStackElement != null && prevStackElement.getClassName().equals(className);
if (sameClass) {
className = repeat(prevDisplayedClass.length(), ' ');
classColor = null;
}
else if (className != null && (className.startsWith("org.") || className.startsWith("com."))) {
className = "..." + className.substring(className.indexOf('.', 5) + 1);
}
int totalWidth = classWidth + 1 + functionWidth;
int classPadding = 0;
if (className.length() > classWidth) {
if (classWidth > 0) {
className = className.substring(0, classWidth - 1) + '-';
}
else {
className = "";
}
}
else {
classPadding = classWidth - className.length();
}
if (classColor != null) {
buf.append(classColor);
}
buf.append(className);
if (classColor != null) {
buf.append(Qualog.NONE);
}
if (columns) {
repeat(buf, classPadding, ' ');
}
prevDisplayedClass = className;
buf.append('#');
String methodName = stackElement.getMethodName();
if (methodColor == null) {
methodColor = (ANSIColor)methodColors.get(methodName);
}
if (sameClass && prevStackElement != null && prevStackElement.getMethodName().equals(methodName)) {
methodName = repeat(prevDisplayedMethod.length(), ' ');
methodColor = null;
}
int methodPadding = 0;
if (methodName.length() > functionWidth) {
methodName = methodName.substring(0, functionWidth - 1) + '-';
}
else {
methodPadding = functionWidth - methodName.length();
}
if (methodColor != null) {
buf.append(methodColor);
}
buf.append(methodName);
if (methodColor != null) {
buf.append(Qualog.NONE);
}
if (!columns) {
repeat(buf, classPadding, ' ');
}
repeat(buf, methodPadding, ' ');
prevDisplayedMethod = methodName;
buf.append("} ");
}
protected void outputMessage(StringBuffer buf,
int framesShown,
ANSIColor[] msgColor,
String msg,
StackTraceElement stackElement)
{
// remove ending EOLN
if (framesShown > 0) {
msg = "\"\"";
}
else {
while (msg.length() > 0 && "\r\n".indexOf(msg.charAt(msg.length() - 1)) != -1) {
msg = msg.substring(0, msg.length() - 1);
}
if (useColor) {
boolean hasColor = false;
if (msgColor == null || (msgColor.length > 0 && msgColor[0] == null)) {
ANSIColor col = null;
col = (ANSIColor)methodColors.get(stackElement.getClassName() + "#" + stackElement.getMethodName());
if (col == null) {
col = (ANSIColor)classColors.get(stackElement.getClassName());
if (col == null) {
col = (ANSIColor)fileColors.get(stackElement.getFileName());
}
}
if (col != null) {
msg = col + msg;
hasColor = true;
}
}
else {
for (int i = 0; i < msgColor.length; ++i) {
if (msgColor[i] != null) {
msg = msgColor[i] + msg;
hasColor = true;
}
}
}
if (hasColor) {
msg += Qualog.NONE;
}
}
}
buf.append(msg);
}
protected StackTraceElement[] getStack(int depth)
{
return (new Exception("")).getStackTrace();
}
protected String repeat(int len, char ch)
{
StringBuffer buf = new StringBuffer();
for (int i = 0; i < len; ++i) {
buf.append(ch);
}
return buf.toString();
}
protected StringBuffer repeat(StringBuffer buf, int len, char ch)
{
for (int i = 0; i < len; ++i) {
buf.append(ch);
}
return buf;
}
protected void appendPadded(StringBuffer buf, String str, int maxSize)
{
if (str.length() > maxSize) {
buf.append(str.substring(0, maxSize - 1)).append("-");
}
else {
buf.append(str);
repeat(buf, maxSize - str.length(), ' ');
}
}
protected String objectToString(Object obj)
{
String str = null;
if (obj == null) {
str = "null";
}
else {
Class[] undecorated = new Class[] {
String.class,
Number.class,
Character.class,
Boolean.class
};
Class cls = obj.getClass();
for (int ui = 0; ui < undecorated.length; ++ui) {
if (undecorated[ui].isAssignableFrom(cls)) {
str = obj.toString();
break;
}
}
if (str == null) {
str = obj.toString() + " (" + obj.getClass() + ") #" + Integer.toHexString(obj.hashCode());
}
}
return str;
}
}
|