package com.google.code.joto.eventrecorder.spy.calls; import java.io.PrintStream; import java.lang.reflect.Method; import com.google.code.joto.ObjectToCodeGenerator; import com.google.code.joto.ast.beanstmt.impl.BeanASTPrettyPrinter; import com.google.code.joto.eventrecorder.RecordEventSummary; import com.google.code.joto.eventrecorder.processor.RecordEventsProcessor; import com.google.code.joto.eventrecorder.processor.RecordEventsProcessorFactory; import com.google.code.joto.eventrecorder.spy.calls.ObjectReplacementMap.ObjectInstanceReplacement; import com.google.code.joto.reflect.ReflectUtils; /** * Formatter for processing RecordEvent(s) corresponding to Method calls Request/Reply * and converting to String as Java "call" code, * * using ObjectToCodeGenerator for expression/parameters + check object replacements */ public class MethodCallToCodeRecordEventsProcessor implements RecordEventsProcessor { public static class Factory implements RecordEventsProcessorFactory<PrintStream> { private ObjectToCodeGenerator objToCode; private ObjectReplacementMap objectReplacementMap; public Factory(ObjectToCodeGenerator objToCode, ObjectReplacementMap objectReplacementMap) { super(); this.objToCode = objToCode; this.objectReplacementMap = objectReplacementMap; } @Override public RecordEventsProcessor create(PrintStream out) { return new MethodCallToCodeRecordEventsProcessor(objToCode, objectReplacementMap, out); } } private ObjectToCodeGenerator objToCode; private ObjectReplacementMap objectReplacementMap; private boolean useCommentBlockMarker = true; private PrintStream out; //------------------------------------------------------------------------- public MethodCallToCodeRecordEventsProcessor( ObjectToCodeGenerator objToCode, ObjectReplacementMap objectReplacementMap, PrintStream out) { this.objToCode = objToCode; this.objectReplacementMap = objectReplacementMap; this.out = out; } // ------------------------------------------------------------------------- @Override public boolean needEventObjectData() { return true; } @Override public void processEvent(RecordEventSummary event, Object eventData) { boolean isRequest = event.getEventSubType().equals("request"); if (isRequest) { EventMethodRequestData reqData = (EventMethodRequestData) eventData; Object expr = reqData.getExpr(); Method meth = reqData.getMethod(); Object[] args = reqData.getArguments(); String commentBlockMarker = (useCommentBlockMarker)? " eventId:" + event.getEventId() : ""; out.println("{ // methodCall" + commentBlockMarker + " " + meth.getName() + "(...)"); try { String exprName = prepareGenerateObj(meth.getReturnType(), expr, "expr"); String[] argNames = null; int len = (args != null)? args.length : 0; if (len != 0) { argNames = new String[len]; Class<?>[] parameterTypes = meth.getParameterTypes(); for (int i = 0; i < len; i++) { argNames[i] = prepareGenerateObj(parameterTypes[i], args[i], "arg" + i); if (len > 3) { out.println(); } } } // out.println("// ... args for \"expr." + meth.getName() + "(...);\""); StringBuilder sb = new StringBuilder(); sb.append(exprName); sb.append("."); sb.append(meth.getName()); sb.append("("); if (len != 0) { for (int i = 0; i < len; i++) { sb.append(argNames[i]); if (i + 1 < len) { sb.append(", "); } } } sb.append(");"); out.println(sb.toString()); } catch(Exception ex) { // ignore, no rethrow! out.println("*** FAILED to decode object(method request) to java code: "); ex.printStackTrace(out); } out.println("} // end methodCall" + commentBlockMarker); out.println(); } else { // method response EventMethodResponseData respData = (EventMethodResponseData) eventData; // ignore real result in current impl... Throwable methodEx = respData.getException(); Object result = respData.getResult(); String commentBlockMarker = (useCommentBlockMarker)? " eventId:" + event.getEventId() + " (call eventId:" + event.getCorrelatedEventId() + ")" : ""; out.println("{ // methodCall result" + commentBlockMarker); try { if (methodEx != null) { String stmtsStr = objToCode.objToStmtsString(methodEx, "methodException"); out.println(stmtsStr); } else { // String stmtsStr = objToCode.objToStmtsString(result, "result"); String resExprName = prepareGenerateObj((result!=null)? result.getClass():Object.class, result, "result"); out.println("// assert res ... " + resExprName); } } catch(Exception ex) { // ignore, no rethrow! out.println("*** FAILED to decode object(method request) to java code: " + ex.getMessage()); } out.println("} // methodCall result" + commentBlockMarker); out.println(); } } private String prepareGenerateObj(Class<?> declaredObjClass, Object obj, String exprVarName) { // check for replacement if (obj instanceof ObjectInstanceReplacement) { ObjectInstanceReplacement obj2 = (ObjectInstanceReplacement) obj; return obj2.getReplacedObjName(); } obj = objectReplacementMap.checkReplace(obj); if (declaredObjClass != null && declaredObjClass.isPrimitive() && ReflectUtils.primitiveTypeToWrapperType(declaredObjClass) == obj.getClass() // check! ) { // encode primitive value directly! return BeanASTPrettyPrinter.litteralToJava(obj); } StringBuilder stmtBuffer = new StringBuilder(); String res = objToCode.objToStmtsString(declaredObjClass, obj, exprVarName, stmtBuffer); String stmtsText = stmtBuffer.toString(); if (stmtsText.length() != 0) { // out.println("// " + exprVarName + ":"); out.print(stmtBuffer); // out.println("\n"); } return res; } //------------------------------------------------------------------------- @Override public String toString() { return "MethodCallToCodeRecordEventsProcessor[..]"; } }