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.ComponentModel;
6 using System.ComponentModel.Design.Serialization;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.Reflection;
10 
11 namespace System.Data.Common
12 {
13     [TypeConverter(typeof(DataColumnMappingConverter))]
14     public sealed class DataColumnMapping : MarshalByRefObject, IColumnMapping, ICloneable
15     {
16         private DataColumnMappingCollection _parent;
17         private string _dataSetColumnName;
18         private string _sourceColumnName;
19 
DataColumnMapping()20         public DataColumnMapping()
21         {
22         }
23 
DataColumnMapping(string sourceColumn, string dataSetColumn)24         public DataColumnMapping(string sourceColumn, string dataSetColumn)
25         {
26             SourceColumn = sourceColumn;
27             DataSetColumn = dataSetColumn;
28         }
29 
30         [DefaultValue("")]
31         public string DataSetColumn
32         {
33             get { return _dataSetColumnName ?? string.Empty; }
34             set { _dataSetColumnName = value; }
35         }
36 
37         internal DataColumnMappingCollection Parent
38         {
39             get
40             {
41                 return _parent;
42             }
43             set
44             {
45                 _parent = value;
46             }
47         }
48 
49         [DefaultValue("")]
50         public string SourceColumn
51         {
52             get { return _sourceColumnName ?? string.Empty; }
53             set
54             {
55                 if ((null != Parent) && (0 != ADP.SrcCompare(_sourceColumnName, value)))
56                 {
57                     Parent.ValidateSourceColumn(-1, value);
58                 }
59                 _sourceColumnName = value;
60             }
61         }
62 
ICloneable.Clone()63         object ICloneable.Clone()
64         {
65             DataColumnMapping clone = new DataColumnMapping();
66             clone._sourceColumnName = _sourceColumnName;
67             clone._dataSetColumnName = _dataSetColumnName;
68             return clone;
69         }
70 
71         [EditorBrowsable(EditorBrowsableState.Advanced)]
GetDataColumnBySchemaAction(DataTable dataTable, Type dataType, MissingSchemaAction schemaAction)72         public DataColumn GetDataColumnBySchemaAction(DataTable dataTable, Type dataType, MissingSchemaAction schemaAction)
73         {
74             return GetDataColumnBySchemaAction(SourceColumn, DataSetColumn, dataTable, dataType, schemaAction);
75         }
76 
77         [EditorBrowsable(EditorBrowsableState.Advanced)]
GetDataColumnBySchemaAction(string sourceColumn, string dataSetColumn, DataTable dataTable, Type dataType, MissingSchemaAction schemaAction)78         public static DataColumn GetDataColumnBySchemaAction(string sourceColumn, string dataSetColumn, DataTable dataTable, Type dataType, MissingSchemaAction schemaAction)
79         {
80             if (null == dataTable)
81             {
82                 throw ADP.ArgumentNull(nameof(dataTable));
83             }
84             if (string.IsNullOrEmpty(dataSetColumn))
85             {
86 #if DEBUG
87                 if (AdapterSwitches.DataSchema.TraceWarning)
88                 {
89                     Debug.WriteLine("explicit filtering of SourceColumn \"" + sourceColumn + "\"");
90                 }
91 #endif
92                 return null;
93             }
94             DataColumnCollection columns = dataTable.Columns;
95             Debug.Assert(null != columns, "GetDataColumnBySchemaAction: unexpected null DataColumnCollection");
96 
97             int index = columns.IndexOf(dataSetColumn);
98             if ((0 <= index) && (index < columns.Count))
99             {
100                 DataColumn dataColumn = columns[index];
101                 Debug.Assert(null != dataColumn, "GetDataColumnBySchemaAction: unexpected null dataColumn");
102 
103                 if (!string.IsNullOrEmpty(dataColumn.Expression))
104                 {
105 #if DEBUG
106                     if (AdapterSwitches.DataSchema.TraceError)
107                     {
108                         Debug.WriteLine("schema mismatch on DataColumn \"" + dataSetColumn + "\" which is a computed column");
109                     }
110 #endif
111                     throw ADP.ColumnSchemaExpression(sourceColumn, dataSetColumn);
112                 }
113                 if ((null == dataType) || (dataType.IsArray == dataColumn.DataType.IsArray))
114                 {
115 #if DEBUG
116                     if (AdapterSwitches.DataSchema.TraceInfo)
117                     {
118                         Debug.WriteLine("schema match on DataColumn \"" + dataSetColumn + "\"");
119                     }
120 #endif
121                     return dataColumn;
122                 }
123 #if DEBUG
124                 if (AdapterSwitches.DataSchema.TraceWarning)
125                 {
126                     Debug.WriteLine("schema mismatch on DataColumn \"" + dataSetColumn + "\" " + dataType.Name + " != " + dataColumn.DataType.Name);
127                 }
128 #endif
129                 throw ADP.ColumnSchemaMismatch(sourceColumn, dataType, dataColumn);
130             }
131 
132             return CreateDataColumnBySchemaAction(sourceColumn, dataSetColumn, dataTable, dataType, schemaAction);
133         }
134 
CreateDataColumnBySchemaAction(string sourceColumn, string dataSetColumn, DataTable dataTable, Type dataType, MissingSchemaAction schemaAction)135         internal static DataColumn CreateDataColumnBySchemaAction(string sourceColumn, string dataSetColumn, DataTable dataTable, Type dataType, MissingSchemaAction schemaAction)
136         {
137             Debug.Assert(dataTable != null, "Should not call with a null DataTable");
138             if (string.IsNullOrEmpty(dataSetColumn))
139             {
140                 return null;
141             }
142 
143             switch (schemaAction)
144             {
145                 case MissingSchemaAction.Add:
146                 case MissingSchemaAction.AddWithKey:
147 #if DEBUG
148                     if (AdapterSwitches.DataSchema.TraceInfo)
149                     {
150                         Debug.WriteLine("schema add of DataColumn \"" + dataSetColumn + "\" <" + Convert.ToString(dataType, CultureInfo.InvariantCulture) + ">");
151                     }
152 #endif
153                     return new DataColumn(dataSetColumn, dataType);
154 
155                 case MissingSchemaAction.Ignore:
156 #if DEBUG
157                     if (AdapterSwitches.DataSchema.TraceWarning)
158                     {
159                         Debug.WriteLine("schema filter of DataColumn \"" + dataSetColumn + "\" <" + Convert.ToString(dataType, CultureInfo.InvariantCulture) + ">");
160                     }
161 #endif
162                     return null;
163 
164                 case MissingSchemaAction.Error:
165 #if DEBUG
166                     if (AdapterSwitches.DataSchema.TraceError)
167                     {
168                         Debug.WriteLine("schema error on DataColumn \"" + dataSetColumn + "\" <" + Convert.ToString(dataType, CultureInfo.InvariantCulture) + ">");
169                     }
170 #endif
171                     throw ADP.ColumnSchemaMissing(dataSetColumn, dataTable.TableName, sourceColumn);
172             }
173             throw ADP.InvalidMissingSchemaAction(schemaAction);
174         }
175 
ToString()176         public override string ToString()
177         {
178             return SourceColumn;
179         }
180 
181         internal sealed class DataColumnMappingConverter : System.ComponentModel.ExpandableObjectConverter
182         {
183             // converter classes should have public ctor
DataColumnMappingConverter()184             public DataColumnMappingConverter()
185             {
186             }
187 
CanConvertTo(ITypeDescriptorContext context, Type destinationType)188             public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
189             {
190                 if (typeof(InstanceDescriptor) == destinationType)
191                 {
192                     return true;
193                 }
194                 return base.CanConvertTo(context, destinationType);
195             }
196 
ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)197             public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
198             {
199                 if (null == destinationType)
200                 {
201                     throw ADP.ArgumentNull(nameof(destinationType));
202                 }
203 
204                 if ((typeof(InstanceDescriptor) == destinationType) && (value is DataColumnMapping))
205                 {
206                     DataColumnMapping mapping = (DataColumnMapping)value;
207 
208                     object[] values = new object[] { mapping.SourceColumn, mapping.DataSetColumn };
209                     Type[] types = new Type[] { typeof(string), typeof(string) };
210 
211                     ConstructorInfo ctor = typeof(DataColumnMapping).GetConstructor(types);
212                     return new InstanceDescriptor(ctor, values);
213                 }
214                 return base.ConvertTo(context, culture, value, destinationType);
215             }
216         }
217     }
218 }
219