RankAwarePromoter.java revision bf61e445cbe423cc2554b722b6dd38675015c36d
1a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney/*
2a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * Copyright (C) 2010 The Android Open Source Project
3a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney *
4a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * Licensed under the Apache License, Version 2.0 (the "License");
5a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * you may not use this file except in compliance with the License.
6a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * You may obtain a copy of the License at
7a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney *
8a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney *      http://www.apache.org/licenses/LICENSE-2.0
9a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney *
10a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * Unless required by applicable law or agreed to in writing, software
11a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * distributed under the License is distributed on an "AS IS" BASIS,
12a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * See the License for the specific language governing permissions and
14a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * limitations under the License.
15a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney */
16a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
17a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinneypackage com.android.quicksearchbox;
18a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
1987e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringertimport android.util.Log;
2087e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert
2187e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringertimport java.util.ArrayList;
2287e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringertimport java.util.HashSet;
2387e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringertimport java.util.Iterator;
2487e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringertimport java.util.LinkedList;
25a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
26a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney/**
27a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney * A promoter that gives preference to suggestions from higher ranking corpora.
28a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney */
29a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinneypublic class RankAwarePromoter implements Promoter {
30a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
31bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert    private static final boolean DBG = true;
32bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert    private static final String TAG = "QSB.RankAwarePromoter";
33bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert
34a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    private final HashSet<Corpus> mPromotedCorpora;
35a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
36a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    public RankAwarePromoter(Corpora corpora, CorpusRanker corpusRanker, int maxPromotedCorpora) {
37a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        // Create a set of the corpora we consider to be promoted.  Note that we
38a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        // only do this once, even though these may change slightly during a session.
39bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert        ArrayList<Corpus> orderedCorpora = corpusRanker.rankCorpora(corpora.getEnabledCorpora());
40a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        int count = Math.min(orderedCorpora.size(), maxPromotedCorpora);
41a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        mPromotedCorpora = new HashSet<Corpus>(orderedCorpora.subList(0, count));
42bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert        if (DBG) Log.d(TAG, "Promoted: " + mPromotedCorpora);
43a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    }
44a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
45a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    private boolean isPromoted(Corpus corpus) {
46a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        return mPromotedCorpora.contains(corpus);
47a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    }
48a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
49a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    public void pickPromoted(SuggestionCursor shortcuts, ArrayList<CorpusResult> suggestions,
50a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            int maxPromoted, ListSuggestionCursor promoted) {
51a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
52bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert        if (DBG) Log.d(TAG, "Available results: " + suggestions);
53bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert
54a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        // Split non-empty results into promoted and other, positioned at first suggestion
55a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        LinkedList<CorpusResult> promotedResults = new LinkedList<CorpusResult>();
56a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        LinkedList<CorpusResult> otherResults = new LinkedList<CorpusResult>();
57a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        for (int i = 0; i < suggestions.size(); i++) {
58a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            CorpusResult result = suggestions.get(i);
59a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            if (result.getCount() > 0) {
60a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                result.moveTo(0);
61a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                if (isPromoted(result.getCorpus())) {
62a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                    promotedResults.add(result);
63a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                } else {
64a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                    otherResults.add(result);
65a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                }
66a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            }
67a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        }
68a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
69a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        // Pick 2 results from each of the promoted corpora
70a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        maxPromoted -= roundRobin(promotedResults, maxPromoted, 2, promoted);
71a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
72a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        // Then try to fill with the remaining promoted results
73a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        if (maxPromoted > 0 && !promotedResults.isEmpty()) {
74a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            int stripeSize = Math.max(1, maxPromoted / promotedResults.size());
75a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            maxPromoted -= roundRobin(promotedResults, maxPromoted, stripeSize, promoted);
76a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            // We may still have a few slots left
77a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            maxPromoted -= roundRobin(promotedResults, maxPromoted, maxPromoted, promoted);
78a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        }
79a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
80a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        // Then try to fill with the rest
81a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        if (maxPromoted > 0 && !otherResults.isEmpty()) {
82a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            int stripeSize = Math.max(1, maxPromoted / otherResults.size());
83a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            maxPromoted -= roundRobin(otherResults, maxPromoted, stripeSize, promoted);
84a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            // We may still have a few slots left
85a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            maxPromoted -= roundRobin(otherResults, maxPromoted, maxPromoted, promoted);
86a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        }
87bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert
88bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert        if (DBG) Log.d(TAG, "Returning " + promoted.toString());
89a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    }
90a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
91a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    /**
92a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * Promotes "stripes" of suggestions from each corpus.
93a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     *
94a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @param results     the list of CorpusResults from which to promote.
95a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     *                    Exhausted CorpusResults are removed from the list.
96a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @param maxPromoted maximum number of suggestions to promote.
97a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @param stripeSize  number of suggestions to take from each corpus.
98a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @param promoted    the list to which promoted suggestions are added.
99a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @return the number of suggestions actually promoted.
100a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     */
101a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    private int roundRobin(LinkedList<CorpusResult> results, int maxPromoted, int stripeSize,
102a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            ListSuggestionCursor promoted) {
103a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        int count = 0;
104a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        if (maxPromoted > 0 && !results.isEmpty()) {
105a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            for (Iterator<CorpusResult> iter = results.iterator();
106a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                 count < maxPromoted && iter.hasNext();) {
107a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                CorpusResult result = iter.next();
108a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                count += promote(result, stripeSize, promoted);
109a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                if (result.getPosition() == result.getCount()) {
110a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                    iter.remove();
111a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney                }
112a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            }
113a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        }
114a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        return count;
115a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    }
116a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney
117a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    /**
118a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * Copies suggestions from a SuggestionCursor to the list of promoted suggestions.
119a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     *
120a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @param cursor from which to copy the suggestions
121a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @param count maximum number of suggestions to copy
122a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @param promoted the list to which to add the suggestions
123a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     * @return the number of suggestions actually copied.
124a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney     */
125a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    private int promote(SuggestionCursor cursor, int count, ListSuggestionCursor promoted) {
12687e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        if (count < 1 || cursor.getPosition() >= cursor.getCount()) {
12787e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert            return 0;
12887e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        }
129a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        int i = 0;
13087e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        do {
131a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            promoted.add(new SuggestionPosition(cursor));
132a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney            i++;
13387e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        } while (cursor.moveToNext() && i < count);
134a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney        return i;
135a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney    }
136a4cd9e7cdd5bdc6198ce73eed55696554a146514Bryan Mawhinney}
137