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