View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.internal.expectations;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.lang.reflect.Array;
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.Collections;
15  import java.util.Iterator;
16  import java.util.LinkedHashSet;
17  import java.util.List;
18  import java.util.Set;
19  import java.util.SortedSet;
20  import java.util.TreeSet;
21  
22  final class SequenceOfReturnValues {
23      @NonNull
24      private final Expectation expectation;
25      @NonNull
26      private final Class<?> returnType;
27      @Nullable
28      private final Object firstValue;
29      @NonNull
30      private final Object[] remainingValues;
31  
32      SequenceOfReturnValues(@NonNull Expectation expectation, @Nullable Object firstValue,
33              @NonNull Object[] remainingValues) {
34          this.expectation = expectation;
35          returnType = expectation.getReturnType();
36          this.firstValue = firstValue;
37          this.remainingValues = remainingValues;
38      }
39  
40      boolean addResultWithSequenceOfValues() {
41          boolean added = false;
42  
43          if (returnType != void.class) {
44              if (returnType.isArray()) {
45                  added = addValuesInArrayIfApplicable();
46              } else if (Iterator.class.isAssignableFrom(returnType)) {
47                  added = addValuesInIteratorIfApplicable();
48              } else if (Iterable.class.isAssignableFrom(returnType)) {
49                  added = addValuesInIterableIfApplicable();
50              }
51          }
52  
53          return added;
54      }
55  
56      private boolean addValuesInArrayIfApplicable() {
57          if (firstValue == null || !firstValue.getClass().isArray()) {
58              addArrayAsReturnValue();
59              return true;
60          }
61  
62          return false;
63      }
64  
65      private void addArrayAsReturnValue() {
66          Class<?> elementType = returnType.getComponentType();
67          int n = 1 + remainingValues.length;
68          Object values = Array.newInstance(elementType, n);
69          setArrayElement(elementType, values, 0, firstValue);
70  
71          for (int i = 1; i < n; i++) {
72              setArrayElement(elementType, values, i, remainingValues[i - 1]);
73          }
74  
75          expectation.getResults().addReturnValue(values);
76      }
77  
78      private static void setArrayElement(Class<?> elementType, Object array, int index, @Nullable Object value) {
79          Object arrayValue = value;
80  
81          if (value != null) {
82              if (elementType == byte.class || elementType == Byte.class) {
83                  arrayValue = ((Number) value).byteValue();
84              } else if (elementType == short.class || elementType == Short.class) {
85                  arrayValue = ((Number) value).shortValue();
86              }
87          }
88  
89          Array.set(array, index, arrayValue);
90      }
91  
92      private boolean addValuesInIteratorIfApplicable() {
93          if (firstValue == null || !Iterator.class.isAssignableFrom(firstValue.getClass())) {
94              List<Object> values = new ArrayList<>(1 + remainingValues.length);
95              addAllValues(values);
96              expectation.getResults().addReturnValue(values.iterator());
97              return true;
98          }
99  
100         return false;
101     }
102 
103     private void addAllValues(@NonNull Collection<Object> values) {
104         values.add(firstValue);
105         Collections.addAll(values, remainingValues);
106     }
107 
108     private boolean addValuesInIterableIfApplicable() {
109         if (firstValue == null || !Iterable.class.isAssignableFrom(firstValue.getClass())) {
110             Collection<Object> values;
111 
112             if (returnType.isAssignableFrom(List.class)) {
113                 values = new ArrayList<>(1 + remainingValues.length);
114             } else if (returnType.isAssignableFrom(Set.class)) {
115                 values = new LinkedHashSet<>(1 + remainingValues.length);
116             } else if (returnType.isAssignableFrom(SortedSet.class)) {
117                 values = new TreeSet<>();
118             } else {
119                 return false;
120             }
121 
122             addReturnValues(values);
123             return true;
124         }
125 
126         return false;
127     }
128 
129     private void addReturnValues(@NonNull Collection<Object> values) {
130         addAllValues(values);
131         expectation.getResults().addReturnValue(values);
132     }
133 }