1/*
2 * Copyright (c) 2002-2012, the original author or authors.
3 *
4 * This software is distributable under the BSD license. See the terms of the
5 * BSD license in the documentation provided with this software.
6 *
7 * http://www.opensource.org/licenses/bsd-license.php
8 */
9package jdk.internal.jline.console.completer;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.LinkedList;
15import java.util.List;
16
17import static jdk.internal.jline.internal.Preconditions.checkNotNull;
18
19/**
20 * Completer which contains multiple completers and aggregates them together.
21 *
22 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
23 * @since 2.3
24 */
25public class AggregateCompleter
26    implements Completer
27{
28    private final List<Completer> completers = new ArrayList<Completer>();
29
30    public AggregateCompleter() {
31        // empty
32    }
33
34    /**
35     * Construct an AggregateCompleter with the given collection of completers.
36     * The completers will be used in the iteration order of the collection.
37     *
38     * @param completers the collection of completers
39     */
40    public AggregateCompleter(final Collection<Completer> completers) {
41        checkNotNull(completers);
42        this.completers.addAll(completers);
43    }
44
45    /**
46     * Construct an AggregateCompleter with the given completers.
47     * The completers will be used in the order given.
48     *
49     * @param completers the completers
50     */
51    public AggregateCompleter(final Completer... completers) {
52        this(Arrays.asList(completers));
53    }
54
55    /**
56     * Retrieve the collection of completers currently being aggregated.
57     *
58     * @return the aggregated completers
59     */
60    public Collection<Completer> getCompleters() {
61        return completers;
62    }
63
64    /**
65     * Perform a completion operation across all aggregated completers.
66     *
67     * @see Completer#complete(String, int, java.util.List)
68     * @return the highest completion return value from all completers
69     */
70    public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
71        // buffer could be null
72        checkNotNull(candidates);
73
74        List<Completion> completions = new ArrayList<Completion>(completers.size());
75
76        // Run each completer, saving its completion results
77        int max = -1;
78        for (Completer completer : completers) {
79            Completion completion = new Completion(candidates);
80            completion.complete(completer, buffer, cursor);
81
82            // Compute the max cursor position
83            max = Math.max(max, completion.cursor);
84
85            completions.add(completion);
86        }
87
88        // Append candidates from completions which have the same cursor position as max
89        for (Completion completion : completions) {
90            if (completion.cursor == max) {
91                candidates.addAll(completion.candidates);
92            }
93        }
94
95        return max;
96    }
97
98    /**
99     * @return a string representing the aggregated completers
100     */
101    @Override
102    public String toString() {
103        return getClass().getSimpleName() + "{" +
104            "completers=" + completers +
105            '}';
106    }
107
108    private class Completion
109    {
110        public final List<CharSequence> candidates;
111
112        public int cursor;
113
114        public Completion(final List<CharSequence> candidates) {
115            checkNotNull(candidates);
116            this.candidates = new LinkedList<CharSequence>(candidates);
117        }
118
119        public void complete(final Completer completer, final String buffer, final int cursor) {
120            checkNotNull(completer);
121            this.cursor = completer.complete(buffer, cursor, candidates);
122        }
123    }
124}
125