aboutsummaryrefslogtreecommitdiffstats
path: root/docproc/src/main/java/com/yahoo/docproc/CallStack.java
blob: 467b556f2277d1533bfbb177f98f3758f13468b6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.docproc;

import com.yahoo.component.ComponentId;
import com.yahoo.docproc.jdisc.metric.NullMetric;
import com.yahoo.jdisc.Metric;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * A stack of the processors to call next in this processing. To push which
 * processor to call next, call addNext, to get and remove the next processor,
 * call pop.
 *
 * This is not thread safe.
 *
 * @author bratseth
 */
public class CallStack {

    /** The name of this stack, or null if it is not named */
    private String name;

    /** The Call objects of this stack */
    private final List<Call> elements = new java.util.LinkedList<>();

    /** The last element popped from the call stack, if any */
    private Call lastPopped = null;

    /** Used for metrics in Call */
    private final Metric metric;

    public CallStack() {
        this(new NullMetric());
    }

    public CallStack(String name) {
        this(name, new NullMetric());
    }

    /** Creates an empty stack */
    public CallStack(Metric metric) {
        this.name = null;
        this.metric = metric;
    }
    /** Creates an empty stack with a name */
    public CallStack(String name, Metric metric) {
        this.name = name;
        this.metric = metric;
    }

    /**
     * Creates a stack from another stack (starting at the next of the given
     * callstack) This does a deep copy of the stack.
     */
    public CallStack(CallStack stackToCopy) {
        name = stackToCopy.name;
        for (Iterator<Call> i = stackToCopy.iterator(); i.hasNext();) {
            Call callToCopy = i.next();
            elements.add((Call) callToCopy.clone());
        }
        this.metric = stackToCopy.metric;
    }

    /**
     * Creates a stack (with a given name) based on a collection of document processors, which are added to the stack
     * in the iteration order of the collection.
     *
     * @param name the name of the stack
     * @param docprocs the document processors to call
     */
    public CallStack(String name, Collection<DocumentProcessor> docprocs, Metric metric) {
        this(name, metric);
        for (DocumentProcessor docproc : docprocs) {
            addLast(docproc);
        }
    }

    /** Returns the name of this stack, or null if it is not named */
    public String getName() {
        return name;
    }

    /** Sets the name of this stack */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Push an element as the <i>next</i> element on this stack
     *
     * @return this for convenience
     */
    public CallStack addNext(Call call) {
        elements.add(0, call);
        return this;
    }

    /**
     * Push an element as the <i>next</i> element on this stack
     *
     * @return this for convenience
     */
    public CallStack addNext(DocumentProcessor processor) {
        return addNext(new Call(processor, name, metric));
    }

    /**
     * Push multiple elements as the <i>next</i> elements on this stack
     *
     * @return this for convenience
     */
    public CallStack addNext(CallStack callStack) {
        elements.addAll(0, callStack.elements);
        return this;
    }

    /**
     * Adds an element as the <i>last</i> element on this stack
     *
     * @return this for convenience
     */
    public CallStack addLast(Call call) {
        elements.add(call);
        return this;
    }

    /**
     * Adds an element as the <i>last</i> element on this stack
     *
     * @return this for convenience
     */
    public CallStack addLast(DocumentProcessor processor) {
        return addLast(new Call(processor, name, metric));
    }

    /**
     * Adds multiple elements as the <i>last</i> elements on this stack
     *
     * @return this for convenience
     */
    public CallStack addLast(CallStack callStack) {
        elements.addAll(callStack.elements);
        return this;
    }

    /**
     * Adds an element just before the first occurence of some other element on
     * the stack. This can not be called during an iteration.
     *
     * @param before
     *            the call to add this before. If this call is not present (the
     *            same object instance), new processor is added as the last
     *            element
     * @param call the call to add
     * @return this for convenience
     */
    public CallStack addBefore(Call before, Call call) {
        int insertPosition = elements.indexOf(before);
        if (insertPosition < 0) {
            addLast(call);
        } else {
            elements.add(insertPosition, call);
        }
        return this;
    }

    /**
     * Adds an element just before the first occurence of some element on the
     * stack. This can not be called during an iteration.
     *
     * @param before
     *            the call to add this before. If this call is not present (the
     *            same object instance), the new processor is added as the last
     *            element
     * @param processor the processor to add
     * @return this for convenience
     */
    public CallStack addBefore(Call before, DocumentProcessor processor) {
        return addBefore(before, new Call(processor, name, metric));
    }

