MultiSourceCorpus.java revision cd1e3ba5f7c3f5242345ff6f674281e3d6366e24
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 19 20import com.android.quicksearchbox.util.BarrierConsumer; 21 22import android.content.Context; 23 24import java.util.ArrayList; 25import java.util.Collection; 26import java.util.List; 27import java.util.concurrent.Executor; 28 29/** 30 * Base class for corpora backed by multiple sources. 31 */ 32public abstract class MultiSourceCorpus extends AbstractCorpus { 33 34 private final Executor mExecutor; 35 36 private final ArrayList<Source> mSources; 37 38 // calculated values based on properties of sources: 39 private boolean mSourcePropertiesValid; 40 private int mQueryThreshold; 41 private boolean mQueryAfterZeroResults; 42 private boolean mVoiceSearchEnabled; 43 44 45 public MultiSourceCorpus(Context context, Config config, 46 Executor executor, Source... sources) { 47 super(context, config); 48 mExecutor = executor; 49 50 mSources = new ArrayList<Source>(); 51 for (Source source : sources) { 52 addSource(source); 53 } 54 55 } 56 57 protected void addSource(Source source) { 58 if (source != null) { 59 mSources.add(source); 60 // invalidate calculated values: 61 mSourcePropertiesValid = false; 62 } 63 } 64 65 public Collection<Source> getSources() { 66 return mSources; 67 } 68 69 /** 70 * Creates a corpus result object for a set of source results. 71 * This method should not call {@link Result#fill}. 72 * 73 * @param query The query text. 74 * @param results The results of the queries. 75 * @param latency Latency in milliseconds of the suggestion queries. 76 * @return An instance of {@link Result} or a subclass of it. 77 */ 78 protected Result createResult(String query, ArrayList<SourceResult> results, int latency) { 79 return new Result(query, results, latency); 80 } 81 82 /** 83 * Gets the sources to query for suggestions for the given input. 84 * 85 * @param query The current input. 86 * @param onlyCorpus If true, this is the only corpus being queried. 87 * @return The sources to query. 88 */ 89 protected List<Source> getSourcesToQuery(String query, boolean onlyCorpus) { 90 List<Source> sources = new ArrayList<Source>(); 91 for (Source candidate : getSources()) { 92 if (candidate.getQueryThreshold() <= query.length()) { 93 sources.add(candidate); 94 } 95 } 96 return sources; 97 } 98 99 private void calculateSourceProperties() { 100 mQueryThreshold = Integer.MAX_VALUE; 101 mQueryAfterZeroResults = false; 102 mVoiceSearchEnabled = false; 103 for (Source s : getSources()) { 104 mQueryThreshold = Math.min(mQueryThreshold, s.getQueryThreshold()); 105 mQueryAfterZeroResults |= s.queryAfterZeroResults(); 106 mVoiceSearchEnabled |= s.voiceSearchEnabled(); 107 } 108 if (mQueryThreshold == Integer.MAX_VALUE) { 109 mQueryThreshold = 0; 110 } 111 mSourcePropertiesValid = true; 112 } 113 114 public int getQueryThreshold() { 115 if (!mSourcePropertiesValid) { 116 calculateSourceProperties(); 117 } 118 return mQueryThreshold; 119 } 120 121 public boolean queryAfterZeroResults() { 122 if (!mSourcePropertiesValid) { 123 calculateSourceProperties(); 124 } 125 return mQueryAfterZeroResults; 126 } 127 128 public boolean voiceSearchEnabled() { 129 if (!mSourcePropertiesValid) { 130 calculateSourceProperties(); 131 } 132 return mVoiceSearchEnabled; 133 } 134 135 public CorpusResult getSuggestions(String query, int queryLimit, boolean onlyCorpus) { 136 LatencyTracker latencyTracker = new LatencyTracker(); 137 List<Source> sources = getSourcesToQuery(query, onlyCorpus); 138 BarrierConsumer<SourceResult> consumer = 139 new BarrierConsumer<SourceResult>(sources.size()); 140 boolean onlySource = sources.size() == 1; 141 for (Source source : sources) { 142 QueryTask<SourceResult> task = new QueryTask<SourceResult>(query, queryLimit, 143 source, null, consumer, onlySource); 144 mExecutor.execute(task); 145 } 146 ArrayList<SourceResult> results = consumer.getValues(); 147 int latency = latencyTracker.getLatency(); 148 Result result = createResult(query, results, latency); 149 result.fill(); 150 return result; 151 } 152 153 /** 154 * Base class for results returned by {@link MultiSourceCorpus#getSuggestions}. 155 * Subclasses of {@link MultiSourceCorpus} should override 156 * {@link MultiSourceCorpus#createResult} and return an instance of this class or a 157 * subclass. 158 */ 159 protected class Result extends ListSuggestionCursor implements CorpusResult { 160 161 private final ArrayList<SourceResult> mResults; 162 163 private final int mLatency; 164 165 public Result(String userQuery, ArrayList<SourceResult> results, int latency) { 166 super(userQuery); 167 mResults = results; 168 mLatency = latency; 169 } 170 171 protected ArrayList<SourceResult> getResults() { 172 return mResults; 173 } 174 175 /** 176 * Fills the list of suggestions using the list of results. 177 * The default implementation concatenates the results. 178 */ 179 public void fill() { 180 for (SourceResult result : getResults()) { 181 int count = result.getCount(); 182 for (int i = 0; i < count; i++) { 183 result.moveTo(i); 184 add(new SuggestionPosition(result)); 185 } 186 } 187 } 188 189 public Corpus getCorpus() { 190 return MultiSourceCorpus.this; 191 } 192 193 public int getLatency() { 194 return mLatency; 195 } 196 197 @Override 198 public void close() { 199 super.close(); 200 for (SourceResult result : mResults) { 201 result.close(); 202 } 203 } 204 205 @Override 206 public String toString() { 207 return getCorpus() + "[" + getUserQuery() + "]"; 208 } 209 } 210 211} 212