1/*
2 * Copyright (c) 2007 Mockito contributors
3 * This program is made available under the terms of the MIT License.
4 */
5
6package org.mockito.internal.invocation;
7
8import java.util.LinkedList;
9import java.util.List;
10
11import org.mockito.internal.util.collections.ListUtil;
12import org.mockito.internal.util.collections.ListUtil.Filter;
13import org.mockito.internal.verification.api.InOrderContext;
14import org.mockito.invocation.Invocation;
15import org.mockito.invocation.Location;
16import org.mockito.invocation.MatchableInvocation;
17
18public class InvocationsFinder {
19
20    private InvocationsFinder() {
21    }
22
23    public static List<Invocation> findInvocations(List<Invocation> invocations, MatchableInvocation wanted) {
24        return ListUtil.filter(invocations, new RemoveNotMatching(wanted));
25    }
26
27    public static List<Invocation> findAllMatchingUnverifiedChunks(List<Invocation> invocations, MatchableInvocation wanted, InOrderContext orderingContext) {
28        List<Invocation> unverified = removeVerifiedInOrder(invocations, orderingContext);
29        return ListUtil.filter(unverified, new RemoveNotMatching(wanted));
30    }
31
32    /**
33     * some examples how it works:
34     *
35     * Given invocations sequence:
36     * 1,1,2,1
37     *
38     * if wanted is 1 and mode is times(2) then returns
39     * 1,1
40     *
41     * if wanted is 1 and mode is atLeast() then returns
42     * 1,1,1
43     *
44     * if wanted is 1 and mode is times(x), where x != 2 then returns
45     * 1,1,1
46     */
47    public static List<Invocation> findMatchingChunk(List<Invocation> invocations, MatchableInvocation wanted, int wantedCount, InOrderContext context) {
48        List<Invocation> unverified = removeVerifiedInOrder(invocations, context);
49        List<Invocation> firstChunk = getFirstMatchingChunk(wanted, unverified);
50
51        if (wantedCount != firstChunk.size()) {
52            return findAllMatchingUnverifiedChunks(invocations, wanted, context);
53        } else {
54            return firstChunk;
55        }
56    }
57
58    private static List<Invocation> getFirstMatchingChunk(MatchableInvocation wanted, List<Invocation> unverified) {
59        List<Invocation> firstChunk = new LinkedList<Invocation>();
60        for (Invocation invocation : unverified) {
61            if (wanted.matches(invocation)) {
62                firstChunk.add(invocation);
63            } else if (!firstChunk.isEmpty()) {
64                break;
65            }
66        }
67        return firstChunk;
68    }
69
70    public static Invocation findFirstMatchingUnverifiedInvocation(List<Invocation> invocations, MatchableInvocation wanted, InOrderContext context ){
71        for( Invocation invocation : removeVerifiedInOrder( invocations, context )){
72            if( wanted.matches( invocation )){
73                return invocation;
74            }
75        }
76        return null;
77    }
78
79    public static Invocation findSimilarInvocation(List<Invocation> invocations, MatchableInvocation wanted) {
80        Invocation firstSimilar = null;
81        for (Invocation invocation : invocations) {
82            if (!wanted.hasSimilarMethod(invocation)) {
83                continue;
84            }
85            if (firstSimilar == null) {
86                firstSimilar = invocation;
87            }
88            if (wanted.hasSameMethod(invocation)) {
89                return invocation;
90            }
91        }
92
93        return firstSimilar;
94    }
95
96    public static Invocation findFirstUnverified(List<Invocation> invocations) {
97        return findFirstUnverified(invocations, null);
98    }
99
100    static Invocation findFirstUnverified(List<Invocation> invocations, Object mock) {
101        for (Invocation i : invocations) {
102            boolean mockIsValid = mock == null || mock == i.getMock();
103            if (!i.isVerified() && mockIsValid) {
104                return i;
105            }
106        }
107        return null;
108    }
109
110    public static Location getLastLocation(List<Invocation> invocations) {
111        if (invocations.isEmpty()) {
112            return null;
113        } else {
114            Invocation last = invocations.get(invocations.size() - 1);
115            return last.getLocation();
116        }
117    }
118
119    public static Invocation findPreviousVerifiedInOrder(List<Invocation> invocations, InOrderContext context) {
120        LinkedList<Invocation> verifiedOnly = ListUtil.filter(invocations, new RemoveUnverifiedInOrder(context));
121
122        if (verifiedOnly.isEmpty()) {
123            return null;
124        } else {
125            return verifiedOnly.getLast();
126        }
127    }
128
129    private static List<Invocation> removeVerifiedInOrder(List<Invocation> invocations, InOrderContext orderingContext) {
130        List<Invocation> unverified = new LinkedList<Invocation>();
131        for (Invocation i : invocations) {
132            if (orderingContext.isVerified(i)) {
133                unverified.clear();
134            } else {
135                unverified.add(i);
136            }
137        }
138        return unverified;
139    }
140
141    private static class RemoveNotMatching implements Filter<Invocation> {
142        private final MatchableInvocation wanted;
143
144        private RemoveNotMatching(MatchableInvocation wanted) {
145            this.wanted = wanted;
146        }
147
148        public boolean isOut(Invocation invocation) {
149            return !wanted.matches(invocation);
150        }
151    }
152
153    private static class RemoveUnverifiedInOrder implements Filter<Invocation> {
154        private final InOrderContext orderingContext;
155
156        public RemoveUnverifiedInOrder(InOrderContext orderingContext) {
157            this.orderingContext = orderingContext;
158        }
159
160        public boolean isOut(Invocation invocation) {
161            return !orderingContext.isVerified(invocation);
162        }
163    }
164
165    /**
166     * i3 is unverified here:
167     *
168     * i1, i2, i3
169     *     v
170     *
171     * all good here:
172     *
173     * i1, i2, i3
174     *     v   v
175     *
176     * @param context
177     * @param orderedInvocations
178     */
179    public static Invocation findFirstUnverifiedInOrder(InOrderContext context, List<Invocation> orderedInvocations) {
180        Invocation candidate = null;
181        for(Invocation i : orderedInvocations) {
182            if (!context.isVerified(i)) {
183                candidate = candidate != null ? candidate : i;
184            } else {
185                candidate = null;
186            }
187        }
188        return candidate;
189    }
190}
191