Update of /cvsroot/mocklib/mocklib3/input/javasrc/biz/xsoftware/impl/mock
In directory sc8-pr-cvs7.sourceforge.net:/tmp/cvs-serv19035/input/javasrc/biz/xsoftware/impl/mock
Added Files:
MessageHelper.java MockSuperclass.java BehaviorInfo.java
Action.java MockObjectFactoryImpl.java MethodVerifier.java
ReturnValue.java GenericAction.java ThrowException.java
MockObjectImpl.java
Log Message:
changes to mocklib3 to prepare for delivery.
--- NEW FILE: ReturnValue.java ---
package biz.xsoftware.impl.mock;
public class ReturnValue extends GenericAction implements Action {
private Object retVal;
public ReturnValue(Object retVal) {
this.retVal = retVal;
}
public Object execute(Object[] args) {
return retVal;
}
}
--- NEW FILE: GenericAction.java ---
package biz.xsoftware.impl.mock;
import java.lang.reflect.Method;
public class GenericAction {
private Method method;
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
--- NEW FILE: ThrowException.java ---
package biz.xsoftware.impl.mock;
public class ThrowException extends GenericAction implements Action {
private Throwable e;
public ThrowException(Throwable e) {
this.e = e;
}
public Object execute(Object[] args) throws Throwable {
e.fillInStackTrace();
throw e;
}
}
--- NEW FILE: BehaviorInfo.java ---
package biz.xsoftware.impl.mock;
import java.lang.reflect.Method;
import biz.xsoftware.mock.Behavior;
public class BehaviorInfo extends GenericAction implements Action {
private Behavior behavior;
private Method behaviorMethod;
private Method clonerMethod;
public BehaviorInfo(Behavior behavior) {
this.behavior = behavior;
}
public Object execute(Object[] args) throws Throwable {
return behaviorMethod.invoke(behavior, args);
}
public Object[] runClonerMethod(Object[] args) throws Throwable {
return (Object[])clonerMethod.invoke(behavior, args);
}
public void setBehaviorMethod(Method behaviorMethod) {
this.behaviorMethod = behaviorMethod;
}
public void setClonerMethod(Method clonerMethod) {
this.clonerMethod = clonerMethod;
}
}
--- NEW FILE: MockObjectFactoryImpl.java ---
package biz.xsoftware.impl.mock;
import java.lang.reflect.Proxy;
import biz.xsoftware.mock.MockObject;
import biz.xsoftware.mock.MockObjectFactory;
public class MockObjectFactoryImpl extends MockObjectFactory {
public MockObject createMockImpl(String id, Class[] interfaces) {
Class[] interfacesPlusMock = new Class[interfaces.length+1];
interfacesPlusMock[0] = MockObject.class;
for(int i = 1; i < interfacesPlusMock.length; i++) {
interfacesPlusMock[i] = interfaces[i-1];
}
ClassLoader cl = MockObjectFactory.class.getClassLoader();
MockObjectImpl impl = new MockObjectImpl(id, interfaces);
Object o = Proxy.newProxyInstance(cl, interfacesPlusMock, impl);
MockObject m = (MockObject)o;
return m;
}
}
--- NEW FILE: Action.java ---
package biz.xsoftware.impl.mock;
import java.lang.reflect.Method;
public interface Action {
public Object execute(Object[] args) throws Throwable;
public void setMethod(Method m);
public Method getMethod();
}
--- NEW FILE: MockSuperclass.java ---
package biz.xsoftware.impl.mock;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import biz.xsoftware.mock.Behavior;
import biz.xsoftware.mock.CalledMethod;
import biz.xsoftware.mock.ExpectFailedException;
import biz.xsoftware.mock.MockObject;
/**
* This is a super class for mock Objects. It has the following options
* <ol>
* <li>Guarantee order of events and that events happen on one object</li>
* <li>Guarantee events called with order not mattering on one object</li>
* <li>Guarantee order of events is correct between two objects(One mock obj. implements two interfaces)</li>
* </ol>
* This class will also return the parameters that were passed into the MockObject
* so they can be introspected in testing to make sure they were correct.
*
* The MockObject extending this class can also be told to throw exceptions on certain
* methods so we can test error leg behavior.
*
* Example of how to use
* MockActionListener implements ActionListener and extends this class
* The only method in MockActionListener
* is
* <PRE>
* public final static ACTION_METHOD = "actionPerformed method";
* public void actionPerformed(ActionEvent evt) {
* super.methodCalled(ACTION_METHOD, evt);
* }
* </PRE>
*
* In the test, when you expect an ActionEvent, you can call
* <PRE>
* Object o = MockActionListener.expectEvent(ACTION_METHOD);
* ActionEvent evt = (ActionEvent)evt;
* assertNonNull(evt.getSource());
* </PRE>
*
* Another useful behavior is throwing any type of exception using
* setExceptionOnMethod(String method, Throwable e). This can test
* robustness in a system to make sure listeners or services that
* throw exceptions don't affect your system, or at least affect
* your system in the proper way.
*
* @author Dean Hiller (de...@xs...)
*/
public abstract class MockSuperclass implements MockObject {
private static final Logger log = Logger.getLogger(MockSuperclass.class.getName());
/**
* List of the current methods that have been called.
*/
private List<CalledMethod> methodsCalled = new LinkedList<CalledMethod>();
/**
* A map of queues, containing either
* 1. objects to return when methods are called
* 2. Behaviors to run which return objects to return
* 3. Exceptions to throw
*/
private Map<String, List<Action>> methodToReturnVal = new HashMap<String, List<Action>>();
/**
* A map of default return values, which are used to return if the
* queue for the method is empty.
*/
private Map<String, Action> methodToDefaultRetVal = new HashMap<String, Action>();
private List<String> ignoredMethods = new ArrayList<String>();
/**
* Default wait time to wait for a method to be called once expectCall is
* called.
*/
public static final int DEFAULT_WAIT_TIME = 10000;
private int waitTime = DEFAULT_WAIT_TIME;
private String id;
/**
* Default constructor of superclass of all mockObjects with a delay of
* 10 seconds. This delay is the amount of time the mock object waits for
* a method to be called when a client calls expectCall.
*/
public MockSuperclass() {
}
/**
* The constructor to use to override the default delay({@link #DEFAULT_WAIT_TIME})
* such that the mock object will give methods a longer time to be called
* before timing out to fail the test.
*
* @param delay The amount of time in milliseconds to wait for a method to be
* called.
*/
public MockSuperclass(int delay) {
waitTime = delay;
}
public MockSuperclass(String id) {
this.id = id;
}
public void setExpectTimeout(int delay) {
this.waitTime = delay;
}
public int getExpectTimeout() {
return waitTime;
}
public void addIgnore(String method, Class ... argTypes)
{
ignoreImpl(method);
}
public void removeIgnore(String method, Class ... argTypes)
{
removeIgnoreImpl(method);
}
private void removeIgnoreImpl(String ... methods)
{
for(String method : methods)
{
ignoredMethods.remove(method);
}
}
private void ignoreImpl(String ... methods)
{
addIgnoredMethods(methods);
}
public void addIgnoredMethods(String[] methods) {
if(methods == null)
return;
verifyMethodsExist(methods);
for(String method : methods)
{
ignoredMethods.add(method);
}
}
public void setIgnoredMethods(String[] methods)
{
if(methods == null)
return;
ignoredMethods = new ArrayList<String>();
verifyMethodsExist(methods);
for(String method : methods)
{
ignoredMethods.add(method);
}
}
/**
* Subclasses should call this method when a method on their interface
* is called. This method is for users to create subclasses and call so
* they don't have to wrap it in a try-catch block.
*
* @param method
* @param parameters
* @return whatever the client has specified using addReturnValue
*/
protected Object methodCalled(String method, Object parameters) {
try {
if(!(parameters instanceof Object[])) {
parameters = new Object[] {parameters};
}
return methodCalledImpl(method, (Object[])parameters);
} catch (Throwable e) {
if(e instanceof RuntimeException)
throw (RuntimeException)e;
throw new RuntimeException("Sorry, must wrap exception, unwrap in " +
"your mockobject, or have mockObject call methodCalledImpl instead", e);
}
}
protected synchronized Object methodCalledImpl(String method, Object[] parameters) throws Throwable {
method = method.intern();
String params = "";
if(parameters == null) {
params = "no params";
} else {
Object[] array = parameters;
for(int i = 0; i < array.length-1; i++) {
params += array[i]+", ";
}
params += array[array.length-1];
}
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, id+"method called="+method+"("+params+") on obj="+this);
//sometimes, the contract is for the "code" to give a parameter
//to a library and then modify the parameter after the library
//is done with it. This means the object the "code" gave to
//the MockObject will change out from under the MockObject and be
//corrupted meaning you can't write a test to test the contract
//without cloning the object so it can't be changed.
Object[] newParams = clone(method, parameters);
methodsCalled.add(new CalledMethod(method, newParams, new Throwable().fillInStackTrace()));
this.notifyAll();
return findNextAction(method, parameters);
}
private Object findNextAction(String method, Object[] params) throws Throwable {
Action action = methodToDefaultRetVal.get(method);
List<Action> l = methodToReturnVal.get(method);
if(l != null) {
action = l.remove(0);
//if that was the last one in the list, remove the list...
if(l.size()<=0)
methodToReturnVal.remove(method);
}
if(action != null)
return action.execute(params);
return null;
}
// /**
// * @see biz.xsoftware.mock.MockObject#expectUnorderedCalls(java.lang.String[])
// */
// public synchronized CalledMethod[] expectUnorderedCalls(String[] methods) {
// if(methods == null)
// throw new IllegalArgumentException("methods cannot be null");
// else if(methods.length <= 0)
// throw new IllegalArgumentException("methods.length must be >= 1");
// for(int i = 0; i < methods.length; i++) {
// if(methods[i] == null)
// throw new IllegalArgumentException("None of values in methods " +
// "can be null, yet methods["+i+"] was null");
// }
//
// Map<String, Integer> expectedMethods = new HashMap<String, Integer>();
// for(int i = 0; i < methods.length; i++) {
// expectedMethods.put(methods[i], new Integer(i));
// }
//
// Set<String> ignorables = createIgnorableMap(ignoredMethods);
// CalledMethod[] retVal = new CalledMethod[methods.length];
//
// List<CalledMethod> methodsCalledList = new ArrayList<CalledMethod>();
// for(int i = 0; i < methods.length; i++) {
// if(ANY.equals(methods[i]))
// throw new IllegalArgumentException("The parameter 'methods' in " +
// "expectUnorderedCalls cannot contain MockSuperclass.ANY(use expectOrderedCalls instead)");
//
// CalledMethod o = expectUnignoredCall(ANY, ignorables, methodsCalledList);
// if(o == null) {
// String reason = putTogetherReason(methods, ignorables, methodsCalledList,
// "timed out on next expected method\n");
// throw new ExpectFailedException("Timed out waiting for a method call\n"
// +reason, retVal, ExpectFailedException.TIMED_OUT);
// }
// Integer index = expectedMethods.remove(o.getMethodName());
// if(index == null) {
// String reason = putTogetherReason(methods, ignorables, methodsCalledList, null);
// throw new ExpectFailedException(reason, retVal, ExpectFailedException.UNEXPECTED_CALL_BEFORE);
// }
//
// retVal[index.intValue()] = o;
// }
//
// LeftOverMethods leftOver = getLeftOverMethods(ignorables);
// if(leftOver != null) {
// String reason = putTogetherReason(methods, ignorables, methodsCalledList, "extra method(s)="+leftOver+"\n");
// throw new ExpectFailedException("There was a method called after your expected methods.\n"+reason, retVal
// , ExpectFailedException.UNEXPECTED_CALL_AFTER);
// }
//
// return retVal;
// }
private CalledMethod[] cleanup(CalledMethod[] methods, int size) {
CalledMethod[] retVal = new CalledMethod[size];
for(int i = 0; i < retVal.length; i++) {
retVal[i] = methods[i];
}
return retVal;
}
/**
* @see biz.xsoftware.mock.MockObject#expect(java.lang.String)
*/
public CalledMethod expect(String method) {
return expectImpl(method)[0];
}
/**
* @see biz.xsoftware.mock.MockObject#expect(java.lang.String[])
*/
public synchronized CalledMethod[] expect(String ... methods)
{
return expectImpl(methods);
}
/**
* @see biz.xsoftware.mock.MockObject#expectOrderedCalls(java.lang.String[])
*/
private synchronized CalledMethod[] expectImpl(String ... methods) {
if(methods == null)
throw new IllegalArgumentException("methods cannot be null");
else if(methods.length <= 0)
throw new IllegalArgumentException("methods.length must be >= 1");
for(int i = 0; i < methods.length; i++) {
if(methods[i] == null)
throw new IllegalArgumentException("None of values in methods can be null, yet methods["+i+"] was null");
}
verifyMethodsExist(methods);
Set<String> ignorables = createIgnorableMap(ignoredMethods);
List<CalledMethod> methodsCalledList = new ArrayList<CalledMethod>();
CalledMethod[] retVal = new CalledMethod[methods.length];
int i = 0;
for(; i < methods.length; i++) {
String method = methods[i];
CalledMethod o = null;
try {
o = expectUnignoredCall(method, ignorables, methodsCalledList);
} catch(ExpectFailedException e) {
if(!ExpectFailedException.UNEXPECTED_ON_NONE.equals(e.getReason())) {
throw e;
}
CalledMethod[] leftOver = e.getCalledMethods();
throw new ExpectFailedException(e.getMessage(), leftOver, e.getReason());
}
if(o == null) {
String reason = MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList,
"timed out on next expected method\n");
throw new ExpectFailedException("Timed out waiting for call="
+method+"\n"+reason, cleanup(retVal, i), ExpectFailedException.TIMED_OUT);
}
retVal[i] = o;
if(!method.equals(o.getMethodName()) && !ANY.equals(method)) {
String reason = MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, null);
reason += MessageHelper.getHowMethodWasCalled(o);
throw new ExpectFailedException(reason, cleanup(retVal, i), null);
}
}
LeftOverMethods leftOver = getLeftOverMethods(ignorables);
if(leftOver != null) {
String reason = MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, "extra method(s)="+leftOver+"\n");
reason += MessageHelper.getHowMethodWasCalled(leftOver.getMethods()[0]);
CalledMethod[] calledOnes = new CalledMethod[retVal.length+1];
System.arraycopy(retVal, 0, calledOnes, 0, retVal.length);
calledOnes[retVal.length] = leftOver.getMethods()[0];
throw new ExpectFailedException("There was a method called after your expected methods.\n"+reason, calledOnes
, ExpectFailedException.UNEXPECTED_CALL_AFTER);
}
return retVal;
}
protected synchronized CalledMethod expectUnignoredCall(String method, Set<String> ignorables, List<CalledMethod> calledMethods) {
if(method == null)
method = NONE;
//kind of dangerous if used with multiple threads because we might miss
//a bad event coming in, but if you keep using the same listener for every test
//the problem should still manifest itself in a
//different test case which sucks but works.
if(method.equals(NONE)) {
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, "method expected="+NONE+" on obj="+this);
// we have to strip out all of the ignored methods from methodsCalled
CalledMethod[] methods = methodsCalled.toArray(new CalledMethod[0]);
for(CalledMethod calledMethod : methods)
{
if(ignorables.contains(calledMethod.getMethodName()))
{
while(methodsCalled.contains(calledMethod))
{
methodsCalled.remove(calledMethod);
}
}
}
LeftOverMethods leftOver = getLeftOverMethods(ignorables);
if(leftOver != null) {
String reason = "You were expecting no methods to be called, but method(s) not\n"
+"in the ignore list were called earlier. method(s)="+leftOver;
reason += MessageHelper.getHowMethodWasCalled(leftOver.getMethods()[0]);
throw new ExpectFailedException(reason, leftOver.getMethods(), ExpectFailedException.UNEXPECTED_ON_NONE);
}
return new CalledMethod(NONE, null, null);
}
try {
return waitForUnignoredCall(method, ignorables, calledMethods);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private CalledMethod waitForUnignoredCall(
String logM, Set<String> ignorables, List<CalledMethod> calledMethods) throws InterruptedException {
long waitTime2 = waitTime+50;
long currentTime;
//only while waitTime is greater than 50 milliseconds
while(waitTime2 >= 50) {
//if there are no methods called yet, begin waiting
if(methodsCalled.size() <= 0) {
currentTime = System.currentTimeMillis();
if (log.isLoggable(Level.FINE))
log.fine("method expected(not called yet-waiting)="+logM+" on obj="+this+" wait="+waitTime2);
this.wait(waitTime2);
long waitedOnWait = System.currentTimeMillis() - currentTime;
waitTime2 -= waitedOnWait;
}
//check for new methods to be called now...if no non-ignorable methods, then
//continue while loop.
for(int i = 0; i < methodsCalled.size(); i++) {
CalledMethod calledMethod = methodsCalled.remove(0);
calledMethods.add(calledMethod);
if(!ignorables.contains(calledMethod.getMethodName())) {
if(log.isLoggable(Level.FINE))
log.fine("method expected and was called="+logM+" on obj="+this);
return calledMethod;
}
}
}
//return null means timeout....
return null;
}
private Set<String> createIgnorableMap(List<String> ignorableMethods) {
Set<String> ignorables = new HashSet<String>();
if(ignorableMethods != null) {
for(String method : ignorableMethods)
{
ignorables.add(method);
}
}
return ignorables;
}
private LeftOverMethods getLeftOverMethods(Set<String> ignorables) {
List<CalledMethod> leftOver = new ArrayList<CalledMethod>();
for(int i = 0; i < methodsCalled.size(); i++) {
CalledMethod o = methodsCalled.get(i);
if(!ignorables.contains(o.getMethodName()) && o != null) {
leftOver.add(o);
}
}
if(leftOver.size() <= 0)
return null;
CalledMethod[] m = new CalledMethod[0];
m = leftOver.toArray(m);
return new LeftOverMethods(m);
}
private class LeftOverMethods {
private CalledMethod[] leftOver;
public LeftOverMethods(CalledMethod[] m) {
leftOver = m;
}
/**
*/
public CalledMethod[] getMethods() {
return leftOver;
}
@Override
public String toString() {
String retVal = "";
for(int i = 0; i < leftOver.length; i++) {
retVal+="\n"+leftOver[i];
}
return retVal;
}
}
public void setDefaultBehavior(Behavior b, String method, Class... argTypes) {
checkMethod(method, argTypes);
if(b == null)
throw new IllegalArgumentException("behavior parameter cannot be null");
methodToDefaultRetVal.put(method, new BehaviorInfo(b));
}
public void setDefaultReturnValue(Object o, String method, Class... argTypes) {
checkMethod(method, argTypes);
methodToDefaultRetVal.put(method, new ReturnValue(o));
}
public void addBehavior(Behavior behavior, String method, Class... argTypes) {
if(behavior == null)
throw new IllegalArgumentException("behavior parameter cannot be null");
BehaviorInfo action = new BehaviorInfo(behavior);
addToActionList(action, method, argTypes);
Method m = action.getMethod();
//verify behavior has correct methods
Class clazz = behavior.getClass();
try {
Method behaviorMethod = clazz.getMethod(m.getName(), (Class[])m.getParameterTypes());
action.setBehaviorMethod(behaviorMethod);
behaviorMethod.setAccessible(true);
} catch (SecurityException e) {
throw new RuntimeException("Your Behavior class seems to be too secure. " +
"I can't reflect on it and call getClass on it's class object");
} catch (NoSuchMethodException e) {
String methodSig = MessageHelper.getMethodSignature(m, "");
throw new IllegalArgumentException("You Behavior class is missing the method='"+methodSig);
}
try {
Method clonerMethod = clazz.getMethod(m.getName()+"Cloner", (Class[])m.getParameterTypes());
action.setClonerMethod(clonerMethod);
if(!Object[].class.isAssignableFrom(clonerMethod.getReturnType()))
throw new IllegalArgumentException("Method="+clonerMethod+" does not return Object[] which it must do");
clonerMethod.setAccessible(true);
} catch (SecurityException e) {
throw new RuntimeException("Your Behavior class seems to be too secure. " +
"I can't reflect on it and call getClass on it's class object");
} catch (NoSuchMethodException e) {
String methodSig = MessageHelper.getMethodSignature(m, "Cloner");
throw new IllegalArgumentException("You Behavior class is missing the method='"+methodSig);
}
}
/**
* @see biz.xsoftware.mock.MockObject#addThrowException(java.lang.Throwable, java.lang.String, Class...)
*/
public synchronized void addThrowException(Throwable e, String method, Class... argTypes) {
if(e == null)
throw new IllegalArgumentException("e parameter to this method cannot be null");
Action action = new ThrowException(e);
addToActionList(action, method, argTypes);
}
/**
* @see biz.xsoftware.mock.MockObject#addReturnValue(java.lang.Object, java.lang.String, Class...)
*/
public synchronized void addReturnValue(Object o, String method, Class... argTypes) {
Action action = new ReturnValue(o);
addToActionList(action, method, argTypes);
}
private void addToActionList(Action action, String method, Class... argTypes) {
Method m = checkMethod(method, argTypes);
action.setMethod(m);
List<Action> l = methodToReturnVal.get(method);
if(l == null) {
l = new ArrayList<Action>();
methodToReturnVal.put(method, l);
}
l.add(action);
}
/**
* This is the method that calls the cloner to clone
* objects like ByteBuffer that can change after
* they are called.
*
* @param o
* @return
* @throws Throwable
*/
private Object[] clone(String method, Object[] params) throws Throwable {
if(params == null)
return null;
Action action = methodToDefaultRetVal.get(method);
//now override the default if there is an override specified...
List<Action> actions = methodToReturnVal.get(method);
if(actions != null) {
action = actions.get(0);
}
//check if action is a behaviorInfo object
if(action instanceof BehaviorInfo) {
BehaviorInfo behavior = (BehaviorInfo)action;
return behavior.runClonerMethod(params);
}
return params;
}
public Class[] getClasses() {
return new Class[] {this.getClass()};
}
private void verifyMethodsExist(String[] methods) {
for(int i = 0; i < methods.length; i++) {
MethodVerifier.getMethod(getClasses(), false, methods[i]);
}
}
private Method checkMethod(String method, Class[] argTypes) {
return MethodVerifier.getMethod(getClasses(), true, method, argTypes);
}
}
--- NEW FILE: MessageHelper.java ---
package biz.xsoftware.impl.mock;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import biz.xsoftware.mock.CalledMethod;
public final class MessageHelper {
private MessageHelper() {}
public static String getMethodSignature(Method method, String postfix) {
String methodSig = "public "+method.getReturnType()+" "+method.getName()+postfix+"(";
for(Class c : method.getParameterTypes()) {
methodSig += c.getName()+", ";
}
return methodSig.substring(0, methodSig.length()-2)+")";
}
public static String putTogetherReason(String[] methods, Set<String> ignorables, List methodsCalled, String lastLine) {
String reason = "\nMethods you expected...\n";
for(int i = 0; i < methods.length; i++) {
reason += "method["+i+"]="+methods[i]+"\n";
}
reason += "Methods you ignored...\n";
String[] ignored = ignorables.toArray(new String[0]);
if(ignored.length <= 0)
reason += "no ignored methods\n";
for(int i = 0; i < ignored.length; i++) {
reason += "ignored["+i+"]="+ignored[i]+"\n";
}
reason += "\nMethods that were called...\n";
for(int i = 0; i < methodsCalled.size(); i++) {
if(ignorables.contains(methodsCalled.get(i)))
reason += "method["+i+"](ignored)=";
else
reason += "method["+i+"]=";
reason += methodsCalled.get(i)+"\n";
}
if(lastLine == null)
reason += "(possibly more but we quit after finding unexpected methods)\n";
else
reason += lastLine+"\n";
return reason;
}
public static String getHowMethodWasCalled(CalledMethod method) {
Throwable t = method.getHowItWasCalled();
String retVal = "\nThe last method was="+method.getMethodName();
retVal += "\nHere is a stack trace showing ";
retVal += "you how it was called...\n";
retVal += "--------BEGIN="+method.getMethodName()+"---------------\n";
StringWriter s = new StringWriter();
PrintWriter p = new PrintWriter(s);
t.printStackTrace(p);
retVal += s.toString();
retVal += "--------END="+method.getMethodName() +"---------------\n";
return retVal;
}
}
--- NEW FILE: MockObjectImpl.java ---
/*
* Created on Jun 7, 2004
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package biz.xsoftware.impl.mock;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import biz.xsoftware.mock.MockObject;
/**
* @author Dean Hiller
*/
public class MockObjectImpl extends MockSuperclass implements InvocationHandler {
private static Set<Method> isMethodInSuper = new HashSet<Method>();
private static Map<Type, Class> primitiveToClass = new HashMap<Type, Class>();
private Class[] classes;
static {
//reflect and find all the methods of the superclass.
Class c = MockObject.class;
Method[] m = c.getMethods();
for(int i = 0; i < m.length; i++) {
isMethodInSuper.add(m[i]);
}
c = Object.class;
m = c.getMethods();
for(int i = 0; i < m.length; i++) {
isMethodInSuper.add(m[i]);
}
primitiveToClass.put(Integer.TYPE, Integer.class);
primitiveToClass.put(Double.TYPE, Double.class);
primitiveToClass.put(Float.TYPE, Float.class);
primitiveToClass.put(Boolean.TYPE, Boolean.class);
primitiveToClass.put(Character.TYPE, Character.class);
primitiveToClass.put(Byte.TYPE, Byte.class);
primitiveToClass.put(Short.TYPE, Short.class);
primitiveToClass.put(Long.TYPE, Long.class);
}
public MockObjectImpl(String id, Class[] interfaces) {
super(id);
this.classes = interfaces;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(isMethodInSuper.contains(method))
return callSuperMethod(proxy, method, args);
Object o = methodCalledImpl(method.getName(), args);
Class returnType = method.getReturnType();
if(returnType == null)
throw new RuntimeException("This is a bug or something");
String methodString = getCleanMethodString(method);
if(returnType.equals(void.class)) {
//The return type is void
if(o != null)
throw new IllegalArgumentException("You are trying to return something on a method that returns void.\n" +
"Method="+methodString+" value you tried to return="+o);
} else {
//Return type is not void...(could be primitive or Object)
if(o == null) {
if(returnType.isPrimitive())
throw new IllegalArgumentException("Must call addReturnValue and " +
"specify a non-null value as method="+methodString+" returns a primitive value");
} else if(returnType.isPrimitive()) {
//TODO: this is not working correctly no matter what I do here.....
Class primitiveClass = primitiveToClass.get(returnType);
if(!primitiveClass.isInstance(o))
throw new IllegalArgumentException("You specified an incorrect return type on method\n="+methodString+"\n"
+"You specified a return type of="+o.getClass()
+" which needs to be or extend type="+returnType);
} else if(!returnType.isInstance(o)) //if not a primitive, make sure is assignable....
throw new IllegalArgumentException("You specified an incorrect return type on method\n="+methodString+"\n"
+"You specified a return type of="+o.getClass()+" which needs to be or extend type="+returnType);
}
return o;
}
/**
* @param method
* @return
*/
private String getCleanMethodString(Method method)
{
String retType = method.getReturnType().getName();
String methodArgs = retType+" "+method.getName()+"(";
Class<?>[] parameterTypes = method.getParameterTypes();
for(int ii = 0; ii < parameterTypes.length; ii++)
{
Class arg = method.getParameterTypes()[ii];
methodArgs += arg.getName();
if(ii < parameterTypes.length -1)
methodArgs += ", ";
}
return methodArgs+")";
}
// private void handlePrimitiveReturns(String methodName, Object ret, Class<?> expectedReturnType)
// {
// Class<?>[] primitiveTypes = {Integer.TYPE, Double.TYPE, Float.TYPE, Boolean.TYPE,
// Character.TYPE, Byte.TYPE, Short.TYPE, Long.TYPE};
// Object[] primitiveClasses = {Integer.class, Double.class, Float.class, Boolean.class,
// Character.class, Byte.class, Short.class, Long.class};
// if(ret.getClass().isPrimitive() && expectedReturnType.isPrimitive())
// {
// for(Class<?> currentType : primitiveTypes)
// {
// if(ret.getClass() == currentType && expectedReturnType != currentType)
// {
// throwTypeException(methodName, ret, ret.getClass().getName(), expectedReturnType.getName());
// }
// }
// }
// else if(ret.getClass().isPrimitive())
// {
// for(int ii = 0; ii < primitiveTypes.length; ii++)
// {
// if(ret.getClass() == primitiveTypes[ii] && expectedReturnType != primitiveClasses[ii])
// {
// throwTypeException(methodName, ret, ret.getClass().getName(), expectedReturnType.getName());
// }
// }
// }
// else
// {
// for(int ii = 0; ii < primitiveTypes.length; ii++)
// {
// if(expectedReturnType == primitiveTypes[ii] && ret.getClass() != primitiveClasses[ii])
// {
// throwTypeException(methodName, ret, ret.getClass().getName(), expectedReturnType.getName());
// }
// }
// }
// }
//
// private void throwTypeException(String methodName, Object returnValue,
// String returnType, String expectedReturnType)
// {
// throw new RuntimeException("You specified an incorrect return type for " +
// "ignored or expected method " + methodName + "()" +
// "\nYou specified: \"" + returnValue + "\" of type " +
// returnType + " but should have been of type " + expectedReturnType);
// }
/**
*
* @param proxy The method was invoked on this object. This is the dynamicly
* created class
* @param m This is the method that was invoked
* @param args These are the arguments that were passed to the method
* @throws Throwable
*/
private Object callSuperMethod(Object proxy, Method m, Object[] args) throws Throwable {
try {
if("equals".equals(m.getName()))
return new Boolean(proxy == args[0]);
else if("toString".equals(m.getName()))
return ""+this;
return m.invoke(this, args);
} catch(InvocationTargetException e) {
if(e.getCause() != null)
throw e.getCause();
throw e;
}
}
/* (non-Javadoc)
* @see biz.xsoftware.mock.MockSuperclass#getClasses()
*/
@Override
public Class[] getClasses() {
return classes;
}
public Object inst() {
// TODO Auto-generated method stub
return null;
}
}
--- NEW FILE: MethodVerifier.java ---
package biz.xsoftware.impl.mock;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import biz.xsoftware.mock.MockObject;
public final class MethodVerifier {
private static final Logger log = Logger.getLogger(MethodVerifier.class.getName());
private MethodVerifier() {}
private static boolean paramsMatch(Method method, Class[] argTypes) {
Class<?>[] parameterTypes = method.getParameterTypes();
if(parameterTypes.length != argTypes.length)
return false;
for(int i = 0; i < parameterTypes.length; i++) {
if(!(parameterTypes[i].equals(argTypes[i])))
return false;
}
return true;
}
private static boolean paramsNotNeedMatch(Class c, Method method) {
Method[] classMethods = c.getMethods();
int matches = 0;
for(Method m : classMethods) {
if(m.getName().equals(method.getName()))
matches++;
}
if(matches == 1)
return true;
return false;
}
private static Method methodExistInThisClass(Class c, boolean isVerifyArgs, String method, Class... argTypes) {
Method[] methods = c.getMethods();
for(int i = 0; i < methods.length; i++) {
if(log.isLoggable(Level.FINEST))
log.finest("method in class='"+methods[i].getName()+"' expected='"+method+"'");
if(method.equals(methods[i].getName())) {
if(!isVerifyArgs)
return methods[i];
else if(paramsNotNeedMatch(c, methods[i]))
return methods[i];
else if(paramsMatch(methods[i], argTypes))
return methods[i];
}
}
return null;
}
static Method getMethod(Class[] classes, boolean isVerifyArgs, String method, Class ... argTypes) {
if(method == null)
throw new IllegalArgumentException("method parameter cannot be null");
else if(MockObject.ANY.equals(method) || MockObject.NONE.equals(method))
return null;
else if(classes == null)
return null;
String classNames = "";
for(int i = 0; i < classes.length; i++) {
if(log.isLoggable(Level.FINEST))
log.finest("class="+classes[i].getName());
Method m = methodExistInThisClass(classes[i], isVerifyArgs, method, argTypes);
if(m != null)
return m;
classNames += "\n"+classes[i];
}
String args = "";
if(argTypes != null && argTypes.length > 0) {
int i = 0;
for(; i < argTypes.length-1; i++) {
Class c = argTypes[i];
args += c.getName()+",";
}
args+=argTypes[i].getName();
}
String methodSig = method+"("+args+")";
throw new IllegalArgumentException("method='"+methodSig+"' is not a public method on any of the" +
"\nfollowing Classes/Interfaces"+classNames);
}
}
|