13e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/*
2848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani * Copyright (C) 2010 The Android Open Source Project
33e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
43e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
53e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * you may not use this file except in compliance with the License.
63e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * You may obtain a copy of the License at
73e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
83e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
93e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * See the License for the specific language governing permissions and
143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * limitations under the License.
153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpackage com.android.quicksearchbox;
183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.database.DataSetObservable;
203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.database.DataSetObserver;
213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
23b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert/**
24b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert * Collects all corpus results for a single query.
25b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert */
26b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringertpublic class Suggestions {
27cef2c4c9d54f513babd74801dbed5cbf709b9b79Bjorn Bringert    private static final boolean DBG = false;
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static final String TAG = "QSB.Suggestions";
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
30848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    /** True if {@link Suggestions#close} has been called. */
31848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    private boolean mClosed = false;
32848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    protected final String mQuery;
333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * The observers that want notifications of changes to the published suggestions.
363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * This object may be accessed on any thread.
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final DataSetObservable mDataSetObservable = new DataSetObservable();
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
40ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    private Source mSource;
41b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
42ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    private SourceResult mResult;
43b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
44b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private int mRefCount = 0;
45b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
463540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert    private boolean mDone = false;
473540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert
48ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    public Suggestions(String query, Source source) {
493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQuery = query;
50ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        mSource = source;
51b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
52b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
53b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    public void acquire() {
54b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        mRefCount++;
55b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
56b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
57b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    public void release() {
58b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        mRefCount--;
59b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        if (mRefCount <= 0) {
60b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert            close();
61b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        }
62b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
63b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
64ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    public Source getSource() {
65ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mSource;
663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
693540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert     * Marks the suggestions set as complete, regardless of whether all corpora have
703540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert     * returned.
713540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert     */
723540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert    public void done() {
733540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert        mDone = true;
743540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert    }
753540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert
763540e5a9eab6ff33e6d45f1d908bcaf4c4f49152Bjorn Bringert    /**
77b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * Checks whether all sources have reported.
78b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * Must be called on the UI thread, or before this object is seen by the UI thread.
79b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     */
80b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    public boolean isDone() {
81ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mDone || mResult != null;
82b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
83b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
84b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    /**
85b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * Adds a list of corpus results. Must be called on the UI thread, or before this
86b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * object is seen by the UI thread.
87b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     */
88ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    public void addResults(SourceResult result) {
89b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        if (isClosed()) {
90ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            result.close();
91b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert            return;
92b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        }
93b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
94ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        if (DBG) {
95ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            Log.d(TAG, "addResults["+ hashCode() + "] source:" +
96ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood                    result.getSource().getName() + " results:" + result.getCount());
97ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        }
98ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        if (!mQuery.equals(result.getUserQuery())) {
99ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood          throw new IllegalArgumentException("Got result for wrong query: "
100ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood                + mQuery + " != " + result.getUserQuery());
101b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        }
102ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        mResult = result;
103b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        notifyDataSetChanged();
104b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
105b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
106b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    /**
1073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Registers an observer that will be notified when the reported results or
1083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * the done status changes.
1093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
1103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void registerDataSetObserver(DataSetObserver observer) {
1113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mClosed) {
1123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new IllegalStateException("registerDataSetObserver() when closed");
1133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mDataSetObservable.registerObserver(observer);
1153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
117848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
1183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
1193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Unregisters an observer.
1203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
1213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void unregisterDataSetObserver(DataSetObserver observer) {
1223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mDataSetObservable.unregisterObserver(observer);
1233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
125e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert    /**
1263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Calls {@link DataSetObserver#onChanged()} on all observers.
1273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
128848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    protected void notifyDataSetChanged() {
1293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "notifyDataSetChanged()");
1303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mDataSetObservable.notifyChanged();
1313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
133b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    /**
134b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * Closes all the source results and unregisters all observers.
135b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     */
136b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private void close() {
137b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        if (DBG) Log.d(TAG, "close() [" + hashCode() + "]");
1383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mClosed) {
139185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert            throw new IllegalStateException("Double close()");
1403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mClosed = true;
142848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani        mDataSetObservable.unregisterAll();
143ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        if (mResult != null) {
144ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            mResult.close();
1454572856ac85bb53ea06e65d00beebdf336af9b27Mathew Inwood        }
146ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        mResult = null;
1473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
14927d3d4eb3b0414cf7001020d8ddcfdde81fd516bBryan Mawhinney    public boolean isClosed() {
15027d3d4eb3b0414cf7001020d8ddcfdde81fd516bBryan Mawhinney        return mClosed;
15127d3d4eb3b0414cf7001020d8ddcfdde81fd516bBryan Mawhinney    }
15227d3d4eb3b0414cf7001020d8ddcfdde81fd516bBryan Mawhinney
1533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void finalize() {
1553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (!mClosed) {
156848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani            Log.e(TAG, "LEAK! Finalized without being closed: Suggestions[" + getQuery() + "]");
1573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
160848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    public String getQuery() {
161848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani        return mQuery;
162ced9f76b761536341d739e9a243c98a4bf90638cBjorn Bringert    }
163ced9f76b761536341d739e9a243c98a4bf90638cBjorn Bringert
164b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    /**
165b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * Gets the list of corpus results reported so far. Do not modify or hang on to
166b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * the returned iterator.
167b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     */
168ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    public SourceResult getResult() {
169ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mResult;
170b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
171b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
172ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    public SourceResult getWebResult() {
173ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mResult;
174b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
17594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
176b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    /**
177b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * Gets the number of source results.
178b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     * Must be called on the UI thread, or before this object is seen by the UI thread.
179b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert     */
180b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    public int getResultCount() {
181b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        if (isClosed()) {
182b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert            throw new IllegalStateException("Called getSourceCount() when closed.");
183b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        }
184ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mResult == null ? 0 : mResult.getCount();
185b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
186b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
187b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    @Override
188b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    public String toString() {
189ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return "Suggestions@" + hashCode() + "{source=" + mSource
190ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood                + ",getResultCount()=" + getResultCount() + "}";
1914572856ac85bb53ea06e65d00beebdf336af9b27Mathew Inwood    }
1924572856ac85bb53ea06e65d00beebdf336af9b27Mathew Inwood
1933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
194