Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
SettingsService.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.Core.IO;
11 using SiliconStudio.Core.Yaml;
12 
13 namespace SiliconStudio.Presentation.Settings
14 {
15  /// <summary>
16  /// A static class that manages settings loading and saving for an application.
17  /// </summary>
18  public static class SettingsService
19  {
20  /// <summary>
21  /// A dictionary containing every existing <see cref="SettingsKey"/>.
22  /// </summary>
23  private static readonly Dictionary<UFile, SettingsKey> SettingsKeys = new Dictionary<UFile, SettingsKey>();
24 
25  /// <summary>
26  /// A <see cref="SettingsProfile"/> that contains the default value of all registered <see cref="SettingsKey"/>.
27  /// </summary>
28  private static readonly SettingsProfile DefaultProfile = new SettingsProfile(null);
29 
30  private static readonly List<SettingsProfile> ProfileList = new List<SettingsProfile>();
31 
32  private static SettingsProfile currentProfile;
33 
34  static SettingsService()
35  {
36  ProfileList.Add(DefaultProfile);
37  currentProfile = DefaultProfile;
38  Logger = new LoggerResult();
39  }
40 
41  /// <summary>
42  /// Gets the logger associated to the <see cref="SettingsService"/>.
43  /// </summary>
44  public static LoggerResult Logger { get; private set; }
45 
46  /// <summary>
47  /// Gets or sets the <see cref="SettingsProfile"/> that is currently active.
48  /// </summary>
49  public static SettingsProfile CurrentProfile { get { return currentProfile; } set { ChangeCurrentProfile(currentProfile, value); } }
50 
51  /// <summary>
52  /// Gets the list of registered profiles.
53  /// </summary>
54  public static IEnumerable<SettingsProfile> Profiles { get { return ProfileList; } }
55 
56  /// <summary>
57  /// Raised when a settings file has been loaded.
58  /// </summary>
59  public static event EventHandler<SettingsFileLoadedEventArgs> SettingsFileLoaded;
60 
61  /// <summary>
62  /// Gets a list of all registered <see cref="SettingsKey"/> instances.
63  /// </summary>
64  /// <param name="includeNonEditable">Inidcates whether to include or not settings key which have the <see cref="SettingsKey.IsEditable"/> property set to <c>false</c>.</param>
65  /// <returns>A list of all registered <see cref="SettingsKey"/> instances.</returns>
66  public static List<SettingsKey> GetAllSettingsKeys(bool includeNonEditable)
67  {
68  return (includeNonEditable ? SettingsKeys.Values : SettingsKeys.Values.Where(x => x.IsEditable)).ToList();
69  }
70 
71  /// <summary>
72  /// Creates a new settings profile.
73  /// </summary>
74  /// <param name="setAsCurrent">If <c>true</c>, the created profile will also be set as <see cref="CurrentProfile"/>.</param>
75  /// <param name="parent">The parent profile of the settings to create. If <c>null</c>, a default profile will be used.</param>
76  /// <returns>A new instance of the <see cref="SettingsProfile"/> class.</returns>
77  public static SettingsProfile CreateSettingsProfile(bool setAsCurrent, SettingsProfile parent = null)
78  {
79  var profile = new SettingsProfile(parent ?? DefaultProfile);
80  ProfileList.Add(profile);
81  if (setAsCurrent)
82  CurrentProfile = profile;
83 
84  return profile;
85  }
86 
87  /// <summary>
88  /// Loads a settings profile from the given file.
89  /// </summary>
90  /// <param name="filePath">The path of the file from which to load settings.</param>
91  /// <param name="setAsCurrent">If <c>true</c>, the loaded profile will also be set as <see cref="CurrentProfile"/>.</param>
92  /// <param name="parent">The profile to use as parent for the loaded profile. If <c>null</c>, a default profile will be used.</param>
93  /// <returns><c>true</c> if settings were correctly loaded, <c>false</c> otherwise.</returns>
94  public static SettingsProfile LoadSettingsProfile(UFile filePath, bool setAsCurrent, SettingsProfile parent = null)
95  {
96  if (filePath == null) throw new ArgumentNullException("filePath");
97 
98  if (!File.Exists(filePath))
99  {
100  Logger.Error("Settings file [{0}] was not found", filePath);
101  return null;
102  }
103 
104  SettingsProfile profile;
105  try
106  {
107  SettingsFile settingsFile;
108  using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
109  {
110  settingsFile = (SettingsFile)YamlSerializer.Deserialize(stream);
111  }
112  profile = new SettingsProfile(parent ?? DefaultProfile) { FilePath = filePath };
113 
114  foreach (var settings in settingsFile.Settings)
115  {
116  SettingsKey key;
117  var value = settings.Value;
118  if (SettingsKeys.TryGetValue(settings.Key, out key))
119  {
120  value = key.ConvertValue(value);
121  }
122  profile.SetValue(settings.Key, value);
123  }
124  }
125  catch (Exception e)
126  {
127  Logger.Error("Error while loading settings file [{0}]: {1}", e, filePath, Extensions.StringExtensions.FormatExceptionForReport(e));
128  return null;
129  }
130 
131  ProfileList.Add(profile);
132  if (setAsCurrent)
133  {
134  CurrentProfile = profile;
135  }
136 
137  var handler = SettingsFileLoaded;
138  if (handler != null)
139  {
140  SettingsFileLoaded(null, new SettingsFileLoadedEventArgs(filePath));
141  }
142  return profile;
143  }
144 
145  /// <summary>
146  /// Reloads a profile from its file, updating the value that have changed.
147  /// </summary>
148  /// <param name="profile">The profile to reload.</param>
149  public static void ReloadSettingsProfile(SettingsProfile profile)
150  {
151  var filePath = profile.FilePath;
152  if (filePath == null) throw new ArgumentException("profile");
153  if (!File.Exists(filePath))
154  {
155  Logger.Error("Settings file [{0}] was not found", filePath);
156  throw new ArgumentException("profile");
157  }
158 
159  try
160  {
161  SettingsFile settingsFile;
162  using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
163  {
164  settingsFile = (SettingsFile)YamlSerializer.Deserialize(stream);
165  }
166 
167  foreach (var settings in settingsFile.Settings)
168  {
169  SettingsKey key;
170  var value = settings.Value;
171  if (SettingsKeys.TryGetValue(settings.Key, out key))
172  {
173  value = key.ConvertValue(value);
174  }
175  profile.SetValue(settings.Key, value);
176  }
177  }
178  catch (Exception e)
179  {
180  Logger.Error("Error while loading settings file [{0}]: {1}", e, filePath, Extensions.StringExtensions.FormatExceptionForReport(e));
181  }
182 
183  var handler = SettingsFileLoaded;
184  if (handler != null)
185  {
186  SettingsFileLoaded(null, new SettingsFileLoadedEventArgs(filePath));
187  }
188  }
189 
190  /// <summary>
191  /// Unloads a profile that was previously loaded.
192  /// </summary>
193  /// <param name="profile">The profile to unload.</param>
194  public static void UnloadSettingsProfile(SettingsProfile profile)
195  {
196  if (profile == DefaultProfile)
197  throw new ArgumentException("The default profile cannot be unloaded");
198  if (profile == CurrentProfile)
199  throw new InvalidOperationException("Unable to unload the current profile.");
200  ProfileList.Remove(profile);
201  }
202 
203  /// <summary>
204  /// Saves the given settings profile to a file at the given path.
205  /// </summary>
206  /// <param name="profile">The profile to save.</param>
207  /// <param name="filePath">The path of the file.</param>
208  /// <returns><c>true</c> if the file was correctly saved, <c>false</c> otherwise.</returns>
209  public static bool SaveSettingsProfile(SettingsProfile profile, UFile filePath)
210  {
211  if (profile == null) throw new ArgumentNullException("profile");
212  try
213  {
214  profile.Saving = true;
215  Directory.CreateDirectory(filePath.GetFullDirectory());
216 
217  var settingsFile = new SettingsFile();
218  foreach (var entry in profile.Settings.Values)
219  {
220  settingsFile.Settings.Add(entry.Name, entry.GetSerializableValue());
221  }
222 
223  using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
224  {
225  YamlSerializer.Serialize(stream, settingsFile);
226  }
227  }
228  catch (Exception e)
229  {
230  Logger.Error("Error while saving settings file [{0}]: {1}", e, filePath, Extensions.StringExtensions.FormatExceptionForReport(e));
231  return false;
232  }
233  finally
234  {
235  profile.Saving = false;
236  }
237  return true;
238  }
239 
240  /// <summary>
241  /// Gets the settings key that matches the given name.
242  /// </summary>
243  /// <param name="name">The name of the settings property to fetch.</param>
244  /// <returns>The settings key that matches the given name, or <c>null</c>.</returns>
245  public static SettingsKey GetSettingsKey(UFile name)
246  {
247  SettingsKey key;
248  SettingsKeys.TryGetValue(name, out key);
249  return key;
250  }
251 
252  /// <summary>
253  /// Clears the current settings, including registered <see cref="SettingsKey"/> and <see cref="SettingsProfile"/> instances. This method should be used only for tests.
254  /// </summary>
255  public static void ClearSettings()
256  {
257  CurrentProfile = DefaultProfile;
258  CurrentProfile.ValidateSettingsChanges();
259  ProfileList.Clear();
260  DefaultProfile.Settings.Clear();
261  SettingsKeys.Clear();
262  }
263 
264  internal static void RegisterSettingsKey(UFile name, object defaultValue, SettingsKey settingsKey)
265  {
266  SettingsKeys.Add(name, settingsKey);
267  var entry = SettingsEntry.CreateFromValue(DefaultProfile, name, defaultValue);
268  DefaultProfile.RegisterEntry(entry);
269  // Ensure that the value is converted to the key type in each loaded profile.
270  foreach (var profile in Profiles.Where(x => x != DefaultProfile))
271  {
272  if (profile.Settings.TryGetValue(name, out entry))
273  {
274  var convertedValue = settingsKey.ConvertValue(entry.Value);
275  entry = SettingsEntry.CreateFromValue(profile, name, convertedValue);
276  profile.Settings[name] = entry;
277  }
278  }
279  }
280 
281  private static void ChangeCurrentProfile(SettingsProfile oldProfile, SettingsProfile newProfile)
282  {
283  if (oldProfile == null) throw new ArgumentNullException("oldProfile");
284  if (newProfile == null) throw new ArgumentNullException("newProfile");
285  currentProfile = newProfile;
286 
287  foreach (var key in SettingsKeys)
288  {
289  object oldValue;
290  oldProfile.GetValue(key.Key, out oldValue, true, false);
291  object newValue;
292  newProfile.GetValue(key.Key, out newValue, true, false);
293  var oldList = oldValue as IList;
294  var newList = newValue as IList;
295 
296  bool isDifferent;
297  if (oldList != null && newList != null)
298  {
299  isDifferent = oldList.Count != newList.Count;
300  for (int i = 0; i < oldList.Count && !isDifferent; ++i)
301  {
302  if (!Equals(oldList[i], newList[i]))
303  isDifferent = true;
304  }
305  }
306  else
307  {
308  isDifferent = !Equals(oldValue, newValue);
309  }
310  if (isDifferent)
311  {
312  newProfile.NotifyEntryChanged(key.Key);
313  }
314  }
315 
316  // Changes have been notified, empty the list of modified settings.
317  newProfile.ValidateSettingsChanges();
318  }
319  }
320 }
static void ClearSettings()
Clears the current settings, including registered SettingsKey and SettingsProfile instances...
static SettingsProfile CreateSettingsProfile(bool setAsCurrent, SettingsProfile parent=null)
Creates a new settings profile.
SiliconStudio.Core.Diagnostics.LoggerResult LoggerResult
A logger that stores messages locally useful for internal log scenarios.
Definition: LoggerResult.cs:14
This class represents a collection of values for all registered SettingsKey. It may also contains val...
System.IO.FileMode FileMode
Definition: ScriptSync.cs:33
static SettingsKey GetSettingsKey(UFile name)
Gets the settings key that matches the given name.
This class represents property to store in the settings that is identified by a key.
Definition: SettingsKey.cs:37
static EventHandler< SettingsFileLoadedEventArgs > SettingsFileLoaded
Raised when a settings file has been loaded.
static List< SettingsKey > GetAllSettingsKeys(bool includeNonEditable)
Gets a list of all registered SettingsKey instances.
Base implementation for ILogger.
Definition: Logger.cs:10
static bool SaveSettingsProfile(SettingsProfile profile, UFile filePath)
Saves the given settings profile to a file at the given path.
static void ReloadSettingsProfile(SettingsProfile profile)
Reloads a profile from its file, updating the value that have changed.
System.IO.File File
This class represents a set of settings that can be stored in a file. This class is public for serial...
Definition: SettingsFile.cs:14
Default Yaml serializer used to serialize assets by default.
Arguments of the SettingsService.SettingsFileLoaded event.
static void UnloadSettingsProfile(SettingsProfile profile)
Unloads a profile that was previously loaded.
A static class that manages settings loading and saving for an application.
static object Deserialize(Stream stream)
Deserializes an object from the specified stream (expecting a YAML string).
static SettingsProfile LoadSettingsProfile(UFile filePath, bool setAsCurrent, SettingsProfile parent=null)
Loads a settings profile from the given file.
Defines a normalized file path. See UPath for details. This class cannot be inherited.
Definition: UFile.cs:13