sbLocalDatabaseCascadeFilterSet.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
28 
29 #include "sbLocalDatabaseLibrary.h"
33 
34 #include <nsArrayUtils.h>
35 #include <nsComponentManagerUtils.h>
36 #include <nsIArray.h>
37 #include <nsIMutableArray.h>
38 #include <nsIObjectOutputStream.h>
39 #include <nsIObjectInputStream.h>
40 #include <nsISimpleEnumerator.h>
41 #include <nsITreeView.h>
42 #include <nsServiceManagerUtils.h>
43 #include <nsISupportsPrimitives.h>
44 #include <prlog.h>
45 
46 #include <DatabaseQuery.h>
47 #include <sbIDatabaseQuery.h>
48 #include <sbIDatabaseResult.h>
49 #include <sbIFilterableMediaListView.h>
50 #include <sbILibraryConstraints.h>
51 #include <sbILocalDatabaseAsyncGUIDArray.h>
52 #include <sbILocalDatabaseLibrary.h>
53 #include <sbIMediaList.h>
54 #include <sbIMediaListView.h>
55 #include <sbIPropertyArray.h>
56 #include <sbIPropertyInfo.h>
57 #include <sbIPropertyManager.h>
58 #include <sbISQLBuilder.h>
59 #include <sbISearchableMediaListView.h>
60 #include <sbPropertiesCID.h>
61 #include <sbSQLBuilderCID.h>
62 #include <sbStandardProperties.h>
64 
69 #ifdef PR_LOGGING
70 static PRLogModuleInfo* sFilterSetLog = nsnull;
71 #define TRACE(args) if (sFilterSetLog) PR_LOG(sFilterSetLog, PR_LOG_DEBUG, args)
72 #define LOG(args) if (sFilterSetLog) PR_LOG(sFilterSetLog, PR_LOG_WARN, args)
73 #else /* PR_LOGGING */
74 #define TRACE(args) /* nothing */
75 #define LOG(args) /* nothing */
76 #endif /* PR_LOGGING */
77 
78 
80 {
81 public:
82  explicit
84  : mArray(aArray) {
85  mArray->SuppressInvalidation(PR_TRUE);
86  }
87 
89  mArray->SuppressInvalidation(PR_FALSE);
90  }
91 private:
92  nsCOMPtr<sbILocalDatabaseGUIDArray> mArray;
93 };
94 
96  mMediaListView(aMediaListView)
97 {
98  MOZ_COUNT_CTOR(sbLocalDatabaseCascadeFilterSet);
99  NS_ASSERTION(aMediaListView, "aMediaListView is null");
100 #ifdef PR_LOGGING
101  if (!sFilterSetLog) {
102  sFilterSetLog = PR_NewLogModule("sbLocalDatabaseCascadeFilterSet");
103  }
104 #endif
105  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - Constructed", this));
106 }
107 
109 {
110  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - Destructed", this));
111  MOZ_COUNT_DTOR(sbLocalDatabaseCascadeFilterSet);
112 
113  if (mMediaList) {
114  mMediaList->RemoveListener(this);
115  }
116 }
117 
122 
123 nsresult
125  sbILocalDatabaseAsyncGUIDArray* aProtoArray,
127 {
128  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - Init", this));
129  NS_ENSURE_ARG_POINTER(aLibrary);
130  NS_ENSURE_ARG_POINTER(aProtoArray);
131  NS_ENSURE_STATE(mMediaListView);
132 
133  nsresult rv;
134 
135  mLibrary = aLibrary;
136 
137  // Set up our prototype array
138  mProtoArray = aProtoArray;
139 
140  rv = mProtoArray->ClearFilters();
141  NS_ENSURE_SUCCESS(rv, rv);
142 
143  rv = mProtoArray->ClearSorts();
144  NS_ENSURE_SUCCESS(rv, rv);
145 
146  rv = ApplyConstraintFilters(mProtoArray);
147  NS_ENSURE_SUCCESS(rv, rv);
148 
149  rv = mProtoArray->SetIsDistinct(PR_TRUE);
150  NS_ENSURE_SUCCESS(rv, rv);
151 
152  rv = mProtoArray->SetDistinctWithSortableValues(PR_TRUE);
153  NS_ENSURE_SUCCESS(rv, rv);
154 
155  PRBool success = mListeners.Init();
156  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
157 
158  rv = mMediaListView->GetMediaList(getter_AddRefs(mMediaList));
159  NS_ENSURE_SUCCESS(rv, rv);
160 
161  // If there is any state to restore, restore it
162  if (aState) {
163 #ifdef DEBUG
164  {
165  nsString buff;
166  aState->ToString(buff);
167  nsCOMPtr<nsISupports> supports = do_QueryInterface(aState);
168  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - restoring state [0x%.8x] %s",
169  this, supports.get(), NS_LossyConvertUTF16toASCII(buff).get()));
170  }
171 #endif
172 
173  for (PRUint32 i = 0; i < aState->mFilters.Length(); i++) {
174 
176 
177  sbFilterSpec* fs = mFilters.AppendElement();
178  NS_ENSURE_TRUE(fs, NS_ERROR_OUT_OF_MEMORY);
179 
180  fs->isSearch = spec.isSearch;
181  fs->property = spec.property;
182  nsString* added = fs->propertyList.AppendElements(spec.propertyList);
183  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
184  added = fs->values.AppendElements(spec.values);
185  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
186 
187  if (spec.isSearch) {
188  rv = ConfigureFilterArray(fs, NS_LITERAL_STRING(SB_PROPERTY_CREATED));
189  NS_ENSURE_SUCCESS(rv, rv);
190  }
191  else {
192  rv = ConfigureFilterArray(fs, spec.property);
193  NS_ENSURE_SUCCESS(rv, rv);
194  }
195 
196  rv = ConfigureArray(i);
197  NS_ENSURE_SUCCESS(rv, rv);
198 
199  if (spec.treeViewState) {
200  nsRefPtr<sbLocalDatabaseTreeView> treeView =
202  NS_ENSURE_TRUE(treeView, NS_ERROR_OUT_OF_MEMORY);
203 
204  rv = treeView->Init(mMediaListView,
205  fs->array,
206  nsnull,
207  spec.treeViewState);
208  NS_ENSURE_SUCCESS(rv, rv);
209 
210  fs->treeView = treeView;
211  }
212  }
213  }
214 
215  rv = UpdateListener(PR_FALSE);
216  NS_ENSURE_SUCCESS(rv, rv);
217 
218  return NS_OK;
219 }
220 
221 NS_IMETHODIMP
222 sbLocalDatabaseCascadeFilterSet::GetLength(PRUint16* aLength)
223 {
224  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - GetLength", this));
225  NS_ENSURE_ARG_POINTER(aLength);
226 
227  *aLength = mFilters.Length();
228 
229  return NS_OK;
230 }
231 
232 NS_IMETHODIMP
234  nsAString& _retval)
235 {
236  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - GetProperty", this));
237  PRUint32 filterLength = mFilters.Length();
238  NS_ENSURE_TRUE(filterLength, NS_ERROR_UNEXPECTED);
239  NS_ENSURE_ARG_MAX(aIndex, filterLength - 1);
240 
241  _retval = mFilters[aIndex].property;
242 
243  return NS_OK;
244 }
245 
246 NS_IMETHODIMP
247 sbLocalDatabaseCascadeFilterSet::IsSearch(PRUint16 aIndex,
248  PRBool* _retval)
249 {
250  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - IsSearch", this));
251  NS_ENSURE_ARG_POINTER(_retval);
252  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
253 
254  *_retval = mFilters[aIndex].isSearch;
255 
256  return NS_OK;
257 }
258 
259 NS_IMETHODIMP
260 sbLocalDatabaseCascadeFilterSet::AppendFilter(const nsAString& aProperty,
261  PRUint16 *_retval)
262 {
263  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - AppendFilter", this));
264  NS_ENSURE_ARG_POINTER(_retval);
265 
266  nsresult rv;
267 
268  sbFilterSpec* fs = mFilters.AppendElement();
269  NS_ENSURE_TRUE(fs, NS_ERROR_OUT_OF_MEMORY);
270 
271  fs->isSearch = PR_FALSE;
272  fs->property = aProperty;
273 
274  rv = ConfigureFilterArray(fs, aProperty);
275  NS_ENSURE_SUCCESS(rv, rv);
276 
277  rv = ConfigureArray(mFilters.Length() - 1);
278  NS_ENSURE_SUCCESS(rv, rv);
279 
280  *_retval = mFilters.Length() - 1;
281 
282  rv = UpdateListener();
283  NS_ENSURE_SUCCESS(rv, rv);
284 
285  return NS_OK;
286 }
287 
288 NS_IMETHODIMP
289 sbLocalDatabaseCascadeFilterSet::AppendSearch(const PRUnichar** aPropertyArray,
290  PRUint32 aPropertyArrayCount,
291  PRUint16 *_retval)
292 {
293  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - AppendSearch", this));
294  if (aPropertyArrayCount) {
295  NS_ENSURE_ARG_POINTER(aPropertyArray);
296  }
297  NS_ENSURE_ARG_POINTER(_retval);
298 
299  nsresult rv;
300 
301  // A filter set can only have one search
302  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
303  if (mFilters[i].isSearch) {
304  return NS_ERROR_INVALID_ARG;
305  }
306  }
307 
308  sbFilterSpec* fs = mFilters.AppendElement();
309  NS_ENSURE_TRUE(fs, NS_ERROR_OUT_OF_MEMORY);
310 
311  fs->isSearch = PR_TRUE;
312 
313  for (PRUint32 i = 0; i < aPropertyArrayCount; i++) {
314  if (aPropertyArray[i]) {
315  nsString* success = fs->propertyList.AppendElement(aPropertyArray[i]);
316  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
317  }
318  else {
319  NS_WARNING("Null pointer passed in array");
320  }
321  }
322 
323  rv = ConfigureFilterArray(fs, NS_LITERAL_STRING(SB_PROPERTY_CREATED));
324  NS_ENSURE_SUCCESS(rv, rv);
325 
326  rv = ConfigureArray(mFilters.Length() - 1);
327  NS_ENSURE_SUCCESS(rv, rv);
328 
329  *_retval = mFilters.Length() - 1;
330 
331  rv = UpdateListener();
332  NS_ENSURE_SUCCESS(rv, rv);
333 
334  return NS_OK;
335 }
336 
337 NS_IMETHODIMP
338 sbLocalDatabaseCascadeFilterSet::Remove(PRUint16 aIndex)
339 {
340  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - Remove", this));
341  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
342 
343  nsresult rv;
344 
345  sbFilterSpec& fs = mFilters[aIndex];
346  PRBool isSearch = fs.isSearch;
347 
348  if (fs.arrayListener)
349  fs.array->RemoveAsyncListener(fs.arrayListener);
350 
351  mFilters.RemoveElementAt(aIndex);
352 
353  // Update the filters downstream from the removed filter
354  for (PRUint32 i = aIndex; i < mFilters.Length(); i++) {
355  rv = ConfigureArray(i);
356  NS_ENSURE_SUCCESS(rv, rv);
357 
358  // Notify listeners
359  mListeners.EnumerateEntries(OnValuesChangedCallback, &i);
360  }
361 
362  rv = UpdateListener();
363  NS_ENSURE_SUCCESS(rv, rv);
364 
365  // Tell the view to update its configuration. It will first apply its
366  // filters and then ask us for ours
367  if (mMediaListView) {
368  rv = mMediaListView->UpdateViewArrayConfiguration(PR_TRUE);
369  NS_ENSURE_SUCCESS(rv, rv);
370 
371  // And notify the view's listeners
372  if (isSearch) {
373  mMediaListView->NotifyListenersSearchChanged();
374  }
375  else {
376  mMediaListView->NotifyListenersFilterChanged();
377  }
378  }
379 
380  return NS_OK;
381 }
382 
383 NS_IMETHODIMP
384 sbLocalDatabaseCascadeFilterSet::ChangeFilter(PRUint16 aIndex,
385  const nsAString& aProperty)
386 {
387  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - ChangeFilter", this));
388  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
389 
390  nsresult rv;
391 
392  sbFilterSpec& fs = mFilters[aIndex];
393  if (fs.isSearch)
394  return NS_ERROR_INVALID_ARG;
395 
396  fs.property = aProperty;
397  fs.invalidationPending = PR_FALSE;
398 
399  rv = fs.array->ClearSorts();
400  NS_ENSURE_SUCCESS(rv, rv);
401 
402  rv = fs.array->AddSort(aProperty, PR_TRUE);
403  NS_ENSURE_SUCCESS(rv, rv);
404 
405  fs.values.Clear();
406  rv = ConfigureArray(aIndex);
407  NS_ENSURE_SUCCESS(rv, rv);
408 
409  rv = UpdateListener();
410  NS_ENSURE_SUCCESS(rv, rv);
411 
412  if (fs.treeView) {
413  nsCOMPtr<nsITreeSelection> selection;
414  rv = fs.treeView->GetSelection(getter_AddRefs(selection));
415  if (selection) {
416  // will fail at getting selection if it's not set up yet
417  rv = selection->ClearSelection();
418  NS_ENSURE_SUCCESS(rv, rv);
419  }
420  }
421 
422  // Tell the view to update its configuration. It will first apply its
423  // filters and then ask us for ours
424  if (mMediaListView) {
425  rv = mMediaListView->UpdateViewArrayConfiguration(PR_TRUE);
426  NS_ENSURE_SUCCESS(rv, rv);
427 
428  // And notify the view's listeners
429  mMediaListView->NotifyListenersFilterChanged();
430  }
431 
432  return NS_OK;
433 }
434 
435 NS_IMETHODIMP
436 sbLocalDatabaseCascadeFilterSet::Set(PRUint16 aIndex,
437  const PRUnichar** aValueArray,
438  PRUint32 aValueArrayCount)
439 {
440 #ifdef DEBUG
441  {
442  nsString buff;
443  for (PRUint32 i = 0; i < aValueArrayCount && aValueArray; i++) {
444  buff.Append(aValueArray[i]);
445  if (i + 1 < aValueArrayCount) {
446  buff.AppendLiteral(", ");
447  }
448  }
449  if (!aValueArrayCount) {
450  buff.AssignLiteral("no values");
451  }
452  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - Set %d, %d [%s]",
453  this, aIndex, aValueArrayCount,
454  NS_LossyConvertUTF16toASCII(buff).get()));
455  }
456 #endif
457  if (aValueArrayCount) {
458  NS_ENSURE_ARG_POINTER(aValueArray);
459  }
460  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
461 
462  nsresult rv;
463 
464  sbFilterSpec& fs = mFilters[aIndex];
465  fs.values.Clear();
466 
467  for (PRUint32 i = 0; i < aValueArrayCount; i++) {
468  if (aValueArray[i]) {
469  nsString* value = fs.values.AppendElement(aValueArray[i]);
470  NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
471  }
472  else {
473  NS_WARNING("Null pointer passed in array");
474  }
475  }
476 
477  // Update downstream filters
478  for (PRUint32 i = aIndex + 1; i < mFilters.Length(); i++) {
479 
480  // We want to clear the downstream filter since the upstream filter has
481  // changed
482  sbFilterSpec& downstream = mFilters[i];
483  downstream.values.Clear();
484 
485  if (downstream.treeView) {
486  nsCOMPtr<nsITreeSelection> selection;
487  rv = downstream.treeView->GetSelection(getter_AddRefs(selection));
488  // the selection may not exist if the tree hasn't been shown yet
489  if (selection) {
490  rv = selection->ClearSelection();
491  NS_ENSURE_SUCCESS(rv, rv);
492  }
493  }
494 
495  rv = ConfigureArray(i);
496  NS_ENSURE_SUCCESS(rv, rv);
497 
498  // Notify listeners
499  mListeners.EnumerateEntries(OnValuesChangedCallback, &i);
500  }
501 
502  // Tell the view to update its configuration. It will first apply its
503  // filters and then ask us for ours
504  if (mMediaListView) {
505  rv = mMediaListView->UpdateViewArrayConfiguration(PR_TRUE);
506  NS_ENSURE_SUCCESS(rv, rv);
507 
508  // And notify the view's listeners
509  if (fs.isSearch) {
510  mMediaListView->NotifyListenersSearchChanged();
511  }
512  else {
513  mMediaListView->NotifyListenersFilterChanged();
514  }
515  }
516 
517  return NS_OK;
518 }
519 
520 NS_IMETHODIMP
521 sbLocalDatabaseCascadeFilterSet::Get(PRUint16 aIndex,
522  nsIArray **_retval)
523 {
524  NS_ENSURE_ARG_POINTER(_retval);
525 
526  nsresult rv = NS_ERROR_UNEXPECTED;
527  nsCOMPtr<nsIMutableArray> outArr =
528  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
529  NS_ENSURE_SUCCESS(rv, rv);
530 
531  nsCOMPtr<nsISupportsString> supportsStr;
532  sbFilterSpec& fs = mFilters[aIndex];
533  for (PRUint32 i = 0; i < fs.values.Length(); i++) {
534  supportsStr = do_CreateInstance("@mozilla.org/supports-string;1", &rv);
535  NS_ENSURE_SUCCESS(rv, rv);
536  supportsStr->SetData(fs.values[i]);
537 
538  outArr->AppendElement(supportsStr, PR_FALSE);
539  }
540 
541  NS_ADDREF(*_retval = outArr);
542  return NS_OK;
543 }
544 
545 NS_IMETHODIMP
546 sbLocalDatabaseCascadeFilterSet::ClearAll()
547 {
548  nsresult rv;
549 
550  PRBool filterChanged = PR_FALSE, searchChanged = PR_FALSE;
551 
552  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
553  sbFilterSpec& fs = mFilters[i];
554 
555  if (fs.isSearch) {
556  if (!searchChanged) {
557  searchChanged = PR_TRUE;
558  }
559  }
560  else if (!filterChanged) {
561  filterChanged = PR_TRUE;
562  }
563 
564  fs.values.Clear();
565  rv = ConfigureArray(i);
566  NS_ENSURE_SUCCESS(rv, rv);
567  }
568 
569  if (mMediaListView) {
570  rv = mMediaListView->UpdateViewArrayConfiguration(PR_TRUE);
571  NS_ENSURE_SUCCESS(rv, rv);
572 
573  // And notify the view's listeners
574  if (filterChanged) {
575  mMediaListView->NotifyListenersFilterChanged();
576  }
577  if (searchChanged) {
578  mMediaListView->NotifyListenersSearchChanged();
579  }
580  }
581 
582  return NS_OK;
583 }
584 
585 NS_IMETHODIMP
586 sbLocalDatabaseCascadeFilterSet::GetValues(PRUint16 aIndex,
587  nsIStringEnumerator **_retval)
588 {
589  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - GetValues", this));
590  NS_ENSURE_ARG_POINTER(_retval);
591  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
592 
594  new sbGUIDArrayPrimarySortEnumerator(mFilters[aIndex].array);
595  NS_ENSURE_TRUE(values, NS_ERROR_OUT_OF_MEMORY);
596 
597  NS_ADDREF(*_retval = values);
598  return NS_OK;
599 }
600 
601 NS_IMETHODIMP
602 sbLocalDatabaseCascadeFilterSet::GetValueAt(PRUint16 aIndex,
603  PRUint32 aValueIndex,
604  nsAString& aValue)
605 {
606  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - GetValueAt", this));
607  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
608 
609  mFilters[aIndex].array->GetSortPropertyValueByIndex(aValueIndex, aValue);
610 
611  return NS_OK;
612 }
613 
614 NS_IMETHODIMP
615 sbLocalDatabaseCascadeFilterSet::GetTreeView(PRUint16 aIndex,
616  nsITreeView **_retval)
617 {
618  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - GetTreeView", this));
619  NS_ENSURE_ARG_POINTER(_retval);
620  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
621  NS_ENSURE_STATE(mMediaListView);
622 
623  sbFilterSpec& fs = mFilters[aIndex];
624 
625  if (fs.isSearch) {
626  return NS_ERROR_INVALID_ARG;
627  }
628 
629  if (!fs.treeView) {
630 
631  nsresult rv;
632 
633  nsCOMPtr<sbIMutablePropertyArray> propArray =
634  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
635  NS_ENSURE_SUCCESS(rv, rv);
636 
637  rv = propArray->SetStrict(PR_FALSE);
638  NS_ENSURE_SUCCESS(rv, rv);
639 
640  rv = propArray->AppendProperty(fs.property, NS_LITERAL_STRING("a"));
641  NS_ENSURE_SUCCESS(rv, rv);
642 
643  nsRefPtr<sbLocalDatabaseTreeView> newTreeView =
645  NS_ENSURE_TRUE(newTreeView, NS_ERROR_OUT_OF_MEMORY);
646 
647  rv = newTreeView->Init(mMediaListView, fs.array, propArray, nsnull);
648  NS_ENSURE_SUCCESS(rv, rv);
649 
650  fs.treeView = newTreeView;
651  }
652 
653  NS_ADDREF(*_retval = fs.treeView);
654 
655  return NS_OK;
656 }
657 
658 NS_IMETHODIMP
659 sbLocalDatabaseCascadeFilterSet::GetValueCount(PRUint16 aIndex,
660  PRBool aUseCache,
661  PRUint32 *_retval)
662 {
663  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - GetValueCount", this));
664  NS_ENSURE_ARG_POINTER(_retval);
665  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
666 
667  if (aUseCache) {
668  *_retval = mFilters[aIndex].cachedValueCount;
669  }
670  else {
671  PRUint32 valueCount;
672  nsresult rv = mFilters[aIndex].array->GetLength(&valueCount);
673  NS_ENSURE_SUCCESS(rv, rv);
674  rv = OnGetLength(aIndex, valueCount);
675  NS_ENSURE_SUCCESS(rv, rv);
676  *_retval = valueCount;
677  }
678 
679  return NS_OK;
680 }
681 
682 NS_IMETHODIMP
683 sbLocalDatabaseCascadeFilterSet::AddListener(sbICascadeFilterSetListener* aListener)
684 {
685  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - AddListener", this));
686  nsISupportsHashKey* success = mListeners.PutEntry(aListener);
687  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
688 
689  return NS_OK;
690 }
691 
692 NS_IMETHODIMP
693 sbLocalDatabaseCascadeFilterSet::RemoveListener(sbICascadeFilterSetListener* aListener)
694 {
695  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - RemoveListener", this));
696 
697  mListeners.RemoveEntry(aListener);
698 
699  return NS_OK;
700 }
701 
702 nsresult
704 {
705  NS_ENSURE_ARG_POINTER(mArray);
706  nsresult rv;
707 
708  sbSuppressArrayInvalidation suppressInvalidation(mArray);
709 
710  nsCOMPtr<sbIPropertyManager> propMan =
711  do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
712  NS_ENSURE_SUCCESS(rv, rv);
713 
714  PRUint32 numFilters = mFilters.Length();
715  for (PRUint32 i = 0; i < numFilters; i++) {
716  const sbFilterSpec& filter = mFilters[i];
717 
718  // Skip filters and searches that have no values
719  if (filter.values.Length() == 0) {
720  continue;
721  }
722 
723  if (filter.isSearch) {
724 
725  // A search can be over many properties (held in propertyList). Add a
726  // search filter for each property being searched. The value being
727  // searched for is the first element of the values array. Note that
728  // we make the values to search for sortable
729  PRUint32 length = filter.propertyList.Length();
730  for (PRUint32 j = 0; j < length; j++) {
731  nsCOMPtr<sbIPropertyInfo> info;
732  rv = propMan->GetPropertyInfo(filter.propertyList[j],
733  getter_AddRefs(info));
734  NS_ENSURE_SUCCESS(rv, rv);
735 
736  PRUint32 valuesLength = filter.values.Length();
737  sbStringArray valueArray(valuesLength);
738 
739  for(PRUint32 k = 0; k < valuesLength; k++) {
740  nsString sortableValue;
741  rv = info->MakeSortable(filter.values[k], sortableValue);
742  NS_ENSURE_SUCCESS(rv, rv);
743  nsString* successString = valueArray.AppendElement(sortableValue);
744  NS_ENSURE_TRUE(successString, NS_ERROR_OUT_OF_MEMORY);
745  }
746 
747  nsCOMPtr<nsIStringEnumerator> valueEnum =
748  new sbTArrayStringEnumerator(&valueArray);
749  NS_ENSURE_TRUE(valueEnum, NS_ERROR_OUT_OF_MEMORY);
750 
751  rv = mArray->AddFilter(filter.propertyList[j], valueEnum, PR_TRUE);
752  NS_ENSURE_SUCCESS(rv, rv);
753  }
754  }
755  else {
756  nsCOMPtr<sbIPropertyInfo> info;
757  rv = propMan->GetPropertyInfo(filter.property, getter_AddRefs(info));
758  NS_ENSURE_SUCCESS(rv, rv);
759 
760  PRUint32 length = filter.values.Length();
761  sbStringArray sortableValueArray(length);
762  for (PRUint32 i = 0; i < length; i++) {
763  nsAutoString sortableValue;
764  if (SB_IsTopLevelProperty(filter.property)) {
765  sortableValue = filter.values[i];
766  }
767  else {
768  rv = info->MakeSortable(filter.values[i], sortableValue);
769  NS_ENSURE_SUCCESS(rv, rv);
770  }
771  nsString* appended = sortableValueArray.AppendElement(sortableValue);
772  NS_ENSURE_TRUE(appended, NS_ERROR_OUT_OF_MEMORY);
773  }
774 
775  nsCOMPtr<nsIStringEnumerator> valueEnum =
776  new sbTArrayStringEnumerator(&sortableValueArray);
777  NS_ENSURE_TRUE(valueEnum, NS_ERROR_OUT_OF_MEMORY);
778 
779  rv = mArray->AddFilter(filter.property, valueEnum, PR_FALSE);
780  NS_ENSURE_SUCCESS(rv, rv);
781  }
782  }
783 
784  return NS_OK;
785 }
786 
787 nsresult
789  PRBool* aChanged)
790 {
791  NS_ENSURE_ARG_POINTER(aBuilder);
792  NS_ENSURE_ARG_POINTER(aChanged);
793  nsresult rv;
794 
795  *aChanged = PR_FALSE;
796  PRUint32 numFilters = mFilters.Length();
797  for (PRUint32 i = 0; i < numFilters; i++) {
798  const sbFilterSpec& filter = mFilters[i];
799 
800  if (!filter.isSearch && filter.values.Length()) {
801  *aChanged = PR_TRUE;
802 
803  rv = aBuilder->Intersect(nsnull);
804  NS_ENSURE_SUCCESS(rv, rv);
805 
806  nsCOMPtr<nsIStringEnumerator> values =
807  new sbTArrayStringEnumerator(&filter.values);
808  NS_ENSURE_TRUE(values, NS_ERROR_OUT_OF_MEMORY);
809 
810  rv = aBuilder->IncludeList(filter.property, values, nsnull);
811  NS_ENSURE_SUCCESS(rv, rv);
812  }
813  }
814 
815  return NS_OK;
816 }
817 
818 nsresult
820  PRBool* aChanged)
821 {
822  NS_ENSURE_ARG_POINTER(aBuilder);
823  NS_ENSURE_ARG_POINTER(aChanged);
824  nsresult rv;
825 
826  *aChanged = PR_FALSE;
827  PRUint32 numFilters = mFilters.Length();
828  for (PRUint32 i = 0; i < numFilters; i++) {
829  const sbFilterSpec& filter = mFilters[i];
830 
831  if (filter.isSearch && filter.values.Length()) {
832 
833  PRUint32 numProperties = filter.propertyList.Length();
834  PRUint32 numValues = filter.values.Length();
835  for (PRUint32 j = 0; j < numValues; j++) {
836  *aChanged = PR_TRUE;
837  for (PRUint32 k = 0; k < numProperties; k++) {
838  rv = aBuilder->Include(filter.propertyList[k],
839  filter.values[j],
840  nsnull);
841  NS_ENSURE_SUCCESS(rv, rv);
842  }
843 
844  if (j + 1 < numValues) {
845  rv = aBuilder->Intersect(nsnull);
846  NS_ENSURE_SUCCESS(rv, rv);
847  }
848  }
849  }
850  }
851 
852  return NS_OK;
853 }
854 
855 nsresult
857 {
858  PRUint32 numFilters = mFilters.Length();
859  for (PRUint32 i = 0; i < numFilters; i++) {
860  sbFilterSpec& filter = mFilters[i];
861 
862  if (!filter.isSearch) {
863  filter.values.Clear();
864  }
865  }
866  return NS_OK;
867 }
868 
869 nsresult
871 {
872  PRUint32 numFilters = mFilters.Length();
873  for (PRUint32 i = 0; i < numFilters; i++) {
874  sbFilterSpec& filter = mFilters[i];
875 
876  if (filter.isSearch) {
877  filter.values.Clear();
878  }
879  }
880  return NS_OK;
881 }
882 
883 void
885 {
886  mMediaListView = nsnull;
887 }
888 
889 nsresult
891 {
892  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - GetState", this));
893  NS_ASSERTION(aState, "aState is null");
894 
895  nsRefPtr<sbLocalDatabaseCascadeFilterSetState> state =
897  NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY);
898 
899  nsresult rv;
900  PRUint32 numFilters = mFilters.Length();
901  for (PRUint32 i = 0; i < numFilters; i++) {
902  sbFilterSpec& fs = mFilters[i];
903 
905  state->mFilters.AppendElement();
906  NS_ENSURE_TRUE(spec, NS_ERROR_OUT_OF_MEMORY);
907 
908  spec->isSearch = fs.isSearch;
909  spec->property = fs.property;
910 
911  nsString* added = spec->propertyList.AppendElements(fs.propertyList);
912  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
913 
914  added = spec->values.AppendElements(fs.values);
915  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
916 
917  if (fs.treeView) {
918  rv = fs.treeView->GetState(getter_AddRefs(spec->treeViewState));
919  NS_ENSURE_SUCCESS(rv, rv);
920  }
921  }
922 
923 #ifdef DEBUG
924  {
925  nsString buff;
926  state->ToString(buff);
927  nsCOMPtr<nsISupports> supports = do_QueryInterface(state);
928  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - returning state [0x%.8x] %s",
929  this, supports.get(), NS_LossyConvertUTF16toASCII(buff).get()));
930  }
931 #endif
932 
933  NS_ADDREF(*aState = state);
934  return NS_OK;
935 }
936 
937 nsresult
939  PRUint32 aLength)
940 {
941  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - OnGetLength(%d, %d)",
942  this, aIndex, aLength));
943  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
944 
945  PRUint32 index = aIndex;
946  if (aLength != mFilters[index].cachedValueCount) {
947  mFilters[index].cachedValueCount = aLength;
948 
949  // Notify listeners
950  mListeners.EnumerateEntries(OnValuesChangedCallback, &index);
951  }
952 
953  return NS_OK;
954 }
955 
956 nsresult
957 sbLocalDatabaseCascadeFilterSet::ConfigureArray(PRUint32 aIndex)
958 {
959  TRACE(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - ConfigureArray", this));
960  NS_ENSURE_TRUE(aIndex < mFilters.Length(), NS_ERROR_INVALID_ARG);
961 
962  nsresult rv;
963 
964  sbFilterSpec& fs = mFilters[aIndex];
965  fs.arrayListener->mIndex = aIndex;
966 
967  // Suppress Invalidation to avoid rebuilds until
968  // we're done applying all of the filters.
969  sbSuppressArrayInvalidation suppressArray(fs.array);
970 
971  // Clear this filter since our upstream filters have changed
972  rv = fs.array->ClearFilters();
973  NS_ENSURE_SUCCESS(rv, rv);
974 
975  rv = ApplyConstraintFilters(fs.array);
976  NS_ENSURE_SUCCESS(rv, rv);
977 
978  nsCOMPtr<sbIPropertyManager> propMan =
979  do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
980  NS_ENSURE_SUCCESS(rv, rv);
981 
982  // Apply the filters of each upstream filter to this filter
983  for (PRUint32 i = 0; i < aIndex; i++) {
984  const sbFilterSpec& upstream = mFilters[i];
985 
986  if (upstream.values.Length() > 0) {
987 
988  if(upstream.isSearch) {
989 
990  for (PRUint32 j = 0; j < upstream.propertyList.Length(); j++) {
991 
992  nsCOMPtr<sbIPropertyInfo> info;
993  rv = propMan->GetPropertyInfo(upstream.propertyList[j],
994  getter_AddRefs(info));
995  NS_ENSURE_SUCCESS(rv, rv);
996 
997  sbStringArray sortableValues;
998  for (PRUint32 k = 0; k < upstream.values.Length(); k++) {
999  nsAutoString sortableValue;
1000  rv = info->MakeSortable(upstream.values[k], sortableValue);
1001  NS_ENSURE_SUCCESS(rv, rv);
1002 
1003  nsString* success = sortableValues.AppendElement(sortableValue);
1004  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1005  }
1006 
1007  nsCOMPtr<nsIStringEnumerator> values =
1008  new sbTArrayStringEnumerator(const_cast<sbStringArray*>(
1009  &sortableValues));
1010  NS_ENSURE_TRUE(values, NS_ERROR_OUT_OF_MEMORY);
1011 
1012  rv = fs.array->AddFilter(upstream.propertyList[j],
1013  values,
1014  PR_TRUE);
1015  NS_ENSURE_SUCCESS(rv, rv);
1016 
1017  }
1018 
1019  }
1020  else {
1021  nsCOMPtr<sbIPropertyInfo> info;
1022  rv = propMan->GetPropertyInfo(upstream.property, getter_AddRefs(info));
1023  NS_ENSURE_SUCCESS(rv, rv);
1024 
1025  sbStringArray sortableValues;
1026  for (PRUint32 k = 0; k < upstream.values.Length(); k++) {
1027  nsAutoString sortableValue;
1028  rv = info->MakeSortable(upstream.values[k], sortableValue);
1029  NS_ENSURE_SUCCESS(rv, rv);
1030 
1031  nsString* success = sortableValues.AppendElement(sortableValue);
1032  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1033  }
1034 
1035  nsCOMPtr<nsIStringEnumerator> values =
1036  new sbTArrayStringEnumerator(const_cast<sbStringArray*>(
1037  &sortableValues));
1038  NS_ENSURE_TRUE(values, NS_ERROR_OUT_OF_MEMORY);
1039 
1040  rv = fs.array->AddFilter(upstream.property,
1041  values,
1042  PR_FALSE);
1043  NS_ENSURE_SUCCESS(rv, rv);
1044  }
1045  }
1046  }
1047 
1048  return NS_OK;
1049 }
1050 
1051 nsresult
1052 sbLocalDatabaseCascadeFilterSet::ConfigureFilterArray(sbFilterSpec* aSpec,
1053  const nsAString& aSortProperty)
1054 {
1055  NS_ASSERTION(aSpec, "aSpec is null");
1056 
1057  nsresult rv;
1058 
1059  rv = mProtoArray->CloneAsyncArray(getter_AddRefs(aSpec->array));
1060  NS_ENSURE_SUCCESS(rv, rv);
1061 
1062  // Guidarrays typically fetch their length, then page in the actual content
1063  // bit by bit as needed. Unfortunately in complex filtering/searching
1064  // cases the cost of the length query can be about the same as the
1065  // cost of just fetching all the rows. Since there aren't likely to be
1066  // that many rows for a distinct filter, we tell the guidarray to
1067  // fetch everything at once and avoid the length query entirely.
1068  rv = aSpec->array->SetFetchSize(PR_UINT32_MAX);
1069  NS_ENSURE_SUCCESS(rv, rv);
1070 
1071  rv = aSpec->array->AddSort(aSortProperty, PR_TRUE);
1072  NS_ENSURE_SUCCESS(rv, rv);
1073 
1074  NS_NEWXPCOM(aSpec->arrayListener, sbLocalDatabaseCascadeFilterSetArrayListener);
1075  NS_ENSURE_TRUE(aSpec->arrayListener, NS_ERROR_OUT_OF_MEMORY);
1076  rv = aSpec->arrayListener->Init(this);
1077  NS_ENSURE_SUCCESS(rv, rv);
1078  rv = aSpec->array->AddAsyncListener(aSpec->arrayListener);
1079  NS_ENSURE_SUCCESS(rv, rv);
1080 
1081  nsCOMPtr<sbILocalDatabasePropertyCache> propertyCache;
1082  rv = mLibrary->GetPropertyCache(getter_AddRefs(propertyCache));
1083  NS_ENSURE_SUCCESS(rv, rv);
1084 
1085  rv = aSpec->array->SetPropertyCache(propertyCache);
1086  NS_ENSURE_SUCCESS(rv, rv);
1087 
1088  return NS_OK;
1089 }
1090 
1091 nsresult
1092 sbLocalDatabaseCascadeFilterSet::InvalidateFilter(sbFilterSpec& aFilter)
1093 {
1094  LOG(("sbLocalDatabaseCascadeFilterSet[0x%.8x] - Invalidating %s",
1095  this, NS_ConvertUTF16toUTF8(aFilter.property).get()));
1096 
1097  // Always invalidate the length when invalidating the filter`s
1098  // guid array as there is now way to accurately tell how it's
1099  // length may have been affected.
1100  nsresult rv = aFilter.array->Invalidate(PR_TRUE);
1101  NS_ENSURE_SUCCESS(rv, rv);
1102 
1103  aFilter.invalidationPending = PR_FALSE;
1104 
1105  return NS_OK;
1106 }
1107 
1108 nsresult
1109 sbLocalDatabaseCascadeFilterSet::UpdateListener(PRBool aRemoveListener)
1110 {
1111  NS_ENSURE_STATE(mMediaList);
1112 
1113  nsresult rv;
1114 
1115  nsCOMPtr<sbIMediaListListener> listener =
1116  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaListListener*, this));
1117 
1118  if (aRemoveListener) {
1119  rv = mMediaList->RemoveListener(listener);
1120  NS_ENSURE_SUCCESS(rv, rv);
1121  }
1122 
1123  nsCOMPtr<sbIMutablePropertyArray> filter =
1124  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1125  NS_ENSURE_SUCCESS(rv, rv);
1126 
1127  nsAutoString voidString;
1128  voidString.SetIsVoid(PR_TRUE);
1129 
1130  // Filter this listener on the properties of all the filters
1131  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1132  sbFilterSpec& fs = mFilters[i];
1133  if (!fs.isSearch) {
1134  rv = filter->AppendProperty(mFilters[i].property, voidString);
1135  NS_ENSURE_SUCCESS(rv, rv);
1136  }
1137  }
1138 
1139  // Use a weak reference here since we have an owning reference to the view
1140  // which has an owning reference to the list
1141  rv = mMediaList->AddListener(listener,
1142  PR_TRUE,
1145  filter);
1146  NS_ENSURE_SUCCESS(rv, rv);
1147 
1148  return NS_OK;
1149 }
1150 
1151 nsresult
1152 sbLocalDatabaseCascadeFilterSet::ApplyConstraintFilters(sbILocalDatabaseGUIDArray* aArray)
1153 {
1154  nsresult rv;
1155 
1156  nsCOMPtr<sbILibraryConstraint> constraint;
1157 
1158  // ask the media list view for the constraints it is using (not including
1159  // any that come from this CFS)
1160  if (mMediaListView) {
1161  rv = mMediaListView->GetViewConstraint(getter_AddRefs(constraint));
1162  if (NS_FAILED(rv)) {
1163  constraint = nsnull;
1164  }
1165  }
1166 
1167  if (!constraint) {
1168  // no constraints set, use the standard
1169  nsAutoTArray<nsString, 1> values;
1170  nsString* appended = values.AppendElement(NS_LITERAL_STRING("0"));
1171  NS_ENSURE_TRUE(appended, NS_ERROR_OUT_OF_MEMORY);
1172 
1173  nsCOMPtr<nsIStringEnumerator> valuesEnum =
1174  new sbTArrayStringEnumerator(&values);
1175  NS_ENSURE_TRUE(valuesEnum, NS_ERROR_OUT_OF_MEMORY);
1176 
1177  rv = aArray->AddFilter(NS_LITERAL_STRING(SB_PROPERTY_ISLIST),
1178  valuesEnum,
1179  PR_FALSE);
1180  NS_ENSURE_SUCCESS(rv, rv);
1181 
1182  valuesEnum = new sbTArrayStringEnumerator(&values);
1183  NS_ENSURE_TRUE(valuesEnum, NS_ERROR_OUT_OF_MEMORY);
1184 
1185  rv = aArray->AddFilter(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
1186  valuesEnum,
1187  PR_FALSE);
1188  NS_ENSURE_SUCCESS(rv, rv);
1189 
1190  }
1191  else {
1192  nsCOMPtr<sbIPropertyManager> propMan =
1193  do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
1194  NS_ENSURE_SUCCESS(rv, rv);
1195 
1196  nsCOMPtr<nsISimpleEnumerator> groupEnum;
1197  rv = constraint->GetGroups(getter_AddRefs(groupEnum));
1198  NS_ENSURE_SUCCESS(rv, rv);
1199 
1200  PRBool hasMore;
1201  while (NS_SUCCEEDED(groupEnum->HasMoreElements(&hasMore)) && hasMore) {
1202  nsCOMPtr<nsISupports> groupSupports;
1203  rv = groupEnum->GetNext(getter_AddRefs(groupSupports));
1204  NS_ENSURE_SUCCESS(rv, rv);
1205 
1206  nsCOMPtr<sbILibraryConstraintGroup> group(do_QueryInterface(groupSupports));
1207  NS_ENSURE_TRUE(group, NS_ERROR_NO_INTERFACE);
1208 
1209  nsCOMPtr<nsIStringEnumerator> propEnum;
1210  rv = group->GetProperties(getter_AddRefs(propEnum));
1211  NS_ENSURE_SUCCESS(rv, rv);
1212 
1213  while (NS_SUCCEEDED(propEnum->HasMore(&hasMore)) && hasMore) {
1214  nsString prop;
1215  rv = propEnum->GetNext(prop);
1216  NS_ENSURE_SUCCESS(rv, rv);
1217 
1218  nsCOMPtr<nsIStringEnumerator> valueEnum;
1219  rv = group->GetValues(prop, getter_AddRefs(valueEnum));
1220  NS_ENSURE_SUCCESS(rv, rv);
1221 
1222  nsCOMPtr<sbIPropertyInfo> info;
1223  rv = propMan->GetPropertyInfo(prop,
1224  getter_AddRefs(info));
1225  NS_ENSURE_SUCCESS(rv, rv);
1226 
1227  /* Convert our valueEnum to an enum of the sortable values */
1228  sbStringArray valueArray;
1229  PRBool hasMore;
1230  rv = valueEnum->HasMore(&hasMore);
1231  NS_ENSURE_SUCCESS(rv, rv);
1232  while (hasMore) {
1233  nsString sortableValue;
1234  nsAutoString value;
1235  rv = valueEnum->GetNext(value);
1236  NS_ENSURE_SUCCESS(rv, rv);
1237 
1238  rv = info->MakeSortable(value, sortableValue);
1239  NS_ENSURE_SUCCESS(rv, rv);
1240  nsString* successString = valueArray.AppendElement(sortableValue);
1241  NS_ENSURE_TRUE(successString, NS_ERROR_OUT_OF_MEMORY);
1242 
1243  rv = valueEnum->HasMore(&hasMore);
1244  NS_ENSURE_SUCCESS(rv, rv);
1245  }
1246 
1247  nsCOMPtr<nsIStringEnumerator> sortedValueEnum =
1248  new sbTArrayStringEnumerator(&valueArray);
1249  NS_ENSURE_TRUE(sortedValueEnum, NS_ERROR_OUT_OF_MEMORY);
1250 
1251  rv = aArray->AddFilter(prop, sortedValueEnum, PR_FALSE);
1252  NS_ENSURE_SUCCESS(rv, rv);
1253  }
1254  }
1255  }
1256 
1257  return NS_OK;
1258 }
1259 
1260 
1261 // sbIMediaListListener
1262 NS_IMETHODIMP
1263 sbLocalDatabaseCascadeFilterSet::OnItemAdded(sbIMediaList* aMediaList,
1264  sbIMediaItem* aMediaItem,
1265  PRUint32 aIndex,
1266  PRBool* aNoMoreForBatch)
1267 {
1268  NS_ENSURE_ARG_POINTER(aMediaList);
1269  NS_ENSURE_ARG_POINTER(aMediaItem);
1270  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1271 
1272  // If we are in a batch, set all the filters to invalidate and ignore
1273  // any future adds in this batch
1274  if (mBatchHelper.IsActive()) {
1275  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1276  mFilters[i].invalidationPending = PR_TRUE;
1277  }
1278  *aNoMoreForBatch = PR_TRUE;
1279  return NS_OK;
1280  }
1281 
1282  // Only need to invalidate a filter list if the new item has a value for
1283  // its property
1284  nsresult rv;
1285  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1286  sbFilterSpec& fs = mFilters[i];
1287 
1288  nsAutoString junk;
1289  rv = aMediaItem->GetProperty(fs.property, junk);
1290  if (NS_SUCCEEDED(rv) && !junk.IsVoid()) {
1291  rv = InvalidateFilter(fs);
1292  NS_ENSURE_SUCCESS(rv, rv);
1293  }
1294  }
1295 
1296  *aNoMoreForBatch = PR_FALSE;
1297  return NS_OK;
1298 }
1299 
1300 NS_IMETHODIMP
1301 sbLocalDatabaseCascadeFilterSet::OnBeforeItemRemoved(sbIMediaList* aMediaList,
1302  sbIMediaItem* aMediaItem,
1303  PRUint32 aIndex,
1304  PRBool* aNoMoreForBatch)
1305 {
1306  NS_ENSURE_ARG_POINTER(aMediaList);
1307  NS_ENSURE_ARG_POINTER(aMediaItem);
1308  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1309 
1310  // Don't care
1311 
1312  *aNoMoreForBatch = PR_TRUE;
1313  return NS_OK;
1314 }
1315 
1316 NS_IMETHODIMP
1317 sbLocalDatabaseCascadeFilterSet::OnAfterItemRemoved(sbIMediaList* aMediaList,
1318  sbIMediaItem* aMediaItem,
1319  PRUint32 aIndex,
1320  PRBool* aNoMoreForBatch)
1321 {
1322  NS_ENSURE_ARG_POINTER(aMediaList);
1323  NS_ENSURE_ARG_POINTER(aMediaItem);
1324  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1325 
1326  // If we are in a batch, set all the filters to invalidate the ignore
1327  // any future adds in this batch
1328  if (mBatchHelper.IsActive()) {
1329  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1330  mFilters[i].invalidationPending = PR_TRUE;
1331  }
1332  *aNoMoreForBatch = PR_TRUE;
1333  return NS_OK;
1334  }
1335 
1336  // Invalidate a filter only when the removed item has a value in its
1337  // property
1338  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1339  sbFilterSpec& fs = mFilters[i];
1340  nsAutoString junk;
1341  nsresult rv = aMediaItem->GetProperty(fs.property, junk);
1342  if (NS_SUCCEEDED(rv) && !junk.IsVoid()) {
1343  rv = InvalidateFilter(fs);
1344  NS_ENSURE_SUCCESS(rv, rv);
1345  }
1346  }
1347 
1348  *aNoMoreForBatch = PR_FALSE;
1349  return NS_OK;
1350 }
1351 
1352 NS_IMETHODIMP
1353 sbLocalDatabaseCascadeFilterSet::OnItemUpdated(sbIMediaList* aMediaList,
1354  sbIMediaItem* aMediaItem,
1355  sbIPropertyArray* aProperties,
1356  PRBool* aNoMoreForBatch)
1357 {
1358  NS_ENSURE_ARG_POINTER(aMediaList);
1359  NS_ENSURE_ARG_POINTER(aMediaItem);
1360  NS_ENSURE_ARG_POINTER(aProperties);
1361  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1362 
1363  nsresult rv;
1364 
1365  // Invalidate any filters whose property was updated
1366  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1367  sbFilterSpec& fs = mFilters[i];
1368 
1369  nsAutoString junk;
1370  rv = aProperties->GetPropertyValue(fs.property, junk);
1371  if (NS_SUCCEEDED(rv)) {
1372  if (mBatchHelper.IsActive()) {
1373  fs.invalidationPending = PR_TRUE;
1374  }
1375  else {
1376  rv = InvalidateFilter(fs);
1377  NS_ENSURE_SUCCESS(rv, rv);
1378  }
1379  }
1380  }
1381 
1382  *aNoMoreForBatch = PR_FALSE;
1383  return NS_OK;
1384 }
1385 
1386 NS_IMETHODIMP
1387 sbLocalDatabaseCascadeFilterSet::OnItemMoved(sbIMediaList* aMediaList,
1388  PRUint32 aFromIndex,
1389  PRUint32 aToIndex,
1390  PRBool* aNoMoreForBatch)
1391 {
1392  NS_ENSURE_ARG_POINTER(aMediaList);
1393  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1394 
1395  // Don't care
1396 
1397  *aNoMoreForBatch = PR_TRUE;
1398  return NS_OK;
1399 }
1400 
1401 NS_IMETHODIMP
1402 sbLocalDatabaseCascadeFilterSet::OnBeforeListCleared(sbIMediaList* aMediaList,
1403  PRBool aExcludeLists,
1404  PRBool* aNoMoreForBatch)
1405 {
1406  NS_ENSURE_ARG_POINTER(aMediaList);
1407  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1408 
1409  // Don't care
1410 
1411  *aNoMoreForBatch = PR_TRUE;
1412  return NS_OK;
1413 }
1414 
1415 NS_IMETHODIMP
1416 sbLocalDatabaseCascadeFilterSet::OnListCleared(sbIMediaList* aMediaList,
1417  PRBool aExcludeLists,
1418  PRBool* aNoMoreForBatch)
1419 {
1420  NS_ENSURE_ARG_POINTER(aMediaList);
1421  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1422 
1423  // Invalidate all filters
1424  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1425  sbFilterSpec& fs = mFilters[i];
1426  if (mBatchHelper.IsActive()) {
1427  fs.invalidationPending = PR_TRUE;
1428  }
1429  else {
1430  nsresult rv = InvalidateFilter(fs);
1431  NS_ENSURE_SUCCESS(rv, rv);
1432  }
1433  }
1434 
1435  *aNoMoreForBatch = PR_FALSE;
1436  return NS_OK;
1437 }
1438 
1439 NS_IMETHODIMP
1440 sbLocalDatabaseCascadeFilterSet::OnBatchBegin(sbIMediaList* aMediaList)
1441 {
1442  mBatchHelper.Begin();
1443  return NS_OK;
1444 }
1445 
1446 NS_IMETHODIMP
1447 sbLocalDatabaseCascadeFilterSet::OnBatchEnd(sbIMediaList* aMediaList)
1448 {
1449  mBatchHelper.End();
1450 
1451  if (!mBatchHelper.IsActive()) {
1452  // Do all pending invalidations
1453  for (PRUint32 i = 0; i < mFilters.Length(); i++) {
1454  sbFilterSpec& fs = mFilters[i];
1455  if (fs.invalidationPending) {
1456  nsresult rv = InvalidateFilter(fs);
1457  NS_ENSURE_SUCCESS(rv, rv);
1458  }
1459  }
1460  }
1461 
1462  return NS_OK;
1463 }
1464 
1465 
1466 PLDHashOperator PR_CALLBACK
1467 sbLocalDatabaseCascadeFilterSet::OnValuesChangedCallback(nsISupportsHashKey* aKey,
1468  void* aUserData)
1469 {
1470  TRACE(("sbLocalDatabaseCascadeFilterSet[static] - OnValuesChangedCallback"));
1471  NS_ASSERTION(aKey && aUserData, "Args should not be null!");
1472 
1473  nsresult rv;
1474  nsCOMPtr<sbICascadeFilterSetListener> listener =
1475  do_QueryInterface(aKey->GetKey(), &rv);
1476 
1477  if (NS_SUCCEEDED(rv)) {
1478  PRUint32* index = static_cast<PRUint32*>(aUserData);
1479  rv = listener->OnValuesChanged(*index);
1480  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1481  "OnValuesChanged returned a failure code");
1482  }
1483  return PL_DHASH_NEXT;
1484 }
1485 
1489 
1491  (sbLocalDatabaseCascadeFilterSet *aCascadeFilterSet)
1492 {
1493  nsresult rv;
1494 
1495  mWeakCascadeFilterSet =
1496  do_GetWeakReference
1497  (NS_ISUPPORTS_CAST(sbICascadeFilterSet*, aCascadeFilterSet), &rv);
1498  NS_ENSURE_SUCCESS(rv, rv);
1499  mCascadeFilterSet = aCascadeFilterSet;
1500 
1501  return NS_OK;
1502 }
1503 
1504 // sbILocalDatabaseAsyncGUIDArrayListener
1505 NS_IMETHODIMP
1506 sbLocalDatabaseCascadeFilterSetArrayListener::OnGetLength(PRUint32 aLength,
1507  nsresult aResult)
1508 {
1509  TRACE(("sbLocalDatabaseCascadeFilterSetArrayListener[0x%.8x] - "
1510  "OnGetLength(%d)", this, aLength));
1511 
1512  nsresult rv;
1513 
1514  if (NS_SUCCEEDED(aResult)) {
1515  nsCOMPtr<nsISupports> cascadeFilterSetRef =
1516  do_QueryReferent(mWeakCascadeFilterSet, &rv);
1517  if (cascadeFilterSetRef) {
1518 #ifdef DEBUG
1519  nsCOMPtr<nsISupports> classPtr = do_QueryInterface
1520  (NS_ISUPPORTS_CAST(sbICascadeFilterSet*, mCascadeFilterSet), &rv);
1521  NS_ENSURE_SUCCESS(rv, rv);
1522  NS_ASSERTION(classPtr == cascadeFilterSetRef,
1523  "Weak reference and class pointer are out of sync!");
1524 #endif
1525  rv = mCascadeFilterSet->OnGetLength(mIndex, aLength);
1526  NS_ENSURE_SUCCESS(rv, rv);
1527  }
1528  }
1529  return NS_OK;
1530 }
1531 
1532 NS_IMETHODIMP
1533 sbLocalDatabaseCascadeFilterSetArrayListener::OnGetGuidByIndex
1534  (PRUint32 aIndex,
1535  const nsAString& aGUID,
1536  nsresult aResult)
1537 {
1538  return NS_OK;
1539 }
1540 
1541 NS_IMETHODIMP
1542 sbLocalDatabaseCascadeFilterSetArrayListener::OnGetSortPropertyValueByIndex
1543  (PRUint32 aIndex,
1544  const nsAString& aGUID,
1545  nsresult aResult)
1546 {
1547  return NS_OK;
1548 }
1549 
1550 NS_IMETHODIMP
1551 sbLocalDatabaseCascadeFilterSetArrayListener::OnGetMediaItemIdByIndex
1552  (PRUint32 aIndex,
1553  PRUint32 aMediaItemId,
1554  nsresult aResult)
1555 {
1556  return NS_OK;
1557 }
1558 
1559 NS_IMETHODIMP
1560 sbLocalDatabaseCascadeFilterSetArrayListener::OnStateChange(PRUint32 aState)
1561 {
1562  return NS_OK;
1563 }
1564 
1566 
1568  mArray(aArray),
1569  mNextIndex(0)
1570 {
1571 }
1572 
1573 NS_IMETHODIMP
1574 sbGUIDArrayPrimarySortEnumerator::HasMore(PRBool *_retval)
1575 {
1576  NS_ENSURE_ARG_POINTER(_retval);
1577  nsresult rv;
1578 
1579  PRUint32 length;
1580  rv = mArray->GetLength(&length);
1581  NS_ENSURE_SUCCESS(rv, rv);
1582 
1583  *_retval = mNextIndex < length;
1584  return NS_OK;
1585 }
1586 
1587 NS_IMETHODIMP
1588 sbGUIDArrayPrimarySortEnumerator::GetNext(nsAString& _retval)
1589 {
1590  nsresult rv;
1591 
1592  rv = mArray->GetSortPropertyValueByIndex(mNextIndex, _retval);
1593  NS_ENSURE_SUCCESS(rv, rv);
1594 
1595  mNextIndex++;
1596  return NS_OK;
1597 }
1598 
1600  nsISerializable);
1601 
1602 NS_IMETHODIMP
1603 sbLocalDatabaseCascadeFilterSetState::Read(nsIObjectInputStream* aStream)
1604 {
1605  NS_ENSURE_ARG_POINTER(aStream);
1606 
1607  nsresult rv;
1608 
1609  mFilters.Clear();
1610 
1611  PRUint32 length;
1612  rv = aStream->Read32(&length);
1613  NS_ENSURE_SUCCESS(rv, rv);
1614 
1615  for (PRUint32 i = 0; i < length; i++) {
1616  Spec* spec = mFilters.AppendElement();
1617  NS_ENSURE_TRUE(spec, NS_ERROR_OUT_OF_MEMORY);
1618 
1619  rv = aStream->ReadBoolean(&spec->isSearch);
1620  NS_ENSURE_SUCCESS(rv, rv);
1621 
1622  rv = aStream->ReadString(spec->property);
1623  NS_ENSURE_SUCCESS(rv, rv);
1624 
1625  PRUint32 propertyListLength;
1626  rv = aStream->Read32(&propertyListLength);
1627  for (PRUint32 j = 0; j < propertyListLength; j++) {
1628  nsString property;
1629  rv = aStream->ReadString(property);
1630  NS_ENSURE_SUCCESS(rv, rv);
1631 
1632  nsString* added = spec->propertyList.AppendElement(property);
1633  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
1634  }
1635 
1636  PRUint32 valuesength;
1637  rv = aStream->Read32(&valuesength);
1638  for (PRUint32 j = 0; j < valuesength; j++) {
1639  nsString value;
1640  rv = aStream->ReadString(value);
1641  NS_ENSURE_SUCCESS(rv, rv);
1642 
1643  nsString* added = spec->values.AppendElement(value);
1644  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
1645  }
1646 
1647  PRBool hasTreeViewState;
1648  rv = aStream->ReadBoolean(&hasTreeViewState);
1649  NS_ENSURE_SUCCESS(rv, rv);
1650 
1651  if (hasTreeViewState) {
1652  spec->treeViewState = new sbLocalDatabaseTreeViewState();
1653  NS_ENSURE_TRUE(spec->treeViewState, NS_ERROR_OUT_OF_MEMORY);
1654 
1655  rv = spec->treeViewState->Init();
1656  NS_ENSURE_SUCCESS(rv, rv);
1657 
1658  rv = spec->treeViewState->Read(aStream);
1659  NS_ENSURE_SUCCESS(rv, rv);
1660  }
1661  }
1662 
1663  return NS_OK;
1664 }
1665 
1666 NS_IMETHODIMP
1667 sbLocalDatabaseCascadeFilterSetState::Write(nsIObjectOutputStream* aStream)
1668 {
1669  NS_ENSURE_ARG_POINTER(aStream);
1670 
1671  nsresult rv;
1672 
1673  PRUint32 length = mFilters.Length();
1674  rv = aStream->Write32(length);
1675  NS_ENSURE_SUCCESS(rv, rv);
1676 
1677  for (PRUint32 i = 0; i < length; i++) {
1678  Spec& fs = mFilters[i];
1679 
1680  rv = aStream->WriteBoolean(fs.isSearch);
1681  NS_ENSURE_SUCCESS(rv, rv);
1682 
1683  rv = aStream->WriteWStringZ(fs.property.BeginReading());
1684  NS_ENSURE_SUCCESS(rv, rv);
1685 
1686  PRUint32 propertyListLength = fs.propertyList.Length();
1687  rv = aStream->Write32(propertyListLength);
1688  NS_ENSURE_SUCCESS(rv, rv);
1689 
1690  for (PRUint32 j = 0; j < propertyListLength; j++) {
1691  rv = aStream->WriteWStringZ(fs.propertyList[j].BeginReading());
1692  NS_ENSURE_SUCCESS(rv, rv);
1693  }
1694 
1695  PRUint32 valuesLength = fs.values.Length();
1696  rv = aStream->Write32(valuesLength);
1697  NS_ENSURE_SUCCESS(rv, rv);
1698 
1699  for (PRUint32 j = 0; j < valuesLength; j++) {
1700  rv = aStream->WriteWStringZ(fs.values[j].BeginReading());
1701  NS_ENSURE_SUCCESS(rv, rv);
1702  }
1703 
1704  if (fs.treeViewState) {
1705  rv = aStream->WriteBoolean(PR_TRUE);
1706  NS_ENSURE_SUCCESS(rv, rv);
1707 
1708  rv = fs.treeViewState->Write(aStream);
1709  NS_ENSURE_SUCCESS(rv, rv);
1710  }
1711  else {
1712  rv = aStream->WriteBoolean(PR_FALSE);
1713  NS_ENSURE_SUCCESS(rv, rv);
1714  }
1715  }
1716 
1717  return NS_OK;
1718 }
1719 
1720 nsresult
1722 {
1723  nsresult rv;
1724  nsString buff;
1725 
1726  PRUint32 numFilters = mFilters.Length();
1727  for (PRUint32 i = 0; i < numFilters; i++) {
1728  Spec& fs = mFilters[i];
1729 
1730  if (fs.isSearch) {
1731  buff.AppendLiteral("search [[");
1732  PRUint32 propertyListLength = fs.propertyList.Length();
1733  for (PRUint32 j = 0; j < propertyListLength; j++) {
1734  buff.Append(fs.propertyList[j]);
1735  if (j + 1 < propertyListLength) {
1736  buff.AppendLiteral(", ");
1737  }
1738  }
1739  buff.AppendLiteral("] ");
1740  }
1741  else {
1742  buff.AppendLiteral("filter [");
1743  buff.Append(fs.property);
1744  buff.AppendLiteral(" ");
1745  }
1746 
1747  buff.AppendLiteral("values [");
1748 
1749  PRUint32 valesLength = fs.values.Length();
1750  for (PRUint32 j = 0; j < valesLength; j++) {
1751  buff.Append(fs.values[j]);
1752  if (j + 1 < valesLength) {
1753  buff.AppendLiteral(", ");
1754  }
1755  }
1756  buff.AppendLiteral("]");
1757 
1758  if (fs.treeViewState) {
1759  buff.AppendLiteral("treeView: ");
1760  nsString tmp;
1761  rv = fs.treeViewState->ToString(tmp);
1762  NS_ENSURE_SUCCESS(rv, rv);
1763  buff.Append(tmp);
1764  }
1765 
1766  if (i + 1 < numFilters) {
1767  buff.AppendLiteral(", ");
1768  }
1769  }
1770 
1771  aStr = buff;
1772 
1773  return NS_OK;
1774 }
nsresult AddConfiguration(sbILocalDatabaseGUIDArray *aArray)
return NS_OK
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
nsRefPtr< sbLocalDatabaseTreeViewState > treeViewState
nsresult AddFilters(sbILibraryConstraintBuilder *aBuilder, PRBool *aChanged)
NS_DECL_ISUPPORTS NS_DECL_SBICASCADEFILTERSET NS_DECL_SBIMEDIALISTLISTENER sbLocalDatabaseCascadeFilterSet(sbLocalDatabaseMediaListView *aMediaListView)
#define TRACE(args)
onPageChanged aValue
Definition: FeedWriter.js:1395
nsTArray< nsString > sbStringArray
inArray array
Cascade filter management for a media list.
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
nsresult GetState(sbLocalDatabaseCascadeFilterSetState **aState)
boolean isSearch(in unsigned short aIndex)
Determine if the filter at a given index is a search filter.
nsresult UpdateViewArrayConfiguration(PRBool aClearTreeSelection)
#define SB_PROPERTY_HIDDEN
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
nsresult Init(sbLocalDatabaseLibrary *aLibrary, sbILocalDatabaseAsyncGUIDArray *aProtoArray, sbLocalDatabaseCascadeFilterSetState *aState)
static PRBool SB_IsTopLevelProperty(PRUint32 aPropertyDBID)
const unsigned long LISTENER_FLAGS_BEFOREITEMREMOVED
A brief description of the contents of this interface.
#define SB_PROPERTYMANAGER_CONTRACTID
Interface used to listen to changes to a media list.
readonly attribute unsigned short length
#define SB_PROPERTY_CREATED
nsresult AddSearches(sbILibraryConstraintBuilder *aBuilder, PRBool *aChanged)
NS_IMPL_ISUPPORTS3(sbLocalDatabaseCascadeFilterSet, sbICascadeFilterSet, sbIMediaListListener, nsISupportsWeakReference)
NS_IMPL_ISUPPORTS2(sbLocalDatabaseCascadeFilterSetArrayListener, nsISupportsWeakReference, sbILocalDatabaseAsyncGUIDArrayListener) nsresult sbLocalDatabaseCascadeFilterSetArrayListener
function Init()
static nsresult GetProperty(nsIPropertyBag2 *aProperties, nsAString const &aProp, nsAString &aValue)
nsISerializable
Listener interface for sbICascadeFilterSet.
nsresult GetViewConstraint(sbILibraryConstraint **aFilterConstraint)
NS_DECL_ISUPPORTS NS_DECL_NSISERIALIZABLE nsresult ToString(nsAString &aStr)
const PR_UINT32_MAX
Definition: httpd.js:55
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
#define LOG(args)
const unsigned long LISTENER_FLAGS_ALL
Interface that defines a single item of media in the system.
NS_IMPL_ISUPPORTS1(sbLocalDatabaseCascadeFilterSetState, nsISerializable)
#define SB_PROPERTY_ISLIST
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
restoreWindow aState
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
sbSuppressArrayInvalidation(sbILocalDatabaseGUIDArray *aArray)
nsresult OnGetLength(PRUint32 aIndex, PRUint32 aLength)
_getSelectedPageStyle s i
Array filter(tab.attributes, function(aAttr){return(_this.xulAttributes.indexOf(aAttr.name) >-1);}).forEach(tab.removeAttribute
var group
var treeView
Songbird Database Object Definition.