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