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 17 package com.android.quicksearchbox; 18 19 import android.database.DataSetObservable; 20 import android.database.DataSetObserver; 21 import android.util.Log; 22 23 /** 24 * Collects all corpus results for a single query. 25 */ 26 public class Suggestions { 27 private static final boolean DBG = false; 28 private static final String TAG = "QSB.Suggestions"; 29 30 /** True if {@link Suggestions#close} has been called. */ 31 private boolean mClosed = false; 32 protected final String mQuery; 33 34 /** 35 * The observers that want notifications of changes to the published suggestions. 36 * This object may be accessed on any thread. 37 */ 38 private final DataSetObservable mDataSetObservable = new DataSetObservable(); 39 40 private Source mSource; 41 42 private SourceResult mResult; 43 44 private int mRefCount = 0; 45 46 private boolean mDone = false; 47 48 public Suggestions(String query, Source source) { 49 mQuery = query; 50 mSource = source; 51 } 52 53 public void acquire() { 54 mRefCount++; 55 } 56 57 public void release() { 58 mRefCount--; 59 if (mRefCount <= 0) { 60 close(); 61 } 62 } 63 64 public Source getSource() { 65 return mSource; 66 } 67 68 /** 69 * Marks the suggestions set as complete, regardless of whether all corpora have 70 * returned. 71 */ 72 public void done() { 73 mDone = true; 74 } 75 76 /** 77 * Checks whether all sources have reported. 78 * Must be called on the UI thread, or before this object is seen by the UI thread. 79 */ 80 public boolean isDone() { 81 return mDone || mResult != null; 82 } 83 84 /** 85 * Adds a list of corpus results. Must be called on the UI thread, or before this 86 * object is seen by the UI thread. 87 */ 88 public void addResults(SourceResult result) { 89 if (isClosed()) { 90 result.close(); 91 return; 92 } 93 94 if (DBG) { 95 Log.d(TAG, "addResults["+ hashCode() + "] source:" + 96 result.getSource().getName() + " results:" + result.getCount()); 97 } 98 if (!mQuery.equals(result.getUserQuery())) { 99 throw new IllegalArgumentException("Got result for wrong query: " 100 + mQuery + " != " + result.getUserQuery()); 101 } 102 mResult = result; 103 notifyDataSetChanged(); 104 } 105 106 /** 107 * Registers an observer that will be notified when the reported results or 108 * the done status changes. 109 */ 110 public void registerDataSetObserver(DataSetObserver observer) { 111 if (mClosed) { 112 throw new IllegalStateException("registerDataSetObserver() when closed"); 113 } 114 mDataSetObservable.registerObserver(observer); 115 } 116 117 118 /** 119 * Unregisters an observer. 120 */ 121 public void unregisterDataSetObserver(DataSetObserver observer) { 122 mDataSetObservable.unregisterObserver(observer); 123 } 124 125 /** 126 * Calls {@link DataSetObserver#onChanged()} on all observers. 127 */ 128 protected void notifyDataSetChanged() { 129 if (DBG) Log.d(TAG, "notifyDataSetChanged()"); 130 mDataSetObservable.notifyChanged(); 131 } 132 133 /** 134 * Closes all the source results and unregisters all observers. 135 */ 136 private void close() { 137 if (DBG) Log.d(TAG, "close() [" + hashCode() + "]"); 138 if (mClosed) { 139 throw new IllegalStateException("Double close()"); 140 } 141 mClosed = true; 142 mDataSetObservable.unregisterAll(); 143 if (mResult != null) { 144 mResult.close(); 145 } 146 mResult = null; 147 } 148 149 public boolean isClosed() { 150 return mClosed; 151 } 152 153 @Override 154 protected void finalize() { 155 if (!mClosed) { 156 Log.e(TAG, "LEAK! Finalized without being closed: Suggestions[" + getQuery() + "]"); 157 } 158 } 159 160 public String getQuery() { 161 return mQuery; 162 } 163 164 /** 165 * Gets the list of corpus results reported so far. Do not modify or hang on to 166 * the returned iterator. 167 */ 168 public SourceResult getResult() { 169 return mResult; 170 } 171 172 public SourceResult getWebResult() { 173 return mResult; 174 } 175 176 /** 177 * Gets the number of source results. 178 * Must be called on the UI thread, or before this object is seen by the UI thread. 179 */ 180 public int getResultCount() { 181 if (isClosed()) { 182 throw new IllegalStateException("Called getSourceCount() when closed."); 183 } 184 return mResult == null ? 0 : mResult.getCount(); 185 } 186 187 @Override 188 public String toString() { 189 return "Suggestions@" + hashCode() + "{source=" + mSource 190 + ",getResultCount()=" + getResultCount() + "}"; 191 } 192 193 } 194