1 /* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.stubbing.answers; 6 7 import org.mockito.exceptions.Reporter; 8 import org.mockito.invocation.InvocationOnMock; 9 import org.mockito.stubbing.Answer; 10 11 import java.io.Serializable; 12 13 /** 14 * Returns the passed parameter identity at specified index. 15 * 16 * <p>The <code>argumentIndex</code> represents the index in the argument array of the invocation.</p> 17 * <p>If this number equals -1 then the last argument is returned.</p> 18 * 19 * @see org.mockito.AdditionalAnswers 20 * @since 1.9.5 21 */ 22 public class ReturnsArgumentAt implements Answer<Object>, Serializable { 23 24 private static final long serialVersionUID = -589315085166295101L; 25 26 public static final int LAST_ARGUMENT = -1; 27 28 private final int wantedArgumentPosition; 29 30 /** 31 * Build the identity answer to return the argument at the given position in the argument array. 32 * 33 * @param wantedArgumentPosition The position of the argument identity to return in the invocation. 34 * Using <code>-1</code> indicates the last argument. 35 */ 36 public ReturnsArgumentAt(int wantedArgumentPosition) { 37 this.wantedArgumentPosition = checkWithinAllowedRange(wantedArgumentPosition); 38 } 39 40 public Object answer(InvocationOnMock invocation) throws Throwable { 41 validateIndexWithinInvocationRange(invocation); 42 return invocation.getArguments()[actualArgumentPosition(invocation)]; 43 } 44 45 46 private int actualArgumentPosition(InvocationOnMock invocation) { 47 return returningLastArg() ? 48 lastArgumentIndexOf(invocation) : 49 argumentIndexOf(invocation); 50 } 51 52 private boolean returningLastArg() { 53 return wantedArgumentPosition == LAST_ARGUMENT; 54 } 55 56 private int argumentIndexOf(InvocationOnMock invocation) { 57 return wantedArgumentPosition; 58 } 59 60 private int lastArgumentIndexOf(InvocationOnMock invocation) { 61 return invocation.getArguments().length - 1; 62 } 63 64 private int checkWithinAllowedRange(int argumentPosition) { 65 if (argumentPosition != LAST_ARGUMENT && argumentPosition < 0) { 66 new Reporter().invalidArgumentRangeAtIdentityAnswerCreationTime(); 67 } 68 return argumentPosition; 69 } 70 71 public int wantedArgumentPosition() { 72 return wantedArgumentPosition; 73 } 74 75 public void validateIndexWithinInvocationRange(InvocationOnMock invocation) { 76 if (!argumentPositionInRange(invocation)) { 77 new Reporter().invalidArgumentPositionRangeAtInvocationTime(invocation, 78 returningLastArg(), 79 wantedArgumentPosition); 80 } 81 } 82 83 private boolean argumentPositionInRange(InvocationOnMock invocation) { 84 int actualArgumentPosition = actualArgumentPosition(invocation); 85 if (actualArgumentPosition < 0) { 86 return false; 87 } 88 if (!invocation.getMethod().isVarArgs()) { 89 return invocation.getArguments().length > actualArgumentPosition; 90 } 91 // for all varargs accepts positive ranges 92 return true; 93 } 94 95 public Class returnedTypeOnSignature(InvocationOnMock invocation) { 96 int actualArgumentPosition = actualArgumentPosition(invocation); 97 98 if(!invocation.getMethod().isVarArgs()) { 99 return invocation.getMethod().getParameterTypes()[actualArgumentPosition]; 100 } 101 102 Class<?>[] parameterTypes = invocation.getMethod().getParameterTypes(); 103 int varargPosition = parameterTypes.length - 1; 104 105 if(actualArgumentPosition < varargPosition) { 106 return parameterTypes[actualArgumentPosition]; 107 } else { 108 return parameterTypes[varargPosition].getComponentType(); 109 } 110 } 111 } 112