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