    /**
     * Adds multiple elements just before the first occurence of some element on
     * the stack. This can not be called during an iteration.
     *
     * @param before the call to add this before. If this call is not present (the
     *               same object instance), the new processor is added as the last element
     * @param callStack the calls to add
     * @return this for convenience
     */
    public CallStack addBefore(Call before, CallStack callStack) {
        int insertPosition = elements.indexOf(before);
        if (insertPosition < 0) {
            addLast(callStack);
        } else {
            elements.addAll(insertPosition, callStack.elements);
        }
        return this;
    }

    /**
     * Adds an element just after the first occurence of some other element on
     * the stack. This can not be called during an iteration.
     *
     * @param after
     *            the call to add this before. If this call is not present, (the
     *            same object instance), the new processor is added as the last
     *            element
     * @param call
     *            the call to add
     * @return this for convenience
     */
    public CallStack addAfter(Call after, Call call) {
        int insertPosition = elements.indexOf(after);
        if (insertPosition < 0) {
            addLast(call);
        } else {
            elements.add(insertPosition + 1, call);
        }
        return this;
    }

    /**
     * Adds an element just after the first occurence of some other element on
     * the stack. This can not be called during an iteration.
     *
     * @param after the call to add this after. If this call is not present, (the
     *              same object instance), the new processor is added as the last element
     * @param processor the processor to add
     * @return this for convenience
     */
    public CallStack addAfter(Call after, DocumentProcessor processor) {
        return addAfter(after, new Call(processor, name, metric));
    }

    /**
     * Adds multiple elements just after another given element on the stack.
     * This can not be called during an iteration.
     *
     * @param after the call to add this before. If this call is not present, (the
     *              same object instance), the new processor is added as the last element
     * @param callStack the calls to add
     * @return this for convenience
     */
    public CallStack addAfter(Call after, CallStack callStack) {
        int insertPosition = elements.indexOf(after);
        if (insertPosition < 0) {
            addLast(callStack);
        } else {
            elements.addAll(insertPosition + 1, callStack.elements);
        }
        return this;
    }

    /**
     * Removes the given call. Does nothing if the call is not present.
     *
     * @param call
     *            the call to remove
     * @return this for convenience
     */
    public CallStack remove(Call call) {
        for (ListIterator<Call> i = iterator(); i.hasNext();) {
            Call current = i.next();
            if (current == call) {
                i.remove();
            }
        }
        return this;
    }

    /**
     * Returns whether this stack has this call (left)
     *
     * @param call
     *            the call to check
     * @return true if the call is present, false otherwise
     */
    public boolean contains(Call call) {
        for (ListIterator<Call> i = iterator(); i.hasNext();) {
            Call current = i.next();
            if (current == call) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the next call to this processor id, or null if no such calls are left
     */
    public Call findCall(ComponentId processorId) {
        for (Iterator<Call> i = iterator(); i.hasNext();) {
            Call call = i.next();
            if (call.getDocumentProcessorId().equals(processorId)) {
                return call;
            }
        }
        return null;
    }

    /**
     * Returns the next call to this processor, or null if no such calls are
     * left
     */
    public Call findCall(DocumentProcessor processor) {
        return findCall(processor.getId());
    }

    /**
     * Returns and removes the next element, or null if there are no more elements
     */
    public Call pop() {
        if (elements.isEmpty()) return null;
        lastPopped = elements.remove(0);
        return lastPopped;
    }

    /**
     * Returns the next element without removing it, or null if there are no
     * more elements
     */
    public Call peek() {
        if (elements.isEmpty()) return null;
        return elements.get(0);
    }

    /**
     * Returns the element that was last popped from this stack, or null if none
     * have been popped or the stack is empty
     */
    public Call getLastPopped() {
        return lastPopped;
    }

    public void clear() {
        elements.clear();
    }

    /**
     * Returns a modifiable ListIterator over all the remaining elements of this
     * stack, starting by the next element
     */
    public ListIterator<Call> iterator() {
        return elements.listIterator();
    }

    /** Returns the number of remaining elements in this stack */
    public int size() {
        return elements.size();
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder("callstack");
        if (name != null) {
            b.append(" ");
            b.append(name);
        }
        b.append(":");
        for (Iterator<Call> i = iterator(); i.hasNext();) {
            b.append("\n");
            b.append("  ");
            b.append(i.next().toString());
        }
        b.append("\n");
        return b.toString();
    }

    public Metric getMetric() {
        return metric;
    }

}