View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
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              // Do nothing
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 }