1
2
3
4
5
6 package mockit.internal.expectations.invocation;
7
8 import static mockit.internal.util.Utilities.JAVA8;
9
10 import edu.umd.cs.findbugs.annotations.NonNull;
11 import edu.umd.cs.findbugs.annotations.Nullable;
12
13 import java.lang.reflect.Array;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.LinkedHashMap;
18 import java.util.LinkedHashSet;
19 import java.util.List;
20 import java.util.ListIterator;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.SortedMap;
24 import java.util.SortedSet;
25 import java.util.TreeMap;
26 import java.util.TreeSet;
27 import java.util.stream.Stream;
28
29 import org.checkerframework.checker.index.qual.NonNegative;
30
31 final class MultiValuedConversion {
32 @NonNull
33 private final InvocationResults invocationResults;
34 @NonNull
35 private final Class<?> returnType;
36 @NonNull
37 private final Object valueToReturn;
38
39 MultiValuedConversion(@NonNull InvocationResults invocationResults, @NonNull Class<?> returnType,
40 @NonNull Object valueToReturn) {
41 this.invocationResults = invocationResults;
42 this.returnType = returnType;
43 this.valueToReturn = valueToReturn;
44 }
45
46 void addMultiValuedResultBasedOnTheReturnType(boolean valueIsArray) {
47 if (returnType == void.class) {
48 addMultiValuedResult(valueIsArray);
49 } else if (returnType == Object.class) {
50 invocationResults.addReturnValueResult(valueToReturn);
51 } else if (valueIsArray && addCollectionOrMapWithElementsFromArray()) {
52
53 } else if (hasReturnOfDifferentType()) {
54 addMultiValuedResult(valueIsArray);
55 } else {
56 invocationResults.addReturnValueResult(valueToReturn);
57 }
58 }
59
60 private void addMultiValuedResult(boolean valueIsArray) {
61 if (valueIsArray) {
62 invocationResults.addResults(valueToReturn);
63 } else if (valueToReturn instanceof Iterable<?>) {
64 if (JAVA8 && valueToReturn instanceof Collection && returnType.isAssignableFrom(Stream.class)) {
65 invocationResults.addReturnValueResult(((Collection<?>) valueToReturn).stream());
66 } else {
67 invocationResults.addResults((Iterable<?>) valueToReturn);
68 }
69 } else {
70 invocationResults.addDeferredResults((Iterator<?>) valueToReturn);
71 }
72 }
73
74 private boolean hasReturnOfDifferentType() {
75 return !returnType.isArray() && !Iterable.class.isAssignableFrom(returnType)
76 && !Iterator.class.isAssignableFrom(returnType)
77 && !returnType.isAssignableFrom(valueToReturn.getClass());
78 }
79
80 private boolean addCollectionOrMapWithElementsFromArray() {
81 @NonNegative
82 int n = Array.getLength(valueToReturn);
83 Object values = null;
84
85 if (returnType.isAssignableFrom(ListIterator.class)) {
86 List<Object> list = new ArrayList<>(n);
87 addArrayElements(list, n);
88 values = list.listIterator();
89 } else if (returnType.isAssignableFrom(List.class)) {
90 values = addArrayElements(new ArrayList<>(n), n);
91 } else if (returnType.isAssignableFrom(Set.class)) {
92 values = addArrayElements(new LinkedHashSet<>(n), n);
93 } else if (returnType.isAssignableFrom(SortedSet.class)) {
94 values = addArrayElements(new TreeSet<>(), n);
95 } else if (returnType.isAssignableFrom(Map.class)) {
96 values = addArrayElements(new LinkedHashMap<>(n), n);
97 } else if (returnType.isAssignableFrom(SortedMap.class)) {
98 values = addArrayElements(new TreeMap<>(), n);
99 } else if (JAVA8 && returnType.isAssignableFrom(Stream.class)) {
100 values = addArrayElements(new ArrayList<>(n), n).stream();
101 }
102
103 if (values != null) {
104 invocationResults.addReturnValue(values);
105 return true;
106 }
107
108 return false;
109 }
110
111 @NonNull
112 private Collection<?> addArrayElements(@NonNull Collection<Object> values, @NonNegative int elementCount) {
113 for (int i = 0; i < elementCount; i++) {
114 Object element = Array.get(valueToReturn, i);
115 values.add(element);
116 }
117
118 return values;
119 }
120
121 @Nullable
122 private Object addArrayElements(@NonNull Map<Object, Object> values, @NonNegative int elementPairCount) {
123 for (int i = 0; i < elementPairCount; i++) {
124 Object keyAndValue = Array.get(valueToReturn, i);
125
126 if (keyAndValue == null || !keyAndValue.getClass().isArray()) {
127 return null;
128 }
129
130 Object key = Array.get(keyAndValue, 0);
131 Object element = Array.getLength(keyAndValue) > 1 ? Array.get(keyAndValue, 1) : null;
132 values.put(key, element);
133 }
134
135 return values;
136 }
137 }