2
0

EnumDataContract.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.Runtime.Serialization
  5. {
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Diagnostics.CodeAnalysis;
  10. using System.Globalization;
  11. using System.Reflection;
  12. using System.Threading;
  13. using System.Text;
  14. using System.Xml;
  15. using System.Security;
  16. #if USE_REFEMIT
  17. public sealed class EnumDataContract : DataContract
  18. #else
  19. internal sealed class EnumDataContract : DataContract
  20. #endif
  21. {
  22. [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that is cached statically for serialization."
  23. + " Static fields are marked SecurityCritical or readonly to prevent data from being modified or leaked to other components in appdomain.")]
  24. [SecurityCritical]
  25. EnumDataContractCriticalHelper helper;
  26. [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
  27. Safe = "Doesn't leak anything.")]
  28. [SecuritySafeCritical]
  29. internal EnumDataContract()
  30. : base(new EnumDataContractCriticalHelper())
  31. {
  32. helper = base.Helper as EnumDataContractCriticalHelper;
  33. }
  34. [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
  35. Safe = "Doesn't leak anything.")]
  36. [SecuritySafeCritical]
  37. internal EnumDataContract(Type type)
  38. : base(new EnumDataContractCriticalHelper(type))
  39. {
  40. helper = base.Helper as EnumDataContractCriticalHelper;
  41. }
  42. [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical static cache to look up base contract name for a type.",
  43. Safe = "Read only access.")]
  44. [SecuritySafeCritical]
  45. static internal XmlQualifiedName GetBaseContractName(Type type)
  46. {
  47. return EnumDataContractCriticalHelper.GetBaseContractName(type);
  48. }
  49. [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical static cache to look up a base contract name.",
  50. Safe = "Read only access.")]
  51. [SecuritySafeCritical]
  52. static internal Type GetBaseType(XmlQualifiedName baseContractName)
  53. {
  54. return EnumDataContractCriticalHelper.GetBaseType(baseContractName);
  55. }
  56. internal XmlQualifiedName BaseContractName
  57. {
  58. [Fx.Tag.SecurityNote(Critical = "Fetches the critical BaseContractName property.",
  59. Safe = "BaseContractName only needs to be protected for write.")]
  60. [SecuritySafeCritical]
  61. get { return helper.BaseContractName; }
  62. [Fx.Tag.SecurityNote(Critical = "Sets the critical BaseContractName property.")]
  63. [SecurityCritical]
  64. set { helper.BaseContractName = value; }
  65. }
  66. internal List<DataMember> Members
  67. {
  68. [Fx.Tag.SecurityNote(Critical = "Fetches the critical Members property.",
  69. Safe = "Members only needs to be protected for write.")]
  70. [SecuritySafeCritical]
  71. get { return helper.Members; }
  72. [Fx.Tag.SecurityNote(Critical = "Sets the critical Members property.")]
  73. [SecurityCritical]
  74. set { helper.Members = value; }
  75. }
  76. internal List<long> Values
  77. {
  78. [Fx.Tag.SecurityNote(Critical = "Fetches the critical Values property.",
  79. Safe = "Values only needs to be protected for write.")]
  80. [SecuritySafeCritical]
  81. get { return helper.Values; }
  82. [Fx.Tag.SecurityNote(Critical = "Sets the critical Values property.")]
  83. [SecurityCritical]
  84. set { helper.Values = value; }
  85. }
  86. internal bool IsFlags
  87. {
  88. [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsFlags property.",
  89. Safe = "IsFlags only needs to be protected for write.")]
  90. [SecuritySafeCritical]
  91. get { return helper.IsFlags; }
  92. [Fx.Tag.SecurityNote(Critical = "Sets the critical IsFlags property.")]
  93. [SecurityCritical]
  94. set { helper.IsFlags = value; }
  95. }
  96. internal bool IsULong
  97. {
  98. [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsULong property.",
  99. Safe = "IsULong only needs to be protected for write.")]
  100. [SecuritySafeCritical]
  101. get { return helper.IsULong; }
  102. }
  103. XmlDictionaryString[] ChildElementNames
  104. {
  105. [Fx.Tag.SecurityNote(Critical = "Fetches the critical ChildElementNames property.",
  106. Safe = "ChildElementNames only needs to be protected for write.")]
  107. [SecuritySafeCritical]
  108. get { return helper.ChildElementNames; }
  109. }
  110. internal override bool CanContainReferences
  111. {
  112. get { return false; }
  113. }
  114. [Fx.Tag.SecurityNote(Critical = "Holds all state used for (de)serializing enums."
  115. + " Since the data is cached statically, we lock down access to it.")]
  116. #if !NO_SECURITY_ATTRIBUTES
  117. [SecurityCritical(SecurityCriticalScope.Everything)]
  118. #endif
  119. class EnumDataContractCriticalHelper : DataContract.DataContractCriticalHelper
  120. {
  121. static Dictionary<Type, XmlQualifiedName> typeToName;
  122. static Dictionary<XmlQualifiedName, Type> nameToType;
  123. XmlQualifiedName baseContractName;
  124. List<DataMember> members;
  125. List<long> values;
  126. bool isULong;
  127. bool isFlags;
  128. bool hasDataContract;
  129. XmlDictionaryString[] childElementNames;
  130. static EnumDataContractCriticalHelper()
  131. {
  132. typeToName = new Dictionary<Type, XmlQualifiedName>();
  133. nameToType = new Dictionary<XmlQualifiedName, Type>();
  134. Add(typeof(sbyte), "byte");
  135. Add(typeof(byte), "unsignedByte");
  136. Add(typeof(short), "short");
  137. Add(typeof(ushort), "unsignedShort");
  138. Add(typeof(int), "int");
  139. Add(typeof(uint), "unsignedInt");
  140. Add(typeof(long), "long");
  141. Add(typeof(ulong), "unsignedLong");
  142. }
  143. [SuppressMessage(FxCop.Category.Usage, "CA2301:EmbeddableTypesInContainersRule", MessageId = "typeToName", Justification = "No need to support type equivalence here.")]
  144. static internal void Add(Type type, string localName)
  145. {
  146. XmlQualifiedName stableName = CreateQualifiedName(localName, Globals.SchemaNamespace);
  147. typeToName.Add(type, stableName);
  148. nameToType.Add(stableName, type);
  149. }
  150. static internal XmlQualifiedName GetBaseContractName(Type type)
  151. {
  152. XmlQualifiedName retVal = null;
  153. typeToName.TryGetValue(type, out retVal);
  154. return retVal;
  155. }
  156. static internal Type GetBaseType(XmlQualifiedName baseContractName)
  157. {
  158. Type retVal = null;
  159. nameToType.TryGetValue(baseContractName, out retVal);
  160. return retVal;
  161. }
  162. internal EnumDataContractCriticalHelper()
  163. {
  164. IsValueType = true;
  165. }
  166. internal EnumDataContractCriticalHelper(Type type)
  167. : base(type)
  168. {
  169. this.StableName = DataContract.GetStableName(type, out hasDataContract);
  170. Type baseType = Enum.GetUnderlyingType(type);
  171. baseContractName = GetBaseContractName(baseType);
  172. ImportBaseType(baseType);
  173. IsFlags = type.IsDefined(Globals.TypeOfFlagsAttribute, false);
  174. ImportDataMembers();
  175. XmlDictionary dictionary = new XmlDictionary(2 + Members.Count);
  176. Name = dictionary.Add(StableName.Name);
  177. Namespace = dictionary.Add(StableName.Namespace);
  178. childElementNames = new XmlDictionaryString[Members.Count];
  179. for (int i = 0; i < Members.Count; i++)
  180. childElementNames[i] = dictionary.Add(Members[i].Name);
  181. DataContractAttribute dataContractAttribute;
  182. if (TryGetDCAttribute(type, out dataContractAttribute))
  183. {
  184. if (dataContractAttribute.IsReference)
  185. {
  186. DataContract.ThrowInvalidDataContractException(
  187. SR.GetString(SR.EnumTypeCannotHaveIsReference,
  188. DataContract.GetClrTypeFullName(type),
  189. dataContractAttribute.IsReference,
  190. false),
  191. type);
  192. }
  193. }
  194. }
  195. internal XmlQualifiedName BaseContractName
  196. {
  197. get
  198. {
  199. return baseContractName;
  200. }
  201. set
  202. {
  203. baseContractName = value;
  204. Type baseType = GetBaseType(baseContractName);
  205. if (baseType == null)
  206. ThrowInvalidDataContractException(SR.GetString(SR.InvalidEnumBaseType, value.Name, value.Namespace, StableName.Name, StableName.Namespace));
  207. ImportBaseType(baseType);
  208. }
  209. }
  210. internal List<DataMember> Members
  211. {
  212. get { return members; }
  213. set { members = value; }
  214. }
  215. internal List<long> Values
  216. {
  217. get { return values; }
  218. set { values = value; }
  219. }
  220. internal bool IsFlags
  221. {
  222. get { return isFlags; }
  223. set { isFlags = value; }
  224. }
  225. internal bool IsULong
  226. {
  227. get { return isULong; }
  228. }
  229. internal XmlDictionaryString[] ChildElementNames
  230. {
  231. get { return childElementNames; }
  232. }
  233. void ImportBaseType(Type baseType)
  234. {
  235. isULong = (baseType == Globals.TypeOfULong);
  236. }
  237. void ImportDataMembers()
  238. {
  239. Type type = this.UnderlyingType;
  240. FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public);
  241. Dictionary<string, DataMember> memberValuesTable = new Dictionary<string, DataMember>();
  242. List<DataMember> tempMembers = new List<DataMember>(fields.Length);
  243. List<long> tempValues = new List<long>(fields.Length);
  244. for (int i = 0; i < fields.Length; i++)
  245. {
  246. FieldInfo field = fields[i];
  247. bool enumMemberValid = false;
  248. if (hasDataContract)
  249. {
  250. object[] memberAttributes = field.GetCustomAttributes(Globals.TypeOfEnumMemberAttribute, false);
  251. if (memberAttributes != null && memberAttributes.Length > 0)
  252. {
  253. if (memberAttributes.Length > 1)
  254. ThrowInvalidDataContractException(SR.GetString(SR.TooManyEnumMembers, DataContract.GetClrTypeFullName(field.DeclaringType), field.Name));
  255. EnumMemberAttribute memberAttribute = (EnumMemberAttribute)memberAttributes[0];
  256. DataMember memberContract = new DataMember(field);
  257. if (memberAttribute.IsValueSetExplicitly)
  258. {
  259. if (memberAttribute.Value == null || memberAttribute.Value.Length == 0)
  260. ThrowInvalidDataContractException(SR.GetString(SR.InvalidEnumMemberValue, field.Name, DataContract.GetClrTypeFullName(type)));
  261. memberContract.Name = memberAttribute.Value;
  262. }
  263. else
  264. memberContract.Name = field.Name;
  265. ClassDataContract.CheckAndAddMember(tempMembers, memberContract, memberValuesTable);
  266. enumMemberValid = true;
  267. }
  268. object[] dataMemberAttributes = field.GetCustomAttributes(Globals.TypeOfDataMemberAttribute, false);
  269. if (dataMemberAttributes != null && dataMemberAttributes.Length > 0)
  270. ThrowInvalidDataContractException(SR.GetString(SR.DataMemberOnEnumField, DataContract.GetClrTypeFullName(field.DeclaringType), field.Name));
  271. }
  272. else
  273. {
  274. if (!field.IsNotSerialized)
  275. {
  276. DataMember memberContract = new DataMember(field);
  277. memberContract.Name = field.Name;
  278. ClassDataContract.CheckAndAddMember(tempMembers, memberContract, memberValuesTable);
  279. enumMemberValid = true;
  280. }
  281. }
  282. if (enumMemberValid)
  283. {
  284. object enumValue = field.GetValue(null);
  285. if (isULong)
  286. tempValues.Add((long)((IConvertible)enumValue).ToUInt64(null));
  287. else
  288. tempValues.Add(((IConvertible)enumValue).ToInt64(null));
  289. }
  290. }
  291. Thread.MemoryBarrier();
  292. members = tempMembers;
  293. values = tempValues;
  294. }
  295. }
  296. internal void WriteEnumValue(XmlWriterDelegator writer, object value)
  297. {
  298. long longValue = IsULong ? (long)((IConvertible)value).ToUInt64(null) : ((IConvertible)value).ToInt64(null);
  299. for (int i = 0; i < Values.Count; i++)
  300. {
  301. if (longValue == Values[i])
  302. {
  303. writer.WriteString(ChildElementNames[i].Value);
  304. return;
  305. }
  306. }
  307. if (IsFlags)
  308. {
  309. int zeroIndex = -1;
  310. bool noneWritten = true;
  311. for (int i = 0; i < Values.Count; i++)
  312. {
  313. long current = Values[i];
  314. if (current == 0)
  315. {
  316. zeroIndex = i;
  317. continue;
  318. }
  319. if (longValue == 0)
  320. break;
  321. if ((current & longValue) == current)
  322. {
  323. if (noneWritten)
  324. noneWritten = false;
  325. else
  326. writer.WriteString(DictionaryGlobals.Space.Value);
  327. writer.WriteString(ChildElementNames[i].Value);
  328. longValue &= ~current;
  329. }
  330. }
  331. // enforce that enum value was completely parsed
  332. if (longValue != 0)
  333. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.InvalidEnumValueOnWrite, value, DataContract.GetClrTypeFullName(UnderlyingType))));
  334. if (noneWritten && zeroIndex >= 0)
  335. writer.WriteString(ChildElementNames[zeroIndex].Value);
  336. }
  337. else
  338. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.InvalidEnumValueOnWrite, value, DataContract.GetClrTypeFullName(UnderlyingType))));
  339. }
  340. internal object ReadEnumValue(XmlReaderDelegator reader)
  341. {
  342. string stringValue = reader.ReadElementContentAsString();
  343. long longValue = 0;
  344. int i = 0;
  345. if (IsFlags)
  346. {
  347. // Skip initial spaces
  348. for (; i < stringValue.Length; i++)
  349. if (stringValue[i] != ' ')
  350. break;
  351. // Read space-delimited values
  352. int startIndex = i;
  353. int count = 0;
  354. for (; i < stringValue.Length; i++)
  355. {
  356. if (stringValue[i] == ' ')
  357. {
  358. count = i - startIndex;
  359. if (count > 0)
  360. longValue |= ReadEnumValue(stringValue, startIndex, count);
  361. for (++i; i < stringValue.Length; i++)
  362. if (stringValue[i] != ' ')
  363. break;
  364. startIndex = i;
  365. if (i == stringValue.Length)
  366. break;
  367. }
  368. }
  369. count = i - startIndex;
  370. if (count > 0)
  371. longValue |= ReadEnumValue(stringValue, startIndex, count);
  372. }
  373. else
  374. {
  375. if (stringValue.Length == 0)
  376. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.InvalidEnumValueOnRead, stringValue, DataContract.GetClrTypeFullName(UnderlyingType))));
  377. longValue = ReadEnumValue(stringValue, 0, stringValue.Length);
  378. }
  379. if (IsULong)
  380. return Enum.ToObject(UnderlyingType, (ulong)longValue);
  381. return Enum.ToObject(UnderlyingType, longValue);
  382. }
  383. long ReadEnumValue(string value, int index, int count)
  384. {
  385. for (int i = 0; i < Members.Count; i++)
  386. {
  387. string memberName = Members[i].Name;
  388. if (memberName.Length == count && String.CompareOrdinal(value, index, memberName, 0, count) == 0)
  389. {
  390. return Values[i];
  391. }
  392. }
  393. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.InvalidEnumValueOnRead, value.Substring(index, count), DataContract.GetClrTypeFullName(UnderlyingType))));
  394. }
  395. internal string GetStringFromEnumValue(long value)
  396. {
  397. if (IsULong)
  398. return XmlConvert.ToString((ulong)value);
  399. else
  400. return XmlConvert.ToString(value);
  401. }
  402. internal long GetEnumValueFromString(string value)
  403. {
  404. if (IsULong)
  405. return (long)XmlConverter.ToUInt64(value);
  406. else
  407. return XmlConverter.ToInt64(value);
  408. }
  409. internal override bool Equals(object other, Dictionary<DataContractPairKey, object> checkedContracts)
  410. {
  411. if (IsEqualOrChecked(other, checkedContracts))
  412. return true;
  413. if (base.Equals(other, null))
  414. {
  415. EnumDataContract dataContract = other as EnumDataContract;
  416. if (dataContract != null)
  417. {
  418. if (Members.Count != dataContract.Members.Count || Values.Count != dataContract.Values.Count)
  419. return false;
  420. string[] memberNames1 = new string[Members.Count], memberNames2 = new string[Members.Count];
  421. for (int i = 0; i < Members.Count; i++)
  422. {
  423. memberNames1[i] = Members[i].Name;
  424. memberNames2[i] = dataContract.Members[i].Name;
  425. }
  426. Array.Sort(memberNames1);
  427. Array.Sort(memberNames2);
  428. for (int i = 0; i < Members.Count; i++)
  429. {
  430. if (memberNames1[i] != memberNames2[i])
  431. return false;
  432. }
  433. return (IsFlags == dataContract.IsFlags);
  434. }
  435. }
  436. return false;
  437. }
  438. public override int GetHashCode()
  439. {
  440. return base.GetHashCode();
  441. }
  442. public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
  443. {
  444. WriteEnumValue(xmlWriter, obj);
  445. }
  446. public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
  447. {
  448. object obj = ReadEnumValue(xmlReader);
  449. if (context != null)
  450. context.AddNewObject(obj);
  451. return obj;
  452. }
  453. }
  454. }