Coverage Report - biz.xsoftware.impl.mock.MockSuperclass
 
Classes in this File Line Coverage Branch Coverage Complexity
MockSuperclass
74%
337/454
91%
100/110
0
MockSuperclass$LeftOverMethods
100%
8/8
100%
1/1
0
 
 1  
 package biz.xsoftware.impl.mock;
 2  
 
 3  
 import java.lang.reflect.Method;
 4  
 import java.util.ArrayList;
 5  
 import java.util.Arrays;
 6  
 import java.util.HashMap;
 7  
 import java.util.HashSet;
 8  
 import java.util.LinkedList;
 9  
 import java.util.List;
 10  
 import java.util.Map;
 11  
 import java.util.Set;
 12  
 import java.util.logging.Level;
 13  
 import java.util.logging.Logger;
 14  
 
 15  
 import biz.xsoftware.mock.Behavior;
 16  
 import biz.xsoftware.mock.BehaviorMethod;
 17  
 import biz.xsoftware.mock.CalledMethod;
 18  
 import biz.xsoftware.mock.CloningBehavior;
 19  
 import biz.xsoftware.mock.ExpectFailedException;
 20  
 import biz.xsoftware.mock.MethodSignature;
 21  
 import biz.xsoftware.mock.MockObject;
 22  
 
 23  
 /**
 24  
  * This is a super class for mock Objects. It has the following options
 25  
  * <ol>
 26  
  * <li>Guarantee order of events and that events happen on one object</li>
 27  
  * <li>Guarantee events called with order not mattering on one object</li>
 28  
  * <li>Guarantee order of events is correct between two objects(One mock obj.
 29  
  * implements two interfaces)</li>
 30  
  * </ol>
 31  
  * This class will also return the parameters that were passed into the
 32  
  * MockObject so they can be introspected in testing to make sure they were
 33  
  * correct.
 34  
  * 
 35  
  * The MockObject extending this class can also be told to throw exceptions on
 36  
  * certain methods so we can test error leg behavior.
 37  
  * 
 38  
  * Example of how to use MockActionListener implements ActionListener and
 39  
  * extends this class The only method in MockActionListener is
 40  
  * 
 41  
  * <PRE>
 42  
  * 
 43  
  * public final static ACTION_METHOD = &quot;actionPerformed method&quot;; public void
 44  
  * actionPerformed(ActionEvent evt) { super.methodCalled(ACTION_METHOD, evt); }
 45  
  * 
 46  
  * </PRE>
 47  
  * 
 48  
  * In the test, when you expect an ActionEvent, you can call
 49  
  * 
 50  
  * <PRE>
 51  
  * 
 52  
  * Object o = MockActionListener.expectEvent(ACTION_METHOD);
 53  
  * ActionEvent evt = (ActionEvent) evt;
 54  
  * assertNonNull(evt.getSource());
 55  
  * 
 56  
  * </PRE>
 57  
  * 
 58  
  * Another useful behavior is throwing any type of exception using
 59  
  * setExceptionOnMethod(String method, Throwable e). This can test robustness in
 60  
  * a system to make sure listeners or services that throw exceptions don't
 61  
  * affect your system, or at least affect your system in the proper way.
 62  
  * 
 63  
  * @author Dean Hiller (dean@xsoftware.biz)
 64  
  */
 65  
 public abstract class MockSuperclass implements MockObject {
 66  
 
 67  17
     private static final Logger log = Logger.getLogger(MockSuperclass.class.getName());
 68  
 
 69  
     /**
 70  
      * List of the current methods that have been called.
 71  
      */
 72  116
     private final List<CalledMethod> methodsCalled = new LinkedList<CalledMethod>();
 73  
 
 74  
     /**
 75  
      * A map of queues, containing either 1. objects to return when methods are
 76  
      * called 2. Behaviors to run which return objects to return 3. Exceptions
 77  
      * to throw
 78  
      */
 79  116
     protected final Map<Method, List<Action>> methodToReturnVal = new HashMap<Method, List<Action>>();
 80  
 
 81  
     /**
 82  
      * A map of default return values, which are used to return if the queue for
 83  
      * the method is empty.
 84  
      */
 85  116
     protected final Map<Method, Action> methodToDefaultRetVal = new HashMap<Method, Action>();
 86  
 
 87  116
     private final Set<String> ignoredMethods = new HashSet<String>();
 88  
 
 89  
     /**
 90  
      * Default wait time to wait for a method to be called once expectCall is
 91  
      * called.
 92  
      */
 93  
     public static final int DEFAULT_WAIT_TIME = 5000;
 94  
 
 95  116
     private int waitTime = DEFAULT_WAIT_TIME;
 96  
 
 97  
     protected String id;
 98  
 
 99  116
     private boolean isCaseSensitive = true;
 100  
 
 101  
     /**
 102  
      * Default constructor of superclass of all mockObjects with a delay of 10
 103  
      * seconds. This delay is the amount of time the mock object waits for a
 104  
      * method to be called when a client calls expectCall.
 105  
      */
 106  2
     public MockSuperclass() {
 107  2
     }
 108  
 
 109  
     /**
 110  
      * The constructor to use to override the default delay({@link #DEFAULT_WAIT_TIME})
 111  
      * such that the mock object will give methods a longer time to be called
 112  
      * before timing out to fail the test.
 113  
      * 
 114  
      * @param delay The amount of time in milliseconds to wait for a method to
 115  
      *            be called.
 116  
      */
 117  16
     public MockSuperclass(int delay) {
 118  16
         waitTime = delay;
 119  16
     }
 120  
 
 121  98
     public MockSuperclass(String id) {
 122  98
         this.id = id;
 123  98
     }
 124  
 
 125  
     /*
 126  
      * (non-Javadoc)
 127  
      * 
 128  
      * @see biz.xsoftware.mock.MockObject#setExpectTimeout(int)
 129  
      */
 130  
     public void setExpectTimeout(int delay) {
 131  1
         this.waitTime = delay;
 132  1
     }
 133  
 
 134  
     /*
 135  
      * (non-Javadoc)
 136  
      * 
 137  
      * @see biz.xsoftware.mock.MockObject#getExpectTimeout()
 138  
      */
 139  
     public int getExpectTimeout() {
 140  1
         return waitTime;
 141  
     }
 142  
 
 143  
     /*
 144  
      * (non-Javadoc)
 145  
      * 
 146  
      * @see biz.xsoftware.mock.MockObject#addIgnore(java.lang.String[])
 147  
      */
 148  
     public void addIgnore(String... methods) {
 149  15
         addIgnoreImpl(methods);
 150  14
     }
 151  
 
 152  
     /*
 153  
      * (non-Javadoc)
 154  
      * 
 155  
      * @see biz.xsoftware.mock.MockObject#addIgnore(java.lang.String,
 156  
      *      java.lang.Class[])
 157  
      */
 158  
     public void addIgnore(MethodSignature... methodSignature) {
 159  0
         throw new UnsupportedOperationException("This method has not been implemented yet");
 160  
     }
 161  
 
 162  
     /*
 163  
      * (non-Javadoc)
 164  
      * 
 165  
      * @see biz.xsoftware.mock.MockObject#removeIgnore(java.lang.String[])
 166  
      */
 167  
     public void removeIgnore(String... methods) {
 168  3
         removeIgnoreImpl(methods);
 169  3
     }
 170  
 
 171  
     /**
 172  
      * 
 173  
      */
 174  
     public void removeIgnore(MethodSignature... methodSignature) {
 175  0
         throw new UnsupportedOperationException("This method has not been implemented yet");
 176  
     }
 177  
 
 178  
     private void removeIgnoreImpl(String... methods) {
 179  6
         for (String method : methods) {
 180  3
             ignoredMethods.remove(method);
 181  
         }
 182  3
     }
 183  
 
 184  
     /**
 185  
      * 
 186  
      * @param methods
 187  
      */
 188  
     private void addIgnoreImpl(String... methods) {
 189  15
         if (methods == null)
 190  0
             return;
 191  
 
 192  
         // validate the given methods
 193  30
         for (String method : methods) {
 194  16
             if (method.equals(MockObject.ANY) || method.equals(MockObject.NONE)) {
 195  0
                 throw new IllegalArgumentException("Cannot add MockObject.ANY or MockObject.NONE as an ignored method");
 196  
             }
 197  16
             MethodVerifier.verifyMethodNameExist(getClasses(), method, isCaseSensitive);
 198  
         }
 199  
 
 200  
         // now add them
 201  29
         for (String method : methods) {
 202  15
             ignoredMethods.add(method);
 203  
         }
 204  14
     }
 205  
 
 206  
     public void setIgnoredMethods(String[] methods) {
 207  0
         if (methods == null)
 208  0
             return;
 209  
 
 210  
         // validate the given methods
 211  0
         for (String method : methods) {
 212  0
             if (method.equals(MockObject.ANY) || method.equals(MockObject.NONE)) {
 213  0
                 throw new IllegalArgumentException("Cannot add MockObject.ANY or MockObject.NONE as an ignored method");
 214  
             }
 215  0
             MethodVerifier.verifyMethodNameExist(getClasses(), method, isCaseSensitive);
 216  
         }
 217  
 
 218  
         // now clear the list and add the new ones
 219  0
         ignoredMethods.clear();
 220  
 
 221  0
         for (String method : methods) {
 222  0
             ignoredMethods.add(method);
 223  
         }
 224  0
     }
 225  
 
 226  
     /**
 227  
      * Subclasses should call this method when a method on their interface is
 228  
      * called. This method is for users to create subclasses and call so they
 229  
      * don't have to wrap it in a try-catch block.
 230  
      * 
 231  
      * @param method The method name
 232  
      * @param parameters The parameter objects that were passed into the called
 233  
      *            method
 234  
      * @return whatever the client has specified using addReturnValue
 235  
      */
 236  
     protected Object methodCalled(final String method, final Object parameters) {
 237  
         try {
 238  
             Object[] resultParams;
 239  32
             if (!(parameters instanceof Object[])) {
 240  32
                 if (parameters != null) {
 241  31
                     resultParams = new Object[] { parameters };
 242  31
                 } else {
 243  1
                     resultParams = new Object[0];
 244  
                 }
 245  1
             } else {
 246  0
                 resultParams = (Object[]) parameters;
 247  
             }
 248  32
             return methodCalledImpl(method, resultParams);
 249  2
         } catch (Throwable e) {
 250  2
             if (e instanceof RuntimeException)
 251  2
                 throw (RuntimeException) e;
 252  0
             throw new RuntimeException("Sorry, must wrap exception, unwrap in "
 253  
                     + "your mockobject, or have mockObject call methodCalledImpl instead", e);
 254  
         }
 255  
     }
 256  
 
 257  
     protected Object methodCalledImpl(final String method, final Object... parameters) throws Throwable {
 258  32
         String resultMethod = method.intern();
 259  32
         Method m = getMethod(resultMethod, parameters);
 260  32
         return methodCalledImpl(m, parameters);
 261  
     }
 262  
 
 263  
     /**
 264  
      * Subclasses should call this method when a method on their interface is
 265  
      * called. This method is for users to create subclasses and call so they
 266  
      * don't have to wrap it in a try-catch block.
 267  
      * 
 268  
      * @param method The method name that was called
 269  
      * @param parameters The parameters passed with the called method
 270  
      * 
 271  
      * @return whatever the client has specified using addReturnValue
 272  
      */
 273  
     protected synchronized Object methodCalledImpl(final Method method, final Object... parameters) throws Throwable {
 274  342
         if (log.isLoggable(Level.FINE)) {
 275  0
             String params = "";
 276  0
             if (parameters == null) {
 277  0
                 params = "no params";
 278  0
             } else {
 279  0
                 Object[] array = parameters;
 280  0
                 if (array.length > 0) {
 281  0
                     for (int i = 0; i < array.length - 1; i++) {
 282  0
                         params += array[i] + ", ";
 283  
                     }
 284  
                     // write out the last parameter without the comma
 285  0
                     params += array[array.length - 1];
 286  
                 }
 287  
             }
 288  
 
 289  0
             log.log(Level.FINE, id + ": method called=" + method.getName() + "(" + params + ") on obj=" + this);
 290  
         }
 291  
 
 292  
         // sometimes, the contract is for the "code" to give a parameter
 293  
         // to a library and then modify the parameter after the library
 294  
         // is done with it. This means the object the "code" gave to
 295  
         // the MockObject will change out from under the MockObject and be
 296  
         // corrupted meaning you can't write a test to test the contract
 297  
         // without cloning the object so it can't be changed.
 298  342
         Object[] newParams = clone(method, parameters);
 299  342
         if (isCaseSensitive) {
 300  327
             methodsCalled.add(new CalledMethod(method.getName(), method, newParams, new Throwable().fillInStackTrace()));
 301  327
         } else {
 302  15
             methodsCalled.add(new CalledMethod(method.getName().toLowerCase(), method, newParams,
 303  
                     new Throwable().fillInStackTrace()));
 304  
         }
 305  
 
 306  342
         this.notifyAll();
 307  
 
 308  342
         return runNextAction(method, parameters);
 309  
     }
 310  
 
 311  
     /**
 312  
      * Runs the next action
 313  
      * 
 314  
      * @param methodStr
 315  
      * @param params The parameter objects used when the method was called
 316  
      * @return
 317  
      * @throws Throwable
 318  
      */
 319  
     private Object runNextAction(Method method, Object[] params) throws Throwable {
 320  
 
 321  
         // if (log.isLoggable(Level.FINEST)) {
 322  
         // log.finest("locating next action for method: " + methodStr);
 323  
         // }
 324  
 
 325  
         // Method method = getMethod(methodStr, params);
 326  
 
 327  
         // load the default
 328  342
         Action action = methodToDefaultRetVal.get(method);
 329  
 
 330  
         // the call order is to run any other set actions before the default
 331  342
         List<Action> l = methodToReturnVal.get(method);
 332  342
         if (l != null) {
 333  141
             action = l.remove(0);
 334  
             // if that was the last one in the list, remove the list...
 335  141
             if (l.size() <= 0)
 336  37
                 methodToReturnVal.remove(method);
 337  
         }
 338  
 
 339  342
         if (action != null)
 340  160
             return action.execute(params);
 341  
 
 342  182
         return null;
 343  
     }
 344  
 
 345  
     /**
 346  
      * Looks for the {@link Method} in the current object's interfaces using the
 347  
      * method string given and the parameters that were obtained when the method
 348  
      * was called. The method parameter types will be determined from the given
 349  
      * params
 350  
      * 
 351  
      * @param methodStr The method name
 352  
      * @param params The parameters that were passed to the method
 353  
      * @return The found {@link Method} obj, otherwise exceptions are thrown
 354  
      */
 355  
     private Method getMethod(String methodStr, Object[] params) {
 356  32
         Class<?>[] argTypes = null;
 357  
 
 358  32
         if (params != null) {
 359  
             // find the Method object for the given params
 360  32
             argTypes = new Class<?>[params.length];
 361  63
             for (int i = 0; i < params.length; i++) {
 362  31
                 if (params[i] != null) {
 363  31
                     argTypes[i] = params[i].getClass();
 364  31
                 } else {
 365  
                     // we don't know what the class type is so we will try
 366  
                     // to match the class with any param object type
 367  
                     // will throw an exception if multiple methods match
 368  0
                     argTypes[i] = MethodSignature.ANY_CLASS_TYPE;
 369  
                 }
 370  
             }
 371  
         }
 372  
 
 373  32
         return MethodVerifier.getMethod(getClasses(), new MethodSignature(methodStr, argTypes), isCaseSensitive);
 374  
     }
 375  
 
 376  
     public CalledMethod expectUnordered(String method) {
 377  1
         return expectUnordered(new String[] { method })[0];
 378  
     }
 379  
 
 380  
     /*
 381  
      * (non-Javadoc)
 382  
      * 
 383  
      * @see biz.xsoftware.mock.MockObject#expectUnordered(java.lang.String[])
 384  
      */
 385  
     public synchronized CalledMethod[] expectUnordered(String... methods) {
 386  7
         if (methods == null)
 387  0
             throw new IllegalArgumentException("methods cannot be null");
 388  7
         else if (methods.length <= 0)
 389  0
             throw new IllegalArgumentException("methods.length must be >= 1");
 390  26
         for (int i = 0; i < methods.length; i++) {
 391  19
             if (methods[i] == null)
 392  0
                 throw new IllegalArgumentException("None of values in methods " + "can be null, yet methods[" + i
 393  
                         + "] was null");
 394  
         }
 395  
 
 396  7
         String[] expectedMethods = new String[methods.length];
 397  
 
 398  26
         for (int i = 0; i < methods.length && i < expectedMethods.length; i++) {
 399  19
             if (isCaseSensitive)
 400  16
                 expectedMethods[i] = methods[i];
 401  
             else
 402  3
                 expectedMethods[i] = methods[i].toLowerCase();
 403  
         }
 404  
 
 405  7
         Set<String> ignorables = createIgnorableMap(ignoredMethods);
 406  7
         CalledMethod[] retVal = new CalledMethod[methods.length];
 407  
 
 408  7
         List<CalledMethod> methodsCalledList = new ArrayList<CalledMethod>();
 409  25
         for (int i = 0; i < methods.length; i++) {
 410  19
             if (ANY.equals(methods[i]))
 411  0
                 throw new IllegalArgumentException("The parameter 'methods' in "
 412  
                         + "expectUnorderedCalls cannot contain MockSuperclass.ANY(use expectOrderedCalls instead)");
 413  
 
 414  19
             CalledMethod o = expectUnignoredCall(ANY, ignorables, methodsCalledList);
 415  19
             if (o == null) {
 416  1
                 String reason =
 417  
                         MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList,
 418  
                                 "timed out on next expected method\n");
 419  1
                 throw new ExpectFailedException("Timed out waiting for a method call\n" + reason, retVal,
 420  
                         ExpectFailedException.TIMED_OUT);
 421  
             }
 422  
 
 423  18
             int index = -1;
 424  
 
 425  18
             if (isCaseSensitive) {
 426  15
                 index = stringArraySearch(o.getMethodName(), expectedMethods);
 427  15
             } else {
 428  3
                 index = stringArraySearch(o.getMethodName().toLowerCase(), expectedMethods);
 429  
             }
 430  
 
 431  18
             if (index >= 0) {
 432  
                 // this is good, we found an expected method
 433  
                 // remove it from the list we're expected
 434  18
                 expectedMethods[index] = null;
 435  18
             } else {
 436  0
                 String reason = MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, null);
 437  0
                 throw new ExpectFailedException(reason, retVal, ExpectFailedException.UNEXPECTED_CALL_BEFORE);
 438  
             }
 439  
 
 440  18
             retVal[index] = o;
 441  
         }
 442  
 
 443  6
         LeftOverMethods leftOver = getLeftOverMethods(ignorables);
 444  6
         if (leftOver != null) {
 445  0
             String reason =
 446  
                     MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, "extra method(s)="
 447  
                             + leftOver + "\n");
 448  0
             throw new ExpectFailedException("There was a method called after your expected methods.\n" + reason,
 449  
                     retVal, ExpectFailedException.UNEXPECTED_CALL_AFTER);
 450  
         }
 451  
 
 452  6
         return retVal;
 453  
     }
 454  
 
 455  
     /**
 456  
      * This method does a search in the given string array for the given string
 457  
      * to match and returns the found index or -1 if not found
 458  
      * 
 459  
      * @param needle The string to search for
 460  
      * @param haystack The array of strings to search in
 461  
      * @return The index in the array where a match is found or -1 if there
 462  
      *         wasn't a match
 463  
      */
 464  
     private static int stringArraySearch(String needle, String[] haystack) {
 465  60
         for (int i = 0; i < haystack.length; i++) {
 466  60
             if (needle.equals(haystack[i])) {
 467  32
                 return i;
 468  
             }
 469  
         }
 470  0
         return -1;
 471  
     }
 472  
 
 473  
     /*
 474  
      * (non-Javadoc)
 475  
      * 
 476  
      * @see biz.xsoftware.mock.MockObject#expectUnorderedCalls(java.lang.String[])
 477  
      */
 478  
 
 479  
     @Deprecated
 480  
     public CalledMethod[] expectUnorderedCalls(String... methods) {
 481  0
         return expectUnordered(methods);
 482  
     }
 483  
 
 484  
     /**
 485  
      * Calls {@link #addIgnore(String[])} with the given ignored methods, then
 486  
      * runs {@link #expectUnorderedCalls(String[])}, saving the result. Then
 487  
      * removes the ignored methods and finally returns the saved result
 488  
      * 
 489  
      * @deprecated This is no longer supported, the use of
 490  
      *             {@link #addIgnore(String)} in combination with
 491  
      *             {@link #expect(String[]) should be used
 492  
      */
 493  
     @Deprecated
 494  
     public CalledMethod[] expectUnorderedCalls(String[] methods, String[] ignoredMethods) {
 495  0
         addIgnore(ignoredMethods);
 496  0
         CalledMethod[] results = expectUnordered(methods);
 497  0
         removeIgnoreImpl(ignoredMethods);
 498  0
         return results;
 499  
     }
 500  
 
 501  
     private CalledMethod[] cleanup(CalledMethod[] methods, int size) {
 502  6
         CalledMethod[] retVal = new CalledMethod[size];
 503  7
         for (int i = 0; i < retVal.length; i++) {
 504  1
             retVal[i] = methods[i];
 505  
         }
 506  6
         return retVal;
 507  
     }
 508  
 
 509  
     /**
 510  
      * @see biz.xsoftware.mock.MockObject#expect(java.lang.String)
 511  
      */
 512  
     public CalledMethod expect(String method) {
 513  55
         return expectImpl(method)[0];
 514  
     }
 515  
 
 516  
     /**
 517  
      * @deprecated Use {@link #expect(String)} now
 518  
      */
 519  
     @Deprecated
 520  
     public CalledMethod expectCall(String method) {
 521  0
         return expectImpl(method)[0];
 522  
     }
 523  
 
 524  
     /*
 525  
      * (non-Javadoc)
 526  
      * 
 527  
      * @see biz.xsoftware.mock.MockObject#expectCall(java.lang.String,
 528  
      *      java.lang.String[])
 529  
      */
 530  
     @Deprecated
 531  
     public CalledMethod expectCall(String method, String... ignoredMethods) {
 532  0
         addIgnore(ignoredMethods);
 533  0
         CalledMethod result = expectImpl(method)[0];
 534  0
         removeIgnoreImpl(ignoredMethods);
 535  0
         return result;
 536  
     }
 537  
 
 538  
     /**
 539  
      * @see biz.xsoftware.mock.MockObject#expect(java.lang.String[])
 540  
      */
 541  
     public synchronized CalledMethod[] expect(String... methods) {
 542  19
         return expectImpl(methods);
 543  
     }
 544  
 
 545  
     /*
 546  
      * (non-Javadoc)
 547  
      * 
 548  
      * @see biz.xsoftware.mock.MockObject#expectOrderedCalls(java.lang.String[])
 549  
      */
 550  
     @Deprecated
 551  
     public synchronized CalledMethod[] expectOrderedCalls(String... methods) {
 552  0
         return expectImpl(methods);
 553  
     }
 554  
 
 555  
     /*
 556  
      * (non-Javadoc)
 557  
      * 
 558  
      * @see biz.xsoftware.mock.MockObject#expectOrderedCalls(java.lang.String[],
 559  
      *      java.lang.String[])
 560  
      */
 561  
     @Deprecated
 562  
     public CalledMethod[] expectOrderedCalls(String[] methods, String[] ignoredMethods) {
 563  0
         addIgnoreImpl(ignoredMethods);
 564  0
         return expectImpl(methods);
 565  
     }
 566  
 
 567  
     /*
 568  
      * (non-Javadoc)
 569  
      * 
 570  
      * @see biz.xsoftware.mock.MockObject#expect(java.lang.String, int)
 571  
      */
 572  
     public CalledMethod[] expect(String method, int times) {
 573  4
         String[] methods = new String[times];
 574  4
         Arrays.fill(methods, 0, times, method);
 575  4
         return expectImpl(methods);
 576  
     }
 577  
 
 578  
     /*
 579  
      * (non-Javadoc)
 580  
      * 
 581  
      * @see biz.xsoftware.mock.MockObject#expectOnceThenIgnore(java.lang.String[])
 582  
      */
 583  
     public CalledMethod[] expectOnceThenIgnore(final String... methods) {
 584  7
         if (methods == null)
 585  0
             throw new IllegalArgumentException("methods cannot be null");
 586  7
         else if (methods.length <= 0)
 587  0
             throw new IllegalArgumentException("methods.length must be >= 1");
 588  21
         for (int i = 0; i < methods.length; i++) {
 589  14
             if (methods[i] == null)
 590  0
                 throw new IllegalArgumentException("None of values in methods " + "can be null, yet methods[" + i
 591  
                         + "] was null");
 592  
         }
 593  
 
 594  
         // need to copy to another array so we can change to lowercase if needed
 595  
         // and to remove the item when a match is found
 596  7
         String[] expectedMethods = new String[methods.length];
 597  
 
 598  21
         for (int i = 0; i < methods.length && i < expectedMethods.length; i++) {
 599  14
             if (ANY.equals(methods[i]))
 600  0
                 throw new IllegalArgumentException("The parameter 'methods' in "
 601  
                         + "expectOnceThenIgnore cannot contain MockObject.ANY(use expectOrderedCalls instead)");
 602  
 
 603  14
             if (isCaseSensitive)
 604  11
                 expectedMethods[i] = methods[i];
 605  
             else
 606  3
                 expectedMethods[i] = methods[i].toLowerCase();
 607  
         }
 608  
 
 609  7
         if (log.isLoggable(Level.FINEST)) {
 610  0
             StringBuilder sb = new StringBuilder();
 611  0
             sb.append("expectedMethods={");
 612  0
             for (String method : expectedMethods)
 613  0
                 sb.append(method).append(",");
 614  0
             sb.append("}");
 615  0
             log.finest(sb.toString());
 616  
         }
 617  
 
 618  7
         Set<String> ignorables = createIgnorableMap(ignoredMethods);
 619  7
         CalledMethod[] retVal = new CalledMethod[methods.length];
 620  
 
 621  7
         List<CalledMethod> methodsCalledList = new ArrayList<CalledMethod>();
 622  
 
 623  
         // We need to loop the expectedMethods number of times. We might not
 624  
         // actually be working on expectedMethods[i]... it could be another
 625  
         // expectedMethod
 626  21
         for (int i = 0; i < expectedMethods.length; i++) {
 627  
 
 628  14
             CalledMethod o = expectUnignoredCall(ANY, ignorables, methodsCalledList);
 629  14
             if (log.isLoggable(Level.FINEST)) {
 630  0
                 log.finest("received CalledMethod: " + o.getMethodName());
 631  
             }
 632  14
             if (o == null) {
 633  0
                 String reason =
 634  
                         MessageHelper.putTogetherReason(expectedMethods, ignorables, methodsCalledList,
 635  
                                 "timed out on next expected method\n");
 636  0
                 throw new ExpectFailedException("Timed out waiting for a method call\n" + reason, retVal,
 637  
                         ExpectFailedException.TIMED_OUT);
 638  
             }
 639  
 
 640  14
             int index = -1;
 641  
 
 642  14
             if (isCaseSensitive) {
 643  11
                 index = stringArraySearch(o.getMethodName(), expectedMethods);
 644  11
             } else {
 645  3
                 index = stringArraySearch(o.getMethodName().toLowerCase(), expectedMethods);
 646  
             }
 647  
 
 648  14
             if (index >= 0) {
 649  14
                 if (log.isLoggable(Level.FINEST)) {
 650  0
                     log.finest("found method(" + o.getMethodName() + ") in expectedMethods, index=" + index
 651  
                             + ".  Now adding to ignorables");
 652  
                 }
 653  
 
 654  
                 // this is good, we found an expected method
 655  
                 // remove it from the expected list
 656  14
                 expectedMethods[index] = null;
 657  14
                 if (isCaseSensitive) {
 658  11
                     ignorables.add(o.getMethodName());
 659  11
                 } else {
 660  3
                     ignorables.add(o.getMethodName().toLowerCase());
 661  
                 }
 662  3
             } else {
 663  0
                 String reason = MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, null);
 664  0
                 throw new ExpectFailedException(reason, retVal, ExpectFailedException.UNEXPECTED_CALL_BEFORE);
 665  
             }
 666  
 
 667  14
             retVal[index] = o;
 668  
         }
 669  
 
 670  7
         LeftOverMethods leftOver = getLeftOverMethods(ignorables);
 671  7
         if (leftOver != null) {
 672  0
             String reason =
 673  
                     MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, "extra method(s)="
 674  
                             + leftOver + "\n");
 675  0
             throw new ExpectFailedException("There was a method called after your expected methods.\n" + reason,
 676  
                     retVal, ExpectFailedException.UNEXPECTED_CALL_AFTER);
 677  
         }
 678  
 
 679  
         // clean up by removing the ignored methods
 680  21
         for (String o : methods) {
 681  14
             ignorables.remove(o);
 682  
         }
 683  
 
 684  7
         return retVal;
 685  
     }
 686  
 
 687  
     /**
 688  
      * @see biz.xsoftware.mock.MockObject#expectOrderedCalls(java.lang.String[])
 689  
      */
 690  
     private synchronized CalledMethod[] expectImpl(String... methods) {
 691  78
         if (methods == null)
 692  0
             throw new IllegalArgumentException("methods cannot be null");
 693  78
         else if (methods.length <= 0)
 694  0
             throw new IllegalArgumentException("methods.length must be >= 1");
 695  197
         for (int i = 0; i < methods.length; i++) {
 696  119
             if (methods[i] == null)
 697  0
                 throw new IllegalArgumentException("None of values in methods can be null, yet methods[" + i
 698  
                         + "] was null");
 699  
         }
 700  
 
 701  
         // validate the given methods
 702  195
         for (String method : methods) {
 703  119
             if (!method.equals(MockObject.ANY) && !method.equals(MockObject.NONE)) {
 704  109
                 MethodVerifier.verifyMethodNameExist(getClasses(), method, isCaseSensitive);
 705  
             }
 706  
         }
 707  
 
 708  76
         Set<String> ignorables = createIgnorableMap(ignoredMethods);
 709  
 
 710  76
         List<CalledMethod> methodsCalledList = new ArrayList<CalledMethod>();
 711  76
         CalledMethod[] retVal = new CalledMethod[methods.length];
 712  76
         int i = 0;
 713  284
         for (; i < methods.length; i++) {
 714  112
             String method = methods[i];
 715  
 
 716  112
             CalledMethod o = null;
 717  
             try {
 718  112
                 o = expectUnignoredCall(method, ignorables, methodsCalledList);
 719  2
             } catch (ExpectFailedException e) {
 720  2
                 if (!ExpectFailedException.UNEXPECTED_ON_NONE.equals(e.getReason())) {
 721  0
                     throw e;
 722  
                 }
 723  2
                 CalledMethod[] leftOver = e.getCalledMethods();
 724  2
                 throw new ExpectFailedException(e.getMessage(), leftOver, e.getReason());
 725  110
             }
 726  110
             if (o == null) {
 727  0
                 String reason =
 728  
                         MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList,
 729  
                                 "timed out on next expected method\n");
 730  0
                 throw new ExpectFailedException("Timed out waiting for call=" + method + "\n" + reason, cleanup(retVal,
 731  
                         i), ExpectFailedException.TIMED_OUT);
 732  
             }
 733  110
             retVal[i] = o;
 734  
 
 735  
             // see if the method method we're expecting matches the received
 736  
             // method name... with some case sensitive funny business
 737  110
             if ((isCaseSensitive && !method.equals(o.getMethodName()) && !ANY.equals(method))
 738  
                     || (!isCaseSensitive && (!method.equalsIgnoreCase(o.getMethodName()) && !ANY.equals(method)))) {
 739  6
                 String reason = MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, null);
 740  6
                 reason += MessageHelper.getHowMethodWasCalled(o);
 741  6
                 throw new ExpectFailedException(reason, cleanup(retVal, i), null);
 742  
             }
 743  
         }
 744  
 
 745  68
         LeftOverMethods leftOver = getLeftOverMethods(ignorables);
 746  68
         if (leftOver != null) {
 747  4
             String reason =
 748  
                     MessageHelper.putTogetherReason(methods, ignorables, methodsCalledList, "extra method(s)="
 749  
                             + leftOver + "\n");
 750  4
             reason += MessageHelper.getHowMethodWasCalled(leftOver.getMethods()[0]);
 751  4
             CalledMethod[] calledOnes = new CalledMethod[retVal.length + 1];
 752  4
             System.arraycopy(retVal, 0, calledOnes, 0, retVal.length);
 753  4
             calledOnes[retVal.length] = leftOver.getMethods()[0];
 754  4
             throw new ExpectFailedException("There was a method called after your expected methods.\n" + reason,
 755  
                     calledOnes, ExpectFailedException.UNEXPECTED_CALL_AFTER);
 756  
         }
 757  
 
 758  64
         return retVal;
 759  
     }
 760  
 
 761  
     protected synchronized CalledMethod expectUnignoredCall(final String method, final Set<String> ignorables,
 762  
             final List<CalledMethod> calledMethods) {
 763  
 
 764  145
         String expectedMethod = NONE;
 765  
 
 766  145
         if (method != null) {
 767  145
             expectedMethod = method;
 768  
         }
 769  
 
 770  
         // kind of dangerous if used with multiple threads because we might miss
 771  
         // a bad event coming in, but if you keep using the same listener for
 772  
         // every test
 773  
         // the problem should still manifest itself in a
 774  
         // different test case which sucks but works.
 775  145
         if (expectedMethod.equals(NONE)) {
 776  7
             if (log.isLoggable(Level.FINE)) {
 777  0
                 log.log(Level.FINE, "method expected=" + NONE + " on obj=" + this);
 778  
             }
 779  
 
 780  
             // we have to strip out all of the ignored methods from
 781  
             // methodsCalled
 782  7
             CalledMethod[] methods = methodsCalled.toArray(new CalledMethod[0]);
 783  15
             for (CalledMethod calledMethod : methods) {
 784  8
                 if (ignorables.contains(calledMethod.getMethodName())) {
 785  12
                     while (methodsCalled.contains(calledMethod)) {
 786  6
                         methodsCalled.remove(calledMethod);
 787  6
                     }
 788  
                 }
 789  
             }
 790  
 
 791  7
             LeftOverMethods leftOver = getLeftOverMethods(ignorables);
 792  7
             if (leftOver != null) {
 793  2
                 String reason =
 794  
                         "You were expecting no methods to be called, but method(s) not\n"
 795  
                                 + "in the ignore list were called earlier. method(s)=" + leftOver;
 796  2
                 reason += MessageHelper.getHowMethodWasCalled(leftOver.getMethods()[0]);
 797  2
                 throw new ExpectFailedException(reason, leftOver.getMethods(), ExpectFailedException.UNEXPECTED_ON_NONE);
 798  
             }
 799  5
             return new CalledMethod(NONE, null, null);
 800  
         }
 801  
 
 802  
         try {
 803  138
             return waitForUnignoredCall(expectedMethod, ignorables, calledMethods);
 804  0
         } catch (InterruptedException e) {
 805  0
             throw new RuntimeException(e);
 806  
         }
 807  
     }
 808  
 
 809  
     private CalledMethod waitForUnignoredCall(String logM, Set<String> ignorables, List<CalledMethod> calledMethods)
 810  
             throws InterruptedException {
 811  
 
 812  138
         long waitTime2 = waitTime + 50;
 813  
         long currentTime;
 814  
         // only while waitTime is greater than 50 milliseconds
 815  142
         while (waitTime2 >= 50) {
 816  
             // if there are no methods called yet, begin waiting
 817  141
             if (methodsCalled.size() <= 0) {
 818  7
                 currentTime = System.currentTimeMillis();
 819  7
                 if (log.isLoggable(Level.FINE)) {
 820  0
                     log.fine("method expected(not called yet-waiting)=" + logM + " on obj=" + this + " wait="
 821  
                             + waitTime2);
 822  
                 }
 823  7
                 this.wait(waitTime2);
 824  7
                 long waitedOnWait = System.currentTimeMillis() - currentTime;
 825  7
                 waitTime2 -= waitedOnWait;
 826  
             }
 827  
 
 828  
             // check for new methods to be called now...if no non-ignorable
 829  
             // methods, then continue while loop
 830  153
             for (int i = 0; i < methodsCalled.size(); i++) {
 831  149
                 CalledMethod calledMethod = methodsCalled.remove(0);
 832  149
                 calledMethods.add(calledMethod);
 833  149
                 if (isCaseSensitive) {
 834  139
                     if (!ignorables.contains(calledMethod.getMethodName())) {
 835  130
                         if (log.isLoggable(Level.FINE)) {
 836  0
                             log.fine("method expected and was called=" + logM + " on obj=" + this);
 837  
                         }
 838  130
                         return calledMethod;
 839  
                     }
 840  
                 } else {
 841  10
                     if (!ignorables.contains(calledMethod.getMethodName().toLowerCase())) {
 842  7
                         if (log.isLoggable(Level.FINE)) {
 843  0
                             log.fine("method expected and was called=" + logM + " on obj=" + this);
 844  
                         }
 845  7
                         return calledMethod;
 846  
                     }
 847  
                 }
 848  
             }
 849  4
         }
 850  
 
 851  
         // return null means timeout....
 852  1
         return null;
 853  
     }
 854  
 
 855  
     private Set<String> createIgnorableMap(final Set<String> ignorableMethods) {
 856  90
         final Set<String> ignorables = new HashSet<String>();
 857  90
         if (ignorableMethods != null) {
 858  90
             for (String method : ignorableMethods) {
 859  18
                 if (isCaseSensitive) {
 860  13
                     ignorables.add(method);
 861  13
                 } else {
 862  5
                     ignorables.add(method.toLowerCase());
 863  
                 }
 864  18
             }
 865  
         }
 866  90
         return ignorables;
 867  
     }
 868  
 
 869  
     private LeftOverMethods getLeftOverMethods(final Set<String> ignorables) {
 870  88
         final List<CalledMethod> leftOver = new ArrayList<CalledMethod>();
 871  88
         for (CalledMethod o : methodsCalled) {
 872  43
             if (isCaseSensitive) {
 873  38
                 if (o != null && !ignorables.contains(o.getMethodName())) {
 874  10
                     leftOver.add(o);
 875  10
                 }
 876  
             } else {
 877  5
                 if (o != null && !ignorables.contains(o.getMethodName().toLowerCase())) {
 878  0
                     leftOver.add(o);
 879  
                 }
 880  
             }
 881  43
         }
 882  88
         methodsCalled.clear();
 883  
 
 884  88
         if (leftOver.size() <= 0) {
 885  82
             return null;
 886  
         }
 887  
 
 888  6
         return new LeftOverMethods(leftOver.toArray(new CalledMethod[0]));
 889  
     }
 890  
 
 891  
     private static final class LeftOverMethods {
 892  
 
 893  
         private final CalledMethod[] leftOver;
 894  
 
 895  6
         public LeftOverMethods(CalledMethod[] m) {
 896  6
             leftOver = m;
 897  6
         }
 898  
 
 899  
         public CalledMethod[] getMethods() {
 900  12
             return leftOver;
 901  
         }
 902  
 
 903  
         @Override
 904  
         public String toString() {
 905  6
             String retVal = "";
 906  16
             for (int i = 0; i < leftOver.length; i++) {
 907  10
                 retVal += "\n" + leftOver[i].getMethodName();
 908  
             }
 909  6
             return retVal;
 910  
         }
 911  
     }
 912  
 
 913  
     /*
 914  
      * (non-Javadoc)
 915  
      * 
 916  
      * @see biz.xsoftware.mock.MockObject#setDefaultBehavior(java.lang.String,
 917  
      *      biz.xsoftware.mock.Behavior)
 918  
      */
 919  
     public void setDefaultBehavior(String method, Behavior b) {
 920  1
         setDefaultBehavior(new MethodSignature(method), b);
 921  1
     }
 922  
 
 923  
     /*
 924  
      * (non-Javadoc)
 925  
      * 
 926  
      * @see biz.xsoftware.mock.MockObject#setDefaultBehavior(java.lang.String,
 927  
      *      biz.xsoftware.mock.Behavior, java.lang.Class<?>[])
 928  
      */
 929  
     @Deprecated
 930  
     public void setDefaultBehavior(String methodStr, Behavior b, Class<?>... argTypes) {
 931  0
         setDefaultBehavior(new MethodSignature(methodStr, argTypes), b);
 932  0
     }
 933  
 
 934  
     /*
 935  
      * (non-Javadoc)
 936  
      * 
 937  
      * @see biz.xsoftware.mock.MockObject#setDefaultBehavior(biz.xsoftware.mock.MethodSignature,
 938  
      *      biz.xsoftware.mock.Behavior)
 939  
      */
 940  
     public void setDefaultBehavior(MethodSignature methodSignature, Behavior b) {
 941  4
         Method method = checkMethod(methodSignature.getName(), methodSignature.getParamTypes());
 942  4
         setDefaultBehavior(method, b);
 943  4
     }
 944  
 
 945  
     /**
 946  
      * This method should only be used when it is specifically necessary. The
 947  
      * passed in Method is not checked to insure the Method belongs to one of
 948  
      * the implemented interface classes. This was done intentionally because of
 949  
      * the BehaviorMethod(override) capability. The override is assuming the
 950  
      * user knows what they're doing
 951  
      * 
 952  
      * @param method Cannot be null
 953  
      * 
 954  
      * @param b Cannot be null
 955  
      */
 956  
     private void setDefaultBehavior(Method method, Behavior b) {
 957  
 
 958  5
         if (method == null) {
 959  0
             throw new NullPointerException("Method given cannot be null");
 960  
         }
 961  
 
 962  5
         if (b == null) {
 963  0
             throw new NullPointerException("Behavior given cannot be null");
 964  
         }
 965  
 
 966  5
         BehaviorInfo action = createBehaviorInfo(method, b);
 967  
 
 968  5
         methodToDefaultRetVal.put(method, action);
 969  5
     }
 970  
 
 971  
     /*
 972  
      * (non-Javadoc)
 973  
      * 
 974  
      * @see biz.xsoftware.mock.MockObject#setDefaultReturnValue(java.lang.String,
 975  
      *      java.lang.Object)
 976  
      */
 977  
     public void setDefaultReturnValue(String method, Object returnValue) {
 978  13
         setDefaultReturnValue(method, returnValue, new Class[] {});
 979  13
     }
 980  
 
 981  
     public void setDefaultReturnValue(String methodStr, Object o, Class<?>... argTypes) {
 982  13
         Method method = checkMethod(methodStr, argTypes);
 983  
 
 984  13
         methodToDefaultRetVal.put(method, new ReturnValue(o));
 985  13
     }
 986  
 
 987  
     /*
 988  
      * (non-Javadoc)
 989  
      * 
 990  
      * @see biz.xsoftware.mock.MockObject#setDefaultReturnValue(java.lang.reflect.Method,
 991  
      *      java.lang.Object)
 992  
      */
 993  
     public void setDefaultReturnValue(Method method, Object o) {
 994  1
         methodToDefaultRetVal.put(method, new ReturnValue(o));
 995  1
     }
 996  
 
 997  
     /*
 998  
      * (non-Javadoc)
 999  
      * 
 1000  
      * @see biz.xsoftware.mock.MockObject#setBehavior(biz.xsoftware.mock.Behavior)
 1001  
      */
 1002  
     public void setBehavior(Behavior behavior) {
 1003  
         // loop through all the methods in the given Behavior class and add the
 1004  
         // found methods that are annotated with the BehaviorMethod
 1005  22
         for (Method m : behavior.getClass().getMethods()) {
 1006  20
             BehaviorMethod a = (BehaviorMethod) m.getAnnotation(BehaviorMethod.class);
 1007  20
             if (a != null) {
 1008  4
                 if (!a.override().equals(void.class)) {
 1009  
                     // the user has attempted to override the Behavior class
 1010  
                     // with a specific
 1011  
                     // class type
 1012  
                     try {
 1013  1
                         Method overrideMethod = a.override().getMethod(m.getName(), m.getParameterTypes());
 1014  
                         // using the override method here because don't want to
 1015  
                         // store the behavior using the interface
 1016  
                         // method as the key. Instead we want to store the
 1017  
                         // behavior using the override object's method
 1018  1
                         setDefaultBehavior(overrideMethod, behavior);
 1019  0
                     } catch (SecurityException e) {
 1020  0
                         throw new IllegalArgumentException(e);
 1021  0
                     } catch (NoSuchMethodException e) {
 1022  0
                         throw new IllegalArgumentException("The Behavior given attempted to override a method that"
 1023  
                                 + " does not exist in the given override class", e);
 1024  1
                     }
 1025  
                 } else {
 1026  
                     // we need to build a MethodSignature here because we want
 1027  
                     // to store the called method using the
 1028  
                     // interface, not the Behavior's method
 1029  3
                     setDefaultBehavior(new MethodSignature(m.getName(), m.getParameterTypes()), behavior);
 1030  
                 }
 1031  
             }
 1032  
         }
 1033  2
     }
 1034  
 
 1035  
     /*
 1036  
      * (non-Javadoc)
 1037  
      * 
 1038  
      * @see biz.xsoftware.mock.MockObject#addBehavior(java.lang.String,
 1039  
      *      biz.xsoftware.mock.Behavior)
 1040  
      */
 1041  
     public void addBehavior(String method, Behavior behavior) {
 1042  7
         addBehavior(new MethodSignature(method, (Class<?>[]) null), behavior);
 1043  7
     }
 1044  
 
 1045  
     /*
 1046  
      * (non-Javadoc)
 1047  
      * 
 1048  
      * @see biz.xsoftware.mock.MockObject#addBehavior(java.lang.String,
 1049  
      *      biz.xsoftware.mock.Behavior, java.lang.Class<?>[])
 1050  
      */
 1051  
     @Deprecated
 1052  
     public void addBehavior(String method, Behavior behavior, Class<?>... argTypes) {
 1053  0
         addBehavior(new MethodSignature(method, argTypes), behavior);
 1054  0
     }
 1055  
 
 1056  
     /*
 1057  
      * (non-Javadoc)
 1058  
      * 
 1059  
      * @see biz.xsoftware.mock.MockObject#addBehavior(biz.xsoftware.mock.MethodSignature,
 1060  
      *      biz.xsoftware.mock.Behavior)
 1061  
      */
 1062  
     public void addBehavior(MethodSignature methodSignature, Behavior behavior) {
 1063  7
         if (methodSignature == null)
 1064  0
             throw new NullPointerException("methodSignature parameter cannot be null");
 1065  
 
 1066  7
         if (behavior == null)
 1067  0
             throw new NullPointerException("behavior parameter cannot be null");
 1068  
 
 1069  7
         Method m = MethodVerifier.getMethod(getClasses(), methodSignature, isCaseSensitive);
 1070  
 
 1071  7
         BehaviorInfo action = createBehaviorInfo(m, behavior);
 1072  
 
 1073  7
         addToActionList(action, m);
 1074  7
     }
 1075  
 
 1076  
     /**
 1077  
      * Creates a BehaviorInfo instance using the given {@link Method} and
 1078  
      * {@link Behavior}
 1079  
      * 
 1080  
      * @param classMethod The method to attach the given {@link Behavior} to
 1081  
      * 
 1082  
      * @param behavior The {@link Behavior} object that implements the method
 1083  
      * 
 1084  
      * @return The built object
 1085  
      */
 1086  
     private BehaviorInfo createBehaviorInfo(Method classMethod, Behavior behavior) {
 1087  12
         BehaviorInfo action = null;
 1088  
 
 1089  
         // verify behavior has correct methods
 1090  12
         Class<?> clazz = behavior.getClass();
 1091  
 
 1092  
         try {
 1093  12
             Method behaviorMethod =
 1094  
                     clazz.getMethod(classMethod.getName(), (Class<?>[]) classMethod.getParameterTypes());
 1095  12
             action = new BehaviorInfo(behavior, behaviorMethod);
 1096  12
             behaviorMethod.setAccessible(true);
 1097  0
         } catch (SecurityException e) {
 1098  0
             throw new RuntimeException("Your Behavior class seems to be too secure.  "
 1099  
                     + "I can't reflect on it and call getClass on it's class object");
 1100  0
         } catch (NoSuchMethodException e) {
 1101  0
             String methodSig = MessageHelper.getMethodSignature(null, classMethod, "");
 1102  0
             throw new IllegalArgumentException("You Behavior class is missing the method='" + methodSig);
 1103  12
         }
 1104  
 
 1105  
         try {
 1106  12
             if (!(behavior instanceof CloningBehavior))
 1107  8
                 return action;
 1108  
 
 1109  4
             Method clonerMethod =
 1110  
                     clazz.getMethod(classMethod.getName() + "Cloner", (Class<?>[]) classMethod.getParameterTypes());
 1111  4
             action.setClonerMethod(clonerMethod);
 1112  4
             if (!Object[].class.isAssignableFrom(clonerMethod.getReturnType()))
 1113  0
                 throw new IllegalArgumentException("Method=" + clonerMethod
 1114  
                         + " does not return Object[] which it must do");
 1115  4
             clonerMethod.setAccessible(true);
 1116  0
         } catch (SecurityException e) {
 1117  0
             throw new RuntimeException("Your Behavior class seems to be too secure.  "
 1118  
                     + "I can't reflect on it and call getClass on it's class object");
 1119  0
         } catch (NoSuchMethodException e) {
 1120  0
             String methodSig = MessageHelper.getMethodSignature("Object[]", classMethod, "Cloner");
 1121  0
             throw new IllegalArgumentException("You Behavior class is missing the method='" + methodSig);
 1122  4
         }
 1123  4
         return action;
 1124  
     }
 1125  
 
 1126  
     /*
 1127  
      * (non-Javadoc)
 1128  
      * 
 1129  
      * @see biz.xsoftware.mock.MockObject#addThrowException(java.lang.String,
 1130  
      *      java.lang.Throwable)
 1131  
      */
 1132  
     public void addThrowException(String method, Throwable e) {
 1133  6
         addThrowException(new MethodSignature(method, (Class<?>[]) null), e);
 1134  6
     }
 1135  
 
 1136  
     /*
 1137  
      * (non-Javadoc)
 1138  
      * 
 1139  
      * @see biz.xsoftware.mock.MockObject#addThrowException(biz.xsoftware.mock.MethodSignature,
 1140  
      *      java.lang.Throwable)
 1141  
      */
 1142  
     public void addThrowException(MethodSignature ms, Throwable e) {
 1143  8
         Method m = MethodVerifier.getMethod(getClasses(), ms, isCaseSensitive);
 1144  8
         Action action = new ThrowException(e);
 1145  8
         addToActionList(action, m);
 1146  8
     }
 1147  
 
 1148  
     /*
 1149  
      * (non-Javadoc)
 1150  
      * 
 1151  
      * @see biz.xsoftware.mock.MockObject#addReturnValue(java.lang.String,
 1152  
      *      java.lang.Object[])
 1153  
      */
 1154  
     public void addReturnValue(String method, Object... objects) {
 1155  23
         addReturnValue(new MethodSignature(method, (Class<?>[]) null), objects);
 1156  22
     }
 1157  
     
 1158  
     /**
 1159  
      * @deprecated  {@link #addReturnValue(Method, Object...)} was enhanced to detect if the given method returns
 1160  
      * an array.  If it does then the object array passed in will be the object returned for a single method call
 1161  
      */
 1162  
     @Deprecated
 1163  
     public void addReturnValue(String method, boolean isArray, Object... objects) {
 1164  0
         addReturnValue(method, objects);
 1165  0
     }
 1166  
 
 1167  
     /*
 1168  
      * (non-Javadoc)
 1169  
      * 
 1170  
      * @see biz.xsoftware.mock.MockObject#addReturnValue(biz.xsoftware.mock.MethodSignature,
 1171  
      *      java.lang.Object[])
 1172  
      */
 1173  
     public void addReturnValue(MethodSignature methodSignature, Object... objects) {
 1174  24
         Method m = MethodVerifier.getMethod(getClasses(), methodSignature, isCaseSensitive);
 1175  
         // determine if the return type is an array, if it is then we'll assume
 1176  
         // that the objects passed in is the array to set as the result
 1177  23
         if (m.getReturnType().isArray()) {
 1178  11
             if (objects.getClass().getName().startsWith("[[")) {
 1179  
                 // for primitive type arrays for some reason we get an Object
 1180  
                 // array that contains the primitive array.  This [[ appears
 1181  
                 // to be unique to this situation (not the best implementation though)
 1182  9
                 addToActionList(new ReturnValue(objects[0]), m);
 1183  9
             } else {
 1184  2
                 addToActionList(new ReturnValue(objects), m);
 1185  
             }
 1186  2
         } else {
 1187  127
             for (Object o : objects) {
 1188  115
                 Action action = new ReturnValue(o);
 1189  115
                 addToActionList(action, m);
 1190  
             }
 1191  
         }
 1192  23
     }
 1193  
 
 1194  
     /*
 1195  
      * (non-Javadoc)
 1196  
      * 
 1197  
      * @see biz.xsoftware.mock.MockObject#addReturnValue(biz.xsoftware.mock.MethodSignature,
 1198  
      *      java.lang.Object[])
 1199  
      */
 1200  
     public void addReturnValue(Method method, Object... objects) {
 1201  0
         if (method.getReturnType().isArray()) {
 1202  0
             addToActionList(new ReturnValue(objects), method);
 1203  0
         } else {
 1204  0
             for (Object o : objects) {
 1205  0
                 addToActionList(new ReturnValue(o), method);
 1206  
             }
 1207  
         }
 1208  0
     }
 1209  
 
 1210  
     /**
 1211  
      * Adds the given action to the end of {@link Action} list mapped to the
 1212  
      * given method
 1213  
      * 
 1214  
      * @param action
 1215  
      * @param method
 1216  
      */
 1217  
     private void addToActionList(Action action, Method method) {
 1218  141
         List<Action> l = methodToReturnVal.get(method);
 1219  141
         if (l == null) {
 1220  37
             l = new ArrayList<Action>();
 1221  37
             methodToReturnVal.put(method, l);
 1222  
         }
 1223  
 
 1224  141
         l.add(action);
 1225  141
     }
 1226  
 
 1227  
     /**
 1228  
      * This is the method that calls the cloner to clone objects like ByteBuffer
 1229  
      * that can change after they are called.
 1230  
      * 
 1231  
      * @param o
 1232  
      * @return
 1233  
      * @throws Throwable
 1234  
      */
 1235  
     private Object[] clone(Method method, Object[] params) throws Throwable {
 1236  342
         if (params == null)
 1237  146
             return null;
 1238  
 
 1239  196
         Action action = methodToDefaultRetVal.get(method);
 1240  
 
 1241  
         // now override the default if there is an override specified...
 1242  196
         List<Action> actions = methodToReturnVal.get(method);
 1243  196
         if (actions != null) {
 1244  129
             action = actions.get(0);
 1245  
         }
 1246  
 
 1247  
         // check if action is a behaviorInfo object
 1248  196
         if (action instanceof BehaviorInfo) {
 1249  9
             BehaviorInfo behavior = (BehaviorInfo) action;
 1250  9
             if (behavior.hasClonerMethod())
 1251  4
                 return behavior.runClonerMethod(params);
 1252  
         }
 1253  
 
 1254  192
         return params;
 1255  
     }
 1256  
 
 1257  
     public Class<?>[] getClasses() {
 1258  4
         return new Class<?>[] { this.getClass() };
 1259  
     }
 1260  
 
 1261  
     /**
 1262  
      * Looks for the method in the object
 1263  
      * 
 1264  
      * @param method The object's method to check for
 1265  
      * @param argTypes The method argument types
 1266  
      * @return The found method or null if not found
 1267  
      */
 1268  
     private Method checkMethod(String method, Class<?>[] argTypes) {
 1269  17
         return MethodVerifier.getMethod(getClasses(), new MethodSignature(method, argTypes), isCaseSensitive);
 1270  
     }
 1271  
 
 1272  
     /*
 1273  
      * (non-Javadoc)
 1274  
      * 
 1275  
      * @see biz.xsoftware.mock.MockObject#isCaseSensitive()
 1276  
      */
 1277  
     public boolean isCaseSensitive() {
 1278  0
         return isCaseSensitive;
 1279  
     }
 1280  
 
 1281  
     /*
 1282  
      * (non-Javadoc)
 1283  
      * 
 1284  
      * @see biz.xsoftware.mock.MockObject#setCaseSensitive(boolean)
 1285  
      */
 1286  
     public void setCaseSensitive(boolean isCaseSensitive) {
 1287  4
         this.isCaseSensitive = isCaseSensitive;
 1288  4
     }
 1289  
 
 1290  
     /*
 1291  
      * (non-Javadoc)
 1292  
      * 
 1293  
      * @see biz.xsoftware.mock.MockObject#reset()
 1294  
      */
 1295  
     public void reset() {
 1296  0
         methodsCalled.clear();
 1297  0
         ignoredMethods.clear();
 1298  0
         methodToReturnVal.clear();
 1299  0
         methodToDefaultRetVal.clear();
 1300  0
     }
 1301  
 
 1302  
     /*
 1303  
      * (non-Javadoc)
 1304  
      * 
 1305  
      * @see java.lang.Object#toString()
 1306  
      */
 1307  
     @Override
 1308  
     public String toString() {
 1309  1
         StringBuilder sb = new StringBuilder();
 1310  1
         sb.append(id);
 1311  1
         sb.append(":");
 1312  1
         sb.append(super.toString());
 1313  1
         return sb.toString();
 1314  
     }
 1315  
 }