blob: b42bc5efefb0f2f7a49e2c31def91aa7593d3c11 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quicksearchbox;
import com.android.quicksearchbox.util.BarrierConsumer;
import android.content.Context;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Base class for corpora backed by multiple sources.
*/
public abstract class MultiSourceCorpus extends AbstractCorpus {
private final Executor mExecutor;
private final ArrayList<Source> mSources;
// calculated values based on properties of sources:
private boolean mSourcePropertiesValid;
private int mQueryThreshold;
private boolean mQueryAfterZeroResults;
private boolean mVoiceSearchEnabled;
private boolean mIncludeInAll;
public MultiSourceCorpus(Context context, Config config,
Executor executor, Source... sources) {
super(context, config);
mExecutor = executor;
mSources = new ArrayList<Source>();
for (Source source : sources) {
addSource(source);
}
}
protected void addSource(Source source) {
if (source != null) {
mSources.add(source);
// invalidate calculated values:
mSourcePropertiesValid = false;
}
}
public Collection<Source> getSources() {
return mSources;
}
/**
* Creates a corpus result object for a set of source results.
* This method should not call {@link Result#fill}.
*
* @param query The query text.
* @param results The results of the queries.
* @param latency Latency in milliseconds of the suggestion queries.
* @return An instance of {@link Result} or a subclass of it.
*/
protected Result createResult(String query, ArrayList<SourceResult> results, int latency) {
return new Result(query, results, latency);
}
/**
* Gets the sources to query for suggestions for the given input.
*
* @param query The current input.
* @param onlyCorpus If true, this is the only corpus being queried.
* @return The sources to query.
*/
protected List<Source> getSourcesToQuery(String query, boolean onlyCorpus) {
List<Source> sources = new ArrayList<Source>();
for (Source candidate : getSources()) {
if (candidate.getQueryThreshold() <= query.length()) {
sources.add(candidate);
}
}
return sources;
}
private void updateSourceProperties() {
if (mSourcePropertiesValid) return;
mQueryThreshold = Integer.MAX_VALUE;
mQueryAfterZeroResults = false;
mVoiceSearchEnabled = false;
mIncludeInAll = false;
for (Source s : getSources()) {
mQueryThreshold = Math.min(mQueryThreshold, s.getQueryThreshold());
mQueryAfterZeroResults |= s.queryAfterZeroResults();
mVoiceSearchEnabled |= s.voiceSearchEnabled();
mIncludeInAll |= s.includeInAll();
}
if (mQueryThreshold == Integer.MAX_VALUE) {
mQueryThreshold = 0;
}
mSourcePropertiesValid = true;
}
public int getQueryThreshold() {
updateSourceProperties();
return mQueryThreshold;
}
public boolean queryAfterZeroResults() {
updateSourceProperties();
return mQueryAfterZeroResults;
}
public boolean voiceSearchEnabled() {
updateSourceProperties();
return mVoiceSearchEnabled;
}
public boolean includeInAll() {
updateSourceProperties();
return mIncludeInAll;
}
public CorpusResult getSuggestions(String query, int queryLimit, boolean onlyCorpus) {
LatencyTracker latencyTracker = new LatencyTracker();
List<Source> sources = getSourcesToQuery(query, onlyCorpus);
BarrierConsumer<SourceResult> consumer =
new BarrierConsumer<SourceResult>(sources.size());
boolean onlySource = sources.size() == 1;
for (Source source : sources) {
QueryTask<SourceResult> task = new QueryTask<SourceResult>(query, queryLimit,
source, null, consumer, onlySource);
mExecutor.execute(task);
}
ArrayList<SourceResult> results = consumer.getValues();
int latency = latencyTracker.getLatency();
Result result = createResult(query, results, latency);
result.fill();
return result;
}
/**
* Base class for results returned by {@link MultiSourceCorpus#getSuggestions}.
* Subclasses of {@link MultiSourceCorpus} should override
* {@link MultiSourceCorpus#createResult} and return an instance of this class or a
* subclass.
*/
protected class Result extends ListSuggestionCursor implements CorpusResult {
private final ArrayList<SourceResult> mResults;
private final int mLatency;
public Result(String userQuery, ArrayList<SourceResult> results, int latency) {
super(userQuery);
mResults = results;
mLatency = latency;
}
protected ArrayList<SourceResult> getResults() {
return mResults;
}
/**
* Fills the list of suggestions using the list of results.
* The default implementation concatenates the results.
*/
public void fill() {
for (SourceResult result : getResults()) {
int count = result.getCount();
for (int i = 0; i < count; i++) {
result.moveTo(i);
add(new SuggestionPosition(result));
}
}
}
public Corpus getCorpus() {
return MultiSourceCorpus.this;
}
public int getLatency() {
return mLatency;
}
@Override
public void close() {
super.close();
for (SourceResult result : mResults) {
result.close();
}
}
@Override
public String toString() {
return "{" + getCorpus() + "[" + getUserQuery() + "]" + ";n=" + getCount() + "}";
}
}
}