PageRenderTime 26ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Utilities/WCell.Util/Variables/VariableConfiguration.cs

http://github.com/WCell/WCell
C# | 458 lines | 346 code | 51 blank | 61 comment | 44 complexity | f4a08fde5b5e9be0aa5cabbc050506b3 MD5 | raw file
Possible License(s): LGPL-2.1
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Text;
  8. using System.Xml;
  9. using System.Xml.Serialization;
  10. using WCell.Util.NLog;
  11. using WCell.Util.Strings;
  12. using WCell.Util.Xml;
  13. namespace WCell.Util.Variables
  14. {
  15. public abstract class VariableConfiguration<C, V> : VariableConfiguration<V>
  16. where C : VariableConfiguration<V>
  17. where V : TypeVariableDefinition, new()
  18. {
  19. protected VariableConfiguration()
  20. {
  21. }
  22. protected VariableConfiguration(Action<string> onError)
  23. : base(onError)
  24. {
  25. }
  26. }
  27. public class VariableConfiguration<V> : IConfiguration, IEnumerable<V>
  28. where V : TypeVariableDefinition, new()
  29. {
  30. protected string RootNodeName = "Config";
  31. private const string SettingsNodeName = "Settings";
  32. //private SerializablePairCollection m_definitions;
  33. public readonly StringTree<TypeVariableDefinition> Tree;
  34. public readonly List<IConfiguration> ChildConfigurations = new List<IConfiguration>();
  35. /// <summary>
  36. /// Holds an array of static variable fields
  37. /// </summary>
  38. [XmlIgnore]
  39. public readonly Dictionary<string, V> Definitions;
  40. [XmlIgnore]
  41. public readonly Dictionary<string, V> ByFullName =
  42. new Dictionary<string, V>(StringComparer.InvariantCultureIgnoreCase);
  43. [XmlIgnore]
  44. public Action<V> VariableDefinintionInitializor = DefaultDefinitionInitializor;
  45. public VariableConfiguration() : this(null)
  46. {
  47. }
  48. public VariableConfiguration(Action<string> onError)
  49. {
  50. Tree = new StringTree<TypeVariableDefinition>(onError, "\t", '.');
  51. Definitions = new Dictionary<string, V>(StringComparer.InvariantCultureIgnoreCase);
  52. AutoSave = true;
  53. }
  54. public Action<string> ErrorHandler
  55. {
  56. get { return Tree.ErrorHandler; }
  57. set { Tree.ErrorHandler = value; }
  58. }
  59. public virtual string FilePath
  60. {
  61. get;
  62. set;
  63. }
  64. //[XmlElement(DefinitionNodeName)]
  65. //public SerializablePairCollection Definitions
  66. //{
  67. // get
  68. // {
  69. // if (m_definitions == null)
  70. // {
  71. // m_definitions = new SerializablePairCollection(DefinitionNodeName);
  72. // }
  73. // foreach (var def in ByFullName.Values)
  74. // {
  75. // m_definitions.Add(def.Name, def.Value.ToString());
  76. // }
  77. // return m_definitions;
  78. // }
  79. // set
  80. // {
  81. // m_definitions = value;
  82. // InitDefs();
  83. // }
  84. //}
  85. //private void InitDefs()
  86. //{
  87. // T def;
  88. // var used = new List<string>();
  89. // foreach (var pair in m_definitions.Pairs)
  90. // {
  91. // if (!ByName.TryGetValue(pair.Key, out def))
  92. // {
  93. // throw new VariableException("Found invalid Variable-definition \"{0}\" with value: {1}", pair.Key, pair.Value);
  94. // }
  95. // if (!def.TrySet(pair.Value))
  96. // {
  97. // throw new VariableException("Unable to parse value of variable \"{0}\": {1}", pair.Key, pair.Value);
  98. // }
  99. // used.Add(def.Name);
  100. // VariableDefinintionInitializor(def);
  101. // }
  102. // var unused = ByName.Keys.Except(used);
  103. // if (unused.Count() > 0)
  104. // {
  105. // log.Warn("The following Config-values were not found in the config-file: " + unused.ToString(", "));
  106. // }
  107. // m_definitions.Pairs.Clear();
  108. //}
  109. public virtual bool AutoSave
  110. {
  111. get;
  112. set;
  113. }
  114. public virtual bool Load()
  115. {
  116. if (File.Exists(FilePath))
  117. {
  118. Deserialize();
  119. return true;
  120. }
  121. return false;
  122. //else
  123. //{
  124. // Save();
  125. //}
  126. }
  127. public void Deserialize()
  128. {
  129. XmlUtil.EnsureCulture();
  130. using (var reader = XmlReader.Create(FilePath))
  131. {
  132. reader.ReadStartElement();
  133. reader.SkipEmptyNodes();
  134. try
  135. {
  136. Tree.ReadXml(reader);
  137. }
  138. catch (Exception e)
  139. {
  140. throw new Exception("Unable to load Configuration from: " + FilePath, e);
  141. }
  142. finally
  143. {
  144. XmlUtil.ResetCulture();
  145. }
  146. //m_definitions = new SerializablePairCollection();
  147. //m_definitions.ReadXml(reader);
  148. //InitDefs();
  149. }
  150. }
  151. public bool Contains(string name)
  152. {
  153. return Definitions.ContainsKey(name);
  154. }
  155. public bool IsReadOnly(string name)
  156. {
  157. var def = GetDefinition(name);
  158. return def.IsReadOnly;
  159. }
  160. public void Save()
  161. {
  162. Save(true, false);
  163. }
  164. #region Save
  165. public virtual void Save(bool backupFirst, bool auto)
  166. {
  167. try
  168. {
  169. // don't backup empty files
  170. if (backupFirst && File.Exists(FilePath) && new FileInfo(FilePath).Length > 0)
  171. {
  172. Backup(".bak");
  173. }
  174. DoSave();
  175. }
  176. catch (Exception e)
  177. {
  178. throw new Exception("Unable to save Configuration to: " + FilePath, e);
  179. }
  180. XmlUtil.EnsureCulture();
  181. try
  182. {
  183. foreach (var cfg in ChildConfigurations)
  184. {
  185. cfg.Save(backupFirst, auto);
  186. }
  187. }
  188. finally
  189. {
  190. XmlUtil.ResetCulture();
  191. }
  192. }
  193. private void Backup(string suffix)
  194. {
  195. var name = FilePath + suffix;
  196. try
  197. {
  198. var file = new FileInfo(FilePath);
  199. if (file.Length > 0)
  200. {
  201. File.Copy(FilePath, name, true);
  202. }
  203. }
  204. catch (Exception e)
  205. {
  206. throw new Exception("Unable to create backup of Configuration \"" + name + "\"", e);
  207. }
  208. }
  209. private void DoSave()
  210. {
  211. using (var stream = new MemoryStream())
  212. {
  213. using (var writer = new XmlTextWriter(stream, Encoding.UTF8))
  214. {
  215. XmlUtil.EnsureCulture();
  216. try
  217. {
  218. writer.Formatting = Formatting.Indented;
  219. //writer.WriteWhitespace("\n");
  220. writer.WriteStartElement(RootNodeName);
  221. //writer.WriteWhitespace("\n\t");
  222. writer.WriteStartElement(SettingsNodeName);
  223. Tree.WriteXml(writer);
  224. writer.WriteEndElement();
  225. //writer.WriteWhitespace("\n");
  226. writer.WriteEndElement();
  227. }
  228. finally
  229. {
  230. XmlUtil.ResetCulture();
  231. }
  232. }
  233. File.WriteAllBytes(FilePath, stream.ToArray());
  234. }
  235. }
  236. #endregion
  237. public static void DefaultDefinitionInitializor(V def)
  238. {
  239. // do nothing
  240. }
  241. #region Get
  242. public object Get(string name)
  243. {
  244. V def;
  245. if (Definitions.TryGetValue(name, out def))
  246. {
  247. return def.Value;
  248. }
  249. return null;
  250. }
  251. public V GetDefinition(string name)
  252. {
  253. V def;
  254. Definitions.TryGetValue(name, out def);
  255. return def;
  256. }
  257. #endregion
  258. #region Set
  259. public bool Set(string name, object value)
  260. {
  261. V def;
  262. if (Definitions.TryGetValue(name, out def))
  263. {
  264. def.Value = value;
  265. return true;
  266. }
  267. return false;
  268. }
  269. public bool Set(string name, string value)
  270. {
  271. V def;
  272. if (Definitions.TryGetValue(name, out def))
  273. {
  274. return def.TrySet(value);
  275. }
  276. return false;
  277. }
  278. #endregion
  279. #region Create & Add
  280. public V CreateDefinition(string name, MemberInfo member, bool serialized, bool readOnly, bool fileOnly)
  281. {
  282. var def = new V { Name = name, Member = member, Serialized = serialized, IsReadOnly = readOnly, IsFileOnly = fileOnly };
  283. VariableDefinintionInitializor(def);
  284. return def;
  285. }
  286. public void AddVariablesOfAsm<A>(Assembly asm)
  287. where A : VariableAttribute
  288. {
  289. Type[] types;
  290. try
  291. {
  292. types = asm.GetTypes();
  293. }
  294. catch (Exception e)
  295. {
  296. LogUtil.ErrorException(e, "Could not initialize assembly \"{0}\". You can probably fix this issue by making sure that the target platform of the assembly and all it's dependencies are equal.", asm.FullName);
  297. return;
  298. }
  299. foreach (var type in types)
  300. {
  301. if ((type.Attributes & TypeAttributes.Public) == 0) continue;
  302. var members = type.GetMembers(BindingFlags.Public | BindingFlags.Static);
  303. InitMembers<A>(members);
  304. var varClassAttr = type.GetCustomAttributes(typeof(VariableClassAttribute), true).FirstOrDefault() as VariableClassAttribute;
  305. if (varClassAttr != null && varClassAttr.Inherit)
  306. {
  307. Type t = type.BaseType;
  308. while (t != null && (t.Namespace == null || !t.Namespace.StartsWith("System")))
  309. {
  310. var members2 = t.GetMembers(BindingFlags.Public | BindingFlags.Static);
  311. InitMembers<A>(members2);
  312. if (t == type.BaseType)
  313. {
  314. break;
  315. }
  316. }
  317. }
  318. }
  319. }
  320. #endregion
  321. public void Foreach(Action<IVariableDefinition> callback)
  322. {
  323. foreach (var def in Definitions.Values)
  324. {
  325. callback(def);
  326. }
  327. }
  328. void InitMembers<A>(MemberInfo[] members)
  329. where A : VariableAttribute
  330. {
  331. foreach (var member in members)
  332. {
  333. var notVarAttr = member.GetCustomAttributes<NotVariableAttribute>().FirstOrDefault();
  334. if (notVarAttr != null)
  335. {
  336. continue;
  337. }
  338. var varAttr = member.GetCustomAttributes(typeof(A), true).FirstOrDefault() as A;
  339. var readOnly = member.IsReadonly() || (varAttr != null && varAttr.IsReadOnly);
  340. var fileOnly = varAttr != null && varAttr.IsFileOnly;
  341. Type memberType;
  342. if (member.IsFieldOrProp() && (!readOnly || varAttr != null) &&
  343. ((memberType = member.GetVariableType()).IsSimpleType() ||
  344. readOnly ||
  345. memberType.IsArray ||
  346. memberType.GetInterface(TypeVariableDefinition.GenericListType.Name) != null ||
  347. memberType.GetInterface(typeof(IXmlSerializable).Name) != null))
  348. {
  349. string name;
  350. var serialized = VariableAttribute.DefaultSerialized && !readOnly;
  351. if (varAttr != null)
  352. {
  353. name = varAttr.Name ?? member.Name;
  354. serialized = !readOnly && varAttr.Serialized;
  355. }
  356. else
  357. {
  358. name = member.Name;
  359. }
  360. Add(name, member, serialized, readOnly, fileOnly);
  361. }
  362. else if (varAttr != null)
  363. {
  364. throw new Exception(string.Format(
  365. "public static member \"{0}\" has VariableAttribute but invalid type.",
  366. member.GetFullMemberName()));
  367. }
  368. }
  369. }
  370. public V Add(string name, MemberInfo member, bool serialized, bool readOnly, bool fileOnly)
  371. {
  372. V existingDef;
  373. if (Definitions.TryGetValue(name, out existingDef))
  374. {
  375. throw new AmbiguousMatchException("Found Variable with name \"" + name + "\" twice (" + existingDef + "). " +
  376. "Either rename the Variable or add a VariableAttribute to it to specify a different name in the Configuration file. " +
  377. "(public static variables that are not read-only, are automatically added to the global variable collection)");
  378. }
  379. var def = CreateDefinition(name, member, serialized, readOnly, fileOnly);
  380. if (def != null)
  381. {
  382. Add(def, serialized);
  383. }
  384. return def;
  385. }
  386. public void Add(V def, bool serialize)
  387. {
  388. Definitions.Add(def.Name, def);
  389. ByFullName.Add(def.FullName, def);
  390. if (serialize)
  391. {
  392. Tree.AddChildInChain(def.FullName, def);
  393. }
  394. }
  395. public IEnumerator<V> GetEnumerator()
  396. {
  397. foreach (var def in Definitions.Values)
  398. {
  399. yield return def;
  400. }
  401. }
  402. IEnumerator IEnumerable.GetEnumerator()
  403. {
  404. return GetEnumerator();
  405. }
  406. }
  407. }