DefaultCorpusRanker.java revision 6859aead3af0680b2c9dc326244aa89835c2c852
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.quicksearchbox;
18
19import com.android.quicksearchbox.util.AsyncCache;
20import com.android.quicksearchbox.util.AsyncDataSetObservable;
21import com.android.quicksearchbox.util.Consumer;
22
23import android.database.DataSetObserver;
24import android.os.Handler;
25import android.util.Log;
26
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.Comparator;
31import java.util.List;
32import java.util.Map;
33
34/**
35 * A corpus ranker that uses corpus scores from the shortcut repository to rank
36 * corpora.
37 */
38public class DefaultCorpusRanker implements CorpusRanker {
39
40    private static final boolean DBG = false;
41    private static final String TAG = "QSB.DefaultCorpusRanker";
42
43    private final ShortcutRepository mShortcuts;
44
45    private final AsyncDataSetObservable mDataSetObservable;
46
47    private final Corpora mCorpora;
48
49    // Cached list of ranked corpora.
50    private final RankedCorporaCache mRankedCorpora;
51
52    /**
53     * Creates a new default corpus ranker.
54     *
55     * @param corpora Corpora to rank.
56     * @param shortcuts Shortcut repository for getting corpus scores.
57     * @param uiThread Handler to call DataSetObservers on.
58     */
59    public DefaultCorpusRanker(Corpora corpora, ShortcutRepository shortcuts,
60            Handler uiThread) {
61        mCorpora = corpora;
62        mCorpora.registerDataSetObserver(new CorporaObserver());
63        mShortcuts = shortcuts;
64        mDataSetObservable = new AsyncDataSetObservable(uiThread);
65        mRankedCorpora = new RankedCorporaCache();
66    }
67
68    public void getRankedCorpora(Consumer<List<Corpus>> consumer) {
69        mRankedCorpora.get(consumer);
70    }
71
72    public void registerDataSetObserver(DataSetObserver observer) {
73        mDataSetObservable.registerObserver(observer);
74    }
75
76    public void unregisterDataSetObserver(DataSetObserver observer) {
77        mDataSetObservable.unregisterObserver(observer);
78    }
79
80    protected void notifyDataSetChanged() {
81        mDataSetObservable.notifyChanged();
82    }
83
84    public void clear() {
85        mRankedCorpora.clear();
86    }
87
88    private class CorporaObserver extends DataSetObserver {
89        @Override
90        public void onChanged() {
91            clear();
92        }
93    }
94
95    private class RankedCorporaCache extends AsyncCache<List<Corpus>> {
96
97        protected void create() {
98            mShortcuts.getCorpusScores(new Consumer<Map<String,Integer>>(){
99                public boolean consume(Map<String, Integer> clickScores) {
100                    Collection<Corpus> enabledCorpora = mCorpora.getEnabledCorpora();
101                    if (DBG) Log.d(TAG, "Ranking: " + enabledCorpora);
102                    ArrayList<Corpus> ordered = new ArrayList<Corpus>(enabledCorpora);
103                    Collections.sort(ordered, new CorpusComparator(clickScores));
104
105                    if (DBG) Log.d(TAG, "Click scores: " + clickScores);
106                    if (DBG) Log.d(TAG, "Ordered: " + ordered);
107
108                    store(ordered);
109                    notifyDataSetChanged();
110                    return true;
111                }
112            });
113        }
114
115    }
116
117    private static class CorpusComparator implements Comparator<Corpus> {
118        private final Map<String,Integer> mClickScores;
119
120        public CorpusComparator(Map<String,Integer> clickScores) {
121            mClickScores = clickScores;
122        }
123
124        public int compare(Corpus corpus1, Corpus corpus2) {
125            boolean corpus1IsDefault = corpus1.isCorpusDefaultEnabled();
126            boolean corpus2IsDefault = corpus2.isCorpusDefaultEnabled();
127
128            if (corpus1IsDefault != corpus2IsDefault) {
129                // Default corpora always come before non-default
130                return corpus1IsDefault ? -1 : 1;
131            }
132
133            // Then by descending score
134            int scoreDiff = getCorpusScore(corpus2) - getCorpusScore(corpus1);
135            if (scoreDiff != 0) {
136                return scoreDiff;
137            }
138
139            // Finally by name
140            return corpus1.getLabel().toString().compareTo(corpus2.getLabel().toString());
141        }
142
143        /**
144         * Scores a corpus. Higher score is better.
145         */
146        private int getCorpusScore(Corpus corpus) {
147            // Web corpus always comes first
148            if (corpus.isWebCorpus()) {
149                return Integer.MAX_VALUE;
150            }
151            // Then use click score
152            return getClickScore(corpus);
153        }
154
155        private int getClickScore(Corpus corpus) {
156            if (mClickScores == null) return 0;
157            Integer clickScore = mClickScores.get(corpus.getName());
158            return clickScore == null ? 0 : clickScore;
159        }
160    }
161
162}
163