mac_pref_file.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:14k
源码类别:

Symbian

开发平台:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK ***** 
  2.  * Version: RCSL 1.0/RPSL 1.0 
  3.  *  
  4.  * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
  5.  *      
  6.  * The contents of this file, and the files included with this file, are 
  7.  * subject to the current version of the RealNetworks Public Source License 
  8.  * Version 1.0 (the "RPSL") available at 
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed 
  10.  * the file under the RealNetworks Community Source License Version 1.0 
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
  12.  * in which case the RCSL will apply. You may also obtain the license terms 
  13.  * directly from RealNetworks.  You may not use this file except in 
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or 
  16.  * RCSL for the rights, obligations and limitations governing use of the 
  17.  * contents of the file.  
  18.  *  
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the 
  20.  * developer of the Original Code and owns the copyrights in the portions 
  21.  * it created. 
  22.  *  
  23.  * This file, and the files included with this file, is distributed and made 
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  28.  * 
  29.  * Technology Compatibility Kit Test Suite(s) Location: 
  30.  *    http://www.helixcommunity.org/content/tck 
  31.  * 
  32.  * Contributor(s): 
  33.  *  
  34.  * ***** END LICENSE BLOCK ***** */ 
  35. #include "platform/mac/mac_pref_file.h"
  36. #ifndef _MAC_UNIX
  37. #include "filespecutils.h"
  38. #endif
  39. CMacPrefFile*
  40. CMacPrefFile::CreatePrefSystemObject( CFStringRef appIDStringRef, 
  41. CFStringRef userNameRef, CFStringRef hostNameRef ) // static
  42. {
  43. HX_RESULT res = HXR_FAIL;
  44. CMacPrefFileCFPref* pObj = new CMacPrefFileCFPref;
  45. check_nonnull( pObj );
  46. if ( pObj )
  47. {
  48. pObj->SetPrefsID( appIDStringRef, userNameRef, hostNameRef );
  49. res = HXR_OK;
  50. }
  51. return (CMacPrefFile*) pObj;
  52. }
  53. #ifndef _MAC_UNIX
  54. CMacPrefFile*
  55. CMacPrefFile::CreatePrefXMLObject( CHXFileSpecifier& fileSpec, CFStringRef appIDStringRef, const char* pProductName ) // static
  56. {
  57. HX_RESULT res = HXR_FAIL;
  58. CMacPrefFileXML* pObj = new CMacPrefFileXML;
  59. check_nonnull( pObj );
  60. if ( pObj )
  61. {
  62. pObj->SetPrefsFileSpec( fileSpec, appIDStringRef, pProductName );
  63. res = HXR_OK;
  64. }
  65. return (CMacPrefFile*) pObj;
  66. }
  67. #endif // _MAC_UNIX
  68. #pragma mark -
  69. //////////////////////////////////////////////////////////////////////////////////
  70. //
  71. // CMacPrefFileCFPref methods are just thin wrappers on CFPreferences functions
  72. //
  73. CMacPrefFileCFPref::CMacPrefFileCFPref()
  74. : m_PrefsAppIDNameRef( NULL ),
  75.   m_UserNameRef( NULL ),
  76.   m_HostNameRef( NULL ),
  77.   m_bInitialized( false )
  78. {
  79. }
  80. CMacPrefFileCFPref::~CMacPrefFileCFPref()
  81. {
  82. Destroy();
  83. }
  84. void 
  85. CMacPrefFileCFPref::SetPrefsID( CFStringRef appIDStringRef, CFStringRef userNameRef, CFStringRef hostNameRef )
  86. {
  87. require_nonnull_void_return( appIDStringRef );
  88. require_nonnull_void_return( userNameRef );
  89. require_nonnull_void_return( hostNameRef );
  90. Destroy();
  91. m_PrefsAppIDNameRef = appIDStringRef;
  92. m_UserNameRef = userNameRef;
  93. m_HostNameRef = hostNameRef;
  94. ::CFRetain( (CFStringRef) m_PrefsAppIDNameRef );
  95. ::CFRetain( (CFStringRef) m_UserNameRef );
  96. ::CFRetain( (CFStringRef) m_HostNameRef );
  97. m_bInitialized = true;
  98. }
  99. void
  100. CMacPrefFileCFPref::Destroy()
  101. {
  102. if ( m_PrefsAppIDNameRef ) ::CFRelease( (CFStringRef) m_PrefsAppIDNameRef );
  103. if ( m_UserNameRef )  ::CFRelease( (CFStringRef) m_UserNameRef );
  104. if ( m_HostNameRef )  ::CFRelease( (CFStringRef) m_HostNameRef );
  105. m_bInitialized = false;
  106. }
  107. CFPropertyListRef
  108. CMacPrefFileCFPref::CopyValue( CFStringRef key )
  109. {
  110. require_return( m_bInitialized, NULL );
  111. return ::CFPreferencesCopyValue(key, m_PrefsAppIDNameRef, m_UserNameRef, m_HostNameRef );
  112. }
  113. void
  114. CMacPrefFileCFPref::SetValue( CFStringRef key, CFPropertyListRef value )
  115. {
  116. require_void_return( m_bInitialized );
  117. ::CFPreferencesSetValue( key, value, m_PrefsAppIDNameRef, m_UserNameRef, m_HostNameRef );
  118. }
  119. CFArrayRef
  120. CMacPrefFileCFPref::CopyKeyList( void )
  121. {
  122. require_return( m_bInitialized, NULL );
  123. return ::CFPreferencesCopyKeyList( m_PrefsAppIDNameRef, m_UserNameRef, m_HostNameRef );
  124. }
  125. void
  126. CMacPrefFileCFPref::Synchronize( void )
  127. {
  128. require_void_return( m_bInitialized );
  129. ::CFPreferencesSynchronize( m_PrefsAppIDNameRef, m_UserNameRef, m_HostNameRef );
  130. }
  131. #pragma mark -
  132. #ifndef _MAC_UNIX
  133. //////////////////////////////////////////////////////////////////////////////////
  134. //
  135. // CMacPrefFileXML methods keep the prefs in a dictionary stores as XML in a file
  136. //
  137. /*  Coherence strategy
  138.     
  139.     Because this object is implemented in a static library and included by multiple
  140.     shared libraries, we have to take care that our data (m_DictRef) doesn't
  141.     become stale compared to other instances data.
  142.     
  143.     The strategy we'll use to ensure coherence is:
  144.     - A "coherence value" is a 32-bit non-zero seed that we store via CFPreferences in user prefs
  145.       every time we write out the preferences.  We don't really care if the seed
  146.       is remembered across runs; it just needs to be the same for all running instances
  147.       of this object.  CFPreferences acts as a global cache for the app, so the 
  148.       seed is independent of the object instance and library.
  149.       
  150.     - For every get, if the object's seed doesn't match the seed stored in the user preferences,
  151.       we'll re-load the dictionary from the file prior to reading the caller's key's value.
  152.       
  153.     - For every set, we'll read the pref first to ensure that our dictionary is up-to-date
  154.       and to avoid saving the new set value if it's the same as the old value. If necessary, we'll
  155.       save the new value in our dictionary and write out the XML file, and increment and save
  156.       in CFPreferences our new seed. 
  157.       
  158.  */
  159.     
  160. CMacPrefFileXML::CMacPrefFileXML()
  161. {
  162. CFIndex noItems = 0;
  163. m_DictRef = ::CFDictionaryCreateMutable( kCFAllocatorDefault, noItems,
  164. &kCFTypeDictionaryKeyCallBacks,
  165. &kCFTypeDictionaryValueCallBacks );
  166. check_nonnull( m_DictRef );
  167. m_CoherenceSeed = 0;
  168. }
  169. CMacPrefFileXML::~CMacPrefFileXML()
  170. {
  171. if ( m_DictRef )
  172. {
  173. ::CFRelease( m_DictRef );
  174. }
  175. }
  176. void 
  177. CMacPrefFileXML::SetPrefsFileSpec( const CHXFileSpecifier& fileSpec, CFStringRef appIDStringRef, const char* pProductName )
  178. {
  179. check( fileSpec.IsSet() );
  180. require_nonnull_void_return( appIDStringRef );
  181. m_FileSpec = fileSpec;
  182. m_cfsPrefsAppIDNameRef = appIDStringRef;
  183. ::CFRetain( m_cfsPrefsAppIDNameRef );
  184. // the coherence key is the pref key where we save the new coherence seed value after each
  185. // write; it's something like "UpdateSync"
  186. m_cfsCoherenceKey = CHXString( pProductName ) + "Sync";
  187. UpdateDictFromFile( true ); // true -> always read from file
  188. }
  189. CFPropertyListRef
  190. CMacPrefFileXML::CopyValue( CFStringRef key )
  191. {
  192. require_nonnull_return( m_DictRef, NULL );
  193. UpdateDictFromFile( false ); // reads m_DictRect from the file if necessary, false -> only if necessary
  194. CFPropertyListRef value = ::CFDictionaryGetValue( m_DictRef, key );
  195. if ( value )
  196. {
  197. ::CFRetain( value );
  198. }
  199. return value;
  200. }
  201. void
  202. CMacPrefFileXML::SetValue( CFStringRef key, CFPropertyListRef value )
  203. {
  204. require_nonnull_void_return( m_DictRef );
  205. // Because of the expense of saving the dict to disk with each write to maintain
  206. // coherence, we want to set and save the value only if it differs from what's
  207. // currently in the dict.
  208. // We have to use CopyValue to get the value out of the dict because
  209. // CopyValue will ensure the dict is synced with what's been saved to the
  210. // file.
  211. CFPropertyListRef oldValue = CopyValue( key );
  212. if ( oldValue == NULL && value == NULL )
  213. {
  214. // both unset, so do nothing
  215. }
  216. else if ( oldValue && value 
  217. && ( kCFCompareEqualTo == ::CFStringCompare( (CFStringRef) oldValue, (CFStringRef) value, (CFStringCompareFlags) 0 ) ) )
  218. {
  219. // both set to the same thing, so do nothing
  220. }
  221. else
  222. {
  223. // change or remove the value in the dictionary
  224. if (value)
  225. {
  226. ::CFDictionarySetValue( m_DictRef, key, value );
  227. }
  228. else
  229. {
  230. ::CFDictionaryRemoveValue( m_DictRef, key );
  231. }
  232. // write immediately to maintain coherence
  233. WriteToFile();
  234. }
  235. if ( oldValue )
  236. {
  237. ::CFRelease( oldValue );
  238. }
  239. }
  240. CFArrayRef
  241. CMacPrefFileXML::CopyKeyList( void )
  242. {
  243. require_return( m_DictRef, NULL );
  244. UpdateDictFromFile( false ); // reads m_DictRect from the file if necessary, false -> only if necessary
  245. CFIndex noItems = 0;
  246. // we'll build an array of keys by calling CopyKeyListProc repeatedly
  247. CFMutableArrayRef arrayRef = ::CFArrayCreateMutable( kCFAllocatorDefault,
  248. noItems, &kCFTypeArrayCallBacks );
  249. if ( arrayRef )
  250. {
  251. ::CFDictionaryApplyFunction( m_DictRef, CMacPrefFileXML::CopyKeyListProc, (void*) arrayRef );
  252. }
  253. return arrayRef;
  254. }
  255. void
  256. CMacPrefFileXML::CopyKeyListProc( const void *key, const void *value, void *context )
  257. {
  258. check_nonnull( context );
  259. ::CFArrayAppendValue( (CFMutableArrayRef) context, key );
  260. }
  261. void
  262. CMacPrefFileXML::Synchronize( void )
  263. {
  264. // we always write out from SetPref, so all we need to do to sync is read in if necessary
  265. UpdateDictFromFile( false ); // false -> read file only if necessary
  266. }
  267. void
  268. CMacPrefFileXML::WriteToFile( void )
  269. {
  270. require_nonnull_void_return( m_DictRef );
  271. require_void_return( m_FileSpec.IsSet() );
  272. // increment and save our coherence seed value; don't let it be zero since
  273. // that's the value of the member variable when the object is first initialized
  274. m_CoherenceSeed++;
  275. if ( !m_CoherenceSeed )
  276. {
  277. m_CoherenceSeed++;
  278. }
  279. CFNumberRef cfnValue = ::CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &m_CoherenceSeed );
  280. check_nonnull( cfnValue );
  281. if ( cfnValue )
  282. {
  283. ::CFPreferencesSetValue(m_cfsCoherenceKey, cfnValue, m_cfsPrefsAppIDNameRef, kCFPreferencesCurrentUser, kCFPreferencesAnyHost );
  284. ::CFRelease( cfnValue );
  285. cfnValue = NULL;
  286. }
  287. // write the dictionary out to the file
  288. CFDataRef  xmlData = NULL;
  289. CFURLRef  fileURL = NULL;
  290. CHXString strPosixPath;
  291. CFStringRef posixPathString = NULL;
  292. OSStatus  err;
  293. Boolean bSuccess;
  294. const Boolean  kIsntADir = false;
  295. // we have to use a path in case the file doesn't exist yet, since
  296. // we can't get an FSRef for a file that doesn't exist
  297. strPosixPath = m_FileSpec.GetPOSIXPath();
  298. posixPathString = ::CFStringCreateWithCString( kCFAllocatorDefault, (const char *) strPosixPath,
  299. ::CFStringGetSystemEncoding() );
  300. require_nonnull( posixPathString, Bail );
  301. fileURL = ::CFURLCreateWithFileSystemPath( kCFAllocatorDefault, posixPathString, kCFURLPOSIXPathStyle, kIsntADir );
  302. require_nonnull( fileURL, Bail );
  303. xmlData = ::CFPropertyListCreateXMLData( kCFAllocatorDefault, m_DictRef );
  304. require_nonnull( xmlData, Bail );
  305. bSuccess = ::CFURLWriteDataAndPropertiesToResource( fileURL, xmlData, NULL, &err );
  306. check( bSuccess );
  307. Bail:
  308. if ( fileURL )  ::CFRelease( fileURL );
  309. if ( xmlData )  ::CFRelease( xmlData );
  310. if ( posixPathString )  ::CFRelease( posixPathString );
  311. }
  312. void
  313. CMacPrefFileXML::UpdateDictFromFile( Boolean forceReadFromFile )
  314. {
  315. require_nonnull_void_return( m_DictRef );
  316. require_void_return( m_FileSpec.IsSet() );
  317. // check if the coherence seed value matches the one saved in prefs; if not,
  318. // re-fill the dict from the file, and set our coherence seed to the saved
  319. // one
  320. SInt32 coherenceValue = GetGlobalCoherenceSeed();
  321. if ( ( coherenceValue != m_CoherenceSeed ) || forceReadFromFile )
  322. {
  323. ReadFromFile();
  324. m_CoherenceSeed = coherenceValue;
  325. }
  326.      return;
  327. }
  328. SInt32
  329. CMacPrefFileXML::GetGlobalCoherenceSeed()
  330. {
  331. SInt32 coherenceValue = 0;
  332. CFNumberRef cfnValue = (CFNumberRef) ::CFPreferencesCopyValue(m_cfsCoherenceKey, m_cfsPrefsAppIDNameRef, kCFPreferencesCurrentUser, kCFPreferencesAnyHost );
  333. if ( cfnValue )
  334. {
  335. check( ::CFGetTypeID( cfnValue ) == ::CFNumberGetTypeID() );
  336. Boolean bSuccess = ::CFNumberGetValue( cfnValue, kCFNumberSInt32Type, &coherenceValue );
  337. check( bSuccess );
  338. }
  339. return coherenceValue;
  340. }
  341. void
  342. CMacPrefFileXML::ReadFromFile( void )
  343. {
  344. require_nonnull_void_return( m_DictRef );
  345. require_void_return( m_FileSpec.IsSet() );
  346. CFDataRef  xmlData = NULL;
  347. CFPropertyListRef  props = NULL;
  348. CFURLRef  fileURL = NULL;
  349. CFStringRef errString = NULL;
  350. FSRef fileRef;
  351. OSStatus  err;
  352. Boolean bSuccess;
  353. ::CFDictionaryRemoveAllValues( m_DictRef );
  354. require_quiet( CHXFileSpecUtils::FileExists( m_FileSpec ), Bail );
  355. fileRef = ( FSRef ) m_FileSpec;
  356. fileURL = ::CFURLCreateFromFSRef( kCFAllocatorDefault, &fileRef );
  357. require_nonnull( fileURL, Bail );
  358. // read the XML file
  359. //
  360. // the nils are for retrieving property info; could we just use those
  361. // and skip the call to CFPropertyListCreateFromXMLData?
  362. bSuccess = ::CFURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault,
  363. fileURL, &xmlData, nil, nil, &err );
  364. require( bSuccess, Bail );
  365. // make a dictionary from the XML
  366. props = ::CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
  367. xmlData, kCFPropertyListMutableContainersAndLeaves, &errString );
  368. require( ::CFGetTypeID( props ) == ::CFDictionaryGetTypeID(), Bail );
  369. ::CFRelease( m_DictRef );
  370. m_DictRef = (CFMutableDictionaryRef) props;
  371. ::CFRetain( m_DictRef ); // the release below is kept for error-handlign cleanup
  372. Bail:
  373. if ( fileURL )  ::CFRelease( fileURL );
  374. if ( xmlData )  ::CFRelease( xmlData );
  375. if ( props )  ::CFRelease( props );
  376. if ( errString ) ::CFRelease( errString );
  377. }
  378. #endif // _MAC_UNIX