PageRenderTime 20ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 1ms

/Utilities/WCell.Util/Variables/TypeVariableDefinition.cs

https://github.com/primax/WCell
C# | 304 lines | 258 code | 31 blank | 15 comment | 41 complexity | f71f27cfe942d6aaaa2fa5df3fc80fed MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Xml;
  7. using System.Xml.Schema;
  8. using System.Xml.Serialization;
  9. using WCell.Util.Xml;
  10. namespace WCell.Util.Variables
  11. {
  12. public class TypeVariableDefinition : IComparable, IVariableDefinition, IXmlSerializable
  13. {
  14. public static readonly object[] EmptyObjectArray = new object[0];
  15. public static readonly Type GenericListType = typeof(IList<>);
  16. private const string ENUMERABLE_ITEM_NAME = "Item";
  17. internal MemberInfo m_Member;
  18. private bool m_isXmlSerializable;
  19. private Type m_collectionType;
  20. /// <summary>
  21. /// The object that holds the field or property (or null if static)
  22. /// </summary>
  23. public readonly Object Object;
  24. public bool Serialized;
  25. private bool m_readOnly;
  26. public TypeVariableDefinition()
  27. {
  28. }
  29. public TypeVariableDefinition(string name, MemberInfo member, bool serialized, bool readOnly)
  30. {
  31. Name = name;
  32. Member = member;
  33. Serialized = serialized;
  34. m_readOnly = readOnly;
  35. }
  36. public TypeVariableDefinition(string name, object obj, MemberInfo member, bool serialized, bool readOnly) :
  37. this(name, member, serialized, readOnly)
  38. {
  39. Object = obj;
  40. }
  41. public string Name
  42. {
  43. get;
  44. internal set;
  45. }
  46. public bool IsReadOnly
  47. {
  48. get
  49. {
  50. return m_readOnly;
  51. }
  52. internal set { m_readOnly = value; }
  53. }
  54. public bool IsFileOnly
  55. {
  56. get;
  57. internal set;
  58. }
  59. public MemberInfo Member
  60. {
  61. get { return m_Member; }
  62. internal set
  63. {
  64. m_Member = value;
  65. FullName = GetSafeName();
  66. var varType = m_Member.GetVariableType();
  67. m_isXmlSerializable = varType.GetInterface("IXmlSerializable") != null;
  68. var interfaceType = varType.GetInterface("IEnumerable");
  69. if (interfaceType != null && varType != typeof(string))
  70. {
  71. if (varType.IsArray)
  72. {
  73. m_collectionType = varType.GetElementType();
  74. }
  75. else
  76. {
  77. var listType = varType.GetInterface(GenericListType.Name);
  78. //TODO:
  79. //varType.GetGenericArguments
  80. if (listType == null)
  81. {
  82. throw new Exception(
  83. "Cannot create TypeVariableDefinition for IEnumerable, unless it is an Array or implements IList<T>.");
  84. }
  85. m_collectionType = listType.GetGenericArguments().First();
  86. }
  87. }
  88. }
  89. }
  90. /// <summary>
  91. ///
  92. /// </summary>
  93. /// <returns></returns>
  94. private string GetSafeName()
  95. {
  96. var name = m_Member.DeclaringType.FullName;
  97. name = name.Replace("+", "."); // + is used for nested classes
  98. name = name.Replace("#", "."); // just in case
  99. return name + "." + Name;
  100. }
  101. public string FullName
  102. {
  103. //get { return m_Member.DeclaringType.FullName + "." + m_Member.Name; }
  104. get;
  105. private set;
  106. }
  107. public Type VariableType
  108. {
  109. get { return m_Member.GetVariableType(); }
  110. }
  111. public object Value
  112. {
  113. get { return m_Member.GetUnindexedValue(Object); }
  114. set { m_Member.SetUnindexedValue(Object, value); }
  115. }
  116. public string TypeName
  117. {
  118. get { return VariableType.Name; }
  119. }
  120. public bool TrySet(string strValue)
  121. {
  122. if (IsReadOnly)
  123. {
  124. return false;
  125. }
  126. var val = TryParse(strValue, VariableType);
  127. if (val != null)
  128. {
  129. Value = val;
  130. return true;
  131. }
  132. return false;
  133. }
  134. static object TryParse(string strValue, Type type)
  135. {
  136. object valueObj = null;
  137. if (StringParser.Parse(strValue, type, ref valueObj))
  138. {
  139. return valueObj;
  140. }
  141. return null;
  142. }
  143. public int CompareTo(object obj)
  144. {
  145. if (obj is TypeVariableDefinition)
  146. {
  147. return ((TypeVariableDefinition)obj).Name.CompareTo(Name);
  148. }
  149. return -1;
  150. }
  151. public void ReadXml(XmlReader reader)
  152. {
  153. var origVal = Value;
  154. try
  155. {
  156. var type = m_Member.GetVariableType();
  157. if (m_isXmlSerializable)
  158. {
  159. if (Value == null)
  160. {
  161. Value = Activator.CreateInstance(type);
  162. }
  163. ((IXmlSerializable)Value).ReadXml(reader);
  164. }
  165. else if (type.IsSimpleType())
  166. {
  167. var str = reader.ReadString();
  168. TrySet(str);
  169. }
  170. else
  171. {
  172. if (m_collectionType != null)
  173. {
  174. IList collection;
  175. if (m_Member.GetVariableType().IsArray)
  176. {
  177. //collection = (IList)Activator.CreateInstance(varType);
  178. collection = new List<object>();
  179. ReadCollection(reader, collection);
  180. var arr = Array.CreateInstance(m_collectionType, collection.Count);
  181. for (var i = 0; i < collection.Count; i++)
  182. {
  183. ArrayUtil.SetValue(arr, i, collection[i]);
  184. }
  185. Value = arr;
  186. }
  187. else
  188. {
  189. collection = (IList)Activator.CreateInstance(type);
  190. ReadCollection(reader, collection);
  191. Value = collection;
  192. }
  193. }
  194. else
  195. {
  196. // should never happen due to the initial checks
  197. throw new NotImplementedException("Cannot serialize Variable because it has an invalid Type: " + type);
  198. }
  199. }
  200. }
  201. catch (Exception e)
  202. {
  203. // reset value
  204. Value = origVal;
  205. throw e;
  206. }
  207. }
  208. void ReadCollection(XmlReader reader, IList col)
  209. {
  210. while (true)
  211. {
  212. reader.Read();
  213. reader.SkipEmptyNodes();
  214. if (reader.NodeType == XmlNodeType.EndElement)
  215. {
  216. // nothing here
  217. return;
  218. }
  219. else if (reader.NodeType == XmlNodeType.Element)
  220. {
  221. if (reader.Name == ENUMERABLE_ITEM_NAME)
  222. {
  223. var str = reader.ReadString();
  224. var value = TryParse(str, m_collectionType);
  225. if (value != null)
  226. {
  227. col.Add(value);
  228. }
  229. }
  230. }
  231. reader.SkipEmptyNodes();
  232. reader.ReadEndElement();
  233. }
  234. }
  235. public virtual void WriteXml(XmlWriter writer)
  236. {
  237. if (IsReadOnly)
  238. {
  239. throw new InvalidOperationException("Tried to write ReadOnly Variable \"" + this + "\" to XML-Stream");
  240. }
  241. if (Value == null)
  242. {
  243. throw new ArgumentException("Tried to write null-value to XML: " + this);
  244. }
  245. var type = m_Member.GetVariableType();
  246. if (m_isXmlSerializable)
  247. {
  248. ((IXmlSerializable)Value).WriteXml(writer);
  249. }
  250. else if (type.IsSimpleType())
  251. {
  252. writer.WriteString(Value.ToString());
  253. }
  254. else if (m_collectionType != null)
  255. {
  256. writer.WriteCollection((IEnumerable)Value, ENUMERABLE_ITEM_NAME);
  257. }
  258. else
  259. {
  260. // in theory this could never happen due to the initial checks
  261. throw new NotImplementedException("Cannot serialize Variable because it has an invalid Type: " + type);
  262. }
  263. }
  264. public XmlSchema GetSchema()
  265. {
  266. throw new System.NotImplementedException(GetType() + " does not support any XmlSchema.");
  267. }
  268. public override string ToString()
  269. {
  270. return Name + " (" + FullName + ")";
  271. }
  272. }
  273. }