/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.wc;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.tmatesoft.svn.util.SVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @author TMate Software Ltd.
* @version 1.3
*/
public class SVNMethodCallLogger implements InvocationHandler {
static Method OBJECT_TOSTRING;
static Method OBJECT_HASHCODE;
static Method OBJECT_EQUALS;
static {
try {
OBJECT_TOSTRING = Object.class.getMethod("toString", new Class[0]);
OBJECT_HASHCODE = Object.class.getMethod("hashCode", new Class[0]);
OBJECT_EQUALS = Object.class.getMethod("equals", new Class[]{Object.class});
} catch (NoSuchMethodException e) {
}
}
public static Object newInstance(Object object, Class[] callSites) {
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new SVNMethodCallLogger(object, callSites));
}
private final Object myTarget;
private final Class[] myCallSites;
public SVNMethodCallLogger(Object target, Class[] callSites) {
myTarget = target;
myCallSites = callSites == null ? new Class[0] : callSites;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (OBJECT_TOSTRING.equals(method)) {
return "Logger: " + myTarget.toString();
}
if (OBJECT_HASHCODE.equals(method)) {
return new Integer(myTarget.hashCode());
}
if (OBJECT_EQUALS.equals(method)) {
return new Boolean(myTarget.equals(args[0]));
}
Object result = null;
Throwable failure = null;
try {
result = method.invoke(myTarget, args);
} catch (IllegalAccessException e) {
throw e;
} catch (IllegalArgumentException e) {
throw e;
} catch (InvocationTargetException e) {
throw e;
} catch (Throwable th) {
failure = th;
}
String message = createMessage(method, args, result, failure);
SVNDebugLog.getDefaultLog().logFine(SVNLogType.DEFAULT, message);
if (failure != null) {
throw failure;
}
return result;
}
private String createMessage(Method method, Object[] args, Object result, Throwable failure) {
StringBuffer buffer = new StringBuffer();
buffer.append('\n');
buffer.append("Invoked: ");
buffer.append('\n');
buffer.append(method);
buffer.append('\n');
buffer.append("Arguments:");
buffer.append('\n');
Class[] parameters = method.getParameterTypes();
if (args == null) {
args = new Object[0];
}
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (parameters != null && parameters.length > i) {
Class parameterClass = parameters[i];
if (parameterClass != null) {
buffer.append(getShortClassName(parameterClass));
buffer.append(" = ");
}
}
buffer.append(String.valueOf(arg));
buffer.append('\n');
}
buffer.append("Call Site:");
buffer.append('\n');
buffer.append(findCallSite());
buffer.append("Returned:");
buffer.append('\n');
buffer.append(String.valueOf(result));
buffer.append('\n');
if (failure != null) {
buffer.append("Thrown:");
buffer.append('\n');
buffer.append(failure.getMessage());
buffer.append('\n');
buffer.append(generateStackTrace(failure));
buffer.append('\n');
}
return buffer.toString();
}
private String getShortClassName(Class cls) {
if (cls == null) {
return "null";
}
int dotIdx = cls.getName().lastIndexOf(".");
if (dotIdx >= 0 && dotIdx < cls.getName().length() - 1) {
return cls.getName().substring(dotIdx + 1);
}
return cls.getName();
}
private String findCallSite() {
Throwable traceProvider = new Throwable();
traceProvider = traceProvider.fillInStackTrace();
StackTraceElement[] stackTrace = traceProvider.getStackTrace();
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < stackTrace.length; i++) {
StackTraceElement stackTraceElement = stackTrace[i];
if (myCallSites != null) {
for (int j = 0; j < myCallSites.length; j++) {
Class allowedCallSite = myCallSites[j];
if (stackTraceElement.getClassName().equalsIgnoreCase(allowedCallSite.getName()) ||
stackTraceElement.getClassName().indexOf(allowedCallSite.getName()) >= 0) {
buffer.append(stackTraceElement.toString());
buffer.append('\n');
}
}
} else {
buffer.append(stackTraceElement);
buffer.append('\n');
}
}
if (buffer.length() == 0) {
return "[NOT DETECTED]";
}
return buffer.toString();
}
private String generateStackTrace(Throwable th) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(baos);
try {
th.printStackTrace(writer);
} finally {
SVNFileUtil.closeFile(writer);
}
try {
return new String(baos.toByteArray(), "UTF-8");
} catch (UnsupportedEncodingException e) {
return new String(baos.toByteArray());
}
}
}