1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Data.SqlTypes;
6 using System.Xml;
7 using System.IO;
8 using System.Xml.Serialization;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Diagnostics;
12 using System.Runtime.CompilerServices;
13 
14 namespace System.Data.Common
15 {
16     internal sealed class SqlUdtStorage : DataStorage
17     {
18         private object[] _values;
19         private readonly bool _implementsIXmlSerializable = false;
20         private readonly bool _implementsIComparable = false;
21 
22         private static readonly Dictionary<Type, object> s_typeToNull = new Dictionary<Type, object>();
23 
SqlUdtStorage(DataColumn column, Type type)24         public SqlUdtStorage(DataColumn column, Type type)
25         : this(column, type, GetStaticNullForUdtType(type))
26         {
27         }
28 
SqlUdtStorage(DataColumn column, Type type, object nullValue)29         private SqlUdtStorage(DataColumn column, Type type, object nullValue)
30         : base(column, type, nullValue, nullValue, typeof(ICloneable).IsAssignableFrom(type), GetStorageType(type))
31         {
32             _implementsIXmlSerializable = typeof(IXmlSerializable).IsAssignableFrom(type);
33             _implementsIComparable = typeof(IComparable).IsAssignableFrom(type);
34         }
35 
36         // to support oracle types and other INUllable types that have static Null as field
GetStaticNullForUdtType(Type type)37         internal static object GetStaticNullForUdtType(Type type)
38         {
39             object value;
40             if (!s_typeToNull.TryGetValue(type, out value))
41             {
42                 System.Reflection.PropertyInfo propInfo = type.GetProperty("Null", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
43                 if (propInfo != null)
44                     value = propInfo.GetValue(null, null);
45                 else
46                 {
47                     System.Reflection.FieldInfo fieldInfo = type.GetField("Null", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
48                     if (fieldInfo != null)
49                     {
50                         value = fieldInfo.GetValue(null);
51                     }
52                     else
53                     {
54                         throw ExceptionBuilder.INullableUDTwithoutStaticNull(type.AssemblyQualifiedName);
55                     }
56                 }
57                 lock (s_typeToNull)
58                 {
59                     //if(50 < TypeToNull.Count) {
60                     //    TypeToNull.Clear();
61                     //}
62                     s_typeToNull[type] = value;
63                 }
64             }
65             return value;
66         }
67 
IsNull(int record)68         public override bool IsNull(int record)
69         {
70             return (((INullable)_values[record]).IsNull);
71         }
72 
Aggregate(int[] records, AggregateType kind)73         public override object Aggregate(int[] records, AggregateType kind)
74         {
75             throw ExceptionBuilder.AggregateException(kind, _dataType);
76         }
77 
Compare(int recordNo1, int recordNo2)78         public override int Compare(int recordNo1, int recordNo2)
79         {
80             return (CompareValueTo(recordNo1, _values[recordNo2]));
81         }
82 
CompareValueTo(int recordNo1, object value)83         public override int CompareValueTo(int recordNo1, object value)
84         {
85             if (DBNull.Value == value)
86             {
87                 // it is not meaningful compare UDT with DBNull.Value
88                 value = _nullValue;
89             }
90             if (_implementsIComparable)
91             {
92                 IComparable comparable = (IComparable)_values[recordNo1];
93                 return comparable.CompareTo(value);
94             }
95             else if (_nullValue == value)
96             {
97                 INullable nullableValue = (INullable)_values[recordNo1];
98                 return nullableValue.IsNull ? 0 : 1; // left may be null, right is null
99             }
100 
101             throw ExceptionBuilder.IComparableNotImplemented(_dataType.AssemblyQualifiedName);
102         }
103 
Copy(int recordNo1, int recordNo2)104         public override void Copy(int recordNo1, int recordNo2)
105         {
106             CopyBits(recordNo1, recordNo2);
107             _values[recordNo2] = _values[recordNo1];
108         }
109 
Get(int recordNo)110         public override object Get(int recordNo)
111         {
112             return (_values[recordNo]);
113         }
114 
Set(int recordNo, object value)115         public override void Set(int recordNo, object value)
116         {
117             if (DBNull.Value == value)
118             {
119                 _values[recordNo] = _nullValue;
120                 SetNullBit(recordNo, true);
121             }
122             else if (null == value)
123             {
124                 if (_isValueType)
125                 {
126                     throw ExceptionBuilder.StorageSetFailed();
127                 }
128                 else
129                 {
130                     _values[recordNo] = _nullValue;
131                     SetNullBit(recordNo, true);
132                 }
133             }
134             else if (!_dataType.IsInstanceOfType(value))
135             {
136                 throw ExceptionBuilder.StorageSetFailed();
137             }
138             else
139             {
140                 // do not clone the value
141                 _values[recordNo] = value;
142                 SetNullBit(recordNo, false);
143             }
144         }
145 
SetCapacity(int capacity)146         public override void SetCapacity(int capacity)
147         {
148             object[] newValues = new object[capacity];
149             if (_values != null)
150             {
151                 Array.Copy(_values, 0, newValues, 0, Math.Min(capacity, _values.Length));
152             }
153             _values = newValues;
154             base.SetCapacity(capacity);
155         }
156 
157         // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
158         [MethodImpl(MethodImplOptions.NoInlining)]
ConvertXmlToObject(string s)159         public override object ConvertXmlToObject(string s)
160         {
161             if (_implementsIXmlSerializable)
162             {
163                 object Obj = System.Activator.CreateInstance(_dataType, true);
164 
165                 string tempStr = string.Concat("<col>", s, "</col>"); // this is done since you can give fragmet to reader
166                 StringReader strReader = new StringReader(tempStr);
167 
168                 using (XmlTextReader xmlTextReader = new XmlTextReader(strReader))
169                 {
170                     ((IXmlSerializable)Obj).ReadXml(xmlTextReader);
171                 }
172                 return Obj;
173             }
174 
175             StringReader strreader = new StringReader(s);
176             XmlSerializer deserializerWithOutRootAttribute = ObjectStorage.GetXmlSerializer(_dataType);
177             return (deserializerWithOutRootAttribute.Deserialize(strreader));
178         }
179 
180         // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
181         [MethodImpl(MethodImplOptions.NoInlining)]
ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)182         public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)
183         {
184             if (null == xmlAttrib)
185             {
186                 string typeName = xmlReader.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.MSDNS);
187                 if (typeName == null)
188                 {
189                     string xsdTypeName = xmlReader.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.XSINS); // this xsd type
190                     if (null != xsdTypeName)
191                     {
192                         typeName = XSDSchema.XsdtoClr(xsdTypeName).FullName;
193                     }
194                 }
195                 Type type = (typeName == null) ? _dataType : Type.GetType(typeName);
196                 object Obj = System.Activator.CreateInstance(type, true);
197                 Debug.Assert(xmlReader is DataTextReader, "Invalid DataTextReader is being passed to customer");
198                 ((IXmlSerializable)Obj).ReadXml(xmlReader);
199                 return Obj;
200             }
201             else
202             {
203                 XmlSerializer deserializerWithRootAttribute = ObjectStorage.GetXmlSerializer(_dataType, xmlAttrib);
204                 return (deserializerWithRootAttribute.Deserialize(xmlReader));
205             }
206         }
207 
208 
ConvertObjectToXml(object value)209         public override string ConvertObjectToXml(object value)
210         {
211             StringWriter strwriter = new StringWriter(FormatProvider);
212             if (_implementsIXmlSerializable)
213             {
214                 using (XmlTextWriter xmlTextWriter = new XmlTextWriter(strwriter))
215                 {
216                     ((IXmlSerializable)value).WriteXml(xmlTextWriter);
217                 }
218             }
219             else
220             {
221                 XmlSerializer serializerWithOutRootAttribute = ObjectStorage.GetXmlSerializer(value.GetType());
222                 serializerWithOutRootAttribute.Serialize(strwriter, value);
223             }
224             return (strwriter.ToString());
225         }
226 
ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib)227         public override void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib)
228         {
229             if (null == xmlAttrib)
230             {
231                 Debug.Assert(xmlWriter is DataTextWriter, "Invalid DataTextWriter is being passed to customer");
232                 ((IXmlSerializable)value).WriteXml(xmlWriter);
233             }
234             else
235             {
236                 // we support polymorphism only for types that implements IXmlSerializable.
237                 // Assumption: value is the same type as DataType
238 
239                 XmlSerializer serializerWithRootAttribute = ObjectStorage.GetXmlSerializer(_dataType, xmlAttrib);
240                 serializerWithRootAttribute.Serialize(xmlWriter, value);
241             }
242         }
243 
GetEmptyStorage(int recordCount)244         protected override object GetEmptyStorage(int recordCount)
245         {
246             return new object[recordCount];
247         }
248 
CopyValue(int record, object store, BitArray nullbits, int storeIndex)249         protected override void CopyValue(int record, object store, BitArray nullbits, int storeIndex)
250         {
251             object[] typedStore = (object[])store;
252             typedStore[storeIndex] = _values[record];
253             nullbits.Set(storeIndex, IsNull(record));
254         }
255 
SetStorage(object store, BitArray nullbits)256         protected override void SetStorage(object store, BitArray nullbits)
257         {
258             _values = (object[])store;
259             //SetNullStorage(nullbits);
260         }
261     }
262 }
263