sbLocalDatabaseResourcePropertyBag.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2011 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 
30 
31 #include <nsAutoLock.h>
32 #include <nsComponentManagerUtils.h>
33 #include <nsIObserverService.h>
34 #include <nsIProxyObjectManager.h>
35 #include <nsServiceManagerUtils.h>
36 #include <nsUnicharUtils.h>
37 #include <nsXPCOM.h>
38 #include <nsXPCOMCIDInternal.h>
39 #include <prlog.h>
40 
41 #include <sbIPropertyInfo.h>
42 #include <sbIPropertyManager.h>
43 #include <sbPropertiesCID.h>
44 
46 #include "sbLocalDatabaseLibrary.h"
49 #include "sbLocalDatabaseSQL.h"
51 #include <sbStringUtils.h>
52 #include <sbDebugUtils.h>
53 
54 PRUint32 const BAG_HASHTABLE_SIZE = 20;
55 
56 // sbILocalDatabaseResourcePropertyBag
59 
61  PRUint32 aMediaItemId,
62  const nsAString &aGuid)
63 : mCache(aCache)
64 , mGuid(aGuid)
65 , mMediaItemId(aMediaItemId)
66 {
68 }
69 
71 {
72 }
73 
74 nsresult
76 {
77  nsresult rv;
78 
79  PRBool success = mValueMap.Init(BAG_HASHTABLE_SIZE);
80  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
81 
82  success = mDirty.Init(BAG_HASHTABLE_SIZE);
83  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
84 
85  mPropertyManager = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
86  NS_ENSURE_SUCCESS(rv, rv);
87 
88  mIdService =
89  do_GetService("@songbirdnest.com/Songbird/IdentityService;1", &rv);
90  NS_ENSURE_SUCCESS(rv, rv);
91 
92  return NS_OK;
93 }
94 
95 /* static */ PLDHashOperator PR_CALLBACK
96 sbLocalDatabaseResourcePropertyBag::PropertyBagKeysToArray(const PRUint32& aPropertyID,
97  sbPropertyData* aPropertyData,
98  void *aArg)
99 {
100  nsTArray<PRUint32>* propertyIDs = static_cast<nsTArray<PRUint32>*>(aArg);
101  if (propertyIDs->AppendElement(aPropertyID)) {
102  return PL_DHASH_NEXT;
103  }
104  else {
105  return PL_DHASH_STOP;
106  }
107 }
108 
109 NS_IMETHODIMP
110 sbLocalDatabaseResourcePropertyBag::GetGuid(nsAString &aGuid)
111 {
112  aGuid = mGuid;
113  return NS_OK;
114 }
115 
116 NS_IMETHODIMP
117 sbLocalDatabaseResourcePropertyBag::GetMediaItemId(PRUint32* aMediaItemId)
118 {
119  NS_ENSURE_ARG_POINTER(aMediaItemId);
120  *aMediaItemId = mMediaItemId;
121  return NS_OK;
122 }
123 
124 NS_IMETHODIMP
125 sbLocalDatabaseResourcePropertyBag::GetIds(nsIStringEnumerator **aIDs)
126 {
127  NS_ENSURE_ARG_POINTER(aIDs);
128 
129  nsTArray<PRUint32> propertyDBIDs;
130  mValueMap.EnumerateRead(PropertyBagKeysToArray, &propertyDBIDs);
131 
132  PRUint32 len = mValueMap.Count();
133  if (propertyDBIDs.Length() < len) {
134  return NS_ERROR_OUT_OF_MEMORY;
135  }
136 
137  nsTArray<nsString> propertyIDs;
138  for (PRUint32 i = 0; i < len; i++) {
139  nsString propertyID;
140  PRBool success = mCache->GetPropertyID(propertyDBIDs[i], propertyID);
141  NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
142  propertyIDs.AppendElement(propertyID);
143  }
144 
145  *aIDs = new sbTArrayStringEnumerator(&propertyIDs);
146  NS_ENSURE_TRUE(*aIDs, NS_ERROR_OUT_OF_MEMORY);
147  NS_ADDREF(*aIDs);
148 
149  return NS_OK;
150 }
151 
152 NS_IMETHODIMP
153 sbLocalDatabaseResourcePropertyBag::GetProperty(const nsAString& aPropertyID,
154  nsAString& _retval)
155 {
156  PRUint32 propertyDBID = mCache->GetPropertyDBIDInternal(aPropertyID);
157  return GetPropertyByID(propertyDBID, _retval);
158 }
159 
160 NS_IMETHODIMP
161 sbLocalDatabaseResourcePropertyBag::GetPropertyByID(PRUint32 aPropertyDBID,
162  nsAString& _retval)
163 {
164  if(aPropertyDBID > 0) {
165  nsAutoMonitor mon(mCache->mMonitor);
166 
168 
169  if (mValueMap.Get(aPropertyDBID, &data)) {
170  _retval.Assign(data->value);
171  return NS_OK;
172  }
173  }
174 
175  // The value hasn't been set, so return a void string.
176  _retval.SetIsVoid(PR_TRUE);
177  return NS_OK;
178 }
179 
180 NS_IMETHODIMP
181 sbLocalDatabaseResourcePropertyBag::GetSortablePropertyByID(PRUint32 aPropertyDBID,
182  nsAString& _retval)
183 {
184  if(aPropertyDBID > 0) {
185  nsAutoMonitor mon(mCache->mMonitor);
187 
188  if (mValueMap.Get(aPropertyDBID, &data)) {
189 
190  // Generate and cache the sortable value
191  // only when needed
192  if (data->sortableValue.IsEmpty()) {
193  nsString propertyID;
194  PRBool success = mCache->GetPropertyID(aPropertyDBID, propertyID);
195  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
196  nsCOMPtr<sbIPropertyInfo> propertyInfo;
197  nsresult rv = mPropertyManager->GetPropertyInfo(propertyID,
198  getter_AddRefs(propertyInfo));
199  NS_ENSURE_SUCCESS(rv, rv);
200 
201  rv = propertyInfo->MakeSortable(data->value, data->sortableValue);
202  NS_ENSURE_SUCCESS(rv, rv);
203  }
204  _retval.Assign(data->sortableValue);
205  return NS_OK;
206  }
207  }
208 
209  // The value hasn't been set, so return a void string.
210  _retval.SetIsVoid(PR_TRUE);
211  return NS_OK;
212 }
213 
214 NS_IMETHODIMP
215 sbLocalDatabaseResourcePropertyBag::
216  GetSearchablePropertyByID(PRUint32 aPropertyDBID,
217  nsAString& _retval)
218 {
219  if(aPropertyDBID > 0) {
220  nsAutoMonitor mon(mCache->mMonitor);
222 
223  if (mValueMap.Get(aPropertyDBID, &data)) {
224 
225  // Generate and cache the searchable value
226  // only when needed
227  if (data->searchableValue.IsEmpty()) {
228  nsString propertyID;
229  PRBool success = mCache->GetPropertyID(aPropertyDBID, propertyID);
230  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
231  nsCOMPtr<sbIPropertyInfo> propertyInfo;
232  nsresult rv = mPropertyManager->GetPropertyInfo(propertyID,
233  getter_AddRefs(propertyInfo));
234  NS_ENSURE_SUCCESS(rv, rv);
235 
236  rv = propertyInfo->MakeSearchable(data->value, data->searchableValue);
237  NS_ENSURE_SUCCESS(rv, rv);
238  }
239  _retval.Assign(data->searchableValue);
240  return NS_OK;
241  }
242  }
243 
244  // The value hasn't been set, so return a void string.
245  _retval.SetIsVoid(PR_TRUE);
246  return NS_OK;
247 }
248 
249 NS_IMETHODIMP
250 sbLocalDatabaseResourcePropertyBag::SetProperty(const nsAString & aPropertyID,
251  const nsAString & aValue)
252 {
253  nsresult rv;
254 
255  PRUint32 propertyDBID = mCache->GetPropertyDBIDInternal(aPropertyID);
256  if(propertyDBID == 0) {
257  rv = mCache->InsertPropertyIDInLibrary(aPropertyID, &propertyDBID);
258  NS_ENSURE_SUCCESS(rv, rv);
259  }
260 
261  nsCOMPtr<sbIPropertyInfo> propertyInfo;
262  rv = mPropertyManager->GetPropertyInfo(aPropertyID,
263  getter_AddRefs(propertyInfo));
264  NS_ENSURE_SUCCESS(rv, rv);
265 
266  PRBool valid = PR_FALSE;
267  rv = propertyInfo->Validate(aValue, &valid);
268  NS_ENSURE_SUCCESS(rv, rv);
269 
270 #if defined(PR_LOGGING)
271  if(NS_UNLIKELY(!valid)) {
272  LOG("Failed to set property id %s with value %s",
273  NS_ConvertUTF16toUTF8(aPropertyID).get(),
274  NS_ConvertUTF16toUTF8(aValue).get());
275  }
276 #endif
277 
278  NS_ENSURE_TRUE(valid, NS_ERROR_ILLEGAL_VALUE);
279 
280  // Find all properties whose secondary sort depends on this
281  // property
282  nsCOMPtr<sbIPropertyArray> dependentProperties;
283  rv = mPropertyManager->GetDependentProperties(aPropertyID,
284  getter_AddRefs(dependentProperties));
285  NS_ENSURE_SUCCESS(rv, rv);
286  PRUint32 dependentPropertyCount;
287  rv = dependentProperties->GetLength(&dependentPropertyCount);
288  NS_ENSURE_SUCCESS(rv, rv);
289 
290  PRUint32 previousDirtyCount = 0;
291  {
292  nsAutoMonitor mon(mCache->mMonitor);
293 
294  rv = PutValue(propertyDBID, aValue);
295  NS_ENSURE_SUCCESS(rv, rv);
296 
297  previousDirtyCount = mDirty.Count();
298 
299  // Mark the property that changed as dirty
300  mDirty.PutEntry(propertyDBID);
301  // Mark the property that changed as dirty for invalidation of guid arrays.
302  mDirtyForInvalidation.insert(propertyDBID);
303 
304  // Also mark as dirty any properties that use
305  // the changed property in their secondary sort values
306  if (dependentPropertyCount > 0) {
307  for (PRUint32 i = 0; i < dependentPropertyCount; i++) {
308  nsCOMPtr<sbIProperty> property;
309  rv = dependentProperties->GetPropertyAt(i, getter_AddRefs(property));
310  NS_ASSERTION(NS_SUCCEEDED(rv),
311  "Property cache failed to update dependent properties!");
312  if (NS_SUCCEEDED(rv)) {
313  nsString propertyID;
314  rv = property->GetId(propertyID);
315  NS_ASSERTION(NS_SUCCEEDED(rv),
316  "Property cache failed to update dependent properties!");
317  if (NS_SUCCEEDED(rv)) {
318  PRUint32 depPropDBID = mCache->GetPropertyDBIDInternal(propertyID);
319  mDirty.PutEntry(depPropDBID);
320  mDirtyForInvalidation.insert(depPropDBID);
321  }
322  }
323  }
324  }
325  }
326  // If this bag just became dirty, then let the property cache know.
327  // Only notify once in order to avoid unnecessarily locking the
328  // property cache
329  if (previousDirtyCount == 0) {
330  rv = mCache->AddDirty(mGuid, this);
331  NS_ENSURE_SUCCESS(rv, rv);
332  }
333 
334  // If this property is user editable we need to
335  // set the updated timestamp. We only
336  // track updates to user editable properties
337  // since that's all the user cares about.
338  PRBool userEditable = PR_FALSE;
339  rv = propertyInfo->GetUserEditable(&userEditable);
340  NS_ENSURE_SUCCESS(rv, rv);
341  if (userEditable) {
342 #ifdef DEBUG
343  // #updated is NOT user editable, so we won't die a firey
344  // recursive death here, but lets assert just in case.
345  NS_ENSURE_TRUE(!aPropertyID.EqualsLiteral(SB_PROPERTY_UPDATED),
346  NS_ERROR_UNEXPECTED);
347 
348 #endif
349  sbAutoString now((PRUint64)(PR_Now()/PR_MSEC_PER_SEC));
350  rv = SetProperty(NS_LITERAL_STRING(SB_PROPERTY_UPDATED), now);
351  NS_ENSURE_SUCCESS(rv, rv);
352  }
353 
354  // If this property is one that may be used in the metadata
355  // hash identity and it was set, then we need to recalculate
356  // the identity for this item.
357  PRBool usedInIdentity = PR_FALSE;
358  rv = propertyInfo->GetUsedInIdentity(&usedInIdentity);
359  NS_ENSURE_SUCCESS(rv, rv);
360 
361  if (usedInIdentity) {
362  // The propertybag has all the information we need to calculate the
363  // identity. Give it to the identity service to get an identity
364  nsString identity;
365  rv = mIdService->CalculateIdentityForBag(this, identity);
366  // If hash isn't available for this item, then just return.
367  if (rv == NS_ERROR_NOT_AVAILABLE) {
368  return NS_OK;
369  }
370  NS_ENSURE_SUCCESS(rv, rv);
371 
372  // save that identity
373  rv = mIdService->SaveIdentityToBag(this, identity);
374  NS_ENSURE_SUCCESS(rv, rv);
375  }
376 
377  return NS_OK;
378 }
379 
380 NS_IMETHODIMP sbLocalDatabaseResourcePropertyBag::Write()
381 {
382  nsresult rv = NS_OK;
383 
384  if(mDirty.Count() > 0) {
385  rv = mCache->Write();
386  NS_ENSURE_SUCCESS(rv, rv);
387  }
388 
389  return rv;
390 }
391 
392 nsresult
394  const nsAString& aValue)
395 {
396  nsAutoPtr<sbPropertyData> data(new sbPropertyData(aValue,
397  EmptyString(),
398  EmptyString()));
399  nsAutoMonitor mon(mCache->mMonitor);
400  PRBool success = mValueMap.Put(aPropertyID, data);
401  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
402  data.forget();
403 
404  return NS_OK;
405 }
406 
407 PRBool
409 {
410  if(mDirty.IsInitialized() && mDirty.GetEntry(aPropertyDBID)) {
411  return PR_TRUE;
412  }
413 
414  return PR_FALSE;
415 }
416 
417 nsresult
419  void *aClosure,
420  PRUint32 *aDirtyCount)
421 {
422  NS_ENSURE_ARG_POINTER(aClosure);
423  NS_ENSURE_ARG_POINTER(aDirtyCount);
424 
425  *aDirtyCount = mDirty.EnumerateEntries(aEnumFunc, aClosure);
426  return NS_OK;
427 }
428 
429 nsresult
431 {
432  nsAutoMonitor mon(mCache->mMonitor);
433  mDirty.Clear();
434  return NS_OK;
435 }
436 
437 nsresult
439 {
440  aDirty.clear();
441 
442  if(!mDirtyForInvalidation.empty()) {
443  aDirty = mDirtyForInvalidation;
444  mDirtyForInvalidation.clear();
445  }
446 
447  return NS_OK;
448 }
nsresult EnumerateDirty(nsTHashtable< nsUint32HashKey >::Enumerator aEnumFunc, void *aClosure, PRUint32 *aDirtyCount)
#define SB_PRLOG_SETUP(x)
Definition: sbDebugUtils.h:115
return NS_OK
#define LOG(args)
onPageChanged aValue
Definition: FeedWriter.js:1395
#define SB_PROPERTYMANAGER_CONTRACTID
PRBool GetPropertyID(PRUint32 aPropertyDBID, nsAString &aPropertyID)
#define SB_PROPERTY_UPDATED
function Enumerator(aItemsList)
static nsresult GetProperty(nsIPropertyBag2 *aProperties, nsAString const &aProp, nsAString &aValue)
void * aClosure
Definition: sbArray.cpp:52
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
function now()
NS_IMPL_THREADSAFE_ISUPPORTS1(sbLocalDatabaseResourcePropertyBag, sbILocalDatabaseResourcePropertyBag) sbLocalDatabaseResourcePropertyBag
PRUint32 const BAG_HASHTABLE_SIZE
observe data
Definition: FeedWriter.js:1329
_getSelectedPageStyle s i
nsresult PutValue(PRUint32 aPropertyID, const nsAString &aValue)
nsresult GetDirtyForInvalidation(std::set< PRUint32 > &aDirty)