sbPropertyUnitConverter.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 
25 // Base class for property unit converters (sbIPropertyUnitConverter)
26 
28 
29 #include <sbIPropertyInfo.h>
30 
31 #include <locale.h>
32 
33 #include <prprf.h>
34 
35 #include <nsArrayEnumerator.h>
36 #include <nsCOMPtr.h>
37 #include <nsEnumeratorUtils.h>
38 #include <nsServiceManagerUtils.h>
39 
40 #include <sbLockUtils.h>
41 
42 static const char *gsFmtFloatOut = "%f";
43 static const char *gsFmtFloatIn = "%lf";
44 
45 static inline
46 PRUnichar GetDecimalPoint() {
47  PRUnichar decimalPoint = '.';
48  lconv *localInfo = localeconv();
49  if(localInfo) {
50  decimalPoint = localInfo->decimal_point[0];
51  }
52 
53  return decimalPoint;
54 }
55 
56 // ---------------------------------------------------------------------------
57 // sbPropertyUnit class - describes a unit exposed by sbIPropertyUnitConverter
58 // ---------------------------------------------------------------------------
59 
61 
62 // ctor
64 : mLock(nsnull)
65 , mInitialized(PR_FALSE)
66 {
67  mLock = PR_NewLock();
68  NS_ASSERTION(mLock, "sbPropertyUnit::mLock failed to create lock!");
69 }
70 
71 // ctor w/init
73  const nsAString& aShortName,
74  const nsAString& aID)
75 : mLock(nsnull)
76 , mName(aName)
77 , mShortName(aShortName)
78 , mID(aID)
79 , mInitialized(PR_TRUE)
80 {
81  mLock = PR_NewLock();
82  NS_ASSERTION(mLock, "sbPropertyOperator::mLock failed to create lock!");
83 }
84 
85 // dtor
87 {
88  if(mLock) {
89  PR_DestroyLock(mLock);
90  }
91 }
92 
93 // Init function, to use in conjunction with the default constructor
94 NS_IMETHODIMP sbPropertyUnit::Init(const nsAString & aName,
95  const nsAString & aShortName,
96  const nsAString & aID)
97 {
98  sbSimpleAutoLock lock(mLock);
99  NS_ENSURE_TRUE(mInitialized == PR_FALSE, NS_ERROR_ALREADY_INITIALIZED);
100 
101  mName = aName;
102  mShortName = aShortName;
103  mID = aID;
104 
105  mInitialized = PR_TRUE;
106 
107  return NS_OK;
108 }
109 
110 // Attribute getter for the the full name of the unit, ie. "Kilobyte".
111 // This should be a partial entity (eg. "&my.partial.entity"), so that
112 // it may be localized.
113 NS_IMETHODIMP sbPropertyUnit::GetName(nsAString & aName)
114 {
115  sbSimpleAutoLock lock(mLock);
116  if (!mInitialized) return NS_ERROR_NOT_INITIALIZED;
117  aName = mName;
118  return NS_OK;
119 }
120 
121 // Attribute getter for the short name of the unit, ie. "kB". This
122 // should be a partial entity (eg. "&my.partial.entity"), so that it may
123 // be localized.
124 NS_IMETHODIMP sbPropertyUnit::GetShortName(nsAString & aShortName)
125 {
126  sbSimpleAutoLock lock(mLock);
127  if (!mInitialized) return NS_ERROR_NOT_INITIALIZED;
128  aShortName = mShortName;
129  return NS_OK;
130 }
131 
132 // Attribute getter for the id of the unit, ie "kb". The id is not a
133 // localized string, it never changes, and is used in calls to
134 // sbIPropertyUnitConverter's convert function, to specify from and
135 // to which unit to convert.
136 NS_IMETHODIMP sbPropertyUnit::GetId(nsAString &aID)
137 {
138  sbSimpleAutoLock lock(mLock);
139  if (!mInitialized) return NS_ERROR_NOT_INITIALIZED;
140  aID = mID;
141  return NS_OK;
142 }
143 
144 // -----------------------------------------------------------------------------
145 // sbPropertyUnitConverter class - exposes the units and the conversion function
146 // -----------------------------------------------------------------------------
147 
149 
150 // ctor
152 : mLock(nsnull)
153 , mNativeInternal((PRUint32)-1)
154 , mDecimalPoint('.')
155 {
156  mLock = PR_NewLock();
157  NS_ASSERTION(mLock, "sbPropertyUnitConverter::mLock failed to create lock!");
158 
159  mDecimalPoint = GetDecimalPoint();
160 }
161 
162 // dtor
164 {
165  if(mLock) {
166  PR_DestroyLock(mLock);
167  }
168 }
169 
170 // Unit registration function. This is not part of the interface, it is meant to
171 // be called by inheritors of this class, so that they may register each of
172 // their particular units.
173 void sbPropertyUnitConverter::RegisterUnit(PRUint32 aUnitInternalID,
174  const nsAString &aUnitExternalID,
175  const nsAString &aUnitName,
176  const nsAString &aUnitShortName,
177  PRBool isNative)
178 {
179  sbSimpleAutoLock lock(mLock);
180  if (isNative) {
181  mNative = aUnitExternalID;
182  mNativeInternal = aUnitInternalID;
183  }
184  sbPropertyUnit *unit = new sbPropertyUnit(aUnitName,
185  aUnitShortName,
186  aUnitExternalID);
187  propertyUnit u = { unit, aUnitInternalID };
188  mUnits.push_back(u);
189  nsString key(aUnitExternalID);
190  mUnitsMap[key] = u;
191  mUnitsMapInternal[aUnitInternalID] = u;
192 }
193 
194 nsresult
196  PRFloat64 &aOutValue) {
197  // parse the string as a double value
198  NS_ConvertUTF16toUTF8 narrow(aValue);
199  if(PR_sscanf(narrow.get(), gsFmtFloatIn, &aOutValue) != 1) {
200  // wrong format, or empty string
201  return NS_ERROR_INVALID_ARG;
202  }
203  return NS_OK;
204 }
205 
206 nsresult
208  nsAString &aOutValue) {
209  // turn the double value into a string
210  char out[64] = {0};
211  if(PR_snprintf(out, 63, gsFmtFloatOut, aValue) == (PRUint32)-1) {
212  aOutValue = EmptyString();
213  return NS_ERROR_FAILURE;
214  }
215  NS_ConvertUTF8toUTF16 wide(out);
216  aOutValue = wide;
217  return NS_OK;
218 }
219 
221  PRUint32 aFromUnit,
222  PRUint32 aToUnit) {
223  // convert into native unit
224  nsresult rv = ConvertFromUnitToNative(aValue, aFromUnit, aValue);
225  NS_ENSURE_SUCCESS(rv, rv);
226 
227  // convert into requested unit
228  rv = ConvertFromNativeToUnit(aValue, aToUnit, aValue);
229  NS_ENSURE_SUCCESS(rv, rv);
230 
231  return NS_OK;
232 }
233 
234 // Unit conversion function
235 NS_IMETHODIMP
236 sbPropertyUnitConverter::Convert(const nsAString & aValue,
237  const nsAString & aFromUnitID,
238  const nsAString & aToUnitID,
239  PRInt32 aMinDecimals,
240  PRInt32 aMaxDecimals,
241  nsAString & _retval)
242 {
243  sbSimpleAutoLock lock(mLock);
244 
245  // If converting to and from the same unit, there is nothing to do
246  if (aFromUnitID.Equals(aToUnitID)) {
247  _retval = aValue;
248  return NS_OK;
249  }
250 
251  // look up the 'from' unit in the map
252  nsString fromKey(aFromUnitID);
253  propertyUnitMap::iterator fromUnitIterator = mUnitsMap.find(fromKey);
254  // not found ?
255  if (fromUnitIterator == mUnitsMap.end())
256  return NS_ERROR_INVALID_ARG;
257 
258  // look up the 'to' unit in the map
259  nsString toKey(aToUnitID);
260  propertyUnitMap::iterator toUnitIterator = mUnitsMap.find(toKey);
261  // not found ?
262  if (toUnitIterator == mUnitsMap.end())
263  return NS_ERROR_INVALID_ARG;
264 
265  // grab the internal ids
266  PRUint32 fromUnit = (*fromUnitIterator).second.mInternalId;
267  PRUint32 toUnit = (*toUnitIterator).second.mInternalId;
268 
269  PRFloat64 floatValue;
270  nsresult rv = SscanfFloat64(aValue, floatValue);
271  NS_ENSURE_SUCCESS(rv, rv);
272 
273  PerformConversion(floatValue, fromUnit, toUnit);
274 
275  nsAutoString out;
276  rv = SprintfFloat64(floatValue, out);
277  NS_ENSURE_SUCCESS(rv, rv);
278 
279  ApplyDecimalLimits(out, aMinDecimals, aMaxDecimals);
280 
281  _retval = out;
282 
283  return rv;
284 }
285 
286 // iteratively remove all trailing zeroes, and the period if necessary
288  PRUint32 decimal = aValue.FindChar(mDecimalPoint);
289  if (decimal != (PRUint32)-1) {
290  while (aValue.CharAt(aValue.Length()-1) == '0')
291  aValue.Cut(aValue.Length()-1, 1);
292  if (aValue.Length() == decimal+1)
293  aValue.Cut(decimal, 1);
294  }
295 }
296 
297 // limit precision of a value to N decimals
299  PRUint32 aDecimals) {
300  PRUint32 decimal = aValue.FindChar(mDecimalPoint);
301  if (decimal != (PRUint32)-1) {
302  PRUint32 p = decimal + aDecimals;
303  if (aValue.Length() > p+1) {
304  aValue.Cut(p+1, aValue.Length()-1-p);
305  }
306  }
307 }
308 
309 // force at least N decimals
311  PRUint32 aDecimals) {
312  PRUint32 decimal = aValue.FindChar(mDecimalPoint);
313  if (decimal == (PRUint32)-1) {
314  aValue += mDecimalPoint;
315  decimal = aValue.Length()-1;
316  }
317  PRUint32 n = aValue.Length() - decimal - 1;
318  for (;n<aDecimals;n++) {
319  aValue += NS_LITERAL_STRING("0");
320  }
321 }
322 
323 // depending on the sprintf docs you read, the # flag could do the job of this
324 // function, but moz doesn't support that flag because "The ANSI C spec. of the
325 // '#' flag is somewhat ambiguous and not ideal".
327  PRInt32 aMinDecimals,
328  PRInt32 aMaxDecimals) {
329  // strip to at most N decimals
330  if (aMaxDecimals != -1) {
331  LimitToNDecimals(aValue, aMaxDecimals);
332  }
333 
334  // remove trailing zeroes
335  RemoveTrailingZeroes(aValue);
336 
337  // ensure there are at least N decimals
338  if (aMinDecimals != -1) {
339  ForceToNDecimals(aValue, aMinDecimals);
340  }
341 }
342 
343 // accessor for an nsISimpleEnumerator of all the exposed units for this
344 // property.
345 NS_IMETHODIMP sbPropertyUnitConverter::GetUnits(nsISimpleEnumerator **aUnits)
346 {
347  NS_ENSURE_ARG_POINTER(aUnits);
348  sbSimpleAutoLock lock(mLock);
349 
350  nsCOMArray<sbIPropertyUnit> array;
351  propertyUnitList::iterator it = mUnits.begin();
352  for(; it != mUnits.end(); it++) {
353  propertyUnit u = (*it);
354  nsCOMPtr<sbIPropertyUnit> unit = u.mUnit;
355  array.AppendObject(unit);
356  }
357 
358  return NS_NewArrayEnumerator(aUnits, array);
359 }
360 
361 // accessor for the id of the native unit used by this property
362 NS_IMETHODIMP sbPropertyUnitConverter::GetNativeUnitId(nsAString &aNativeUnit)
363 {
364  sbSimpleAutoLock lock(mLock);
365  aNativeUnit = mNative;
366  return NS_OK;
367 }
368 
369 // url to the string bundle used to localize partial entities
370 NS_IMETHODIMP sbPropertyUnitConverter::GetStringBundle(nsAString &aStringBundle)
371 {
372  sbSimpleAutoLock lock(mLock);
373  aStringBundle = mStringBundle;
374  return NS_OK;
375 }
376 
377 // sets the url to the string bundle -- not part of the interface, called by inheritors
378 void sbPropertyUnitConverter::SetStringBundle(const nsAString &aStringBundle) {
379  mStringBundle = aStringBundle;
380 }
381 
382 // auto formats a value using the most suitable unit. this is the default
383 // implementation, which simply calls the property info's format function
384 NS_IMETHODIMP
385 sbPropertyUnitConverter::AutoFormat(const nsAString &aValue,
386  PRInt32 aMinDecimals,
387  PRInt32 aMaxDecimals,
388  nsAString &_retval) {
389  sbSimpleAutoLock lock(mLock);
390 
391  // parse as number
392  PRFloat64 v;
393  nsresult rv = SscanfFloat64(aValue, v);
394 
395  // It is okay to fail parsing, just default to propertyinfo.Format
396  if (rv != NS_OK) {
397  if (!mPropertyInfo)
398  return NS_ERROR_FAILURE;
399  nsCOMPtr<sbIPropertyInfo> propInfo = do_QueryReferent(mPropertyInfo, &rv);
400  if (NS_FAILED(rv) || !propInfo)
401  return NS_ERROR_FAILURE;
402  return propInfo->Format(aValue, _retval);
403  }
404 
405  // request the most suited unit for this number, implemented by inheritor
406  PRInt32 autoUnit = GetAutoUnit(v);
407 
408  // if not implemented by inheritor, default implementation returns -1,
409  // for 'not supported'.
410  if (autoUnit < 0) {
411  // in which case we just format using the property info native unit
412  if (!mPropertyInfo)
413  return NS_ERROR_FAILURE;
414  nsCOMPtr<sbIPropertyInfo> propInfo = do_QueryReferent(mPropertyInfo, &rv);
415  if (NS_FAILED(rv) || !propInfo)
416  return NS_ERROR_FAILURE;
417 
418  nsresult rv = propInfo->Format(aValue, _retval);
419  NS_ENSURE_SUCCESS(rv, rv);
420 
421  return NS_OK;
422  }
423 
424  // otherwise, format using that unit
425  PerformConversion(v, mNativeInternal, autoUnit);
426 
427  nsAutoString out;
428  SprintfFloat64(v, out);
429 
430  ApplyDecimalLimits(out, aMinDecimals, aMaxDecimals);
431 
432  // if we formatted to a particular unit, append the unit locale
433  if (autoUnit >= 0) {
434  out += NS_LITERAL_STRING(" ");
435 
436  // get the string for this unit
437  propertyUnitMapInternal::iterator
438  toUnitIterator = mUnitsMapInternal.find(autoUnit);
439 
440  if (toUnitIterator == mUnitsMapInternal.end())
441  return NS_ERROR_FAILURE;
442 
443  propertyUnit u = (*toUnitIterator).second;
444  nsCOMPtr<sbIPropertyUnit> unit = u.mUnit;
445 
446  nsString shortName;
447  nsresult rv = unit->GetShortName(shortName);
448  NS_ENSURE_SUCCESS(rv, rv);
449 
450  // if this is a partial entity, translate it
451  if (shortName.First() == '&' &&
452  shortName.CharAt(shortName.Length()-1) != ';') {
453  shortName.Cut(0, 1);
454 
455  // get bundle if we haven't done so yet
456  if (!mStringBundleObject) {
457  nsCOMPtr<nsIStringBundleService> stringBundleService =
458  do_GetService("@mozilla.org/intl/stringbundle;1", &rv);
459 
460  NS_ConvertUTF16toUTF8 url(mStringBundle);
461  rv = stringBundleService->CreateBundle(url.get(),
462  getter_AddRefs(mStringBundleObject));
463  NS_ENSURE_SUCCESS(rv, rv);
464  }
465 
466  // localize string
467  nsString str;
468  rv = mStringBundleObject->GetStringFromName(shortName.get(),
469  getter_Copies(str));
470  NS_ENSURE_SUCCESS(rv, rv);
471 
472  // and append it
473  out += str;
474  } else {
475  // no localization needed, just append the shortName value
476  out += shortName;
477  }
478  }
479 
480  _retval = out;
481 
482  return NS_OK;
483 }
484 
485 // not exposed to the interface, this function allows the propertyinfo that
486 // instantiates this converter to register itself into it
487 NS_IMETHODIMP
488 sbPropertyUnitConverter::SetPropertyInfo(sbIPropertyInfo *aPropInfo) {
489  sbSimpleAutoLock lock(mLock);
490 
491  if (mPropertyInfo)
492  return NS_ERROR_ALREADY_INITIALIZED;
493 
494  nsresult rv;
495  mPropertyInfo = do_GetWeakReference(aPropInfo, &rv);
496  NS_ENSURE_SUCCESS(rv, rv);
497 
498  return NS_OK;
499 }
500 
501 // returns the propertyinfo object that owns this converter
502 NS_IMETHODIMP
503 sbPropertyUnitConverter::GetPropertyInfo(sbIPropertyInfo **_retval) {
504  NS_ENSURE_ARG_POINTER(_retval);
505 
506  sbSimpleAutoLock lock(mLock);
507 
508  if (!mPropertyInfo)
509  return NS_ERROR_NOT_INITIALIZED;
510 
511  nsresult rv;
512  nsCOMPtr<sbIPropertyInfo> propInfo = do_QueryReferent(mPropertyInfo, &rv);
513  NS_ENSURE_SUCCESS(rv, rv);
514 
515  *_retval = propInfo;
516 
517  return NS_OK;
518 }
519 
NS_DECL_ISUPPORTS NS_DECL_SBIPROPERTYUNIT sbPropertyUnit()
propertyUnitMapInternal mUnitsMapInternal
void ApplyDecimalLimits(nsAString &aValue, PRInt32 aMinDecimals, PRInt32 aMaxDecimals)
return NS_OK
nsresult SprintfFloat64(const PRFloat64 aValue, nsAString &aOutValue)
static const char * gsFmtFloatIn
onPageChanged aValue
Definition: FeedWriter.js:1395
Property unit interface This interface describes a property unit.
inArray array
nsresult SscanfFloat64(const nsAString &aValue, PRFloat64 &aOutValue)
NS_IMETHODIMP Init(const nsAString &aName, const nsAString &aShortName, const nsAString &aID)
void ForceToNDecimals(nsAString &aValue, PRUint32 aDecimals)
nsCOMPtr< nsIWeakReference > mPropertyInfo
void RemoveTrailingZeroes(nsAString &aValue)
NS_IMPL_THREADSAFE_ISUPPORTS1(sbDeviceCapsCompatibility, sbIDeviceCapsCompatibility) sbDeviceCapsCompatibility
NS_IMETHOD ConvertFromUnitToNative(PRFloat64 aValue, PRUint32 aUnitID, PRFloat64 &_retVal)=0
static PRUnichar GetDecimalPoint()
void LimitToNDecimals(nsAString &aValue, PRUint32 aDecimals)
An interface used to describe a metadata property for use by the UI and other sbILibrary interfaces (...
nsCOMPtr< nsIStringBundle > mStringBundleObject
static const char * gsFmtFloatOut
virtual PRInt32 GetAutoUnit(PRFloat64 aValue)
_updateCookies aName
function url(spec)
Property unit converter interface This is an interface songbird property converter, used to convert a property value between its various units representations.
void RegisterUnit(PRUint32 aUnitInternalID, const nsAString &aUnitExternalID, const nsAString &aUnitName, const nsAString &aUnitShortName, PRBool isNative=PR_FALSE)
nsresult PerformConversion(PRFloat64 &aValue, PRUint32 aFromUnit, PRUint32 aToUnit)
NS_IMETHOD ConvertFromNativeToUnit(PRFloat64 aValue, PRUint32 aUnitID, PRFloat64 &_retVal)=0
void SetStringBundle(const nsAString &aStringBundle)