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.CachedLater;
20import com.android.quicksearchbox.util.Consumer;
21
22import android.database.DataSetObserver;
23import android.util.Log;
24
25import java.util.ArrayList;
26import java.util.Collection;
27import java.util.Collections;
28import java.util.Comparator;
29import java.util.List;
30import java.util.Map;
31
32/**
33 * A corpus ranker that uses corpus scores from the shortcut repository to rank
34 * corpora.
35 */
36public class DefaultCorpusRanker implements CorpusRanker {
37
38    private static final boolean DBG = false;
39    private static final String TAG = "QSB.DefaultCorpusRanker";
40
41    private final ShortcutRepository mShortcuts;
42
43    private final Corpora mCorpora;
44
45    // Cached list of ranked corpora.
46    private final RankedCorporaCache mRankedCorpora;
47
48    /**
49     * Creates a new default corpus ranker.
50     *
51     * @param corpora Corpora to rank.
52     * @param shortcuts Shortcut repository for getting corpus scores.
53     */
54    public DefaultCorpusRanker(Corpora corpora, ShortcutRepository shortcuts) {
55        mCorpora = corpora;
56        mCorpora.registerDataSetObserver(new CorporaObserver());
57        mShortcuts = shortcuts;
58        mRankedCorpora = new RankedCorporaCache();
59    }
60
61    public void getCorporaInAll(Consumer<List<Corpus>> consumer) {
62        mRankedCorpora.getLater(consumer);
63    }
64
65    public void clear() {
66        mRankedCorpora.clear();
67    }
68
69    private class CorporaObserver extends DataSetObserver {
70        @Override
71        public void onChanged() {
72            clear();
73        }
74    }
75
76    private class RankedCorporaCache extends CachedLater<List<Corpus>> {
77
78        @Override
79        protected void create() {
80            mShortcuts.getCorpusScores(new Consumer<Map<String,Integer>>(){
81                public boolean consume(Map<String, Integer> clickScores) {
82                    Collection<Corpus> enabledCorpora = mCorpora.getCorporaInAll();
83                    if (DBG) Log.d(TAG, "Ranking: " + enabledCorpora);
84                    ArrayList<Corpus> ordered = new ArrayList<Corpus>(enabledCorpora);
85                    Collections.sort(ordered, new CorpusComparator(clickScores));
86
87                    if (DBG) Log.d(TAG, "Click scores: " + clickScores);
88                    if (DBG) Log.d(TAG, "Ordered: " + ordered);
89
90                    store(ordered);
91                    return true;
92                }
93            });
94        }
95
96    }
97
98    private static class CorpusComparator implements Comparator<Corpus> {
99        private final Map<String,Integer> mClickScores;
100
101        public CorpusComparator(Map<String,Integer> clickScores) {
102            mClickScores = clickScores;
103        }
104
105        public int compare(Corpus corpus1, Corpus corpus2) {
106            boolean corpus1IsDefault = corpus1.isCorpusDefaultEnabled();
107            boolean corpus2IsDefault = corpus2.isCorpusDefaultEnabled();
108
109            if (corpus1IsDefault != corpus2IsDefault) {
110                // Default corpora always come before non-default
111                return corpus1IsDefault ? -1 : 1;
112            }
113
114            // Then by descending score
115            int scoreDiff = getCorpusScore(corpus2) - getCorpusScore(corpus1);
116            if (scoreDiff != 0) {
117                return scoreDiff;
118            }
119
120            // Finally by name
121            return corpus1.getLabel().toString().compareTo(corpus2.getLabel().toString());
122        }
123
124        /**
125         * Scores a corpus. Higher score is better.
126         */
127        private int getCorpusScore(Corpus corpus) {
128            // Web corpus always comes first
129            if (corpus.isWebCorpus()) {
130                return Integer.MAX_VALUE;
131            }
132            // Then use click score
133            return getClickScore(corpus);
134        }
135
136        private int getClickScore(Corpus corpus) {
137            if (mClickScores == null) return 0;
138            Integer clickScore = mClickScores.get(corpus.getName());
139            return clickScore == null ? 0 : clickScore;
140        }
141    }
142
143}
144