Merge pull request #496 from nicolas-raoul/unit-test-for-issue2907
[mono.git] / mcs / class / System / System.Configuration / ApplicationSettingsBase.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (C) 2005, 2006 Novell, Inc (http://www.novell.com)
21 //
22
23 #if CONFIGURATION_DEP && !TARGET_JVM
24 extern alias PrebuiltSystem;
25 using NameValueCollection = PrebuiltSystem.System.Collections.Specialized.NameValueCollection;
26 #endif
27 #if CONFIGURATION_DEP
28 using System.IO;
29 using System.Xml.Serialization;
30 #endif
31
32 using System.ComponentModel;
33 using System.Reflection;
34 using System.Threading;
35 using System.Collections.Specialized;
36
37 namespace System.Configuration {
38
39         public abstract class ApplicationSettingsBase : SettingsBase, INotifyPropertyChanged
40         {
41                 protected ApplicationSettingsBase ()
42                 {
43                         Initialize (Context, Properties, Providers);
44                 }
45
46                 protected ApplicationSettingsBase (IComponent owner)
47                         : this (owner, String.Empty)
48                 {
49                 }
50  
51                 protected ApplicationSettingsBase (string settingsKey)
52                 {
53                         this.settingsKey = settingsKey;
54
55                         Initialize (Context, Properties, Providers);
56                 }
57
58                 protected ApplicationSettingsBase (IComponent owner, 
59                                                    string settingsKey)
60                 {
61                         if (owner == null)
62                                 throw new ArgumentNullException ();
63
64 #if (CONFIGURATION_DEP)
65                         providerService = (ISettingsProviderService)owner.Site.GetService(typeof (ISettingsProviderService));
66 #endif
67                         this.settingsKey = settingsKey;
68
69                         Initialize (Context, Properties, Providers);
70                 }
71
72                 public event PropertyChangedEventHandler PropertyChanged;
73                 public event SettingChangingEventHandler SettingChanging;
74                 public event SettingsLoadedEventHandler SettingsLoaded;
75                 public event SettingsSavingEventHandler SettingsSaving;
76
77                 public object GetPreviousVersion (string propertyName)
78                 {
79                         throw new NotImplementedException ();
80                 }
81
82                 public void Reload ()
83                 {
84 #if (CONFIGURATION_DEP)
85                         foreach (SettingsProvider provider in Providers) {
86                                 IApplicationSettingsProvider iasp = provider as IApplicationSettingsProvider;
87                                 CacheValuesByProvider(provider);
88                         }
89 #endif
90                 }
91
92                 public void Reset()
93                 {
94 #if (CONFIGURATION_DEP)
95                         Reload ();
96                         foreach (SettingsPropertyValue pv in PropertyValues)
97                                 pv.PropertyValue = pv.Reset();
98 #endif
99                 }
100
101                 public override void Save()
102                 {
103 #if (CONFIGURATION_DEP)
104                         Context.CurrentSettings = this;
105                         /* ew.. this needs to be more efficient */
106                         foreach (SettingsProvider provider in Providers) {
107                                 SettingsPropertyValueCollection cache = new SettingsPropertyValueCollection ();
108
109                                 foreach (SettingsPropertyValue val in PropertyValues) {
110                                         if (val.Property.Provider == provider)
111                                                 cache.Add (val);
112                                 }
113
114                                 if (cache.Count > 0)
115                                         provider.SetPropertyValues (Context, cache);
116                         }
117                         Context.CurrentSettings = null;
118 #endif
119                 }
120
121                 public virtual void Upgrade()
122                 {
123                 }
124
125                 protected virtual void OnPropertyChanged (object sender, 
126                                                           PropertyChangedEventArgs e)
127                 {
128                         if (PropertyChanged != null)
129                                 PropertyChanged (sender, e);
130                 }
131
132                 protected virtual void OnSettingChanging (object sender, 
133                                                           SettingChangingEventArgs e)
134                 {
135                         if (SettingChanging != null)
136                                 SettingChanging (sender, e);
137                 }
138
139                 protected virtual void OnSettingsLoaded (object sender, 
140                                                          SettingsLoadedEventArgs e)
141                 {
142                         if (SettingsLoaded != null)
143                                 SettingsLoaded (sender, e);
144                 }
145
146                 protected virtual void OnSettingsSaving (object sender, 
147                                                          CancelEventArgs e)
148                 {
149                         if (SettingsSaving != null)
150                                 SettingsSaving (sender, e);
151                 }
152
153                 [Browsable (false)]
154                 public override SettingsContext Context {
155                         get {
156                                 if (IsSynchronized)
157                                         Monitor.Enter (this);
158
159                                 try {
160                                         if (context == null) {
161                                                 context = new SettingsContext ();
162                                                 context ["SettingsKey"] = "";
163                                                 Type type = GetType ();
164                                                 context ["GroupName"] = type.FullName;
165                                                 context ["SettingsClassType"] = type;
166                                         }
167
168                                         return context;
169                                 } finally {
170                                         if (IsSynchronized)
171                                                 Monitor.Exit (this);
172                                 }
173                         }
174                 }
175
176                 void CacheValuesByProvider (SettingsProvider provider)
177                 {
178                         SettingsPropertyCollection col = new SettingsPropertyCollection ();
179
180                         foreach (SettingsProperty p in Properties) {
181                                 if (p.Provider == provider)
182                                         col.Add (p);
183                         }
184
185                         if (col.Count > 0) {
186                                 SettingsPropertyValueCollection vals = provider.GetPropertyValues (Context, col);
187                                 foreach (SettingsPropertyValue prop in vals) {
188                                         if (PropertyValues [prop.Name] != null)
189                                                 PropertyValues [prop.Name].PropertyValue = prop.PropertyValue;
190                                         else
191                                                 PropertyValues.Add (prop);
192                                 }
193                         }
194
195                         OnSettingsLoaded (this, new SettingsLoadedEventArgs (provider));
196                 }
197
198                 void InitializeSettings (SettingsPropertyCollection settings)
199                 {
200                 }
201
202                 object GetPropertyValue (string propertyName)
203                 {
204                         SettingsProperty prop = Properties [ propertyName ];
205
206                         if (prop == null)
207                                 throw new SettingsPropertyNotFoundException (propertyName);
208
209                         if (propertyValues == null)
210                                 InitializeSettings (Properties);
211
212                         if (PropertyValues [ propertyName ] == null)
213                                 CacheValuesByProvider (prop.Provider);
214
215                         return PropertyValues [ propertyName ].PropertyValue;
216                 }
217
218                 [MonoTODO]
219                 public override object this [ string propertyName ] {
220                         get {
221                                 if (IsSynchronized) {
222                                         lock (this) {
223                                                 return GetPropertyValue (propertyName);
224                                         }
225                                 }
226
227                                 return GetPropertyValue (propertyName);
228                         }
229                         set {
230                                 SettingsProperty prop = Properties [ propertyName ];
231
232                                 if (prop == null)
233                                         throw new SettingsPropertyNotFoundException (propertyName);
234
235                                 if (prop.IsReadOnly)
236                                         throw new SettingsPropertyIsReadOnlyException (propertyName);
237
238                                 /* XXX check the type of the property vs the type of @value */
239                                 if (value != null &&
240                                     !prop.PropertyType.IsAssignableFrom (value.GetType()))
241                                         throw new SettingsPropertyWrongTypeException (propertyName);
242
243                                 if (PropertyValues [ propertyName ] == null)
244                                         CacheValuesByProvider (prop.Provider);
245
246                                 SettingChangingEventArgs changing_args = new SettingChangingEventArgs (propertyName,
247                                                                                                        GetType().FullName,
248                                                                                                        settingsKey,
249                                                                                                        value,
250                                                                                                        false);
251
252                                 OnSettingChanging (this, changing_args);
253
254                                 if (changing_args.Cancel == false) {
255                                         /* actually set the value */
256                                         PropertyValues [ propertyName ].PropertyValue = value;
257
258                                         /* then emit PropertyChanged */
259                                         OnPropertyChanged (this, new PropertyChangedEventArgs (propertyName));
260                                 }
261                         }
262                 }
263
264 #if (CONFIGURATION_DEP)
265                 [Browsable (false)]
266                 public override SettingsPropertyCollection Properties {
267                         get {
268                                 if (IsSynchronized)
269                                         Monitor.Enter (this);
270
271                                 try {
272                                         if (properties == null) {
273                                                 SettingsProvider local_provider = null;
274
275                                                 properties = new SettingsPropertyCollection ();
276
277                                                 Type this_type = GetType();
278                                                 SettingsProviderAttribute[] provider_attrs = (SettingsProviderAttribute[])this_type.GetCustomAttributes (typeof (SettingsProviderAttribute), false);;
279                                                 if (provider_attrs != null && provider_attrs.Length != 0) {
280                                                         Type provider_type = Type.GetType (provider_attrs[0].ProviderTypeName);
281                                                         SettingsProvider provider = (SettingsProvider) Activator.CreateInstance (provider_type);
282                                                         provider.Initialize (null, null);
283                                                         if (provider != null && Providers [provider.Name] == null) {
284                                                                 Providers.Add (provider);
285                                                                 local_provider = provider;
286                                                         }
287                                                 }
288
289                                                 PropertyInfo[] type_props = this_type.GetProperties ();
290                                                 foreach (PropertyInfo prop in type_props) { // only public properties
291                                                         SettingAttribute[] setting_attrs = (SettingAttribute[])prop.GetCustomAttributes (typeof (SettingAttribute), false);
292                                                         if (setting_attrs == null || setting_attrs.Length == 0)
293                                                                 continue;
294                                                         CreateSettingsProperty (prop, properties, ref local_provider);
295                                                 }
296                                         }
297
298                                         return properties;
299                                 } finally {
300                                         if (IsSynchronized)
301                                                 Monitor.Exit (this);
302                                 }
303                         }
304                 }
305
306                 void CreateSettingsProperty (PropertyInfo prop, SettingsPropertyCollection properties, ref SettingsProvider local_provider)
307                 {
308                         SettingsAttributeDictionary dict = new SettingsAttributeDictionary ();
309                         SettingsProvider provider = null;
310                         object defaultValue = null;
311                         SettingsSerializeAs serializeAs = SettingsSerializeAs.String;
312                         bool explicitSerializeAs = false;
313
314                         foreach (Attribute a in prop.GetCustomAttributes (false)) {
315                                 /* the attributes we handle natively here */
316                                 if (a is SettingsProviderAttribute) {
317                                         Type provider_type = Type.GetType (((SettingsProviderAttribute)a).ProviderTypeName);
318                                         provider = (SettingsProvider) Activator.CreateInstance (provider_type);
319                                         provider.Initialize (null, null);
320                                 }
321                                 else if (a is DefaultSettingValueAttribute) {
322                                         defaultValue = ((DefaultSettingValueAttribute)a).Value;
323                                 }
324                                 else if (a is SettingsSerializeAsAttribute) {
325                                         serializeAs = ((SettingsSerializeAsAttribute)a).SerializeAs;
326                                         explicitSerializeAs = true;
327                                 }
328                                 else if (a is ApplicationScopedSettingAttribute ||
329                                          a is UserScopedSettingAttribute) {
330                                         dict.Add (a.GetType(), a);
331                                 }
332                                 else {
333                                         dict.Add (a.GetType(), a);
334                                 }
335                         }
336
337                         if (!explicitSerializeAs) {
338                                 // DefaultValue is a string and if we can't convert from string to the 
339                                 // property type then the only other option left is for the string to 
340                                 // be XML.
341                                 //
342                                 TypeConverter converter = TypeDescriptor.GetConverter (prop.PropertyType);
343                                 if (converter != null && 
344                                     (!converter.CanConvertFrom (typeof (string)) || 
345                                      !converter.CanConvertTo (typeof (string))))
346                                         serializeAs = SettingsSerializeAs.Xml;
347                         }
348
349                         SettingsProperty setting =
350                                 new SettingsProperty (prop.Name, prop.PropertyType, provider, false /* XXX */,
351                                                       defaultValue /* XXX always a string? */, serializeAs, dict,
352                                                       false, false);
353
354
355                         if (providerService != null)
356                                 setting.Provider = providerService.GetSettingsProvider (setting);
357
358                         if (provider == null) {
359                                 if (local_provider == null) {
360                                         local_provider = new LocalFileSettingsProvider () as SettingsProvider;
361                                         local_provider.Initialize (null, null);
362                                 }
363                                 setting.Provider = local_provider;
364                                 // .NET ends up to set this to providers.
365                                 provider = local_provider;
366                         }
367
368                         if (provider != null) {
369                                 /* make sure we're using the same instance of a
370                                    given provider across multiple properties */
371                                 SettingsProvider p = Providers[provider.Name];
372                                 if (p != null)
373                                         setting.Provider = p;
374                         }
375
376                         properties.Add (setting);
377
378                         if (setting.Provider != null && Providers [setting.Provider.Name] == null)
379                                 Providers.Add (setting.Provider);
380                 }
381 #endif
382
383                 [Browsable (false)]
384                 public override SettingsPropertyValueCollection PropertyValues {
385                         get {
386                                 if (IsSynchronized)
387                                         Monitor.Enter (this);
388
389                                 try {
390                                         if (propertyValues == null) {
391                                                 propertyValues = new SettingsPropertyValueCollection ();
392                                         }
393
394                                         return propertyValues;
395                                 } finally {
396                                         if (IsSynchronized)
397                                                 Monitor.Exit (this);
398                                 }
399                         }
400                 }
401
402                 [Browsable (false)]
403                 public override SettingsProviderCollection Providers {
404                         get {
405                                 if (IsSynchronized)
406                                         Monitor.Enter (this);
407
408                                 try {
409                                         if (providers == null)
410                                                 providers = new SettingsProviderCollection ();
411
412                                         return providers;
413                                 } finally {
414                                         if (IsSynchronized)
415                                                 Monitor.Exit (this);
416                                 }
417                         }
418                 }
419
420                 [Browsable (false)]
421                 public string SettingsKey {
422                         get {
423                                 return settingsKey;
424                         }
425                         set {
426                                 settingsKey = value;
427                         }
428                 }
429
430                 string settingsKey;
431                 SettingsContext context;
432 #if (CONFIGURATION_DEP)         
433                 SettingsPropertyCollection properties;
434                 ISettingsProviderService providerService;
435 #endif
436                 SettingsPropertyValueCollection propertyValues;
437                 SettingsProviderCollection providers;
438         }
439
440 }
441