sbLocalDatabaseSmartMediaList.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
26 #include "sbLocalDatabaseCID.h"
27 
28 #include <sbIDatabaseQuery.h>
29 #include <sbIDatabaseResult.h>
30 #include <sbILibrary.h>
31 #include <sbILocalDatabaseLibrary.h>
32 #include <sbILocalDatabasePropertyCache.h>
33 #include <sbILocalDatabaseMediaItem.h>
34 #include <sbILocalDatabaseSimpleMediaList.h>
35 #include <sbIMediaItem.h>
36 #include <sbIMediaList.h>
37 #include <sbIPropertyArray.h>
38 #include <sbIPropertyInfo.h>
39 #include <sbIPropertyManager.h>
40 #include <sbISQLBuilder.h>
42 #include <sbPropertiesCID.h>
43 #include <sbSQLBuilderCID.h>
44 #include <sbStandardOperators.h>
45 #include <sbStandardProperties.h>
46 #include <sbILibraryManager.h>
47 #include <sbDummyProperties.h>
48 #include <sbStringUtils.h>
49 #include <nsAutoPtr.h>
50 #include <nsTArray.h>
51 #include <nsCOMPtr.h>
52 #include <nsComponentManagerUtils.h>
53 #include <nsIClassInfoImpl.h>
54 #include <nsINetUtil.h>
55 #include <nsIObserverService.h>
56 #include <nsIUUIDGenerator.h>
57 #include <nsIProgrammingLanguage.h>
58 #include <nsMemory.h>
59 #include <nsNetCID.h>
60 #include <nsServiceManagerUtils.h>
61 #include <nsIInterfaceRequestorUtils.h>
62 #include <nsVoidArray.h>
63 #include <prlog.h>
64 #include <prprf.h>
65 #include <prtime.h>
66 
67 #define RANDOM_ADD_CHUNK_SIZE 1000;
68 #define SQL_IN_LIMIT 1000
69 
70 #define ONEDAY (1000*60*60*24)
71 #define ONE_MS 1
72 
73 static const char *gsFmtRadix10 = "%lld";
74 
75 static char const LIBRARY_MANAGER_BEFORE_SHUTDOWN[] = "songbird-library-manager-before-shutdown";
76 static char const OBSERVER_SERVICE_CONTRACT_ID[] = "@mozilla.org/observer-service;1";
77 
78 /*
79  * To log this module, set the following environment variable:
80  * NSPR_LOG_MODULES=sbLocalDatabaseSmartMediaList:5
81  */
82 #ifdef PR_LOGGING
83 static PRLogModuleInfo* gLocalDatabaseSmartMediaListLog = nsnull;
84 #define TRACE(args) PR_LOG(gLocalDatabaseSmartMediaListLog, PR_LOG_DEBUG, args)
85 #define LOG(args) PR_LOG(gLocalDatabaseSmartMediaListLog, PR_LOG_WARN, args)
86 #else
87 #define TRACE(args) /* nothing */
88 #define LOG(args) /* nothing */
89 #endif
90 
91 static nsresult
92 ParseAndAddChunk(const nsAString& aString,
93  sbStringMap& aMap)
94 {
95  nsresult rv;
96  nsCOMPtr<nsINetUtil> netUtil = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
97  NS_ENSURE_SUCCESS(rv, rv);
98 
99  static const PRUnichar sEquals = '=';
100 
101  PRInt32 length = aString.Length();
102 
103  if (length == 0) {
104  return NS_OK;
105  }
106 
107  PRInt32 pos = aString.FindChar(sEquals);
108  if (pos > 1) {
109  nsAutoString name(nsDependentSubstring(aString, 0, pos));
110  nsCAutoString unescapedNameUtf8;
111  rv = netUtil->UnescapeString(NS_ConvertUTF16toUTF8(name), 0, unescapedNameUtf8);
112  NS_ENSURE_SUCCESS(rv, rv);
113 
114  nsAutoString value(nsDependentSubstring(aString, pos + 1, length - pos));
115  nsCAutoString unescapedValueUtf8;
116  if (pos < length - 1) {
117  rv = netUtil->UnescapeString(NS_ConvertUTF16toUTF8(value), 0, unescapedValueUtf8);
118  NS_ENSURE_SUCCESS(rv, rv);
119  }
120 
121  PRBool success = aMap.Put(NS_ConvertUTF8toUTF16(unescapedNameUtf8),
122  NS_ConvertUTF8toUTF16(unescapedValueUtf8));
123  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
124  }
125 
126  return NS_OK;
127 }
128 
129 static nsresult ParseQueryStringIntoHashtable(const nsAString& aString,
130  sbStringMap& aMap)
131 {
132  nsresult rv;
133 
134  // Parse a string that looks like name1=value1&name1=value2
135  const PRUnichar *start, *end;
136  PRUint32 length = aString.BeginReading(&start, &end);
137 
138  if (length == 0) {
139  return NS_OK;
140  }
141 
142  nsDependentSubstring chunk;
143 
144  const PRUnichar* chunkStart = start;
145  static const PRUnichar sAmp = '&';
146  for (const PRUnichar* current = start; current < end; current++) {
147 
148  if (*current == sAmp) {
149  chunk.Rebind(chunkStart, current - chunkStart);
150 
151  rv = ParseAndAddChunk(chunk, aMap);
152  NS_ENSURE_SUCCESS(rv, rv);
153  if (current + 1 < end) {
154  chunkStart = current + 1;
155  }
156  else {
157  chunkStart = nsnull;
158  }
159  }
160  }
161 
162  if (chunkStart) {
163  chunk.Rebind(chunkStart, end - chunkStart);
164  rv = ParseAndAddChunk(chunk, aMap);
165  NS_ENSURE_SUCCESS(rv, rv);
166  }
167 
168  return NS_OK;
169 }
170 
171 PLDHashOperator PR_CALLBACK
172 JoinStringMapCallback(nsStringHashKey::KeyType aKey,
173  nsString aEntry,
174  void* aUserData)
175 {
176  NS_ASSERTION(aUserData, "Null user data");
177 
178  nsresult rv;
179  nsCOMPtr<nsINetUtil> netUtil = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
180  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
181 
182  nsString* str =
183  static_cast<nsString*>(aUserData);
184  NS_ENSURE_TRUE(str, PL_DHASH_STOP);
185 
186  nsCAutoString key;
187  rv = netUtil->EscapeString(NS_ConvertUTF16toUTF8(aKey),
188  nsINetUtil::ESCAPE_XALPHAS,
189  key);
190  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
191 
192  nsCAutoString value;
193  rv = netUtil->EscapeString(NS_ConvertUTF16toUTF8(aEntry),
194  nsINetUtil::ESCAPE_XALPHAS,
195  value);
196  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
197 
198  str->Append(NS_ConvertUTF8toUTF16(key));
199  str->AppendLiteral("=");
200  str->Append(NS_ConvertUTF8toUTF16(value));
201  str->AppendLiteral("&");
202 
203  return PL_DHASH_NEXT;
204 }
205 
206 static nsresult
208  nsAString &aString)
209 {
210 
211  nsAutoString str;
212  aMap.EnumerateRead(JoinStringMapCallback, &str);
213 
214  if (str.Length() > 0) {
215  aString = nsDependentSubstring(str, 0, str.Length() - 1);
216  }
217  else {
218  aString = EmptyString();
219  }
220 
221  return NS_OK;
222 }
223 
224 //==============================================================================
225 // sbLocalDatabaseSmartMediaListCondition
226 //==============================================================================
229 
231  const nsAString& aOperatorString,
232  const nsAString& aLeftValue,
233  const nsAString& aRightValue,
234  const nsAString& aDisplayUnit)
235 : mLock(nsnull)
236 , mPropertyID(aPropertyID)
237 , mOperatorString(aOperatorString)
238 , mLeftValue(aLeftValue)
239 , mRightValue(aRightValue)
240 , mDisplayUnit(aDisplayUnit)
241 {
242  mLock = nsAutoLock::NewLock("sbLocalDatabaseSmartMediaListCondition::mLock");
243  NS_ASSERTION(mLock,
244  "sbLocalDatabaseSmartMediaListCondition::mLock failed to create lock!");
245 }
246 
248 {
249  if(mLock) {
250  nsAutoLock::DestroyLock(mLock);
251  }
252 }
253 
254 NS_IMETHODIMP
255 sbLocalDatabaseSmartMediaListCondition::GetPropertyID(nsAString& aPropertyID)
256 {
257  nsAutoLock lock(mLock);
258  aPropertyID = mPropertyID;
259 
260  return NS_OK;
261 }
262 
263 NS_IMETHODIMP
264 sbLocalDatabaseSmartMediaListCondition::GetOperator(sbIPropertyOperator** aOperator)
265 {
266  NS_ENSURE_ARG_POINTER(aOperator);
267 
268  nsAutoLock lock(mLock);
269 
270  if (!mOperator) {
271  nsresult rv;
272  // Get the property manager
273  nsCOMPtr<sbIPropertyManager> propMan;
274  propMan = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
275  NS_ENSURE_SUCCESS(rv, rv);
276 
277  // Get the property info for this property.
278  nsCOMPtr<sbIPropertyInfo> info;
279  rv = propMan->GetPropertyInfo(mPropertyID, getter_AddRefs(info));
280  NS_ENSURE_SUCCESS(rv, rv);
281 
282  // Get the operator.
283  rv = info->GetOperator(mOperatorString, getter_AddRefs(mOperator));
284  NS_ENSURE_SUCCESS(rv, rv);
285  }
286 
287  *aOperator = mOperator;
288  NS_IF_ADDREF(*aOperator);
289 
290  return NS_OK;
291 }
292 
293 NS_IMETHODIMP
294 sbLocalDatabaseSmartMediaListCondition::GetLeftValue(nsAString& aLeftValue)
295 {
296  nsAutoLock lock(mLock);
297  aLeftValue = mLeftValue;
298 
299  return NS_OK;
300 }
301 
302 NS_IMETHODIMP
303 sbLocalDatabaseSmartMediaListCondition::GetRightValue(nsAString& aRightValue)
304 {
305  nsAutoLock lock(mLock);
306  aRightValue = mRightValue;
307 
308  return NS_OK;
309 }
310 
311 NS_IMETHODIMP
312 sbLocalDatabaseSmartMediaListCondition::GetDisplayUnit(nsAString& aDisplayUnit)
313 {
314  nsAutoLock lock(mLock);
315  aDisplayUnit = mDisplayUnit;
316 
317  return NS_OK;
318 }
319 
320 nsresult
322 {
323  nsAutoLock lock(mLock);
324 
325  nsresult rv;
326 
327  sbStringMap map;
328  PRBool success = map.Init();
329  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
330 
331  success = map.Put(NS_LITERAL_STRING("property"), mPropertyID);
332  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
333 
334  success = map.Put(NS_LITERAL_STRING("operator"), mOperatorString);
335  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
336 
337  success = map.Put(NS_LITERAL_STRING("leftValue"), mLeftValue);
338  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
339 
340  success = map.Put(NS_LITERAL_STRING("rightValue"), mRightValue);
341  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
342 
343  success = map.Put(NS_LITERAL_STRING("displayUnit"), mDisplayUnit);
344  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
345 
346  rv = JoinStringMapIntoQueryString(map, _retval);
347  NS_ENSURE_SUCCESS(rv, rv);
348 
349  return NS_OK;
350 }
351 
352 //==============================================================================
353 // sbLocalDatabaseSmartMediaList
354 //==============================================================================
355 
356 
358  nsIClassInfo,
363  sbIMediaItem,
364  sbIMediaList,
366  nsIObserver);
367 
369  nsIClassInfo,
373  sbIMediaItem,
374  sbIMediaList,
376  nsIObserver);
377 
379 : mInnerMonitor(nsnull)
380 , mConditionsMonitor(nsnull)
381 , mMatchType(sbILocalDatabaseSmartMediaList::MATCH_TYPE_ANY)
382 , mLimitType(sbILocalDatabaseSmartMediaList::LIMIT_TYPE_NONE)
383 , mLimit(0)
384 , mSelectDirection(PR_TRUE)
385 , mRandomSelection(PR_FALSE)
386 , mAutoUpdateMonitor(nsnull)
387 , mAutoUpdate(false)
388 , mNotExistsMode(sbILocalDatabaseSmartMediaList::NOTEXISTS_ASZERO)
389 , mListenersMonitor(nsnull)
390 , mSourceMonitor(nsnull)
391 {
392 #ifdef PR_LOGGING
393  if (!gLocalDatabaseSmartMediaListLog) {
394  gLocalDatabaseSmartMediaListLog =
395  PR_NewLogModule("sbLocalDatabaseSmartMediaList");
396  }
397 #endif
398 }
399 
401 {
402  if(mInnerMonitor) {
403  nsAutoMonitor::DestroyMonitor(mInnerMonitor);
404  }
405  if(mConditionsMonitor) {
406  nsAutoMonitor::DestroyMonitor(mConditionsMonitor);
407  }
408  if(mAutoUpdateMonitor) {
409  nsAutoMonitor::DestroyMonitor(mAutoUpdateMonitor);
410  }
411  if (mListenersMonitor) {
412  nsAutoMonitor::DestroyMonitor(mListenersMonitor);
413  }
414  if (mSourceMonitor) {
415  nsAutoMonitor::DestroyMonitor(mSourceMonitor);
416  }
417 
418  if (mItem) {
419  /* use a single-iteration loop to allow breaking out easier */
420  nsCOMPtr<sbILibrary> library;
421  nsresult rv = mItem->GetLibrary(getter_AddRefs(library));
422  NS_ENSURE_SUCCESS(rv, /* void */);
423 
424  nsCOMPtr<sbIMediaList> libraryList = do_QueryInterface(library, &rv);
425  NS_ENSURE_SUCCESS(rv, /* void */);
426 
427  (void) libraryList->RemoveListener(this);
428 
429  // Unregister ourselves as we're done
430  nsCOMPtr<nsIObserverService> observerService =
431  do_GetService(OBSERVER_SERVICE_CONTRACT_ID, &rv);
432  NS_ENSURE_SUCCESS(rv, /* void */);
433 
434  rv = observerService->RemoveObserver(this, LIBRARY_MANAGER_BEFORE_SHUTDOWN);
435  NS_ENSURE_SUCCESS(rv, /* void */);
436  }
437 }
438 
443 {
444 public:
448  sbAutoSuppressor(sbIMediaItem * aItem) : mItem(do_QueryInterface(aItem))
449  {
450  Suppress();
451  }
456  {
457  Unsuppress();
458  }
462  void Suppress()
463  {
464  if (mItem)
465  mItem->SetSuppressNotifications(PR_TRUE);
466  }
470  void Unsuppress()
471  {
472  if (mItem)
473  mItem->SetSuppressNotifications(PR_FALSE);
474  }
475 private:
476  nsCOMPtr<sbILocalDatabaseMediaItem> mItem;
477  // Disallow copying and assignment
479  sbAutoSuppressor & operator =(sbAutoSuppressor const *&);
480 };
481 
482 nsresult
484 {
485  NS_ENSURE_ARG_POINTER(aItem);
486 
487  mInnerMonitor = nsAutoMonitor::NewMonitor("sbLocalDatabaseSmartMediaList::mInnerMonitor");
488  NS_ENSURE_TRUE(mInnerMonitor, NS_ERROR_OUT_OF_MEMORY);
489 
490  mConditionsMonitor = nsAutoMonitor::NewMonitor("sbLocalDatabaseSmartMediaList::mConditionsMonitor");
491  NS_ENSURE_TRUE(mConditionsMonitor, NS_ERROR_OUT_OF_MEMORY);
492 
493  mAutoUpdateMonitor = nsAutoMonitor::NewMonitor("sbLocalDatabaseSmartMediaList::mAutoUpdateMonitor");
494  NS_ENSURE_TRUE(mAutoUpdateMonitor, NS_ERROR_OUT_OF_MEMORY);
495 
496  mListenersMonitor = nsAutoMonitor::NewMonitor("sbLocalDatabaseSmartMediaList::mListenersMonitor");
497  NS_ENSURE_TRUE(mListenersMonitor, NS_ERROR_OUT_OF_MEMORY);
498 
499  mSourceMonitor = nsAutoMonitor::NewMonitor("sbLocalDatabaseSmartMediaList::mSourceMonitor");
500  NS_ENSURE_TRUE(mSourceMonitor, NS_ERROR_OUT_OF_MEMORY);
501 
502  mItem = aItem;
503 
504  nsresult rv;
505  mLocalDBItem = do_QueryInterface(mItem, &rv);
506  NS_ENSURE_SUCCESS(rv, rv);
507 
508  nsAutoString storageGuid;
509  rv = mItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_STORAGEGUID),
510  storageGuid);
511  NS_ENSURE_SUCCESS(rv, rv);
512 
513  nsCOMPtr<sbILibrary> library;
514  rv = mItem->GetLibrary(getter_AddRefs(library));
515  NS_ENSURE_SUCCESS(rv, rv);
516 
517  nsCOMPtr<sbIMediaItem> mediaItem;
518  rv = library->GetMediaItem(storageGuid, getter_AddRefs(mediaItem));
519  NS_ENSURE_SUCCESS(rv, rv);
520 
521  mList = do_QueryInterface(mediaItem, &rv);
522  NS_ENSURE_SUCCESS(rv, rv);
523 
524  // Supress notifications until we're intialized
525  sbAutoSuppressor suppressor(mediaItem);
526 
527  // let the inner list know about us
528  nsAutoString guid;
529  rv = GetGuid(guid);
530  NS_ENSURE_SUCCESS(rv, rv);
531 
532  nsCOMPtr<sbIMediaItem> mi =
533  do_QueryInterface(mList, &rv);
534  NS_ENSURE_SUCCESS(rv, rv);
535 
536  rv = mi->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_OUTERGUID), guid);
537  NS_ENSURE_SUCCESS(rv, rv);
538 
539  // Register for library shutdown
540  nsCOMPtr<nsIObserverService> observerService =
541  do_GetService(OBSERVER_SERVICE_CONTRACT_ID, &rv);
542  NS_ENSURE_SUCCESS(rv, rv);
543 
544  rv = observerService->AddObserver(this, LIBRARY_MANAGER_BEFORE_SHUTDOWN, PR_TRUE);
545  NS_ENSURE_SUCCESS(rv, rv);
546 
547  // Register self for "before delete" so we can delete our storage list
548  nsCOMPtr<sbIMediaList> libraryList = do_QueryInterface(library, &rv);
549  NS_ENSURE_SUCCESS(rv, rv);
550 
551  // Use a weak reference here because we already have a strong reference to
552  // the inner list
553  rv = libraryList->AddListener(this,
554  PR_TRUE,
556  nsnull);
557  NS_ENSURE_SUCCESS(rv, rv);
558 
559  mPropMan = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
560  NS_ENSURE_SUCCESS(rv, rv);
561 
562  // Get the property cache for id lookups
563  mLocalDatabaseLibrary = do_QueryInterface(library, &rv);
564  NS_ENSURE_SUCCESS(rv, rv);
565 
566  rv = mLocalDatabaseLibrary->GetPropertyCache(getter_AddRefs(mPropertyCache));
567  NS_ENSURE_SUCCESS(rv, rv);
568 
569  rv = CreateQueries();
570  NS_ENSURE_SUCCESS(rv, rv);
571 
572  // Read out saved state
573  rv = ReadConfiguration();
574  NS_ENSURE_SUCCESS(rv, rv);
575 
576  return NS_OK;
577 }
578 
579 NS_IMETHODIMP
581 {
582  NS_ENSURE_ARG_POINTER(aContentType);
583  nsresult rv;
584 
585  PRUint32 conditionLength = mConditions.Length();
586 
587  // No condition available. Smart playlist not ready.
588  if (!conditionLength)
589  return NS_ERROR_NOT_AVAILABLE;
590 
591  nsCOMPtr<sbILocalDatabaseSmartMediaListCondition> condition;
592  nsCOMPtr<sbIPropertyOperator> propertyOperator;
593  nsString propertyID, leftValue, operatorStr;
594  for (PRUint32 i = 0; i < conditionLength; ++i) {
595  condition = mConditions[i];
596 
597  rv = condition->GetPropertyID(propertyID);
598  NS_ENSURE_SUCCESS(rv, rv);
599  // Deal with smart playlists with condition "mediatype".
600  if (propertyID.EqualsLiteral(SB_PROPERTY_CONTENTTYPE)) {
601  rv = condition->GetOperator(getter_AddRefs(propertyOperator));
602  NS_ENSURE_SUCCESS(rv, rv);
603  rv = propertyOperator->GetOperator(operatorStr);
604  NS_ENSURE_SUCCESS(rv, rv);
605  rv = condition->GetLeftValue(leftValue);
606  NS_ENSURE_SUCCESS(rv, rv);
607 
608  // XXX Alfred: Update the function whenever any content type other than
609  // audio or video is added in the future.
610 
611  // mediatype is video, or mediatype is not audio
612  if ((operatorStr.EqualsLiteral(SB_OPERATOR_EQUALS) &&
613  leftValue.EqualsLiteral("video")) ||
614  (operatorStr.EqualsLiteral(SB_OPERATOR_NOTEQUALS) &&
615  leftValue.EqualsLiteral("audio"))) {
616  *aContentType = sbIMediaList::CONTENTTYPE_VIDEO;
617  return NS_OK;
618  }
619  // mediatype is audio, or mediatype is not video
620  else if ((operatorStr.EqualsLiteral(SB_OPERATOR_EQUALS) &&
621  leftValue.EqualsLiteral("audio")) ||
622  (operatorStr.EqualsLiteral(SB_OPERATOR_NOTEQUALS) &&
623  leftValue.EqualsLiteral("video"))) {
624  *aContentType = sbIMediaList::CONTENTTYPE_AUDIO;
625  return NS_OK;
626  }
627  }
628  }
629 
630  mList->GetListContentType(aContentType);
631  return NS_OK;
632 }
633 
634 NS_IMETHODIMP
636 {
637  aType.Assign(NS_LITERAL_STRING("smart"));
638  return NS_OK;
639 }
640 
641 NS_IMETHODIMP
642 sbLocalDatabaseSmartMediaList::GetMatchType(PRUint32* aMatchType)
643 {
644  NS_ENSURE_ARG_POINTER(aMatchType);
645  nsAutoMonitor monitor(mConditionsMonitor);
646 
647  *aMatchType = mMatchType;
648 
649  return NS_OK;
650 }
651 NS_IMETHODIMP
652 sbLocalDatabaseSmartMediaList::SetMatchType(PRUint32 aMatchType)
653 {
654  NS_ENSURE_ARG_RANGE(aMatchType,
657 
658  nsAutoMonitor monitor(mConditionsMonitor);
659  mMatchType = aMatchType;
660 
661  nsresult rv = WriteConfiguration();
662  NS_ENSURE_SUCCESS(rv, rv);
663 
664  return NS_OK;
665 }
666 
667 NS_IMETHODIMP
668 sbLocalDatabaseSmartMediaList::GetConditionCount(PRUint32* aConditionCount)
669 {
670  NS_ENSURE_ARG_POINTER(aConditionCount);
671 
672  nsAutoMonitor monitor(mConditionsMonitor);
673  *aConditionCount = mConditions.Length();
674 
675  return NS_OK;
676 }
677 
678 NS_IMETHODIMP
679 sbLocalDatabaseSmartMediaList::GetLimitType(PRUint32* aLimitType)
680 {
681  NS_ENSURE_ARG_POINTER(aLimitType);
682  *aLimitType = mLimitType;
683 
684  return NS_OK;
685 }
686 NS_IMETHODIMP
687 sbLocalDatabaseSmartMediaList::SetLimitType(PRUint32 aLimitType)
688 {
689  NS_ENSURE_ARG_RANGE(aLimitType,
692 
693  nsAutoMonitor monitor(mConditionsMonitor);
694  mLimitType = aLimitType;
695 
696  nsresult rv = WriteConfiguration();
697  NS_ENSURE_SUCCESS(rv, rv);
698 
699  return NS_OK;
700 }
701 
702 NS_IMETHODIMP
703 sbLocalDatabaseSmartMediaList::GetLimit(PRUint64* aLimit)
704 {
705  NS_ENSURE_ARG_POINTER(aLimit);
706  *aLimit = mLimit;
707 
708  return NS_OK;
709 }
710 NS_IMETHODIMP
711 sbLocalDatabaseSmartMediaList::SetLimit(PRUint64 aLimit)
712 {
713  nsAutoMonitor monitor(mConditionsMonitor);
714  mLimit = aLimit;
715 
716  nsresult rv = WriteConfiguration();
717  NS_ENSURE_SUCCESS(rv, rv);
718 
719  return NS_OK;
720 }
721 
722 NS_IMETHODIMP
723 sbLocalDatabaseSmartMediaList::GetSelectPropertyID(nsAString& aSelectPropertyID)
724 {
725  aSelectPropertyID = mSelectPropertyID;
726 
727  return NS_OK;
728 }
729 NS_IMETHODIMP
730 sbLocalDatabaseSmartMediaList::SetSelectPropertyID(const nsAString& aSelectPropertyID)
731 {
732  nsAutoMonitor monitor(mConditionsMonitor);
733  mSelectPropertyID = aSelectPropertyID;
734 
735  nsresult rv = WriteConfiguration();
736  NS_ENSURE_SUCCESS(rv, rv);
737 
738  return NS_OK;
739 }
740 
741 NS_IMETHODIMP
742 sbLocalDatabaseSmartMediaList::GetSelectDirection(PRBool* aSelectDirection)
743 {
744  NS_ENSURE_ARG_POINTER(aSelectDirection);
745 
746  nsAutoMonitor monitor(mConditionsMonitor);
747  *aSelectDirection = mSelectDirection;
748 
749  return NS_OK;
750 }
751 NS_IMETHODIMP
752 sbLocalDatabaseSmartMediaList::SetSelectDirection(PRBool aSelectDirection)
753 {
754  nsAutoMonitor monitor(mConditionsMonitor);
755  mSelectDirection = aSelectDirection;
756 
757  nsresult rv = WriteConfiguration();
758  NS_ENSURE_SUCCESS(rv, rv);
759 
760  return NS_OK;
761 }
762 
763 NS_IMETHODIMP
764 sbLocalDatabaseSmartMediaList::GetRandomSelection(PRBool* aRandomSelection)
765 {
766  NS_ENSURE_ARG_POINTER(aRandomSelection);
767 
768  nsAutoMonitor monitor(mConditionsMonitor);
769  *aRandomSelection = mRandomSelection;
770 
771  return NS_OK;
772 }
773 NS_IMETHODIMP
774 sbLocalDatabaseSmartMediaList::SetRandomSelection(PRBool aRandomSelection)
775 {
776  nsAutoMonitor monitor(mConditionsMonitor);
777  mRandomSelection = aRandomSelection;
778 
779  nsresult rv = WriteConfiguration();
780  NS_ENSURE_SUCCESS(rv, rv);
781 
782  return NS_OK;
783 }
784 
785 NS_IMETHODIMP
786 sbLocalDatabaseSmartMediaList::GetAutoUpdate(PRBool* aAutoUpdate)
787 {
788  NS_ENSURE_ARG_POINTER(aAutoUpdate);
789 
790  nsAutoMonitor monitor(mAutoUpdateMonitor);
791  *aAutoUpdate = mAutoUpdate;
792 
793  return NS_OK;
794 }
795 NS_IMETHODIMP
796 sbLocalDatabaseSmartMediaList::SetAutoUpdate(PRBool aAutoUpdate)
797 {
798  nsAutoMonitor monitor(mAutoUpdateMonitor);
799  mAutoUpdate = aAutoUpdate;
800 
801  nsresult rv = WriteConfiguration();
802  NS_ENSURE_SUCCESS(rv, rv);
803 
804  return NS_OK;
805 }
806 
807 NS_IMETHODIMP
808 sbLocalDatabaseSmartMediaList::GetNotExistsMode(PRUint32* aNotExistsMode)
809 {
810  NS_ENSURE_ARG_POINTER(aNotExistsMode);
811 
812  nsAutoMonitor monitor(mConditionsMonitor);
813  *aNotExistsMode = mNotExistsMode;
814 
815  return NS_OK;
816 }
817 NS_IMETHODIMP
818 sbLocalDatabaseSmartMediaList::SetNotExistsMode(PRUint32 aNotExistsMode)
819 {
820  nsAutoMonitor monitor(mConditionsMonitor);
821  mNotExistsMode = aNotExistsMode;
822 
823  nsresult rv = WriteConfiguration();
824  NS_ENSURE_SUCCESS(rv, rv);
825 
826  return NS_OK;
827 }
828 
829 NS_IMETHODIMP
830 sbLocalDatabaseSmartMediaList::AppendCondition(const nsAString& aPropertyID,
831  sbIPropertyOperator* aOperator,
832  const nsAString& aLeftValue,
833  const nsAString& aRightValue,
834  const nsAString& aDisplayUnit,
836 {
837  NS_ENSURE_ARG_POINTER(aOperator);
838  NS_ENSURE_ARG_POINTER(_retval);
839  NS_ENSURE_ARG(aPropertyID.Length() > 1);
840 
841  // Make sure the right value is void if the operator is anything else but
842  // between, and that both are void if the operator is istrue, isfalse,
843  // isset, or isnotset
844  nsAutoString op;
845  nsresult rv = aOperator->GetOperator(op);
846  NS_ENSURE_SUCCESS(rv, rv);
847 
848  // XXXsteve IsEmpty should really be IsVoid
849  if (!op.EqualsLiteral(SB_OPERATOR_BETWEEN) &&
850  !op.EqualsLiteral(SB_OPERATOR_BETWEENDATES)) {
851  if (!aRightValue.IsEmpty())
852  return NS_ERROR_ILLEGAL_VALUE;
853  } else {
854  if (aRightValue.IsEmpty())
855  return NS_ERROR_ILLEGAL_VALUE;
856  }
857 
858  if ((op.EqualsLiteral(SB_OPERATOR_ISTRUE) ||
859  op.EqualsLiteral(SB_OPERATOR_ISFALSE) ||
860  op.EqualsLiteral(SB_OPERATOR_ISSET) ||
861  op.EqualsLiteral(SB_OPERATOR_ISNOTSET)) &&
862  !aLeftValue.IsEmpty()) {
863  return NS_ERROR_ILLEGAL_VALUE;
864  };
865 
866  sbRefPtrCondition condition;
867  condition = new sbLocalDatabaseSmartMediaListCondition(aPropertyID,
868  op,
869  aLeftValue,
870  aRightValue,
871  aDisplayUnit);
872  NS_ENSURE_TRUE(condition, NS_ERROR_OUT_OF_MEMORY);
873 
874  sbRefPtrCondition* success = mConditions.AppendElement(condition);
875  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
876 
877  rv = WriteConfiguration();
878  NS_ENSURE_SUCCESS(rv, rv);
879 
880  NS_ADDREF(*_retval = condition);
881 
882  return NS_OK;
883 }
884 
885 NS_IMETHODIMP
886 sbLocalDatabaseSmartMediaList::RemoveConditionAt(PRUint32 aConditionIndex)
887 {
888  nsAutoMonitor monitor(mConditionsMonitor);
889  PRUint32 count = mConditions.Length();
890 
891  NS_ENSURE_ARG(aConditionIndex < count);
892  mConditions.RemoveElementAt(aConditionIndex);
893 
894  nsresult rv = WriteConfiguration();
895  NS_ENSURE_SUCCESS(rv, rv);
896 
897  return NS_OK;
898 }
899 
900 NS_IMETHODIMP
901 sbLocalDatabaseSmartMediaList::GetConditionAt(PRUint32 aConditionIndex,
903 {
904  NS_ENSURE_ARG_POINTER(_retval);
905 
906  nsAutoMonitor monitor(mConditionsMonitor);
907  PRUint32 count = mConditions.Length();
908 
909  NS_ENSURE_ARG(aConditionIndex < count);
910 
911  *_retval = mConditions[aConditionIndex];
912  NS_ADDREF(*_retval);
913 
914  return NS_OK;
915 }
916 
917 NS_IMETHODIMP
918 sbLocalDatabaseSmartMediaList::ClearConditions()
919 {
920  nsAutoMonitor monitor(mConditionsMonitor);
921 
922  mConditions.Clear();
923 
924  nsresult rv = WriteConfiguration();
925  NS_ENSURE_SUCCESS(rv, rv);
926 
927  return NS_OK;
928 }
929 
930 NS_IMETHODIMP
931 sbLocalDatabaseSmartMediaList::Rebuild()
932 {
933  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - Rebuild()", this));
934 
935  nsresult rv;
936  {
937  nsAutoMonitor monitor(mConditionsMonitor);
938  nsAutoMonitor monitor2(mSourceMonitor);
939 
940  // You must either have a match or a limit
941  NS_ENSURE_ARG(!(mMatchType == sbILocalDatabaseSmartMediaList::MATCH_TYPE_NONE &&
943 
944  // If we have a limit, either random or the selection property must be set
946  if (!(mRandomSelection || !mSelectPropertyID.IsEmpty()))
947  return NS_ERROR_INVALID_ARG;
948  }
949 
951  if (mRandomSelection) {
952  rv = RebuildMatchTypeNoneRandom();
953  NS_ENSURE_SUCCESS(rv, rv);
954  }
955  else {
956  rv = RebuildMatchTypeNoneNotRandom();
957  NS_ENSURE_SUCCESS(rv, rv);
958  }
959  }
960  else {
961  rv = RebuildMatchTypeAnyAll();
962  NS_ENSURE_SUCCESS(rv, rv);
963  }
964  }
965  // Notify our inner list that its content changed
966  nsCOMPtr<sbILocalDatabaseSimpleMediaList> ldsml =
967  do_QueryInterface(mList, &rv);
968  NS_ENSURE_SUCCESS(rv, rv);
969 
970  rv = ldsml->NotifyContentChanged();
971  NS_ENSURE_SUCCESS(rv, rv);
972 
973  nsAutoMonitor monitor(mListenersMonitor);
974  for (PRInt32 i=0;i<mListeners.Count();i++)
975  mListeners.ObjectAt(i)->OnRebuild(this);
976 
977  return NS_OK;
978 }
979 
980 nsresult
981 sbLocalDatabaseSmartMediaList::RebuildMatchTypeAnyAll()
982 {
983  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - RebuildMatchTypeAnyAll()",
984  this));
985 
986  nsresult rv;
987 
988  // For an any/all smart media list, execute one query per condition and
989  // stick the result into a temp table. Then determine the row limit (if any)
990  // and copy the data into the data list
991  nsAutoString tempTableName;
992  rv = CreateTempTable(tempTableName);
993  NS_ENSURE_SUCCESS(rv, rv);
994 
995  // The contents of the smart media list will be inserted using a single
996  // insert statement with a compound select statement made up of select
997  // statements for each condition.
998 
999  // The sql builder does not know how to create compound select statements so
1000  // we'll do a little string appending here
1001  nsAutoString sql;
1002  sql.AssignLiteral("insert into ");
1003  sql.Append(tempTableName);
1004  sql.AppendLiteral(" (media_item_id, limitby, selectby) ");
1005 
1006  PRUint32 count = mConditions.Length();
1007  for (PRUint32 i = 0; i < count; i++) {
1008  nsAutoString conditionSql;
1009  rv = CreateSQLForCondition(mConditions[i], i == count-1, conditionSql);
1010  NS_ENSURE_SUCCESS(rv, rv);
1011 
1012  sql.Append(conditionSql);
1013  if (i + 1 < count) {
1014  // Join the select statements with "intersect" if we are doing an ALL
1015  // match, otherwise use "union"
1017  sql.AppendLiteral(" intersect ");
1018  }
1019  else {
1020  sql.AppendLiteral(" union ");
1021  }
1022  }
1023  }
1024 
1025  rv = ExecuteQuery(sql);
1026  NS_ENSURE_SUCCESS(rv, rv);
1027 
1028  // If this is a random selection query, set the selectby column to random
1029  // values
1030  if (mRandomSelection) {
1031  nsAutoString updateRandom;
1032  updateRandom.AppendLiteral("update ");
1033  updateRandom.Append(tempTableName);
1034  updateRandom.AppendLiteral(" set selectby = random()");
1035  rv = ExecuteQuery(updateRandom);
1036  NS_ENSURE_SUCCESS(rv, rv);
1037  }
1038 
1039  // Delete the contents of our inner simple media list
1040  rv = ExecuteQuery(mClearListQuery);
1041  NS_ENSURE_SUCCESS(rv, rv);
1042 
1043  nsAutoString copy;
1044  rv = GetCopyToListQuery(tempTableName, copy);
1045  NS_ENSURE_SUCCESS(rv, rv);
1046 
1047  // If this is a limit query, figure out the row limit and add it to the
1048  // copy query
1050 
1051  // If this is not an item limit, figure out what our row limit should be
1052  PRUint32 rowLimit;
1054  // rolling limit value is 64 bits (since it accommodates for tiny units
1055  // like bits), but rolling queries are limited to 2^32 rows of results.
1056  // Since this is a limit by number of items, we have to cast mLimit down
1057  // to 32 bits. Avoid a warning here by explicitly masking & casting.
1058  rowLimit = (PRUint32)(mLimit & 0xFFFFFFFF);
1059  }
1060  else {
1061  nsAutoString rollingSql;
1062  rollingSql.AssignLiteral("select limitby from ");
1063  rollingSql.Append(tempTableName);
1064  rollingSql.AppendLiteral(" order by selectby ");
1065  rollingSql.AppendLiteral(mSelectDirection ? "asc" : "desc");
1066  rv = GetRollingLimit(rollingSql, 0, &rowLimit);
1067  NS_ENSURE_SUCCESS(rv, rv);
1068  }
1069 
1070  // If the rolling limit was 0, that means we can select everything and
1071  // not go over the limit
1072  if (rowLimit > 0) {
1073  copy.AppendLiteral(" order by selectby ");
1074  copy.AppendLiteral(mSelectDirection ? "asc" : "desc");
1075  copy.AppendLiteral(" limit ");
1076  copy.AppendInt(rowLimit);
1077  }
1078 
1079  }
1080 
1081  rv = ExecuteQuery(copy);
1082  NS_ENSURE_SUCCESS(rv, rv);
1083 
1084  // Drop the temp table
1085  rv = DropTempTable(tempTableName);
1086  NS_ENSURE_SUCCESS(rv, rv);
1087 
1088  return NS_OK;
1089 }
1090 
1091 nsresult
1092 sbLocalDatabaseSmartMediaList::RebuildMatchTypeNoneNotRandom()
1093 {
1094  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - "
1095  "RebuildMatchTypeNoneNotRandom()", this));
1096 
1097  // Must have some kind of limit and be non-random and have a select property
1098  // to get here
1099  NS_ENSURE_STATE(mLimitType != sbLocalDatabaseSmartMediaList::LIMIT_TYPE_NONE);
1100  NS_ENSURE_STATE(!mRandomSelection);
1101  NS_ENSURE_STATE(!mSelectPropertyID.IsEmpty());
1102 
1103  NS_NAMED_LITERAL_STRING(kMediaItems, "media_items");
1104  NS_NAMED_LITERAL_STRING(kMediaItemId, "media_item_id");
1105  NS_NAMED_LITERAL_STRING(kMediaItemsAlias, "_mi");
1106  NS_NAMED_LITERAL_STRING(kLimitAlias, "_limit");
1107  NS_NAMED_LITERAL_STRING(kMediaListTypeId, "media_list_type_id");
1108 
1109  nsresult rv;
1110 
1111  // For match type none, the strategy is to do a rolling limit query on a
1112  // full library query (sorted on the select by attribute) to get the row
1113  // number to select up to. Then do a select directly into the data media
1114  // list
1115 
1116  PRUint32 limitRow;
1117 
1118  // If we don't have an item limit type, use a rolling query to find the
1119  // limit row
1121  nsCOMPtr<sbISQLSelectBuilder> builder =
1122  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
1123  NS_ENSURE_SUCCESS(rv, rv);
1124 
1125  rv = builder->SetBaseTableName(kMediaItems);
1126  NS_ENSURE_SUCCESS(rv, rv);
1127 
1128  rv = builder->SetBaseTableAlias(kMediaItemsAlias);
1129  NS_ENSURE_SUCCESS(rv, rv);
1130 
1131  rv = AddSelectColumnAndJoin(builder, kMediaItemsAlias, PR_TRUE);
1132  NS_ENSURE_SUCCESS(rv, rv);
1133 
1134  rv = AddLimitColumnAndJoin(builder, kMediaItemsAlias);
1135  NS_ENSURE_SUCCESS(rv, rv);
1136 
1137  // Only get media items
1138  nsCOMPtr<sbISQLBuilderCriterion> nullCriterion;
1139  rv = builder->CreateMatchCriterionNull(kMediaItemsAlias,
1140  kMediaListTypeId,
1142  getter_AddRefs(nullCriterion));
1143  NS_ENSURE_SUCCESS(rv, rv);
1144 
1145  rv = builder->AddCriterion(nullCriterion);
1146  NS_ENSURE_SUCCESS(rv, rv);
1147 
1148  nsAutoString sql;
1149  rv = builder->ToString(sql);
1150  NS_ENSURE_SUCCESS(rv, rv);
1151 
1152  rv = GetRollingLimit(sql, 1, &limitRow);
1153  NS_ENSURE_SUCCESS(rv, rv);
1154 
1155  }
1156  else {
1157  // Otherwise, the limit row is the limit
1158  // rolling limit value is 64 bits (since it accommodates for tiny units
1159  // like bits), but rolling queries are limited to 2^32 rows of results.
1160  // Since this is a limit by number of items, we have to cast mLimit down
1161  // to 32 bits. Avoid a warning here by explicitly masking & casting.
1162  limitRow = (PRUint32)(mLimit & 0xFFFFFFFF);
1163  }
1164 
1165  nsAutoString tempTableName;
1166  rv = CreateTempTable(tempTableName);
1167  NS_ENSURE_SUCCESS(rv, rv);
1168 
1169  // Select the media item ids from the library into the temp table. We do
1170  // this so the rows can get numbered
1171  nsCOMPtr<sbISQLSelectBuilder> builder =
1172  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
1173  NS_ENSURE_SUCCESS(rv, rv);
1174 
1175  rv = builder->SetBaseTableName(kMediaItems);
1176  NS_ENSURE_SUCCESS(rv, rv);
1177 
1178  rv = builder->SetBaseTableAlias(kMediaItemsAlias);
1179  NS_ENSURE_SUCCESS(rv, rv);
1180 
1181  rv = builder->AddColumn(kMediaItemsAlias, kMediaItemId);
1182  NS_ENSURE_SUCCESS(rv, rv);
1183 
1184  rv = builder->AddColumn(EmptyString(), NS_LITERAL_STRING("0"));
1185  NS_ENSURE_SUCCESS(rv, rv);
1186 
1187  // Only get media items
1188  nsCOMPtr<sbISQLBuilderCriterion> nullCriterion;
1189  rv = builder->CreateMatchCriterionNull(kMediaItemsAlias,
1190  kMediaListTypeId,
1192  getter_AddRefs(nullCriterion));
1193  NS_ENSURE_SUCCESS(rv, rv);
1194 
1195  rv = builder->AddCriterion(nullCriterion);
1196  NS_ENSURE_SUCCESS(rv, rv);
1197 
1198  rv = AddSelectColumnAndJoin(builder, kMediaItemsAlias, PR_TRUE);
1199  NS_ENSURE_SUCCESS(rv, rv);
1200 
1201  // If we found a row limit, set it here
1202  if (limitRow > 0) {
1203  rv = builder->SetLimit(limitRow);
1204  NS_ENSURE_SUCCESS(rv, rv);
1205  }
1206 
1207  nsAutoString sql;
1208  rv = builder->ToString(sql);
1209  NS_ENSURE_SUCCESS(rv, rv);
1210 
1211  nsAutoString insert;
1212  insert.AssignLiteral("insert into ");
1213  insert.Append(tempTableName);
1214  insert.AppendLiteral(" (media_item_id, limitby, selectby) ");
1215  insert.Append(sql);
1216 
1217  rv = ExecuteQuery(insert);
1218  NS_ENSURE_SUCCESS(rv, rv);
1219 
1220  // Finaly, clear the data simple media list and insert the result
1221  rv = ExecuteQuery(mClearListQuery);
1222  NS_ENSURE_SUCCESS(rv, rv);
1223 
1224  nsAutoString copy;
1225  rv = GetCopyToListQuery(tempTableName, copy);
1226  NS_ENSURE_SUCCESS(rv, rv);
1227 
1228  rv = ExecuteQuery(copy);
1229  NS_ENSURE_SUCCESS(rv, rv);
1230 
1231  // Drop the temp table
1232  rv = DropTempTable(tempTableName);
1233  NS_ENSURE_SUCCESS(rv, rv);
1234 
1235  return NS_OK;
1236 }
1237 
1238 nsresult
1239 sbLocalDatabaseSmartMediaList::RebuildMatchTypeNoneRandom()
1240 {
1241  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - "
1242  "RebuildMatchTypeNoneRandom()", this));
1243 
1244  // Must have some kind of limit and be random and have no select property
1245  // to get here
1246  NS_ENSURE_STATE(mLimitType != sbLocalDatabaseSmartMediaList::LIMIT_TYPE_NONE);
1247  NS_ENSURE_STATE(mRandomSelection);
1248  NS_ENSURE_STATE(mSelectPropertyID.IsEmpty());
1249 
1250  NS_NAMED_LITERAL_STRING(kMediaItemId, "media_item_id");
1251  NS_NAMED_LITERAL_STRING(kLimitBy, "limitby");
1252 
1253  nsresult rv;
1254 
1255  // This is the strangest situation of the bunch. We need to do a random
1256  // selection over the entire library and keep going until the limit condition
1257  // is met. To do this, we'll create an array representing all of the media
1258  // item ids in the database, then shuffle it. Next, we'll add chunks of
1259  // the shuffled media item ids to the temp table until we reach our limit
1260 
1261  // First, get the min and max media_item_ids from the database
1262  PRUint32 min;
1263  PRUint32 max;
1264  rv = GetMediaItemIdRange(&min, &max);
1265  if (NS_FAILED(rv)) {
1266  // If we get NS_ERROR_UNEXPECTED, this means the library is empty.
1267  // Nothing to do so just return
1268  if (rv == NS_ERROR_UNEXPECTED) {
1269  return NS_OK;
1270  }
1271  NS_ENSURE_SUCCESS(rv, rv);
1272  }
1273 
1274  // XXXsteve Perhaps for some small item limits we could just generate a
1275  // handful of non repeating ids rather than go through all of this shuffling
1276  // stuff. This might be useful if a common case is to make a smart playlist
1277  // with a small number of random items on it (perhaps to feed a party shuffle
1278  // type thing)
1279 
1280  // Create an array with max - min elements and fill it with the concecutive
1281  // numbers between the twe
1282  PRUint32 length = max - min + 1;
1283  sbMediaItemIdArray mediaItemIds(length);
1284 
1285  for (PRUint32 i = 0; i < length; i++) {
1286  PRUint32* success = mediaItemIds.AppendElement(min + i);
1287  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1288  }
1289 
1290  // Shuffle the items in the array
1291  ShuffleArray(mediaItemIds);
1292 
1293  // Make our temp table
1294  nsAutoString tempTableName;
1295  rv = CreateTempTable(tempTableName);
1296  NS_ENSURE_SUCCESS(rv, rv);
1297 
1298  // Add the media items into the temp table in batches of the shuffled order.
1299  // After each add, check the rolling sum to see if we've reached paydirt
1300  // Note that we can't optmize this in the case of the item limit since we
1301  // don't really now for sure how many rows we will actually add in each
1302  // batch since the real media item ids in the database may be non concecutive
1303 
1304  // Prepare our rolling sum query
1305  nsCOMPtr<sbISQLSelectBuilder> builder =
1306  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
1307  NS_ENSURE_SUCCESS(rv, rv);
1308 
1309  rv = builder->AddColumn(EmptyString(), kLimitBy);
1310  NS_ENSURE_SUCCESS(rv, rv);
1311 
1312  rv = builder->SetBaseTableName(tempTableName);
1313  NS_ENSURE_SUCCESS(rv, rv);
1314 
1315  nsAutoString rollingQuery;
1316  rv = builder->ToString(rollingQuery);
1317  NS_ENSURE_SUCCESS(rv, rv);
1318 
1319  // XXXsteve There is probably an optmization here were we could get the
1320  // rolling sum of only the records that were just added. However, this is
1321  // hard to do with the current database interface because we can't give
1322  // it a value to start from when counting the rolling sum.
1323 
1324  PRUint32 rowLimit = 0;
1325  PRUint32 pos = 0;
1326 
1327  while (pos < length) {
1328  PRUint32 chunkSize = RANDOM_ADD_CHUNK_SIZE;
1329  if (pos + chunkSize > length) {
1330  chunkSize = length - pos;
1331  }
1332 
1333  rv = AddMediaItemsTempTable(tempTableName, mediaItemIds, pos, chunkSize);
1334  NS_ENSURE_SUCCESS(rv, rv);
1335 
1337  PRUint32 rowCount;
1338  rv = GetRowCount(tempTableName, &rowCount);
1339  NS_ENSURE_SUCCESS(rv, rv);
1340 
1341  if (rowCount >= mLimit) {
1342  // rolling limit value is 64 bits (since it accommodates for tiny units
1343  // like bits), but rolling queries are limited to 2^32 rows of results.
1344  // Since this is a limit by number of items, we have to cast mLimit down
1345  // to 32 bits. Avoid a warning here by explicitly masking & casting.
1346  rowLimit = (PRUint32)(mLimit & 0xFFFFFFFF);
1347  break;
1348  }
1349  }
1350  else {
1351  PRUint32 rollingRow;
1352  rv = GetRollingLimit(rollingQuery, 0, &rollingRow);
1353  NS_ENSURE_SUCCESS(rv, rv);
1354 
1355  if (rollingRow > 0) {
1356  rowLimit = rollingRow;
1357  break;
1358  }
1359  }
1360  pos += chunkSize;
1361  }
1362 
1363  // Finally clear out the old list and copy the items out of the temp table to
1364  // the data simple media list
1365  rv = ExecuteQuery(mClearListQuery);
1366  NS_ENSURE_SUCCESS(rv, rv);
1367 
1368  nsAutoString copy;
1369  rv = GetCopyToListQuery(tempTableName, copy);
1370  NS_ENSURE_SUCCESS(rv, rv);
1371 
1372  // If we found a limit, apply it here. Otherwise we just copy everything.
1373  if (rowLimit > 0) {
1374  copy.AppendLiteral(" limit ");
1375  copy.AppendInt(rowLimit);
1376  }
1377 
1378  rv = ExecuteQuery(copy);
1379  NS_ENSURE_SUCCESS(rv, rv);
1380 
1381  // Drop the temp table
1382  rv = DropTempTable(tempTableName);
1383  NS_ENSURE_SUCCESS(rv, rv);
1384 
1385  return NS_OK;
1386 }
1387 
1388 nsresult
1389 sbLocalDatabaseSmartMediaList::AddMediaItemsTempTable(const nsAutoString& tempTableName,
1390  sbMediaItemIdArray& aArray,
1391  PRUint32 aStart,
1392  PRUint32 aLength)
1393 {
1394  NS_NAMED_LITERAL_STRING(kMediaItems, "media_items");
1395  NS_NAMED_LITERAL_STRING(kMediaItemId, "media_item_id");
1396  NS_NAMED_LITERAL_STRING(kMediaItemsAlias, "_mi");
1397  NS_NAMED_LITERAL_STRING(kMediaListTypeId, "media_list_type_id");
1398 
1399  nsresult rv;
1400 
1401  nsCOMPtr<sbISQLSelectBuilder> builder =
1402  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
1403  NS_ENSURE_SUCCESS(rv, rv);
1404 
1405  rv = builder->SetBaseTableName(kMediaItems);
1406  NS_ENSURE_SUCCESS(rv, rv);
1407 
1408  rv = builder->SetBaseTableAlias(kMediaItemsAlias);
1409  NS_ENSURE_SUCCESS(rv, rv);
1410 
1411  rv = builder->AddColumn(kMediaItemsAlias, kMediaItemId);
1412  NS_ENSURE_SUCCESS(rv, rv);
1413 
1414  rv = AddLimitColumnAndJoin(builder, kMediaItemsAlias);
1415  NS_ENSURE_SUCCESS(rv, rv);
1416 
1417  rv = builder->AddColumn(EmptyString(), NS_LITERAL_STRING("''"));
1418  NS_ENSURE_SUCCESS(rv, rv);
1419 
1420  // Only get media items
1421  nsCOMPtr<sbISQLBuilderCriterion> nullCriterion;
1422  rv = builder->CreateMatchCriterionNull(kMediaItemsAlias,
1423  kMediaListTypeId,
1425  getter_AddRefs(nullCriterion));
1426  NS_ENSURE_SUCCESS(rv, rv);
1427 
1428  rv = builder->AddCriterion(nullCriterion);
1429  NS_ENSURE_SUCCESS(rv, rv);
1430 
1431  nsCOMPtr<sbISQLBuilderCriterionIn> criterion;
1432  rv = builder->CreateMatchCriterionIn(kMediaItemsAlias,
1433  kMediaItemId,
1434  getter_AddRefs(criterion));
1435  NS_ENSURE_SUCCESS(rv, rv);
1436 
1437  rv = builder->AddCriterion(criterion);
1438  NS_ENSURE_SUCCESS(rv, rv);
1439 
1440  nsAutoString insertStart;
1441  insertStart.AssignLiteral("insert into ");
1442  insertStart.Append(tempTableName);
1443  insertStart.AppendLiteral(" (media_item_id, limitby, selectby) ");
1444 
1445  PRUint32 inNum = 0;
1446  for (PRUint32 i = 0; i < aLength; i++) {
1447 
1448  rv = criterion->AddLong(aArray[aStart + i]);
1449  NS_ENSURE_SUCCESS(rv, rv);
1450  inNum++;
1451 
1452  if (inNum > SQL_IN_LIMIT || i + 1 == aLength) {
1453 
1454  // we need the results in random order, otherwise a limit will always
1455  // pick items from the top
1456  rv = builder->AddRandomOrder();
1457  NS_ENSURE_SUCCESS(rv, rv);
1458 
1459  nsAutoString sql;
1460  rv = builder->ToString(sql);
1461  NS_ENSURE_SUCCESS(rv, rv);
1462 
1463  nsAutoString insert(insertStart);
1464  insert.Append(sql);
1465 
1466  rv = ExecuteQuery(insert);
1467  NS_ENSURE_SUCCESS(rv, rv);
1468 
1469  inNum = 0;
1470  rv = criterion->Clear();
1471  NS_ENSURE_SUCCESS(rv, rv);
1472  }
1473  }
1474 
1475  return NS_OK;
1476 }
1477 
1478 nsresult
1479 sbLocalDatabaseSmartMediaList::GetRollingLimit(const nsAString& aSql,
1480  PRUint32 aRollingLimitColumnIndex,
1481  PRUint32* aRow)
1482 {
1483  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - GetRollingLimit()", this));
1484 
1485  nsCOMPtr<sbIDatabaseQuery> query;
1486  nsresult rv = mLocalDatabaseLibrary->CreateQuery(getter_AddRefs(query));
1487  NS_ENSURE_SUCCESS(rv, rv);
1488 
1489  rv = query->SetRollingLimit(mLimit);
1490  NS_ENSURE_SUCCESS(rv, rv);
1491 
1492  rv = query->SetRollingLimitColumnIndex(aRollingLimitColumnIndex);
1493  NS_ENSURE_SUCCESS(rv, rv);
1494 
1495  rv = query->AddQuery(aSql);
1496  NS_ENSURE_SUCCESS(rv, rv);
1497 
1498  PRInt32 dbOk;
1499  rv = query->Execute(&dbOk);
1500  NS_ENSURE_SUCCESS(rv, rv);
1501  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1502 
1503  rv = query->GetRollingLimitResult(aRow);
1504  NS_ENSURE_SUCCESS(rv, rv);
1505 
1506  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - GetRollingLimit() - %s = %d",
1507  this, NS_ConvertUTF16toUTF8(aSql).get(), *aRow));
1508 
1509  return NS_OK;
1510 }
1511 #include <stdio.h>
1512 nsresult
1513 sbLocalDatabaseSmartMediaList::CreateSQLForCondition(sbRefPtrCondition& aCondition,
1514  PRBool aAddOrderBy,
1515  nsAString& _retval)
1516 {
1517  nsresult rv;
1518 
1519  NS_NAMED_LITERAL_STRING(kConditionAlias, "_c");
1520  NS_NAMED_LITERAL_STRING(kMediaItemId, "media_item_id");
1521  NS_NAMED_LITERAL_STRING(kMemberMediaItemId, "member_media_item_id");
1522  NS_NAMED_LITERAL_STRING(kMediaItems, "media_items");
1523  NS_NAMED_LITERAL_STRING(kMediaLists, "simple_media_lists");
1524  NS_NAMED_LITERAL_STRING(kMediaItemsAlias, "_mi");
1525  NS_NAMED_LITERAL_STRING(kMediaListTypeId, "media_list_type_id");
1526  NS_NAMED_LITERAL_STRING(kPropertyId, "property_id");
1527  NS_NAMED_LITERAL_STRING(kResourceProperties, "resource_properties");
1528 
1529  // Get the property info for this property. If the property info is not
1530  // found, return not available
1531  nsCOMPtr<sbIPropertyInfo> info;
1532  rv = mPropMan->GetPropertyInfo(aCondition->mPropertyID,
1533  getter_AddRefs(info));
1534  if (NS_FAILED(rv)) {
1535  if (rv == NS_ERROR_NOT_AVAILABLE) {
1536  return NS_ERROR_NOT_AVAILABLE;
1537  }
1538  else {
1539  NS_ENSURE_SUCCESS(rv, rv);
1540  }
1541  }
1542 
1543  nsCOMPtr<sbISQLSelectBuilder> builder =
1544  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
1545  NS_ENSURE_SUCCESS(rv, rv);
1546 
1547  PRBool isTopLevelProperty = SB_IsTopLevelProperty(aCondition->mPropertyID);
1548 
1549  rv = builder->SetBaseTableName(kMediaItems);
1550  NS_ENSURE_SUCCESS(rv, rv);
1551 
1552  nsAutoString mediaColumn;
1553  nsAutoString joinTable;
1554  PRBool bIsMediaListMatch;
1555 
1556  bIsMediaListMatch = aCondition->mPropertyID.EqualsLiteral(SB_DUMMYPROPERTY_SMARTMEDIALIST_PLAYLIST);
1557 
1558  // use a different refptr so we can reassign it without modifying the actual
1559  // condition
1560  sbRefPtrCondition ruleCondition = aCondition;
1561 
1562  if (bIsMediaListMatch) {
1563  if (ruleCondition->mLeftValue.IsEmpty()) {
1564  // This condition is of the form "Playlist Is|IsNot Library". This is a
1565  // very special case, because although we could tweak the query to match
1566  // any track that is in no playlist at all (for "Is") or in at least one
1567  // playlist (for "IsNot"), that is not actually what we want because
1568  // every other update would add the tracks to the result list, and each
1569  // subsequent one would empty the list. What we want is "Is Library" to
1570  // match everything, and "IsNot Library" to match nothing at all. Yes it
1571  // means that both rules are useless, but they're only valid so that the
1572  // default state for a new playlist rule is a valid one (see bug 12046).
1573  // To this end, we'll construct a dummy rule here, that either matches
1574  // everything or nothing depending on the operator used, it will be of
1575  // the form "guid Is|IsNot ''".
1576 
1577  nsCOMPtr<sbIPropertyOperator> opObj;
1578  rv = ruleCondition->GetOperator(getter_AddRefs(opObj));
1579  NS_ENSURE_SUCCESS(rv, rv);
1580 
1581  nsAutoString op;
1582  rv = opObj->GetOperator(op);
1583  NS_ENSURE_SUCCESS(rv, rv);
1584 
1585  nsAutoString newOp;
1586  if (op.EqualsLiteral(SB_OPERATOR_EQUALS))
1587  newOp = NS_LITERAL_STRING(SB_OPERATOR_ISSET);
1588  else
1589  newOp = NS_LITERAL_STRING(SB_OPERATOR_ISNOTSET);
1590 
1591  ruleCondition = new
1593  newOp,
1594  NS_LITERAL_STRING(""),
1595  NS_LITERAL_STRING(""),
1596  NS_LITERAL_STRING(""));
1597  bIsMediaListMatch = PR_FALSE;
1598  isTopLevelProperty = PR_TRUE;
1599  } else {
1600  // Otherwise, use the playlist table to join
1601  mediaColumn = kMemberMediaItemId;
1602  joinTable = kMediaLists;
1603  }
1604  } else {
1605  mediaColumn = kMediaItemId;
1606  joinTable = kResourceProperties;
1607  }
1608 
1609  nsAutoString baseAlias;
1610 
1611  // Add condition query
1612  if (isTopLevelProperty) {
1613  baseAlias.Assign(kConditionAlias);
1614  rv = builder->SetBaseTableAlias(baseAlias);
1615  NS_ENSURE_SUCCESS(rv, rv);
1616 
1617  rv = builder->AddColumn(baseAlias, kMediaItemId);
1618  NS_ENSURE_SUCCESS(rv, rv);
1619 
1620  rv = AddCriterionForCondition(builder, ruleCondition, info);
1621  NS_ENSURE_SUCCESS(rv, rv);
1622 
1623  // Only show media items
1624  nsCOMPtr<sbISQLBuilderCriterion> criterion;
1625  rv = builder->CreateMatchCriterionNull(baseAlias,
1626  kMediaListTypeId,
1628  getter_AddRefs(criterion));
1629  NS_ENSURE_SUCCESS(rv, rv);
1630 
1631  rv = builder->AddCriterion(criterion);
1632  NS_ENSURE_SUCCESS(rv, rv);
1633  }
1634  else {
1635  baseAlias.Assign(kMediaItemsAlias);
1636  rv = builder->SetBaseTableAlias(baseAlias);
1637  NS_ENSURE_SUCCESS(rv, rv);
1638 
1639  builder->SetDistinct(PR_TRUE);
1640 
1641  rv = builder->AddColumn(baseAlias, kMediaItemId);
1642  NS_ENSURE_SUCCESS(rv, rv);
1643 
1644  // Create a left outer join on two condition: media_item_id matching
1645  // the left table, and property_id matching the rule property id, so that
1646  // non-existing properties end up in the result set as null values
1647 
1648  // media_item_id match with left table media_item_id
1649  nsCOMPtr<sbISQLBuilderCriterion> criterion_media_item;
1650  rv = builder->CreateMatchCriterionTable(baseAlias,
1651  kMediaItemId,
1653  kConditionAlias,
1654  mediaColumn,
1655  getter_AddRefs(criterion_media_item));
1656  NS_ENSURE_SUCCESS(rv, rv);
1657 
1658  PRUint32 longVal;
1659  nsAutoString colId;
1660  if (bIsMediaListMatch) {
1661  rv = MediaListGuidToDB(ruleCondition->mLeftValue, longVal);
1662  NS_ENSURE_SUCCESS(rv, rv);
1663 
1664  if (longVal == (PRUint32)-1)
1665  return NS_ERROR_UNEXPECTED;
1666 
1667  colId = kMediaItemId;
1668  } else {
1669  // get property db id for rule property
1670  rv = mPropertyCache->GetPropertyDBID(ruleCondition->mPropertyID, &longVal);
1671  NS_ENSURE_SUCCESS(rv, rv);
1672 
1673  colId = kPropertyId;
1674  }
1675 
1676  nsCOMPtr<sbISQLBuilderCriterion> join_criterion;
1677 
1678  // property_id match with property db id
1679  nsCOMPtr<sbISQLBuilderCriterion> criterion_property_id;
1680  rv = builder->CreateMatchCriterionLong(kConditionAlias,
1681  colId,
1683  longVal,
1684  getter_AddRefs(criterion_property_id));
1685  NS_ENSURE_SUCCESS(rv, rv);
1686 
1687  // combine the two conditions with AND
1688  rv = builder->CreateAndCriterion(criterion_media_item,
1689  criterion_property_id,
1690  getter_AddRefs(join_criterion));
1691  NS_ENSURE_SUCCESS(rv, rv);
1692 
1693  // create a left outer join with these conditions, so we get null values
1694  // for items that do not have the property we're looking for.
1695  rv = builder->AddJoinWithCriterion(sbISQLBuilder::JOIN_LEFT_OUTER,
1696  joinTable,
1697  kConditionAlias,
1698  join_criterion);
1699  NS_ENSURE_SUCCESS(rv, rv);
1700 
1701  // add conditions for this rule
1702  rv = AddCriterionForCondition(builder, ruleCondition, info);
1703  NS_ENSURE_SUCCESS(rv, rv);
1704 
1705  // Only show media items
1706  nsCOMPtr<sbISQLBuilderCriterion> criterion;
1707  rv = builder->CreateMatchCriterionNull(kMediaItemsAlias,
1708  kMediaListTypeId,
1710  getter_AddRefs(criterion));
1711  NS_ENSURE_SUCCESS(rv, rv);
1712 
1713  rv = builder->AddCriterion(criterion);
1714  NS_ENSURE_SUCCESS(rv, rv);
1715  }
1716 
1717  // Add the value for the limit
1718  rv = AddLimitColumnAndJoin(builder, baseAlias);
1719  NS_ENSURE_SUCCESS(rv, rv);
1720 
1721  // If there is a limit on a select property id, pull it into the result of
1722  // the query, and sort the results by that property
1724  !mSelectPropertyID.IsEmpty()) {
1725  // only add the sort to the last condition, because it is invalid (and
1726  // would be quite unnecessary anyway) to sort before an intersect/union
1727  rv = AddSelectColumnAndJoin(builder, baseAlias, aAddOrderBy);
1728  NS_ENSURE_SUCCESS(rv, rv);
1729  }
1730  else {
1731  // Just add a blank placeholder column
1732  rv = builder->AddColumn(EmptyString(), NS_LITERAL_STRING("''"));
1733  NS_ENSURE_SUCCESS(rv, rv);
1734  }
1735 
1736  rv = builder->ToString(_retval);
1737  NS_ENSURE_SUCCESS(rv, rv);
1738 
1739  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - CreateSQLForCondition() - %s",
1740  this, NS_LossyConvertUTF16toASCII(Substring(_retval, 0, 400)).get()));
1741  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - CreateSQLForCondition() - %s",
1742  this, NS_LossyConvertUTF16toASCII(Substring(_retval, 400, 800)).get()));
1743 
1744  return NS_OK;
1745 }
1746 
1747 nsresult
1748 sbLocalDatabaseSmartMediaList::ScanfInt64(nsAString &aString, PRInt64 *aRetVal) {
1749  PRTime value = 0;
1750  NS_ConvertUTF16toUTF8 narrow(aString);
1751 
1752  if (PR_sscanf(narrow.get(), gsFmtRadix10, &value) != 1) {
1753  return NS_ERROR_INVALID_ARG;
1754  }
1755  *aRetVal = value;
1756  return NS_OK;
1757 }
1758 
1759 PRInt64
1760 sbLocalDatabaseSmartMediaList::ScanfInt64d(nsAString &aString) {
1761  PRTime value = 0;
1762  NS_ConvertUTF16toUTF8 narrow(aString);
1763 
1764  if (PR_sscanf(narrow.get(), gsFmtRadix10, &value) != 1) {
1765  return 0;
1766  }
1767  return value;
1768 }
1769 
1770 void
1771 sbLocalDatabaseSmartMediaList::SPrintfInt64(nsAString &aString,
1772  PRInt64 aValue) {
1773  char out[32] = {0};
1774  if (PR_snprintf(out, 32, gsFmtRadix10, aValue) == (PRUint32)-1) {
1775  aString = NS_LITERAL_STRING("0");
1776  }
1777  NS_ConvertUTF8toUTF16 wide(out);
1778  aString = wide;
1779 }
1780 
1781 PRInt64
1782 sbLocalDatabaseSmartMediaList::StripTime(PRInt64 aDateTime) {
1783  PRExplodedTime explodedTime = {0};
1784  PR_ExplodeTime(aDateTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &explodedTime);
1785  explodedTime.tm_usec = 0;
1786  explodedTime.tm_sec = 0;
1787  explodedTime.tm_min = 0;
1788  explodedTime.tm_hour = 0;
1789  return PR_ImplodeTime(&explodedTime) / PR_USEC_PER_MSEC;
1790 }
1791 
1792 nsresult
1793 sbLocalDatabaseSmartMediaList::AddCriterionForCondition(sbISQLSelectBuilder* aBuilder,
1794  sbRefPtrCondition& aCondition,
1795  sbIPropertyInfo* aInfo)
1796 {
1797  NS_ENSURE_ARG_POINTER(aBuilder);
1798  NS_ENSURE_ARG_POINTER(aInfo);
1799 
1800  NS_NAMED_LITERAL_STRING(kConditionAlias, "_c");
1801  NS_NAMED_LITERAL_STRING(kObjSearchable, "obj_searchable");
1802  NS_NAMED_LITERAL_STRING(kMediaItem, "media_item_id");
1803 
1804  nsresult rv;
1805 
1806  // Get some stuff about the property
1807  PRBool isTopLevelProperty = SB_IsTopLevelProperty(aCondition->mPropertyID);
1808  nsAutoString columnName;
1809 
1810  PRBool bNeedOrIsNull;
1811  rv = GetConditionNeedsNull(aCondition, aInfo, bNeedOrIsNull);
1812  NS_ENSURE_SUCCESS(rv, rv);
1813 
1814  if (isTopLevelProperty) {
1815  rv = SB_GetTopLevelPropertyColumn(aCondition->mPropertyID, columnName);
1816  NS_ENSURE_SUCCESS(rv, rv);
1817  }
1818  else {
1819  columnName.Assign(kObjSearchable);
1820  }
1821 
1822  nsCOMPtr<sbIPropertyOperator> opObj;
1823  rv = aCondition->GetOperator(getter_AddRefs(opObj));
1824  NS_ENSURE_SUCCESS(rv, rv);
1825 
1826  nsAutoString op;
1827  rv = opObj->GetOperator(op);
1828  NS_ENSURE_SUCCESS(rv, rv);
1829 
1830  nsAutoString value;
1831  nsAutoString leftValue;
1832  nsAutoString rightValue;
1833 
1834  if (op.EqualsLiteral(SB_OPERATOR_ISTRUE) ||
1835  op.EqualsLiteral(SB_OPERATOR_ISFALSE)) {
1836  leftValue = NS_LITERAL_STRING("1");
1837  } else if (op.EqualsLiteral(SB_OPERATOR_ISSET) ||
1838  op.EqualsLiteral(SB_OPERATOR_ISNOTSET)) {
1839  leftValue = NS_LITERAL_STRING("");
1840  } else if (op.EqualsLiteral(SB_OPERATOR_INTHELAST) ||
1841  op.EqualsLiteral(SB_OPERATOR_NOTINTHELAST)) {
1842 
1843  PRTime timeValue = 0;
1844  NS_ConvertUTF16toUTF8 narrow(aCondition->mLeftValue);
1845 
1846  if(PR_sscanf(narrow.get(), gsFmtRadix10, &timeValue) != 1) {
1847  return NS_ERROR_INVALID_ARG;
1848  }
1849 
1850  char out[32] = {0};
1851 
1852  PRTime now = PR_Now()/PR_USEC_PER_MSEC;
1853  PRTime when = now - timeValue;
1854  if(PR_snprintf(out, 32, gsFmtRadix10, when) == (PRUint32)-1) {
1855  return NS_ERROR_FAILURE;
1856  }
1857  NS_ConvertUTF8toUTF16 wide(out);
1858  leftValue = wide;
1859  } else {
1860  leftValue = aCondition->mLeftValue;
1861  }
1862 
1863  PRBool bNumericCondition = PR_FALSE;
1864  if (aCondition->mPropertyID.EqualsLiteral(SB_DUMMYPROPERTY_SMARTMEDIALIST_PLAYLIST)) {
1865  columnName.Assign(kMediaItem);
1866 
1867  PRUint32 id;
1868  rv = MediaListGuidToDB(leftValue, id);
1869  NS_ENSURE_SUCCESS(rv, rv);
1870 
1871  leftValue.Truncate();
1872  leftValue.AppendInt(id);
1873 
1874  bNumericCondition = PR_TRUE;
1875  }
1876 
1877  PRBool invertRange = PR_FALSE;
1878 
1879  rightValue = aCondition->mRightValue;
1880 
1881  if (op.EqualsLiteral(SB_OPERATOR_BETWEENDATES)) {
1882  // if we are matching date only (and not time), both values for the range
1883  // needs to be parsed, and stripped of time, then the right value should be
1884  // added 23:59:59.9999.
1885  SPrintfInt64(leftValue, StripTime(ScanfInt64d(leftValue)));
1886  SPrintfInt64(rightValue, StripTime(ScanfInt64d(rightValue))+(ONEDAY-ONE_MS));
1887  op = NS_LITERAL_STRING(SB_OPERATOR_BETWEEN);
1888  } else if (op.EqualsLiteral(SB_OPERATOR_ONDATE)) {
1889  // If we are matching date only (and not time), the date must be stripped of
1890  // its time component, then we need to turn the condition into a range
1891  // of [date,date+23:59:59.9999]
1892  SPrintfInt64(leftValue, StripTime(ScanfInt64d(leftValue)));
1893  SPrintfInt64(rightValue, StripTime(ScanfInt64d(leftValue))+(ONEDAY-ONE_MS));
1894  op = NS_LITERAL_STRING(SB_OPERATOR_BETWEEN);
1895  } else if (op.EqualsLiteral(SB_OPERATOR_NOTONDATE)) {
1896  // If we are matching date only (and not time), the date must be stripped of
1897  // its time component, then we need to turn the condition into an inverted
1898  // range of [date, date+23:59:59.9999]
1899  SPrintfInt64(leftValue, StripTime(ScanfInt64d(leftValue)));
1900  SPrintfInt64(rightValue, StripTime(ScanfInt64d(leftValue))+(ONEDAY-ONE_MS));
1901  op = NS_LITERAL_STRING(SB_OPERATOR_BETWEEN);
1902  invertRange = PR_TRUE;
1903  } else if (op.EqualsLiteral(SB_OPERATOR_BEFOREDATE)) {
1904  // If we are matching date only (and not time), the date must be stripped of
1905  // its time component.
1906  SPrintfInt64(leftValue, StripTime(ScanfInt64d(leftValue)));
1907  op = NS_LITERAL_STRING(SB_OPERATOR_LESS);
1908  } else if (op.EqualsLiteral(SB_OPERATOR_BEFOREORONDATE)) {
1909  // If we are matching date only (and not time), the date must be stripped of
1910  // its time component, and then added 23:59:59.9999.
1911  SPrintfInt64(leftValue, StripTime(ScanfInt64d(leftValue))+(ONEDAY-ONE_MS));
1912  op = NS_LITERAL_STRING(SB_OPERATOR_LESSEQUAL);
1913  } else if (op.EqualsLiteral(SB_OPERATOR_AFTERDATE)) {
1914  // If we are matching date only (and not time), the date must be stripped of
1915  // its time component, and then added 23:59:59.9999.
1916  SPrintfInt64(leftValue, StripTime(ScanfInt64d(leftValue))+(ONEDAY-ONE_MS));
1917  op = NS_LITERAL_STRING(SB_OPERATOR_GREATER);
1918  } else if (op.EqualsLiteral(SB_OPERATOR_AFTERORONDATE)) {
1919  // If we are matching date only (and not time), the date must be stripped of
1920  // its time component.
1921  SPrintfInt64(leftValue, StripTime(ScanfInt64d(leftValue)));
1922  op = NS_LITERAL_STRING(SB_OPERATOR_GREATEREQUAL);
1923  }
1924 
1925  if (!leftValue.IsEmpty()) {
1926  rv = aInfo->MakeSearchable(leftValue, value);
1927  // MakeSearchable may fail if the value fails to validate, but since a smart
1928  // playlist may look for substrings instead of full valid values, it is not
1929  // fatal to fail to make searchable, when that fails we just use the value
1930  // that we were given as is and escape it properly.
1931  if (NS_FAILED(rv)) {
1932  nsCOMPtr<nsINetUtil> netUtil =
1933  do_CreateInstance("@mozilla.org/network/util;1", &rv);
1934  NS_ENSURE_SUCCESS(rv, rv);
1935 
1936  nsCAutoString spec;
1937  rv = netUtil->EscapeString(NS_ConvertUTF16toUTF8(leftValue),
1938  nsINetUtil::ESCAPE_URL_PATH,
1939  spec);
1940  NS_ENSURE_SUCCESS(rv, rv);
1941 
1942  value = NS_ConvertUTF8toUTF16(spec);
1943  }
1944  }
1945 
1946  // If this is a between operator, construct two conditions for it
1947  if (op.EqualsLiteral(SB_OPERATOR_BETWEEN)) {
1948  nsCOMPtr<sbISQLBuilderCriterion> left;
1949  PRUint32 matchType;
1950  if (invertRange)
1951  matchType = sbISQLBuilder::MATCH_LESS;
1952  else
1954  rv = aBuilder->CreateMatchCriterionString(kConditionAlias,
1955  columnName,
1956  matchType,
1957  value,
1958  getter_AddRefs(left));
1959  NS_ENSURE_SUCCESS(rv, rv);
1960 
1961  nsAutoString rvalue;
1962  rv = aInfo->MakeSearchable(rightValue, rvalue);
1963  // MakeSearchable may fail if the value fails to validate, but since a smart
1964  // playlist may look for substrings instead of full valid values, it is not
1965  // fatal to fail to make searchable, when that fails we just use the value
1966  // that we were given as is.
1967  if (NS_FAILED(rv)) {
1968  rvalue = rightValue;
1969  }
1970 
1971  nsCOMPtr<sbISQLBuilderCriterion> right;
1972  if (invertRange)
1973  matchType = sbISQLBuilder::MATCH_GREATER;
1974  else
1975  matchType = sbISQLBuilder::MATCH_LESSEQUAL;
1976  rv = aBuilder->CreateMatchCriterionString(kConditionAlias,
1977  columnName,
1978  matchType,
1979  rvalue,
1980  getter_AddRefs(right));
1981  NS_ENSURE_SUCCESS(rv, rv);
1982 
1983  nsCOMPtr<sbISQLBuilderCriterion> criterion;
1984  if (invertRange)
1985  rv = aBuilder->CreateOrCriterion(left, right, getter_AddRefs(criterion));
1986  else
1987  rv = aBuilder->CreateAndCriterion(left, right, getter_AddRefs(criterion));
1988  NS_ENSURE_SUCCESS(rv, rv);
1989 
1990  if (bNeedOrIsNull) {
1991  nsCOMPtr<sbISQLBuilderCriterion> orIsNull;
1992  rv = aBuilder->CreateMatchCriterionNull(kConditionAlias,
1993  columnName,
1995  getter_AddRefs(orIsNull));
1996  NS_ENSURE_SUCCESS(rv, rv);
1997 
1998  nsCOMPtr<sbISQLBuilderCriterion> criterionOrIsNull;
1999  rv = aBuilder->CreateOrCriterion(criterion,
2000  orIsNull,
2001  getter_AddRefs(criterionOrIsNull));
2002  NS_ENSURE_SUCCESS(rv, rv);
2003  criterion = criterionOrIsNull;
2004  }
2005 
2006  rv = aBuilder->AddCriterion(criterion);
2007  NS_ENSURE_SUCCESS(rv, rv);
2008 
2009  return NS_OK;
2010  }
2011 
2012  // Convert the property operator to a sql builder match condition
2013  PRInt32 matchType = -1;
2014  if (op.EqualsLiteral(SB_OPERATOR_EQUALS)) {
2015  matchType = sbISQLBuilder::MATCH_EQUALS;
2016  }
2017  else if (op.EqualsLiteral(SB_OPERATOR_NOTEQUALS)) {
2018  matchType = sbISQLBuilder::MATCH_NOTEQUALS;
2019  }
2020  else if (op.EqualsLiteral(SB_OPERATOR_GREATER)) {
2021  matchType = sbISQLBuilder::MATCH_GREATER;
2022  }
2023  else if (op.EqualsLiteral(SB_OPERATOR_GREATEREQUAL)) {
2025  }
2026  else if (op.EqualsLiteral(SB_OPERATOR_LESS)) {
2027  matchType = sbISQLBuilder::MATCH_LESS;
2028  }
2029  else if (op.EqualsLiteral(SB_OPERATOR_LESSEQUAL)) {
2030  matchType = sbISQLBuilder::MATCH_LESSEQUAL;
2031  }
2032  else if (op.EqualsLiteral(SB_OPERATOR_ISTRUE)) {
2033  matchType = sbISQLBuilder::MATCH_EQUALS;
2034  }
2035  else if (op.EqualsLiteral(SB_OPERATOR_ISFALSE)) {
2036  matchType = sbISQLBuilder::MATCH_NOTEQUALS;
2037  }
2038  else if (op.EqualsLiteral(SB_OPERATOR_INTHELAST)) {
2040  }
2041  else if (op.EqualsLiteral(SB_OPERATOR_NOTINTHELAST)) {
2042  matchType = sbISQLBuilder::MATCH_LESS;
2043  }
2044  else if (op.EqualsLiteral(SB_OPERATOR_ISSET)) {
2045  matchType = sbISQLBuilder::MATCH_NOTEQUALS;
2046  }
2047  else if (op.EqualsLiteral(SB_OPERATOR_ISNOTSET)) {
2048  matchType = sbISQLBuilder::MATCH_EQUALS;
2049  }
2050 
2051  if (matchType >= 0) {
2052  nsCOMPtr<sbISQLBuilderCriterion> criterion;
2053  if (bNumericCondition) {
2054  PRInt64 numericValue;
2055  rv = ScanfInt64(value, &numericValue);
2056  NS_ENSURE_SUCCESS(rv, rv);
2057  rv = aBuilder->CreateMatchCriterionLongLong(kConditionAlias,
2058  columnName,
2059  matchType,
2060  numericValue,
2061  getter_AddRefs(criterion));
2062  } else {
2063  rv = aBuilder->CreateMatchCriterionString(kConditionAlias,
2064  columnName,
2065  matchType,
2066  value,
2067  getter_AddRefs(criterion));
2068  }
2069  NS_ENSURE_SUCCESS(rv, rv);
2070 
2071  if (bNeedOrIsNull) {
2072  nsCOMPtr<sbISQLBuilderCriterion> orIsNull;
2073  rv = aBuilder->CreateMatchCriterionNull(kConditionAlias,
2074  columnName,
2076  getter_AddRefs(orIsNull));
2077  NS_ENSURE_SUCCESS(rv, rv);
2078 
2079  nsCOMPtr<sbISQLBuilderCriterion> criterionOrIsNull;
2080  rv = aBuilder->CreateOrCriterion(criterion,
2081  orIsNull,
2082  getter_AddRefs(criterionOrIsNull));
2083  NS_ENSURE_SUCCESS(rv, rv);
2084  criterion = criterionOrIsNull;
2085  }
2086 
2087  rv = aBuilder->AddCriterion(criterion);
2088  NS_ENSURE_SUCCESS(rv, rv);
2089 
2090  return NS_OK;
2091  }
2092 
2093  // If were are any of the contains style operators, handle it here
2094  if (op.EqualsLiteral(SB_OPERATOR_CONTAINS) ||
2095  op.EqualsLiteral(SB_OPERATOR_NOTCONTAINS) ||
2096  op.EqualsLiteral(SB_OPERATOR_ENDSWITH) ||
2097  op.EqualsLiteral(SB_OPERATOR_NOTENDSWITH) ||
2098  op.EqualsLiteral(SB_OPERATOR_BEGINSWITH) ||
2099  op.EqualsLiteral(SB_OPERATOR_NOTBEGINSWITH)) {
2100 
2101  nsAutoString like;
2102  if (op.EqualsLiteral(SB_OPERATOR_CONTAINS) ||
2103  op.EqualsLiteral(SB_OPERATOR_NOTCONTAINS) ||
2104  op.EqualsLiteral(SB_OPERATOR_ENDSWITH) ||
2105  op.EqualsLiteral(SB_OPERATOR_NOTENDSWITH)) {
2106  like.AppendLiteral("%");
2107  }
2108 
2109  like.Append(value);
2110 
2111  if (op.EqualsLiteral(SB_OPERATOR_CONTAINS) ||
2112  op.EqualsLiteral(SB_OPERATOR_NOTCONTAINS) ||
2113  op.EqualsLiteral(SB_OPERATOR_BEGINSWITH) ||
2114  op.EqualsLiteral(SB_OPERATOR_NOTBEGINSWITH)) {
2115  like.AppendLiteral("%");
2116  }
2117 
2118  nsCOMPtr<sbISQLBuilderCriterion> criterion;
2119  PRUint32 match;
2120  if (op.EqualsLiteral(SB_OPERATOR_CONTAINS) ||
2121  op.EqualsLiteral(SB_OPERATOR_BEGINSWITH) ||
2122  op.EqualsLiteral(SB_OPERATOR_ENDSWITH)) {
2123  match = sbISQLBuilder::MATCH_LIKE;
2124  } else {
2126  }
2127 
2128  rv = aBuilder->CreateMatchCriterionString(kConditionAlias,
2129  columnName,
2130  match,
2131  like,
2132  getter_AddRefs(criterion));
2133  NS_ENSURE_SUCCESS(rv, rv);
2134 
2135  if (bNeedOrIsNull) {
2136  nsCOMPtr<sbISQLBuilderCriterion> orIsNull;
2137  rv = aBuilder->CreateMatchCriterionNull(kConditionAlias,
2138  columnName,
2140  getter_AddRefs(orIsNull));
2141  NS_ENSURE_SUCCESS(rv, rv);
2142 
2143  nsCOMPtr<sbISQLBuilderCriterion> criterionOrIsNull;
2144  rv = aBuilder->CreateOrCriterion(criterion,
2145  orIsNull,
2146  getter_AddRefs(criterionOrIsNull));
2147  NS_ENSURE_SUCCESS(rv, rv);
2148  criterion = criterionOrIsNull;
2149  }
2150 
2151  rv = aBuilder->AddCriterion(criterion);
2152  NS_ENSURE_SUCCESS(rv, rv);
2153 
2154  return NS_OK;
2155  }
2156 
2157  // If we get here, we don't know how to handle the supplied operator
2158  return NS_ERROR_UNEXPECTED;
2159 }
2160 
2161 nsresult sbLocalDatabaseSmartMediaList::MediaListGuidToDB(nsAString &val, PRUint32 &v)
2162 {
2163  nsCOMPtr<sbILibraryManager> libraryManager;
2164  nsresult rv;
2165 
2166  nsAutoString guid;
2167  guid = val;
2168 
2169  libraryManager =
2170  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
2171  NS_ENSURE_SUCCESS(rv, rv);
2172 
2173  nsCOMPtr<sbILibrary> library;
2174  if (!mSourceLibraryGuid.IsEmpty()) {
2175  rv = libraryManager->GetLibrary(mSourceLibraryGuid, getter_AddRefs(library));
2176  } else {
2177  rv = libraryManager->GetMainLibrary(getter_AddRefs(library));
2178  }
2179  NS_ENSURE_SUCCESS(rv, rv);
2180 
2181  nsCOMPtr<sbIMediaItem> item;
2182  rv = library->GetMediaItem(val, getter_AddRefs(item));
2183  if (rv != NS_OK) {
2184  v = (PRUint32)-1;
2185  } else {
2186  nsAutoString storageGuid;
2187  rv = item->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_STORAGEGUID),
2188  storageGuid);
2189  NS_ENSURE_SUCCESS(rv, rv);
2190 
2191  if (!storageGuid.IsEmpty()) {
2192  nsCOMPtr<sbIMediaItem> storageItem;
2193  rv = library->GetMediaItem(storageGuid, getter_AddRefs(storageItem));
2194  NS_ENSURE_SUCCESS(rv, rv);
2195 
2196  rv = storageItem->GetGuid(guid);
2197  NS_ENSURE_SUCCESS(rv, rv);
2198  }
2199 
2200  PRUint32 id;
2201 
2202  nsCOMPtr<sbILocalDatabaseLibrary> locallibrary =
2203  do_QueryInterface(library, &rv);
2204  NS_ENSURE_SUCCESS(rv, rv);
2205 
2206  rv = locallibrary->GetMediaItemIdForGuid(guid, &id);
2207  NS_ENSURE_SUCCESS(rv, rv);
2208 
2209  v = id;
2210  }
2211 
2212  return NS_OK;
2213 }
2214 
2215 nsresult
2216 sbLocalDatabaseSmartMediaList::GetConditionNeedsNull(sbRefPtrCondition& aCondition,
2217  sbIPropertyInfo* aInfo,
2218  PRBool &bNeedIsNull)
2219 {
2220  nsresult rv;
2221 
2222  if (mNotExistsMode == sbILocalDatabaseSmartMediaList::NOTEXISTS_ASNULL) {
2223  bNeedIsNull = PR_FALSE;
2224  return NS_OK;
2225  }
2226 
2227  nsCOMPtr<sbIPropertyOperator> opObj;
2228  rv = aCondition->GetOperator(getter_AddRefs(opObj));
2229  NS_ENSURE_SUCCESS(rv, rv);
2230  NS_ENSURE_TRUE(opObj, NS_ERROR_FAILURE);
2231 
2232  nsAutoString op;
2233  rv = opObj->GetOperator(op);
2234  NS_ENSURE_SUCCESS(rv, rv);
2235 
2236  if (op.EqualsLiteral(SB_OPERATOR_ISFALSE)) {
2237  bNeedIsNull = PR_TRUE;
2238  return NS_OK;
2239  }
2240  if (op.EqualsLiteral(SB_OPERATOR_ISTRUE)){
2241  bNeedIsNull = PR_FALSE;
2242  return NS_OK;
2243  }
2244  if (op.EqualsLiteral(SB_OPERATOR_ISSET)) {
2245  bNeedIsNull = PR_FALSE;
2246  return NS_OK;
2247  }
2248  if (op.EqualsLiteral(SB_OPERATOR_ISNOTSET)){
2249  bNeedIsNull = PR_TRUE;
2250  return NS_OK;
2251  }
2252 
2253  nsAutoString leftValue, value;
2254  leftValue = aCondition->mLeftValue;
2255  if (!leftValue.IsEmpty()) {
2256  rv = aInfo->MakeSearchable(leftValue, value);
2257  // MakeSearchable may fail if the value fails to validate, but since a smart
2258  // playlist may look for substrings instead of full valid values, it is not
2259  // fatal to fail to make searchable, when that fails we just use the value
2260  // that we were given as is.
2261  if (NS_FAILED(rv)) {
2262  value = leftValue;
2263  }
2264  }
2265 
2266  PRBool bIsEmpty = value.IsEmpty();
2267 
2268  PRInt64 fValue;
2269  PRBool bIsNumber = PR_TRUE;
2270  rv = ScanfInt64(value, &fValue);
2271  if (rv != NS_OK) {
2272  fValue = 0;
2273  bIsNumber = PR_FALSE;
2274  }
2275 
2276  if (op.EqualsLiteral(SB_OPERATOR_EQUALS) ||
2277  op.EqualsLiteral(SB_OPERATOR_ONDATE)) {
2278  if ((bIsNumber && fValue == 0) ||
2279  (!bIsNumber && bIsEmpty)) {
2280  bNeedIsNull = PR_TRUE;
2281  return NS_OK;
2282  }
2283  }
2284 
2285  if (op.EqualsLiteral(SB_OPERATOR_NOTEQUALS) ||
2286  op.EqualsLiteral(SB_OPERATOR_NOTONDATE)) {
2287  if ((bIsNumber && fValue != 0) ||
2288  (!bIsNumber && !bIsEmpty)) {
2289  bNeedIsNull = PR_TRUE;
2290  return NS_OK;
2291  }
2292  }
2293 
2294  if (op.EqualsLiteral(SB_OPERATOR_GREATER)) {
2295  if (fValue < 0) {
2296  bNeedIsNull = PR_TRUE;
2297  return NS_OK;
2298  }
2299  }
2300 
2301  if (op.EqualsLiteral(SB_OPERATOR_GREATEREQUAL) ||
2302  op.EqualsLiteral(SB_OPERATOR_BETWEEN)) {
2303  if (fValue <= 0) {
2304  bNeedIsNull = PR_TRUE;
2305  return NS_OK;
2306  }
2307  }
2308 
2309  if (op.EqualsLiteral(SB_OPERATOR_LESS)) {
2310  if (fValue > 0) {
2311  bNeedIsNull = PR_TRUE;
2312  return NS_OK;
2313  }
2314  }
2315 
2316  if (op.EqualsLiteral(SB_OPERATOR_LESSEQUAL)) {
2317  if (fValue >= 0) {
2318  bNeedIsNull = PR_TRUE;
2319  return NS_OK;
2320  }
2321  }
2322 
2323  if (op.EqualsLiteral(SB_OPERATOR_NOTCONTAINS) ||
2324  op.EqualsLiteral(SB_OPERATOR_NOTBEGINSWITH) ||
2325  op.EqualsLiteral(SB_OPERATOR_NOTENDSWITH) ||
2326  op.EqualsLiteral(SB_OPERATOR_NOTINTHELAST) ||
2327  op.EqualsLiteral(SB_OPERATOR_NOTONDATE)) {
2328  if (!bIsEmpty) {
2329  bNeedIsNull = PR_TRUE;
2330  return NS_OK;
2331  }
2332  }
2333 
2334  bNeedIsNull = PR_FALSE;
2335  return NS_OK;
2336 }
2337 
2338 nsresult
2339 sbLocalDatabaseSmartMediaList::CreateQueries()
2340 {
2341  nsresult rv;
2342 
2343  NS_NAMED_LITERAL_STRING(kSimpleMediaLists, "simple_media_lists");
2344  NS_NAMED_LITERAL_STRING(kMediaItemId, "media_item_id");
2345 
2346  nsCOMPtr<sbILocalDatabaseMediaItem> ldmi = do_QueryInterface(mList, &rv);
2347  NS_ENSURE_SUCCESS(rv, rv);
2348 
2349  PRUint32 mediaItemId;
2350  rv = ldmi->GetMediaItemId(&mediaItemId);
2351  NS_ENSURE_SUCCESS(rv, rv);
2352 
2353  nsCOMPtr<sbISQLDeleteBuilder> deleteb =
2354  do_CreateInstance(SB_SQLBUILDER_DELETE_CONTRACTID, &rv);
2355 
2356  rv = deleteb->SetTableName(kSimpleMediaLists);
2357  NS_ENSURE_SUCCESS(rv, rv);
2358 
2359  nsCOMPtr<sbISQLBuilderCriterion> criterion;
2360  rv = deleteb->CreateMatchCriterionLong(EmptyString(),
2361  kMediaItemId,
2363  mediaItemId,
2364  getter_AddRefs(criterion));
2365  NS_ENSURE_SUCCESS(rv, rv);
2366 
2367  rv = deleteb->AddCriterion(criterion);
2368  NS_ENSURE_SUCCESS(rv, rv);
2369 
2370  rv = deleteb->ToString(mClearListQuery);
2371  NS_ENSURE_SUCCESS(rv, rv);
2372 
2373  return NS_OK;
2374 }
2375 
2376 nsresult
2377 sbLocalDatabaseSmartMediaList::AddSelectColumnAndJoin(sbISQLSelectBuilder* aBuilder,
2378  const nsAString& aBaseTableAlias,
2379  PRBool aAddOrderBy)
2380 {
2381  NS_ENSURE_ARG_POINTER(aBuilder);
2382 
2383  NS_NAMED_LITERAL_STRING(kObjSortable, "obj_sortable");
2384  NS_NAMED_LITERAL_STRING(kPropertyId, "property_id");
2385  NS_NAMED_LITERAL_STRING(kMediaItemId, "media_item_id");
2386  NS_NAMED_LITERAL_STRING(kResourceProperties, "resource_properties");
2387  NS_NAMED_LITERAL_STRING(kSelectAlias, "_select");
2388 
2389  nsresult rv;
2390 
2391  if (SB_IsTopLevelProperty(mSelectPropertyID)) {
2392  // If the select property is a top level property, just add it to the
2393  // column list off the base table
2394  nsAutoString columnName;
2395  rv = SB_GetTopLevelPropertyColumn(mSelectPropertyID, columnName);
2396  NS_ENSURE_SUCCESS(rv, rv);
2397 
2398  rv = aBuilder->AddColumn(aBaseTableAlias, columnName);
2399  NS_ENSURE_SUCCESS(rv, rv);
2400 
2401  if (aAddOrderBy) {
2402  rv = aBuilder->AddOrder(aBaseTableAlias, columnName, mSelectDirection);
2403  NS_ENSURE_SUCCESS(rv, rv);
2404  }
2405  }
2406  else {
2407  // Otherwise, join the property to the base table
2408  rv = aBuilder->AddColumn(kSelectAlias, kObjSortable);
2409  NS_ENSURE_SUCCESS(rv, rv);
2410 
2411  // get property db id for rule property
2412  PRUint32 propertyDBID;
2413  rv = mPropertyCache->GetPropertyDBID(mSelectPropertyID, &propertyDBID);
2414  NS_ENSURE_SUCCESS(rv, rv);
2415 
2416  // media_item_id match with left table media_item_id
2417  nsCOMPtr<sbISQLBuilderCriterion> criterion_media_item;
2418  rv = aBuilder->CreateMatchCriterionTable(aBaseTableAlias,
2419  kMediaItemId,
2421  kSelectAlias,
2422  kMediaItemId,
2423  getter_AddRefs(criterion_media_item));
2424  NS_ENSURE_SUCCESS(rv, rv);
2425 
2426 
2427  // select.property_id match with select property db id
2428  nsCOMPtr<sbISQLBuilderCriterion> criterion_property_id;
2429  rv = aBuilder->CreateMatchCriterionLong(kSelectAlias,
2430  kPropertyId,
2432  propertyDBID,
2433  getter_AddRefs(criterion_property_id));
2434  NS_ENSURE_SUCCESS(rv, rv);
2435 
2436  // combine the two conditions with AND
2437  nsCOMPtr<sbISQLBuilderCriterion> join_criterion;
2438  rv = aBuilder->CreateAndCriterion(criterion_media_item,
2439  criterion_property_id,
2440  getter_AddRefs(join_criterion));
2441  NS_ENSURE_SUCCESS(rv, rv);
2442 
2443  // create a left outer join with these conditions, so we get null values
2444  // for items that do not have the property we're looking for.
2445  rv = aBuilder->AddJoinWithCriterion(sbISQLBuilder::JOIN_LEFT_OUTER,
2446  kResourceProperties,
2447  kSelectAlias,
2448  join_criterion);
2449  NS_ENSURE_SUCCESS(rv, rv);
2450 
2451  if (aAddOrderBy) {
2452  rv = aBuilder->AddOrder(kSelectAlias, kObjSortable, mSelectDirection);
2453  NS_ENSURE_SUCCESS(rv, rv);
2454  }
2455  }
2456 
2457  return NS_OK;
2458 }
2459 
2460 nsresult
2461 sbLocalDatabaseSmartMediaList::AddLimitColumnAndJoin(sbISQLSelectBuilder* aBuilder,
2462  const nsAString& aBaseTableAlias)
2463 {
2464  NS_ENSURE_ARG_POINTER(aBuilder);
2465 
2466  NS_NAMED_LITERAL_STRING(kContentLength, "content_length");
2467  NS_NAMED_LITERAL_STRING(kLimitAlias, "_limit");
2468  NS_NAMED_LITERAL_STRING(kObjSortable, "obj_sortable");
2469  NS_NAMED_LITERAL_STRING(kPropertyId, "property_id");
2470  NS_NAMED_LITERAL_STRING(kMediaItemId, "media_item_id");
2471  NS_NAMED_LITERAL_STRING(kResourceProperties, "resource_properties");
2472 
2473  nsresult rv;
2474 
2475  switch(mLimitType) {
2478  // For these limit types, we don't need any data so add a placeholder
2479  rv = aBuilder->AddColumn(EmptyString(), NS_LITERAL_STRING("0"));
2480  NS_ENSURE_SUCCESS(rv, rv);
2481  break;
2483  {
2484  // For microseconds limits, we need to join the duration property
2485  rv = aBuilder->AddColumn(kLimitAlias, kObjSortable);
2486  NS_ENSURE_SUCCESS(rv, rv);
2487 
2488  rv = aBuilder->AddJoin(sbISQLBuilder::JOIN_INNER,
2489  kResourceProperties,
2490  kLimitAlias,
2491  kMediaItemId,
2492  aBaseTableAlias,
2493  kMediaItemId);
2494  NS_ENSURE_SUCCESS(rv, rv);
2495 
2496  PRUint32 propertyId;
2497  rv = mPropertyCache->GetPropertyDBID(NS_LITERAL_STRING(SB_PROPERTY_DURATION),
2498  &propertyId);
2499  NS_ENSURE_SUCCESS(rv, rv);
2500 
2501  nsCOMPtr<sbISQLBuilderCriterion> criterion;
2502  rv = aBuilder->CreateMatchCriterionLong(kLimitAlias,
2503  kPropertyId,
2505  propertyId,
2506  getter_AddRefs(criterion));
2507  rv = aBuilder->AddCriterion(criterion);
2508  NS_ENSURE_SUCCESS(rv, rv);
2509  }
2510  break;
2512  // For bytes, we use the value of the content_length column
2513  rv = aBuilder->AddColumn(aBaseTableAlias, kContentLength);
2514  NS_ENSURE_SUCCESS(rv, rv);
2515  break;
2516  }
2517 
2518  return NS_OK;
2519 }
2520 
2521 nsresult
2522 sbLocalDatabaseSmartMediaList::GetCopyToListQuery(const nsAString& aTempTableName,
2523  nsAString& aSql)
2524 {
2525  nsresult rv;
2526 
2527  nsCOMPtr<sbILocalDatabaseMediaItem> ldmi = do_QueryInterface(mList, &rv);
2528  NS_ENSURE_SUCCESS(rv, rv);
2529 
2530  PRUint32 mediaItemId;
2531  rv = ldmi->GetMediaItemId(&mediaItemId);
2532  NS_ENSURE_SUCCESS(rv, rv);
2533 
2534  nsCOMPtr<sbISQLInsertBuilder> insert =
2535  do_CreateInstance(SB_SQLBUILDER_INSERT_CONTRACTID, &rv);
2536 
2537  rv = insert->SetIntoTableName(NS_LITERAL_STRING("simple_media_lists"));
2538  NS_ENSURE_SUCCESS(rv, rv);
2539 
2540  rv = insert->AddColumn(NS_LITERAL_STRING("media_item_id"));
2541  NS_ENSURE_SUCCESS(rv, rv);
2542 
2543  rv = insert->AddColumn(NS_LITERAL_STRING("member_media_item_id"));
2544  NS_ENSURE_SUCCESS(rv, rv);
2545 
2546  rv = insert->AddColumn(NS_LITERAL_STRING("ordinal"));
2547  NS_ENSURE_SUCCESS(rv, rv);
2548 
2549  nsCOMPtr<sbISQLSelectBuilder> select =
2550  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
2551 
2552  rv = select->SetBaseTableName(aTempTableName);
2553  NS_ENSURE_SUCCESS(rv, rv);
2554 
2555  nsAutoString mediaItemIdStr;
2556  mediaItemIdStr.AppendInt(mediaItemId);
2557  rv = select->AddColumn(EmptyString(), mediaItemIdStr);
2558  NS_ENSURE_SUCCESS(rv, rv);
2559 
2560  rv = select->AddColumn(EmptyString(), NS_LITERAL_STRING("media_item_id"));
2561  NS_ENSURE_SUCCESS(rv, rv);
2562 
2563  rv = select->AddColumn(EmptyString(), NS_LITERAL_STRING("count"));
2564  NS_ENSURE_SUCCESS(rv, rv);
2565 
2566  rv = insert->SetSelect(select);
2567  NS_ENSURE_SUCCESS(rv, rv);
2568 
2569  rv = insert->ToString(aSql);
2570  NS_ENSURE_SUCCESS(rv, rv);
2571 
2572  return NS_OK;
2573 }
2574 
2575 nsresult
2576 sbLocalDatabaseSmartMediaList::CreateTempTable(nsAString& aName)
2577 {
2578  nsresult rv;
2579 
2580  rv = MakeTempTableName(aName);
2581  NS_ENSURE_SUCCESS(rv, rv);
2582 
2583  nsAutoString sql;
2584  sql.AssignLiteral("create table ");
2585  sql.Append(aName);
2586  sql.AppendLiteral(" (media_item_id integer unique, limitby integer, selectby text, count integer primary key autoincrement)");
2587 
2588  rv = ExecuteQuery(sql);
2589  NS_ENSURE_SUCCESS(rv, rv);
2590 
2591  return NS_OK;
2592 }
2593 
2594 nsresult
2595 sbLocalDatabaseSmartMediaList::DropTempTable(const nsAString& aName)
2596 {
2597  nsresult rv;
2598 
2599  nsAutoString sql;
2600  sql.AssignLiteral("drop table ");
2601  sql.Append(aName);
2602 
2603  rv = ExecuteQuery(sql);
2604  NS_ENSURE_SUCCESS(rv, rv);
2605 
2606  return NS_OK;
2607 }
2608 
2609 nsresult
2610 sbLocalDatabaseSmartMediaList::ExecuteQuery(const nsAString& aSql)
2611 {
2612  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - ExecuteQuery() - %s",
2613  this, NS_LossyConvertUTF16toASCII(Substring(aSql, 0, 400)).get()));
2614  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - ExecuteQuery() - %s",
2615  this, NS_LossyConvertUTF16toASCII(Substring(aSql, 400, 800)).get()));
2616 
2617  nsCOMPtr<sbIDatabaseQuery> query;
2618  nsresult rv = mLocalDatabaseLibrary->CreateQuery(getter_AddRefs(query));
2619  NS_ENSURE_SUCCESS(rv, rv);
2620 
2621  rv = query->AddQuery(aSql);
2622  NS_ENSURE_SUCCESS(rv, rv);
2623 
2624  PRInt32 dbOk;
2625  rv = query->Execute(&dbOk);
2626  NS_ENSURE_SUCCESS(rv, rv);
2627  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2628 
2629  return NS_OK;
2630 }
2631 
2632 
2633 nsresult
2634 sbLocalDatabaseSmartMediaList::MakeTempTableName(nsAString& aName)
2635 {
2636  nsresult rv;
2637 
2638  nsCOMPtr<nsIUUIDGenerator> uuidGen =
2639  do_GetService("@mozilla.org/uuid-generator;1", &rv);
2640  NS_ENSURE_SUCCESS(rv, rv);
2641 
2642  nsID id;
2643  rv = uuidGen->GenerateUUIDInPlace(&id);
2644  NS_ENSURE_SUCCESS(rv, rv);
2645 
2646  char guidChars[NSID_LENGTH];
2647  id.ToProvidedString(guidChars);
2648 
2649  nsString guid(NS_ConvertASCIItoUTF16(nsDependentCString(guidChars,
2650  NSID_LENGTH - 1)));
2651 
2652  nsAutoString stripped;
2653 
2654  // Extract all the numbers from the guid
2655  // 01234567890123456789012345789012345678
2656  // {5ee2439a-bb40-47d6-938b-de24a6c72815}
2657  stripped.Append(Substring(guid, 1, 8));
2658  stripped.Append(Substring(guid, 10, 4));
2659  stripped.Append(Substring(guid, 15, 4));
2660  stripped.Append(Substring(guid, 20, 4));
2661  stripped.Append(Substring(guid, 25, 12));
2662 
2663  nsAutoString tempTableName;
2664  tempTableName.AssignLiteral("temp_smart_");
2665  tempTableName.Append(stripped);
2666 
2667  aName = tempTableName;
2668  return NS_OK;
2669 }
2670 
2671 nsresult
2672 sbLocalDatabaseSmartMediaList::GetMediaItemIdRange(PRUint32* aMin,
2673  PRUint32* aMax)
2674 {
2675  nsAutoString sql;
2676  sql.AssignLiteral("select min(media_item_id), max(media_item_id) from media_items");
2677 
2678  nsCOMPtr<sbIDatabaseQuery> query;
2679  nsresult rv = mLocalDatabaseLibrary->CreateQuery(getter_AddRefs(query));
2680  NS_ENSURE_SUCCESS(rv, rv);
2681 
2682  rv = query->AddQuery(sql);
2683  NS_ENSURE_SUCCESS(rv, rv);
2684 
2685  PRInt32 dbOk;
2686  rv = query->Execute(&dbOk);
2687  NS_ENSURE_SUCCESS(rv, rv);
2688  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2689 
2690  nsCOMPtr<sbIDatabaseResult> result;
2691  rv = query->GetResultObject(getter_AddRefs(result));
2692  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2693 
2694  PRUint32 rowCount;
2695  rv = result->GetRowCount(&rowCount);
2696  NS_ENSURE_SUCCESS(rv, rv);
2697 
2698  if (rowCount != 1) {
2699  return NS_ERROR_UNEXPECTED;
2700  }
2701 
2702  nsAutoString temp;
2703  rv = result->GetRowCell(0, 0, temp);
2704  NS_ENSURE_SUCCESS(rv, rv);
2705 
2706  *aMin = temp.ToInteger(&rv);
2707  NS_ENSURE_SUCCESS(rv, rv);
2708 
2709  rv = result->GetRowCell(0, 1, temp);
2710  NS_ENSURE_SUCCESS(rv, rv);
2711 
2712  *aMax = temp.ToInteger(&rv);
2713  NS_ENSURE_SUCCESS(rv, rv);
2714 
2715  return NS_OK;
2716 }
2717 
2718 nsresult
2719 sbLocalDatabaseSmartMediaList::GetRowCount(const nsAString& aTableName,
2720  PRUint32* _retval)
2721 {
2722  nsAutoString sql;
2723  sql.AssignLiteral("select count(1) from ");
2724  sql.Append(aTableName);
2725 
2726  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - GetRowCount() - %s",
2727  this, NS_LossyConvertUTF16toASCII(sql).get()));
2728 
2729  nsCOMPtr<sbIDatabaseQuery> query;
2730  nsresult rv = mLocalDatabaseLibrary->CreateQuery(getter_AddRefs(query));
2731  NS_ENSURE_SUCCESS(rv, rv);
2732 
2733  rv = query->AddQuery(sql);
2734  NS_ENSURE_SUCCESS(rv, rv);
2735 
2736  PRInt32 dbOk;
2737  rv = query->Execute(&dbOk);
2738  NS_ENSURE_SUCCESS(rv, rv);
2739  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2740 
2741  nsCOMPtr<sbIDatabaseResult> result;
2742  rv = query->GetResultObject(getter_AddRefs(result));
2743  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2744 
2745  PRUint32 rowCount;
2746  rv = result->GetRowCount(&rowCount);
2747  NS_ENSURE_SUCCESS(rv, rv);
2748 
2749  if (rowCount != 1) {
2750  return NS_ERROR_UNEXPECTED;
2751  }
2752 
2753  nsAutoString temp;
2754  rv = result->GetRowCell(0, 0, temp);
2755  NS_ENSURE_SUCCESS(rv, rv);
2756 
2757  *_retval = temp.ToInteger(&rv);
2758  NS_ENSURE_SUCCESS(rv, rv);
2759 
2760  return NS_OK;
2761 }
2762 
2763 /*
2764  * \breif Shuffle the contents of the array using Fisher-Yates shuffle
2765  * http://www.nist.gov/dads/HTML/fisherYatesShuffle.html
2766  */
2767 void
2768 sbLocalDatabaseSmartMediaList::ShuffleArray(sbMediaItemIdArray& aArray)
2769 {
2770  PRUint32 n = aArray.Length();
2771  if (n > 1) {
2772  PRUint32 i;
2773  for (i = 0; i < n - 1; i++) {
2774  PRUint32 j = i + rand() / (RAND_MAX / (n - i) + 1);
2775  PRUint32 t = aArray[j];
2776  aArray[j] = aArray[i];
2777  aArray[i] = t;
2778  }
2779  }
2780 }
2781 
2782 nsresult
2783 sbLocalDatabaseSmartMediaList::ReadConfiguration()
2784 {
2785  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - ReadConfiguration()", this));
2786 
2787  nsresult rv;
2788 
2789  nsAutoMonitor monitor(mConditionsMonitor);
2790 
2791  sbStringMap map;
2792  PRBool success = map.Init();
2793  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2794 
2795  // Set up the defaults
2798  mLimit = 0;
2799  mSelectPropertyID.Truncate();
2800  mSelectDirection = PR_TRUE;
2801  mRandomSelection = PR_FALSE;
2802  mAutoUpdate = false;
2803  mConditions.Clear();
2804 
2805  nsAutoString state;
2806  rv = mItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_SMARTMEDIALIST_STATE),
2807  state);
2808  NS_ENSURE_SUCCESS(rv, rv);
2809 
2810  // If no saved state is available, just return
2811  if (state.IsEmpty()) {
2812  return NS_OK;
2813  }
2814 
2815  // Parse the list's properties from the state
2816  rv = ParseQueryStringIntoHashtable(state, map);
2817  NS_ENSURE_SUCCESS(rv, rv);
2818 
2819  nsString value;
2820  nsresult dontCare;
2821 
2822  if (map.Get(NS_LITERAL_STRING("matchType"), &value)) {
2823  mMatchType = value.ToInteger(&dontCare);
2824  if (!(mMatchType >= sbILocalDatabaseSmartMediaList::MATCH_TYPE_ANY &&
2827  }
2828  }
2829 
2830  if (map.Get(NS_LITERAL_STRING("limitType"), &value)) {
2831  mLimitType = value.ToInteger(&dontCare);
2835  }
2836  }
2837 
2838  if (map.Get(NS_LITERAL_STRING("limit"), &value)) {
2839  // Can't use ToInteger here since it is a 64 bit value
2840  PR_sscanf(NS_LossyConvertUTF16toASCII(value).get(), "%llu", &mLimit);
2841  }
2842 
2843  if (map.Get(NS_LITERAL_STRING("selectPropertyID"), &value)) {
2844  mSelectPropertyID = value;
2845  }
2846 
2847  if (map.Get(NS_LITERAL_STRING("selectDirection"), &value)) {
2848  mSelectDirection = value.EqualsLiteral("1");
2849  }
2850 
2851  if (map.Get(NS_LITERAL_STRING("randomSelection"), &value)) {
2852  mRandomSelection = value.EqualsLiteral("1");
2853  }
2854 
2855  if (map.Get(NS_LITERAL_STRING("autoUpdate"), &value)) {
2856  PR_sscanf(NS_LossyConvertUTF16toASCII(value).get(), "%d", &mAutoUpdate);
2857  }
2858 
2859  map.Get(NS_LITERAL_STRING("sourceLibraryGuid"), &mSourceLibraryGuid);
2860 
2861  if (map.Get(NS_LITERAL_STRING("conditionCount"), &value)) {
2862  PRUint32 count = value.ToInteger(&rv);
2863  NS_ENSURE_SUCCESS(rv, rv);
2864 
2865  sbStringMap conditionMap;
2866  success = conditionMap.Init();
2867  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2868 
2869  for (PRUint32 i = 0; i < count; i++) {
2870  nsAutoString key;
2871  key.AssignLiteral("condition");
2872  key.AppendInt(i);
2873 
2874  if (map.Get(key, &value)) {
2875  conditionMap.Clear();
2876  rv = ParseQueryStringIntoHashtable(value, conditionMap);
2877  if (NS_FAILED(rv)) {
2878  NS_WARNING("Could not parse condition state");
2879  continue;
2880  }
2881 
2882  // Extract each property for this condition and set them to local
2883  // variables.
2884  nsAutoString property;
2885  nsAutoString leftValue;
2886  nsAutoString rightValue;
2887  nsAutoString displayUnit;
2888  nsAutoString opString;
2889 
2890  if (conditionMap.Get(NS_LITERAL_STRING("property"), &value)) {
2891  property = value;
2892  }
2893 
2894  if (conditionMap.Get(NS_LITERAL_STRING("leftValue"), &value)) {
2895  leftValue = value;
2896  }
2897 
2898  if (conditionMap.Get(NS_LITERAL_STRING("rightValue"), &value)) {
2899  rightValue = value;
2900  }
2901 
2902  if (conditionMap.Get(NS_LITERAL_STRING("displayUnit"), &value)) {
2903  displayUnit = value;
2904  }
2905 
2906  if (conditionMap.Get(NS_LITERAL_STRING("operator"), &value)) {
2907  opString = value;
2908  }
2909 
2910  if (!property.IsEmpty() && !opString.IsEmpty()) {
2911 
2912  sbRefPtrCondition condition;
2913  condition = new sbLocalDatabaseSmartMediaListCondition(property,
2914  opString,
2915  leftValue,
2916  rightValue,
2917  displayUnit);
2918  NS_ENSURE_TRUE(condition, NS_ERROR_OUT_OF_MEMORY);
2919 
2920  sbRefPtrCondition* victory = mConditions.AppendElement(condition);
2921  NS_ENSURE_TRUE(victory, NS_ERROR_OUT_OF_MEMORY);
2922  }
2923  else {
2924  NS_WARNING("Can't restore condition, blank property or operator");
2925  }
2926  }
2927  }
2928  }
2929 
2930  return NS_OK;
2931 }
2932 
2933 nsresult
2934 sbLocalDatabaseSmartMediaList::WriteConfiguration()
2935 {
2936  TRACE(("sbLocalDatabaseSmartMediaList[0x%.8x] - WriteConfiguration()", this));
2937 
2938  nsresult rv;
2939 
2940  PRUint32 count = mConditions.Length();
2941 
2942  sbStringMap map;
2943  PRBool success = map.Init();
2944  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2945 
2946  nsAutoString match;
2947  match.AppendInt(mMatchType);
2948  success = map.Put(NS_LITERAL_STRING("matchType"), match);
2949  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2950 
2951  nsAutoString limitType;
2952  limitType.AppendInt(mLimitType);
2953  success = map.Put(NS_LITERAL_STRING("limitType"), limitType);
2954  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2955 
2956  // Can't use AppendInt here because limit is a 64 bit value
2957  nsAutoString limit;
2958  char buf[32];
2959  PR_snprintf(buf, sizeof(buf), "%llu", mLimit);
2960  limit.Append(NS_ConvertASCIItoUTF16(buf));
2961  success = map.Put(NS_LITERAL_STRING("limit"), limit);
2962  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2963 
2964  success = map.Put(NS_LITERAL_STRING("selectPropertyID"),
2965  mSelectPropertyID);
2966  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2967 
2968  nsAutoString selectDirection;
2969  selectDirection.AppendLiteral(mSelectDirection ? "1" : "0");
2970  success = map.Put(NS_LITERAL_STRING("selectDirection"), selectDirection);
2971  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2972 
2973  nsAutoString randomSelection;
2974  randomSelection.AppendLiteral(mRandomSelection ? "1" : "0");
2975  success = map.Put(NS_LITERAL_STRING("randomSelection"), randomSelection);
2976  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2977 
2978  nsAutoString autoUpdate;
2979  autoUpdate.AppendInt(mAutoUpdate);
2980  success = map.Put(NS_LITERAL_STRING("autoUpdate"), autoUpdate);
2981  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2982 
2983  nsAutoString conditionCount;
2984  conditionCount.AppendInt(count);
2985  success = map.Put(NS_LITERAL_STRING("conditionCount"), conditionCount);
2986  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2987 
2988  for (PRUint32 i = 0; i < count; i++) {
2989  nsAutoString key;
2990  key.AssignLiteral("condition");
2991  key.AppendInt(i);
2992 
2993  nsAutoString value;
2994  rv = mConditions[i]->ToString(value);
2995  NS_ENSURE_SUCCESS(rv, rv);
2996 
2997  success = map.Put(key, value);
2998  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2999  }
3000 
3001  success = map.Put(NS_LITERAL_STRING("sourceLibraryGuid"), mSourceLibraryGuid);
3002  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
3003 
3004  nsAutoString state;
3005  rv = JoinStringMapIntoQueryString(map, state);
3006  NS_ENSURE_SUCCESS(rv, rv);
3007 
3008  rv = mItem->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_SMARTMEDIALIST_STATE),
3009  state);
3010  NS_ENSURE_SUCCESS(rv, rv);
3011 
3012  // Set the 'last updated' property when the smart media list rules have been
3013  // changed - this is more in line with the semantics than simply when the
3014  // contents of the list have changed (due to it being recomputed, for example)
3015  sbAutoString now((PRUint64)(PR_Now()/PR_MSEC_PER_SEC));
3016  rv = mItem->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_UPDATED),
3017  now);
3018  NS_ENSURE_SUCCESS(rv, rv);
3019 
3020  return NS_OK;
3021 }
3022 
3023 // sbIMediaListListener
3024 NS_IMETHODIMP
3025 sbLocalDatabaseSmartMediaList::OnItemAdded(sbIMediaList* aMediaList,
3026  sbIMediaItem* aMediaItem,
3027  PRUint32 aIndex,
3028  PRBool* aNoMoreForBatch)
3029 {
3030  NS_ENSURE_ARG_POINTER(aMediaList);
3031  NS_ENSURE_ARG_POINTER(aMediaItem);
3032  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
3033 
3034  // Don't care
3035  *aNoMoreForBatch = PR_TRUE;
3036  return NS_OK;
3037 }
3038 
3039 NS_IMETHODIMP
3040 sbLocalDatabaseSmartMediaList::OnBeforeItemRemoved(sbIMediaList* aMediaList,
3041  sbIMediaItem* aMediaItem,
3042  PRUint32 aIndex,
3043  PRBool* aNoMoreForBatch)
3044 {
3045  NS_ENSURE_ARG_POINTER(aMediaList);
3046  NS_ENSURE_ARG_POINTER(aMediaItem);
3047  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
3048 
3049  // Is this notification coming from our library?
3050  nsCOMPtr<sbILibrary> library;
3051  nsresult rv = GetLibrary(getter_AddRefs(library));
3052  NS_ENSURE_SUCCESS(rv, rv);
3053 
3054  PRBool isOurLibrary;
3055  rv = aMediaList->Equals(library, &isOurLibrary);
3056  NS_ENSURE_SUCCESS(rv, rv);
3057 
3058  // Is this notification about this smart media list?
3059  PRBool isThis;
3060  rv = aMediaItem->Equals(mItem, &isThis);
3061  NS_ENSURE_SUCCESS(rv, rv);
3062 
3063  // If this smart media list is being removed from the library, remove the
3064  // data list
3065  if (isThis && isOurLibrary) {
3066 
3067  nsCOMPtr<sbIMediaList> list = do_QueryInterface(library, &rv);
3068  NS_ENSURE_SUCCESS(rv, rv);
3069 
3070  rv = list->Remove(mList);
3071  NS_ENSURE_SUCCESS(rv, rv);
3072 
3073  nsCOMPtr<sbILocalDatabaseSimpleMediaList> ldsml =
3074  do_QueryInterface(mList, &rv);
3075  NS_ENSURE_SUCCESS(rv, rv);
3076  }
3077 
3078  *aNoMoreForBatch = PR_FALSE;
3079  return NS_OK;
3080 }
3081 
3082 NS_IMETHODIMP
3083 sbLocalDatabaseSmartMediaList::OnAfterItemRemoved(sbIMediaList* aMediaList,
3084  sbIMediaItem* aMediaItem,
3085  PRUint32 aIndex,
3086  PRBool* aNoMoreForBatch)
3087 {
3088  NS_ENSURE_ARG_POINTER(aMediaList);
3089  NS_ENSURE_ARG_POINTER(aMediaItem);
3090  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
3091 
3092  // Don't care
3093  *aNoMoreForBatch = PR_TRUE;
3094  return NS_OK;
3095 }
3096 
3097 NS_IMETHODIMP
3098 sbLocalDatabaseSmartMediaList::OnItemUpdated(sbIMediaList* aMediaList,
3099  sbIMediaItem* aMediaItem,
3100  sbIPropertyArray* aProperties,
3101  PRBool* aNoMoreForBatch)
3102 {
3103  NS_ENSURE_ARG_POINTER(aMediaList);
3104  NS_ENSURE_ARG_POINTER(aMediaItem);
3105  NS_ENSURE_ARG_POINTER(aProperties);
3106  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
3107 
3108  // Don't care
3109  *aNoMoreForBatch = PR_TRUE;
3110  return NS_OK;
3111 }
3112 
3113 NS_IMETHODIMP
3114 sbLocalDatabaseSmartMediaList::OnItemMoved(sbIMediaList* aMediaList,
3115  PRUint32 aFromIndex,
3116  PRUint32 aToIndex,
3117  PRBool* aNoMoreForBatch)
3118 {
3119  NS_ENSURE_ARG_POINTER(aMediaList);
3120  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
3121 
3122  // Don't care
3123  *aNoMoreForBatch = PR_TRUE;
3124  return NS_OK;
3125 }
3126 
3127 NS_IMETHODIMP
3128 sbLocalDatabaseSmartMediaList::OnBeforeListCleared(sbIMediaList* aMediaList,
3129  PRBool aExcludeLists,
3130  PRBool* aNoMoreForBatch)
3131 {
3132  NS_ENSURE_ARG_POINTER(aMediaList);
3133  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
3134 
3135  // Don't care
3136  *aNoMoreForBatch = PR_TRUE;
3137  return NS_OK;
3138 }
3139 
3140 NS_IMETHODIMP
3141 sbLocalDatabaseSmartMediaList::OnListCleared(sbIMediaList* aMediaList,
3142  PRBool aExcludeLists,
3143  PRBool* aNoMoreForBatch)
3144 {
3145  NS_ENSURE_ARG_POINTER(aMediaList);
3146  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
3147 
3148  // Don't care
3149  *aNoMoreForBatch = PR_TRUE;
3150  return NS_OK;
3151 }
3152 
3153 NS_IMETHODIMP
3154 sbLocalDatabaseSmartMediaList::OnBatchBegin(sbIMediaList* aMediaList)
3155 {
3156  NS_ENSURE_ARG_POINTER(aMediaList);
3157 
3158  // Don't care
3159  return NS_OK;
3160 }
3161 
3162 NS_IMETHODIMP
3163 sbLocalDatabaseSmartMediaList::OnBatchEnd(sbIMediaList* aMediaList)
3164 {
3165  NS_ENSURE_ARG_POINTER(aMediaList);
3166 
3167  // Don't care
3168  return NS_OK;
3169 }
3170 
3171 NS_IMETHODIMP
3173 {
3174  if (!mList) return NS_ERROR_NULL_POINTER;
3175 
3176  // Create a property array to hold the changed property
3177  nsresult rv;
3178  nsCOMPtr<sbIMutablePropertyArray> properties =
3179  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
3180  NS_ENSURE_SUCCESS(rv, rv);
3181 
3182  // read the old property
3183  nsAutoString oldName;
3184  rv = mList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME), oldName);
3185  NS_ENSURE_SUCCESS(rv, rv);
3186 
3187  // store it in the array
3188  rv = properties->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_MEDIALISTNAME), oldName);
3189  NS_ENSURE_SUCCESS(rv, rv);
3190 
3191  // set the name to the inner list
3192  mList->SetName(aName);
3193 
3194  // notify that the name property changed for this list as well. this is so
3195  // listeners that are watching for list name changes also get notified about
3196  // us, and not just our inner list, since there is no way to go from the
3197  // inner list to the outer one.
3198  rv = mLocalDatabaseLibrary->NotifyListenersItemUpdated(this, properties);
3199  NS_ENSURE_SUCCESS(rv, rv);
3200 
3201  return NS_OK;
3202 }
3203 
3204 NS_IMETHODIMP
3206 {
3207  if (!mList) return NS_ERROR_NULL_POINTER;
3208 
3209  // always return the name from the inner list, it is doing the localization
3210  return mList->GetName(aName);
3211 }
3212 
3213 // nsIClassInfo
3214 NS_IMETHODIMP
3215 sbLocalDatabaseSmartMediaList::GetInterfaces(PRUint32* count, nsIID*** array)
3216 {
3217  return NS_CI_INTERFACE_GETTER_NAME(sbLocalDatabaseSmartMediaList)(count, array);
3218 }
3219 
3220 NS_IMETHODIMP
3221 sbLocalDatabaseSmartMediaList::GetHelperForLanguage(PRUint32 language,
3222  nsISupports** _retval)
3223 {
3224  *_retval = nsnull;
3225  return NS_OK;
3226 }
3227 
3228 NS_IMETHODIMP
3229 sbLocalDatabaseSmartMediaList::GetContractID(char** aContractID)
3230 {
3231  *aContractID = nsnull;
3232  return NS_OK;
3233 }
3234 
3235 NS_IMETHODIMP
3236 sbLocalDatabaseSmartMediaList::GetClassDescription(char** aClassDescription)
3237 {
3238  *aClassDescription = nsnull;
3239  return NS_OK;
3240 }
3241 
3242 NS_IMETHODIMP
3243 sbLocalDatabaseSmartMediaList::GetClassID(nsCID** aClassID)
3244 {
3245  *aClassID = nsnull;
3246  return NS_OK;
3247 }
3248 
3249 NS_IMETHODIMP
3250 sbLocalDatabaseSmartMediaList::GetImplementationLanguage(PRUint32* aImplementationLanguage)
3251 {
3252  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
3253  return NS_OK;
3254 }
3255 
3256 NS_IMETHODIMP
3257 sbLocalDatabaseSmartMediaList::GetFlags(PRUint32 *aFlags)
3258 {
3259  *aFlags = nsIClassInfo::THREADSAFE;
3260  return NS_OK;
3261 }
3262 
3263 NS_IMETHODIMP
3264 sbLocalDatabaseSmartMediaList::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
3265 {
3266  return NS_ERROR_NOT_AVAILABLE;
3267 }
3268 
3269 NS_IMETHODIMP
3270 sbLocalDatabaseSmartMediaList::AddSmartMediaListListener(sbILocalDatabaseSmartMediaListListener *aListener)
3271 {
3272  NS_ENSURE_ARG_POINTER(aListener);
3273 
3274  nsAutoMonitor monitor(mListenersMonitor);
3275 
3276  // Do not add duplicate listener.
3277  if (mListeners.IndexOfObject(aListener) == -1)
3278  mListeners.AppendObject(aListener);
3279  return NS_OK;
3280 }
3281 
3282 NS_IMETHODIMP
3283 sbLocalDatabaseSmartMediaList::RemoveSmartMediaListListener(sbILocalDatabaseSmartMediaListListener *aListener)
3284 {
3285  NS_ENSURE_ARG_POINTER(aListener);
3286 
3287  nsAutoMonitor monitor(mListenersMonitor);
3288  mListeners.RemoveObject(aListener);
3289  return NS_OK;
3290 }
3291 
3292 NS_IMETHODIMP
3293 sbLocalDatabaseSmartMediaList::SetSourceLibraryGuid(const nsAString& aGUID) {
3294 
3295  nsAutoMonitor monitor(mSourceMonitor);
3296 
3297  mSourceLibraryGuid = aGUID;
3298 
3299  nsresult rv = WriteConfiguration();
3300  NS_ENSURE_SUCCESS(rv, rv);
3301 
3302  return NS_OK;
3303 }
3304 
3305 NS_IMETHODIMP
3306 sbLocalDatabaseSmartMediaList::GetSourceLibraryGuid(nsAString &retVal) {
3307 
3308  nsAutoMonitor monitor(mSourceMonitor);
3309 
3310  nsString libraryguid = mSourceLibraryGuid;
3311 
3312  if (libraryguid.IsEmpty()) {
3313  nsCOMPtr<sbILibraryManager> libraryManager;
3314  nsresult rv;
3315 
3316  libraryManager =
3317  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
3318  NS_ENSURE_SUCCESS(rv, rv);
3319 
3320  nsCOMPtr<sbILibrary> mainLib;
3321  rv = libraryManager->GetMainLibrary(getter_AddRefs(mainLib));
3322  NS_ENSURE_SUCCESS(rv, rv);
3323 
3324  rv = mainLib->GetGuid(libraryguid);
3325  NS_ENSURE_SUCCESS(rv, rv);
3326  }
3327 
3328  retVal = libraryguid;
3329 
3330  return NS_OK;
3331 }
3332 
3333 NS_IMETHODIMP
3334 sbLocalDatabaseSmartMediaList::Observe(nsISupports *aObject,
3335  const char *aTopic,
3336  const PRUnichar *aData) {
3337  TRACE(("%s: observing %s", __FUNCTION__, aTopic));
3338  if (strcmp(aTopic, LIBRARY_MANAGER_BEFORE_SHUTDOWN) == 0) {
3339  // Unregister ourselves from the library so we don't leak in odd cases
3340  // See bug 14896 for more information
3341  nsCOMPtr<sbILibrary> library;
3342  nsresult rv = mItem->GetLibrary(getter_AddRefs(library));
3343  NS_ENSURE_SUCCESS(rv, rv);
3344 
3345  nsCOMPtr<sbIMediaList> libraryList = do_QueryInterface(library, &rv);
3346  NS_ENSURE_SUCCESS(rv, rv);
3347 
3348  rv = libraryList->RemoveListener(this);
3349  // Unregister ourselves as we're done
3350  nsCOMPtr<nsIObserverService> observerService =
3351  do_GetService(OBSERVER_SERVICE_CONTRACT_ID, &rv);
3352  NS_ENSURE_SUCCESS(rv, rv);
3353 
3354  rv = observerService->RemoveObserver(this, LIBRARY_MANAGER_BEFORE_SHUTDOWN);
3355  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Problem removing shutdown notification observer");
3356  }
3357  return NS_OK;
3358 }
function start(ch)
NS_IMETHOD SetName(const nsAString &aName)
#define SB_OPERATOR_AFTERDATE
#define SB_OPERATOR_ONDATE
#define SB_OPERATOR_AFTERORONDATE
return NS_OK
NS_IMPL_THREADSAFE_ISUPPORTS9(sbLocalDatabaseSmartMediaList, nsIClassInfo, nsISupportsWeakReference, sbILibraryResource, sbILocalDatabaseSmartMediaList, sbILocalDatabaseMediaItem, sbIMediaItem, sbIMediaList, sbIMediaListListener, nsIObserver)
#define SB_PROPERTY_MEDIALISTNAME
static nsCOMPtr< nsIObserverService > observerService
Definition: UnityProxy.cpp:6
static char const LIBRARY_MANAGER_BEFORE_SHUTDOWN[]
_dialogDatepicker pos
const unsigned long MATCH_NOTLIKE
static char const OBSERVER_SERVICE_CONTRACT_ID[]
static const char * gsFmtRadix10
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
#define SB_OPERATOR_NOTINTHELAST
#define SB_OPERATOR_BEFOREDATE
inArray array
#define SB_OPERATOR_NOTEQUALS
#define SB_OPERATOR_BEFOREORONDATE
_selectMonthYear select
#define SB_OPERATOR_CONTAINS
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
#define SB_OPERATOR_NOTCONTAINS
static nsresult ParseQueryStringIntoHashtable(const nsAString &aString, sbStringMap &aMap)
#define SB_OPERATOR_ISNOTSET
function right(ele) rect(ele).right
readonly attribute sbILibrary library
The library that this media item is contained in.
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
static nsresult SB_GetTopLevelPropertyColumn(const nsAString &aProperty, nsAString &aColumnName)
attribute boolean selectDirection
Direction to sort the selection property, true for ascending, false for descending.
#define SB_SQLBUILDER_INSERT_CONTRACTID
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
readonly attribute unsigned long conditionCount
The number of conditions added to this smart playlist.
attribute boolean autoUpdate
Whether this smart media list updates automatically upon changes to its source library.
#define SB_DUMMYPROPERTY_SMARTMEDIALIST_PLAYLIST
const unsigned long MATCH_LESSEQUAL
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
#define SB_OPERATOR_BEGINSWITH
NS_IMPL_CI_INTERFACE_GETTER8(sbLocalDatabaseSmartMediaList, nsIClassInfo, nsISupportsWeakReference, sbILibraryResource, sbILocalDatabaseSmartMediaList, sbIMediaItem, sbIMediaList, sbIMediaListListener, nsIObserver)
static PRBool SB_IsTopLevelProperty(PRUint32 aPropertyDBID)
var language
Definition: Info.js:44
A brief description of the contents of this interface.
#define SB_OPERATOR_GREATER
const unsigned short CONTENTTYPE_AUDIO
#define SB_OPERATOR_EQUALS
sbAutoSuppressor(sbIMediaItem *aItem)
NS_IMPL_ISUPPORTS1(sbLocalDatabaseSmartMediaListCondition, sbILocalDatabaseSmartMediaListCondition) sbLocalDatabaseSmartMediaListCondition
readonly attribute AString guid
The guid of this resource.
#define SB_PROPERTYMANAGER_CONTRACTID
#define SB_OPERATOR_NOTONDATE
attribute unsigned long limitType
Type of limit to use.
#define SB_PROPERTY_STORAGEGUID
Interface used to listen to changes to a media list.
#define SB_SQLBUILDER_DELETE_CONTRACTID
#define SB_OPERATOR_LESSEQUAL
readonly attribute unsigned long length
Returns the length of the list.
#define SB_SQLBUILDER_SELECT_CONTRACTID
var t
var count
Definition: test_bug7406.js:32
Interface for building SELECT statements.
#define SB_PROPERTY_OUTERGUID
#define SB_PROPERTY_UPDATED
An interface to represent an operator that may act upon a property.
#define SB_PROPERTY_CONTENTTYPE
const unsigned short CONTENTTYPE_VIDEO
General interface to data resources.
const unsigned long JOIN_LEFT_OUTER
this _dialogInput val(dateText)
#define SB_PROPERTY_DURATION
var libraryManager
this _document false
Definition: FeedWriter.js:1085
An interface used to describe a metadata property for use by the UI and other sbILibrary interfaces (...
#define SB_PROPERTY_GUID
const unsigned long MATCH_LESS
#define SB_OPERATOR_NOTENDSWITH
function insert(dbq, size, name)
const unsigned long JOIN_INNER
const unsigned long MATCH_LIKE
#define RANDOM_ADD_CHUNK_SIZE
ExtensionSchemeMatcher prototype match
#define SB_OPERATOR_ISSET
#define SB_PROPERTY_SMARTMEDIALIST_STATE
#define SB_OPERATOR_NOTBEGINSWITH
#define SB_OPERATOR_BETWEENDATES
const unsigned long MATCH_GREATEREQUAL
_updateCookies aName
#define SB_OPERATOR_INTHELAST
countRef value
Definition: FeedWriter.js:1423
const unsigned long MATCH_NOTEQUALS
#define SB_OPERATOR_GREATEREQUAL
#define SB_OPERATOR_ISTRUE
NS_IMETHOD GetListContentType(PRUint16 *aContentType)
static nsresult JoinStringMapIntoQueryString(sbStringMap &aMap, nsAString &aString)
#define SB_OPERATOR_BETWEEN
#define SB_OPERATOR_ISFALSE
#define SQL_IN_LIMIT
const unsigned long LISTENER_FLAGS_ALL
PLDHashOperator PR_CALLBACK JoinStringMapCallback(nsStringHashKey::KeyType aKey, nsString aEntry, void *aUserData)
#define TRACE(args)
readonly attribute unsigned long mediaItemId
function now()
Interface that defines a single item of media in the system.
const unsigned long MATCH_GREATER
#define min(a, b)
#define SB_OPERATOR_LESS
static nsresult ParseAndAddChunk(const nsAString &aString, sbStringMap &aMap)
#define SB_OPERATOR_ENDSWITH
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
attribute boolean randomSelection
Randomly select the items out of the entire result set.
_getSelectedPageStyle s i
attribute unsigned long matchType
Match any or all conditions.
attribute unsigned long long limit
Value to apply to the limit type.
const unsigned long MATCH_EQUALS
_updateTextAndScrollDataForFrame aData
nsresult Init(sbIMediaItem *aMediaItem)