2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.Data / System.Data / DataColumn.cs
1 //\r
2 // System.Data.DataColumn.cs\r
3 //\r
4 // Author:\r
5 //   Franklin Wise (gracenote@earthlink.net)\r
6 //   Christopher Podurgiel (cpodurgiel@msn.com)\r
7 //   Rodrigo Moya (rodrigo@ximian.com)\r
8 //   Daniel Morgan (danmorg@sc.rr.com)\r
9 //   Tim Coleman (tim@timcoleman.com)\r
10 //\r
11 // (C) Copyright 2002, Franklin Wise\r
12 // (C) Chris Podurgiel\r
13 // (C) Ximian, Inc 2002\r
14 // Copyright (C) Tim Coleman, 2002\r
15 // Copyright (C) Daniel Morgan, 2002, 2003\r
16 //\r
17 \r
18 //\r
19 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
20 //\r
21 // Permission is hereby granted, free of charge, to any person obtaining\r
22 // a copy of this software and associated documentation files (the\r
23 // "Software"), to deal in the Software without restriction, including\r
24 // without limitation the rights to use, copy, modify, merge, publish,\r
25 // distribute, sublicense, and/or sell copies of the Software, and to\r
26 // permit persons to whom the Software is furnished to do so, subject to\r
27 // the following conditions:\r
28 //\r
29 // The above copyright notice and this permission notice shall be\r
30 // included in all copies or substantial portions of the Software.\r
31 //\r
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
33 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
34 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
35 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
36 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
37 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
38 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
39 //\r
40 \r
41 using System;\r
42 using System.ComponentModel;\r
43 using System.Reflection;\r
44 using System.Collections;\r
45 using System.Data.Common;\r
46 using System.Data.SqlTypes;\r
47 using System.Globalization;\r
48 using Mono.Data.SqlExpressions;\r
49 \r
50 namespace System.Data {\r
51         internal delegate void DelegateColumnValueChange (DataColumn column, DataRow row, object proposedValue);\r
52 \r
53         /// <summary>\r
54         /// Summary description for DataColumn.\r
55         /// </summary>\r
56 \r
57         [Editor ("Microsoft.VSDesigner.Data.Design.DataColumnEditor, " + Consts.AssemblyMicrosoft_VSDesigner,\r
58                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]\r
59         [ToolboxItem (false)]\r
60         [DefaultProperty ("ColumnName")]\r
61         [DesignTimeVisible (false)]\r
62         public class DataColumn : MarshalByValueComponent {\r
63 #region Events\r
64                 EventHandlerList _eventHandlers = new EventHandlerList ();\r
65 \r
66                 //used for constraint validation\r
67                 //if an exception is fired during this event the change should be canceled\r
68                 //[MonoTODO]\r
69                 //internal event DelegateColumnValueChange ValidateColumnValueChange;\r
70 \r
71                 //used for FK Constraint Cascading rules\r
72                 //[MonoTODO]\r
73                 //internal event DelegateColumnValueChange ColumnValueChanging;\r
74 \r
75                 static readonly object _propertyChangedKey = new object ();\r
76                 internal event PropertyChangedEventHandler PropertyChanged {\r
77                         add { _eventHandlers.AddHandler (_propertyChangedKey, value); }\r
78                         remove { _eventHandlers.RemoveHandler (_propertyChangedKey, value); }\r
79                 }\r
80 \r
81                 #endregion //Events\r
82 \r
83                 #region Fields\r
84 \r
85                 private bool _allowDBNull = true;\r
86                 private bool _autoIncrement;\r
87                 private long _autoIncrementSeed;\r
88                 private long _autoIncrementStep = 1;\r
89                 private long _nextAutoIncrementValue;\r
90                 private string _caption;\r
91                 private MappingType _columnMapping;\r
92                 private string _columnName = String.Empty;\r
93                 private object _defaultValue = GetDefaultValueForType (null);\r
94                 private string _expression;\r
95                 private IExpression _compiledExpression;\r
96                 private PropertyCollection _extendedProperties = new PropertyCollection ();\r
97                 private int _maxLength = -1; //-1 represents no length limit\r
98                 private string _nameSpace;\r
99                 private int _ordinal = -1; //-1 represents not part of a collection\r
100                 private string _prefix = String.Empty;\r
101                 private bool _readOnly;\r
102                 private DataTable _table;\r
103                 private bool _unique;\r
104                 private DataContainer _dataContainer;\r
105 \r
106                 #endregion // Fields\r
107 \r
108                 #region Constructors\r
109 \r
110                 public DataColumn ()\r
111                         : this (String.Empty, typeof (string), String.Empty, MappingType.Element)\r
112                 {\r
113                 }\r
114 \r
115                 //TODO: Ctor init vars directly\r
116                 public DataColumn (string columnName)\r
117                         : this (columnName, typeof (string), String.Empty, MappingType.Element)\r
118                 {\r
119                 }\r
120 \r
121                 public DataColumn (string columnName, Type dataType)\r
122                         : this (columnName, dataType, String.Empty, MappingType.Element)\r
123                 {\r
124                 }\r
125 \r
126                 public DataColumn (string columnName, Type dataType, string expr)\r
127                         : this (columnName, dataType, expr, MappingType.Element)\r
128                 {\r
129                 }\r
130 \r
131                 public DataColumn (string columnName, Type dataType, string expr, MappingType type)\r
132                 {\r
133                         ColumnName = columnName == null ? String.Empty : columnName;\r
134 \r
135                         if (dataType == null)\r
136                                 throw new ArgumentNullException ("dataType");\r
137 \r
138                         DataType = dataType;\r
139                         Expression = expr == null ? String.Empty : expr;\r
140                         ColumnMapping = type;\r
141                 }\r
142                 #endregion\r
143 \r
144                 #region Properties\r
145 \r
146                 internal object this [int index] {\r
147                         get { return DataContainer [index]; }\r
148                         set {\r
149                                 if (!(value == null && AutoIncrement)) {\r
150                                         try {\r
151                                                 DataContainer [index] = value;\r
152                                         } catch(Exception e) {\r
153                                                 throw new ArgumentException (\r
154                                                         String.Format (\r
155                                                                 "{0}. Couldn't store <{1}> in Column named '{2}'. Expected type is {3}.",\r
156                                                                 e.Message, value, ColumnName, DataType.Name),\r
157                                                         e);\r
158                                         }\r
159                                 }\r
160 \r
161                                 if (AutoIncrement && !DataContainer.IsNull (index)) {\r
162                                         long value64 = Convert.ToInt64 (value);\r
163                                         UpdateAutoIncrementValue (value64);\r
164                                 }\r
165                         }\r
166                 }\r
167 \r
168 #if NET_2_0\r
169                 DataSetDateTime _datetimeMode = DataSetDateTime.UnspecifiedLocal;\r
170                 [DefaultValue (DataSetDateTime.UnspecifiedLocal)]\r
171                 [RefreshProperties (RefreshProperties.All)]\r
172                 public DataSetDateTime DateTimeMode {\r
173                         get { return _datetimeMode; }\r
174                         set {\r
175                                 if (DataType != typeof (DateTime))\r
176                                         throw new InvalidOperationException ("The DateTimeMode can be set only on DataColumns of type DateTime.");\r
177 \r
178                                 if (!Enum.IsDefined (typeof (DataSetDateTime), value))\r
179                                         throw new InvalidEnumArgumentException (\r
180                                                 string.Format (\r
181                                                         CultureInfo.InvariantCulture, "The {0} enumeration value, {1}, is invalid",\r
182                                                         typeof (DataSetDateTime).Name, value));\r
183 \r
184                                 if (_datetimeMode == value)\r
185                                         return;\r
186                                 if (_table == null || _table.Rows.Count == 0) {\r
187                                         _datetimeMode = value;\r
188                                         return;\r
189                                 }\r
190                                 if ((_datetimeMode == DataSetDateTime.Unspecified || _datetimeMode == DataSetDateTime.UnspecifiedLocal)\r
191                                         && (value == DataSetDateTime.Unspecified || value == DataSetDateTime.UnspecifiedLocal)) {\r
192                                         _datetimeMode = value;\r
193                                         return;\r
194                                 }\r
195 \r
196                                 throw new InvalidOperationException (\r
197                                         String.Format ("Cannot change DateTimeMode from '{0}' to '{1}' once the table has data.",\r
198                                                        _datetimeMode, value));\r
199                         }\r
200                 }\r
201 #endif\r
202 \r
203                 [DataCategory ("Data")]\r
204 #if !NET_2_0\r
205                 [DataSysDescription ("Indicates whether null values are allowed in this column.")]\r
206 #endif\r
207                 [DefaultValue (true)]\r
208                 public bool AllowDBNull {\r
209                         get { return _allowDBNull; }\r
210                         set {\r
211                                 if (!value && null != _table) {\r
212                                         for (int r = 0; r < _table.Rows.Count; r++) {\r
213                                                 DataRow row = _table.Rows [r];\r
214                                                 DataRowVersion version = row.HasVersion (DataRowVersion.Default) ?\r
215                                                         DataRowVersion.Default : DataRowVersion.Original;\r
216                                                 if (row.IsNull (this, version))\r
217                                                         throw new DataException ("Column '" + ColumnName + "' has null values in it.");\r
218                                                 //TODO: do we also check different versions of the row??\r
219                                         }\r
220                                 }\r
221 \r
222                                 _allowDBNull = value;\r
223                         }\r
224                 }\r
225 \r
226                 /// <summary>\r
227                 /// Gets or sets a value indicating whether the column automatically increments the value of the column for new rows added to the table.\r
228                 /// </summary>\r
229                 /// <remarks>\r
230                 ///             If the type of this column is not Int16, Int32, or Int64 when this property is set,\r
231                 ///             the DataType property is coerced to Int32. An exception is generated if this is a computed column\r
232                 ///             (that is, the Expression property is set.) The incremented value is used only if the row's value for this column,\r
233                 ///             when added to the columns collection, is equal to the default value.\r
234                 ///     </remarks>\r
235                 [DataCategory ("Data")]\r
236 #if !NET_2_0\r
237                 [DataSysDescription ("Indicates whether the column automatically increments itself for new rows added to the table.  The type of this column must be Int16, Int32, or Int64.")]\r
238 #endif\r
239                 [DefaultValue (false)]\r
240                 [RefreshProperties (RefreshProperties.All)]\r
241                 public bool AutoIncrement {\r
242                         get { return _autoIncrement; }\r
243                         set {\r
244                                 if (value) {\r
245                                         //Can't be true if this is a computed column\r
246                                         if (Expression != string.Empty)\r
247                                                 throw new ArgumentException ("Can not Auto Increment a computed column.");\r
248 \r
249                                         if (DefaultValue != DBNull.Value)\r
250                                                 throw new ArgumentException ("Can not set AutoIncrement while default value exists for this column.");\r
251 \r
252                                         if (!CanAutoIncrement (DataType))\r
253                                                 DataType = typeof (Int32);\r
254                                 }\r
255 \r
256                                 if (_table != null)\r
257                                         _table.Columns.UpdateAutoIncrement (this, value);\r
258                                 _autoIncrement = value;\r
259                         }\r
260                 }\r
261 \r
262                 [DataCategory ("Data")]\r
263 #if !NET_2_0\r
264                 [DataSysDescription ("Indicates the starting value for an AutoIncrement column.")]\r
265 #endif\r
266                 [DefaultValue (0)]\r
267                 public long AutoIncrementSeed {\r
268                         get { return _autoIncrementSeed; }\r
269                         set {\r
270                                 _autoIncrementSeed = value;\r
271                                 _nextAutoIncrementValue = _autoIncrementSeed;\r
272                         }\r
273                 }\r
274 \r
275                 [DataCategory ("Data")]\r
276 #if !NET_2_0\r
277                 [DataSysDescription ("Indicates the increment used by an AutoIncrement column.")]\r
278 #endif\r
279                 [DefaultValue (1)]\r
280                 public long AutoIncrementStep {\r
281                         get { return _autoIncrementStep; }\r
282                         set { _autoIncrementStep = value; }\r
283                 }\r
284 \r
285                 internal void UpdateAutoIncrementValue (long value64)\r
286                 {\r
287                         if (_autoIncrementStep > 0) {\r
288                                 if (value64 >= _nextAutoIncrementValue) {\r
289                                         _nextAutoIncrementValue = value64;\r
290                                         AutoIncrementValue ();\r
291                                 }\r
292                         } else if (value64 <= _nextAutoIncrementValue) {\r
293                                 AutoIncrementValue ();\r
294                         }\r
295                 }\r
296 \r
297                 internal long AutoIncrementValue ()\r
298                 {\r
299                         long currentValue = _nextAutoIncrementValue;\r
300                         _nextAutoIncrementValue += AutoIncrementStep;\r
301                         return currentValue;\r
302                 }\r
303 \r
304                 internal long GetAutoIncrementValue ()\r
305                 {\r
306                         return _nextAutoIncrementValue;\r
307                 }\r
308 \r
309                 internal void SetDefaultValue (int index)\r
310                 {\r
311                         if (AutoIncrement)\r
312                                 this [index] = _nextAutoIncrementValue;\r
313                         else\r
314                                 DataContainer.CopyValue (Table.DefaultValuesRowIndex, index);\r
315                 }\r
316 \r
317                 [DataCategory ("Data")]\r
318 #if !NET_2_0\r
319                 [DataSysDescription ("Indicates the default user-interface caption for this column.")]\r
320 #endif\r
321                 public string Caption {\r
322                         get { return _caption == null ? ColumnName : _caption; }\r
323                         set { _caption = value == null ? String.Empty : value; }\r
324                 }\r
325 \r
326 #if !NET_2_0\r
327                 [DataSysDescription ("Indicates how this column persists in XML: as an attribute, element, simple content node, or nothing.")]\r
328 #endif\r
329                 [DefaultValue (MappingType.Element)]\r
330                 public virtual MappingType ColumnMapping {\r
331                         get { return _columnMapping; }\r
332                         set { _columnMapping = value; }\r
333                 }\r
334 \r
335                 [DataCategory ("Data")]\r
336 #if !NET_2_0\r
337                 [DataSysDescription ("Indicates the name used to look up this column in the Columns collection of a DataTable.")]\r
338 #endif\r
339                 [RefreshProperties (RefreshProperties.All)]\r
340                 [DefaultValue ("")]\r
341                 public string ColumnName {\r
342                         get { return _columnName; }\r
343                         set {\r
344                                 if (value == null)\r
345                                         value = String.Empty;\r
346 \r
347                                 CultureInfo info = Table != null ? Table.Locale : CultureInfo.CurrentCulture;\r
348                                 if (String.Compare (value, _columnName, true, info) != 0) {\r
349                                         if (Table != null) {\r
350                                                 if (value.Length == 0)\r
351                                                         throw new ArgumentException ("ColumnName is required when it is part of a DataTable.");\r
352 \r
353                                                 Table.Columns.RegisterName (value, this);\r
354                                                 if (_columnName.Length > 0)\r
355                                                         Table.Columns.UnregisterName (_columnName);\r
356                                         }\r
357 \r
358                                         RaisePropertyChanging ("ColumnName");\r
359                                         _columnName = value;\r
360 \r
361                                         if (Table != null)\r
362                                                 Table.ResetPropertyDescriptorsCache ();\r
363                                 } else if (String.Compare (value, _columnName, false, info) != 0) {\r
364                                         RaisePropertyChanging ("ColumnName");\r
365                                         _columnName = value;\r
366 \r
367                                         if (Table != null)\r
368                                                 Table.ResetPropertyDescriptorsCache ();\r
369                                 }\r
370                         }\r
371                 }\r
372 \r
373                 [DataCategory ("Data")]\r
374 #if !NET_2_0\r
375                 [DataSysDescription ("Indicates the type of data stored in this column.")]\r
376 #endif\r
377                 [DefaultValue (typeof (string))]\r
378                 [RefreshProperties (RefreshProperties.All)]\r
379                 [TypeConverterAttribute (typeof (ColumnTypeConverter))]\r
380                 public Type DataType {\r
381                         get { return DataContainer.Type; }\r
382                         set {\r
383                                 if (value == null)\r
384                                         return;\r
385 \r
386                                 if (_dataContainer != null) {\r
387                                         if (value == _dataContainer.Type)\r
388                                                 return;\r
389 \r
390                                         // check if data already exists can we change the datatype\r
391                                         if (_dataContainer.Capacity > 0)\r
392                                                 throw new ArgumentException ("The column already has data stored.");\r
393                                 }\r
394 \r
395                                 if (null != GetParentRelation () || null != GetChildRelation ())\r
396                                         throw new InvalidConstraintException ("Cannot change datatype when column is part of a relation");\r
397 \r
398                                 Type prevType = _dataContainer != null ? _dataContainer.Type : null; // current\r
399 \r
400 #if NET_2_0\r
401                                 if (_dataContainer != null && _dataContainer.Type == typeof (DateTime))\r
402                                         _datetimeMode = DataSetDateTime.UnspecifiedLocal;\r
403 #endif\r
404                                 _dataContainer = DataContainer.Create (value, this);\r
405 \r
406                                 //Check AutoIncrement status, make compatible datatype\r
407                                 if(AutoIncrement == true) {\r
408                                         // we want to check that the datatype is supported?\r
409                                         // TODO: Is this the same as CanAutoIncrement or was the omission of Decimal intended?\r
410                                         TypeCode typeCode = Type.GetTypeCode(value);\r
411 \r
412                                         if (typeCode != TypeCode.Int16 &&\r
413                                             typeCode != TypeCode.Int32 &&\r
414                                             typeCode != TypeCode.Int64) {\r
415                                                 AutoIncrement = false;\r
416                                         }\r
417                                 }\r
418 \r
419                                 if (DefaultValue != GetDefaultValueForType (prevType))\r
420                                         SetDefaultValue (DefaultValue, true);\r
421                                 else\r
422                                         _defaultValue = GetDefaultValueForType (DataType);\r
423                         }\r
424                 }\r
425 \r
426                 /// <summary>\r
427                 ///\r
428                 /// </summary>\r
429                 /// <remarks>When AutoIncrement is set to true, there can be no default value.</remarks>\r
430                 /// <exception cref="System.InvalidCastException"></exception>\r
431                 /// <exception cref="System.ArgumentException"></exception>\r
432                 [DataCategory ("Data")]\r
433 #if !NET_2_0\r
434                 [DataSysDescription ("Indicates the default column value used when adding new rows to the table.")]\r
435 #endif\r
436                 [TypeConverterAttribute (typeof (System.Data.DefaultValueTypeConverter))]\r
437                 public object DefaultValue {\r
438                         get { return _defaultValue; }\r
439 \r
440                         set {\r
441                                 if (AutoIncrement)\r
442                                         throw new ArgumentException ("Can not set default value while AutoIncrement is true on this column.");\r
443                                 SetDefaultValue (value, false);\r
444                         }\r
445                 }\r
446 \r
447                 void SetDefaultValue (object value, bool forcedTypeCheck)\r
448                 {\r
449                         if (forcedTypeCheck || !this._defaultValue.Equals (value)) {\r
450                                 if (value == null || value == DBNull.Value)\r
451                                         _defaultValue = GetDefaultValueForType (DataType);\r
452                                 else if (DataType.IsInstanceOfType (value))\r
453                                         _defaultValue = value;\r
454                                 else\r
455                                         try {\r
456                                                 _defaultValue = Convert.ChangeType (value, DataType);\r
457                                         } catch (InvalidCastException) {\r
458                                                 string msg = String.Format ("Default Value of type '{0}' is not compatible with column type '{1}'", value.GetType (), DataType);\r
459 #if NET_2_0\r
460                                                 throw new DataException (msg);\r
461 #else\r
462                                                 throw new ArgumentException (msg);\r
463 #endif\r
464                                         }\r
465                         }\r
466 \r
467                         // store default value in the table if already belongs to\r
468                         if (Table != null && Table.DefaultValuesRowIndex != -1)\r
469                                 DataContainer [Table.DefaultValuesRowIndex] = _defaultValue;\r
470                 }\r
471 \r
472                 [DataCategory ("Data")]\r
473 #if !NET_2_0\r
474                 [DataSysDescription ("Indicates the value that this column computes for each row based on other columns instead of taking user input.")]\r
475 #endif\r
476                 [DefaultValue ("")]\r
477                 [RefreshProperties (RefreshProperties.All)]\r
478                 public string Expression {\r
479                         get { return _expression; }\r
480                         set {\r
481                                 if (value == null)\r
482                                         value = String.Empty;\r
483 \r
484                                 if (value != String.Empty) {\r
485                                         if (AutoIncrement || Unique)\r
486                                                 throw new ArgumentException ("Cannot create an expression on a column that has AutoIncrement or Unique.");\r
487 \r
488                                         if (Table != null) {\r
489                                                 for (int i = 0; i < Table.Constraints.Count; i++) {\r
490                                                         if (Table.Constraints [i].IsColumnContained (this))\r
491                                                                 throw new ArgumentException (\r
492                                                                         String.Format (\r
493                                                                                 "Cannot set Expression property on column {0}, because it is a part of a constraint.",\r
494                                                                                 ColumnName));\r
495                                                 }\r
496                                         }\r
497 \r
498                                         Parser parser = new Parser ();\r
499                                         IExpression compiledExpression = parser.Compile (value);\r
500 \r
501                                         if (Table != null) {\r
502                                                 if (compiledExpression.DependsOn (this))\r
503                                                         throw new ArgumentException ("Cannot set Expression property due to circular reference in the expression.");\r
504                                                 // Check if expression is ok\r
505                                                 if (Table.Rows.Count == 0)\r
506                                                         compiledExpression.Eval (Table.NewRow ());\r
507                                                 else\r
508                                                         compiledExpression.Eval (Table.Rows [0]);\r
509                                         }\r
510                                         ReadOnly = true;\r
511                                         _compiledExpression = compiledExpression;\r
512                                 } else {\r
513                                         _compiledExpression = null;\r
514                                         if (Table != null) {\r
515                                                 int defaultValuesRowIndex = Table.DefaultValuesRowIndex;\r
516                                                 if (defaultValuesRowIndex != -1)\r
517                                                         DataContainer.FillValues (defaultValuesRowIndex);\r
518                                         }\r
519                                 }\r
520                                 _expression = value;\r
521                         }\r
522                 }\r
523 \r
524                 internal IExpression CompiledExpression {\r
525                         get { return _compiledExpression; }\r
526                 }\r
527 \r
528                 [Browsable (false)]\r
529                 [DataCategory ("Data")]\r
530 #if !NET_2_0\r
531                 [DataSysDescription ("The collection that holds custom user information.")]\r
532 #endif\r
533                 public PropertyCollection ExtendedProperties {\r
534                         get { return _extendedProperties; }\r
535 #if NET_2_0\r
536                         internal set { _extendedProperties = value; }\r
537 #endif\r
538                 }\r
539 \r
540                 [DataCategory ("Data")]\r
541 #if !NET_2_0\r
542                 [DataSysDescription ("Indicates the maximum length of the value this column allows. ")]\r
543 #endif\r
544                 [DefaultValue (-1)] //Default == -1 no max length\r
545                 public int MaxLength {\r
546                         get { return _maxLength; }\r
547                         set {\r
548                                 if (value >= 0 && _columnMapping == MappingType.SimpleContent)\r
549                                         throw new ArgumentException (\r
550                                                 String.Format (\r
551                                                         "Cannot set MaxLength property on '{0}' column which is mapped to SimpleContent.",\r
552                                                         ColumnName));\r
553                                 //only applies to string columns\r
554                                 _maxLength = value;\r
555                         }\r
556                 }\r
557 \r
558                 [DataCategory ("Data")]\r
559 #if !NET_2_0\r
560                 [DataSysDescription ("Indicates the XML uri for elements or attributes stored in this column.")]\r
561 #endif\r
562                 public string Namespace {\r
563                         get {\r
564                                 if (_nameSpace != null)\r
565                                         return _nameSpace;\r
566                                 if (Table != null && _columnMapping != MappingType.Attribute)\r
567                                         return Table.Namespace;\r
568                                 return String.Empty;\r
569                         }\r
570                         set { _nameSpace = value; }\r
571                 }\r
572 \r
573                 //Need a good way to set the Ordinal when the column is added to a columnCollection.\r
574                 [Browsable (false)]\r
575                 [DataCategory ("Data")]\r
576 #if !NET_2_0\r
577                 [DataSysDescription ("Indicates the index of this column in the Columns collection.")]\r
578 #endif\r
579                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
580                 public int Ordinal {\r
581                         get { return _ordinal; }\r
582 #if NET_2_0\r
583                         internal set { _ordinal = value; }\r
584 #endif\r
585                 }\r
586 \r
587 #if NET_2_0\r
588                 public void SetOrdinal (int ordinal)\r
589                 {\r
590                         if (_ordinal == -1)\r
591                                 throw new ArgumentException ("Column must belong to a table.");\r
592                         _table.Columns.MoveColumn (_ordinal, ordinal);\r
593                         _ordinal = ordinal;\r
594                 }\r
595 #else\r
596                 internal void SetOrdinal(int ordinal)\r
597                 {\r
598                         _ordinal = ordinal;\r
599                 }\r
600 #endif\r
601 \r
602                 [DataCategory ("Data")]\r
603 #if !NET_2_0\r
604                 [DataSysDescription ("Indicates the Prefix used for this DataColumn in xml representation.")]\r
605 #endif\r
606                 [DefaultValue ("")]\r
607                 public string Prefix {\r
608                         get { return _prefix; }\r
609                         set { _prefix = value == null ? String.Empty : value; }\r
610                 }\r
611 \r
612                 [DataCategory ("Data")]\r
613 #if !NET_2_0\r
614                 [DataSysDescription ("Indicates whether this column allows changes once a row has been added to the table.")]\r
615 #endif\r
616                 [DefaultValue (false)]\r
617                 public bool ReadOnly {\r
618                         get { return _readOnly; }\r
619                         set { _readOnly = value; }\r
620                 }\r
621 \r
622                 [Browsable (false)]\r
623                 [DataCategory ("Data")]\r
624 #if !NET_2_0\r
625                 [DataSysDescription ("Returns the DataTable to which this column belongs.")]\r
626 #endif\r
627                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
628                 public DataTable Table {\r
629                         get { return _table; }\r
630 #if NET_2_0\r
631                         internal set { _table = value; }\r
632 #endif\r
633                 }\r
634 \r
635                 [DataCategory ("Data")]\r
636 #if !NET_2_0\r
637                 [DataSysDescription ("Indicates whether this column should restrict its values in the rows of the table to be unique.")]\r
638 #endif\r
639                 [DefaultValue (false)]\r
640                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
641                 public bool Unique {\r
642                         get { return _unique; }\r
643                         set {\r
644                                 if (_unique == value)\r
645                                         return;\r
646 \r
647                                 // Set the property value, so that when adding/removing the constraint\r
648                                 // we dont run into recursive issues.\r
649                                 _unique = value;\r
650 \r
651                                 if (_table == null)\r
652                                         return;\r
653 \r
654                                 try {\r
655                                         if (value) {\r
656                                                 if (Expression != null && Expression != String.Empty)\r
657                                                         throw new ArgumentException ("Cannot change Unique property for the expression column.");\r
658 \r
659                                                 _table.Constraints.Add (null, this, false);\r
660                                         } else {\r
661 \r
662                                                 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet (\r
663                                                         _table.Constraints, new DataColumn[] {this});\r
664                                                 _table.Constraints.Remove (uc);\r
665                                         }\r
666                                 } catch (Exception e) {\r
667                                         _unique = !value;\r
668                                         throw e;\r
669                                 }\r
670                         }\r
671                 }\r
672 \r
673                 internal DataContainer DataContainer {\r
674                         get { return _dataContainer; }\r
675                 }\r
676 \r
677                 internal static bool CanAutoIncrement (Type type)\r
678                 {\r
679                         switch (Type.GetTypeCode (type)) {\r
680                                 case TypeCode.Int16:\r
681                                 case TypeCode.Int32:\r
682                                 case TypeCode.Int64:\r
683                                 case TypeCode.Decimal:\r
684                                         return true;\r
685                         }\r
686 \r
687                         return false;\r
688                 }\r
689 \r
690                 #endregion // Properties\r
691 \r
692                 #region Methods\r
693 \r
694                 [MonoTODO]\r
695                 internal DataColumn Clone ()\r
696                 {\r
697                         DataColumn copy = new DataColumn ();\r
698 \r
699                         // Copy all the properties of column\r
700                         copy._allowDBNull = _allowDBNull;\r
701                         copy._autoIncrement = _autoIncrement;\r
702                         copy._autoIncrementSeed = _autoIncrementSeed;\r
703                         copy._autoIncrementStep = _autoIncrementStep;\r
704                         copy._caption = _caption;\r
705                         copy._columnMapping = _columnMapping;\r
706                         copy._columnName = _columnName;\r
707                         //Copy.Container\r
708                         copy.DataType = DataType;\r
709                         copy._defaultValue = _defaultValue;\r
710                         copy._expression = _expression;\r
711                         //Copy.ExtendedProperties\r
712                         copy._maxLength = _maxLength;\r
713                         copy._nameSpace = _nameSpace;\r
714                         copy._prefix = _prefix;\r
715                         copy._readOnly = _readOnly;\r
716                         //Copy.Site\r
717                         //we do not copy the unique value - it will be copyied when copying the constraints.\r
718                         //Copy.Unique = Column.Unique;\r
719 #if NET_2_0\r
720                         if (DataType == typeof (DateTime))\r
721                                 copy.DateTimeMode = _datetimeMode;\r
722 #endif\r
723 \r
724                         return copy;\r
725                 }\r
726 \r
727                 /// <summary>\r
728                 ///  Sets unique true whithout creating Constraint\r
729                 /// </summary>\r
730                 internal void SetUnique ()\r
731                 {\r
732                         _unique = true;\r
733                 }\r
734 \r
735                 [MonoTODO]\r
736                 internal void AssertCanAddToCollection ()\r
737                 {\r
738                         //Check if Default Value is set and AutoInc is set\r
739                 }\r
740 \r
741                 [MonoTODO]\r
742                 protected internal void CheckNotAllowNull ()\r
743                 {\r
744                         throw new NotImplementedException ();\r
745                 }\r
746 \r
747                 [MonoTODO]\r
748                 protected void CheckUnique ()\r
749                 {\r
750                         throw new NotImplementedException ();\r
751                 }\r
752 \r
753                 protected internal virtual void\r
754                 OnPropertyChanging (PropertyChangedEventArgs pcevent)\r
755                 {\r
756                         PropertyChangedEventHandler eh = _eventHandlers [_propertyChangedKey] as PropertyChangedEventHandler;\r
757 \r
758                         if (eh != null)\r
759                                 eh (this, pcevent);\r
760                 }\r
761 \r
762                 protected internal void RaisePropertyChanging (string name)\r
763                 {\r
764                         PropertyChangedEventArgs e = new PropertyChangedEventArgs (name);\r
765                         OnPropertyChanging (e);\r
766                 }\r
767 \r
768                 /// <summary>\r
769                 /// Gets the Expression of the column, if one exists.\r
770                 /// </summary>\r
771                 /// <returns>The Expression value, if the property is set;\r
772                 /// otherwise, the ColumnName property.</returns>\r
773                 public override string ToString ()\r
774                 {\r
775                         if (_expression != string.Empty)\r
776                                 return ColumnName + " + " + _expression;\r
777 \r
778                         return ColumnName;\r
779                 }\r
780 \r
781                 internal void SetTable (DataTable table)\r
782                 {\r
783                         if(_table != null)\r
784                                 throw new ArgumentException ("The column already belongs to a different table");\r
785 \r
786                         _table = table;\r
787                         // this will get called by DataTable\r
788                         // and DataColumnCollection\r
789                         if (_unique) {\r
790                                 // if the DataColumn is marked as Unique and then\r
791                                 // added to a DataTable , then a UniqueConstraint\r
792                                 // should be created\r
793                                 UniqueConstraint uc = new UniqueConstraint (this);\r
794                                 _table.Constraints.Add (uc);\r
795                         }\r
796 \r
797                         // allocate space in the column data container\r
798                         DataContainer.Capacity = _table.RecordCache.CurrentCapacity;\r
799 \r
800                         int defaultValuesRowIndex = _table.DefaultValuesRowIndex;\r
801                         if (defaultValuesRowIndex != -1) {\r
802                                 // store default value in the table\r
803                                 DataContainer [defaultValuesRowIndex] = _defaultValue;\r
804                                 // Set all the values in data container to default\r
805                                 // it's cheaper that raise event on each row.\r
806                                 DataContainer.FillValues (defaultValuesRowIndex);\r
807                         }\r
808                 }\r
809 \r
810                 // Returns true if all the same collumns are in columnSet and compareSet\r
811                 internal static bool AreColumnSetsTheSame (DataColumn [] columnSet, DataColumn [] compareSet)\r
812                 {\r
813                         if (null == columnSet && null == compareSet)\r
814                                 return true;\r
815 \r
816                         if (null == columnSet || null == compareSet)\r
817                                 return false;\r
818 \r
819                         if (columnSet.Length != compareSet.Length)\r
820                                 return false;\r
821 \r
822                         foreach (DataColumn col in columnSet) {\r
823                                 bool matchFound = false;\r
824                                 foreach (DataColumn compare in compareSet) {\r
825                                         if (col == compare)\r
826                                                 matchFound = true;\r
827                                 }\r
828                                 if (!matchFound)\r
829                                         return false;\r
830                         }\r
831                         return true;\r
832                 }\r
833 \r
834                 internal int CompareValues (int index1, int index2)\r
835                 {\r
836                         return DataContainer.CompareValues (index1, index2);\r
837                 }\r
838 \r
839                 /// <summary>\r
840                 ///     Returns the data relation, which contains this column.\r
841                 ///     This searches in current table's parent relations.\r
842                 /// <summary>\r
843                 /// <returns>\r
844                 ///     DataRelation if found otherwise null.\r
845                 /// </returns>\r
846                 private DataRelation GetParentRelation ()\r
847                 {\r
848                         if (_table == null)\r
849                                 return null;\r
850                         foreach (DataRelation rel in _table.ParentRelations)\r
851                                 if (rel.Contains (this))\r
852                                         return rel;\r
853                         return null;\r
854                 }\r
855 \r
856 \r
857                 /// <summary>\r
858                 ///     Returns the data relation, which contains this column.\r
859                 ///     This searches in current table's child relations.\r
860                 /// <summary>\r
861                 /// <returns>\r
862                 ///     DataRelation if found otherwise null.\r
863                 /// </returns>\r
864                 private DataRelation GetChildRelation ()\r
865                 {\r
866                         if (_table == null)\r
867                                 return null;\r
868                         foreach (DataRelation rel in _table.ChildRelations)\r
869                                 if (rel.Contains (this))\r
870                                         return rel;\r
871                         return null;\r
872                 }\r
873 \r
874                 internal void ResetColumnInfo ()\r
875                 {\r
876                         _ordinal = -1;\r
877                         _table = null;\r
878                         if (_compiledExpression != null)\r
879                                 _compiledExpression.ResetExpression ();\r
880                 }\r
881 \r
882                 internal bool DataTypeMatches (DataColumn col)\r
883                 {\r
884                         if (DataType != col.DataType)\r
885                                 return false;\r
886 #if NET_2_0\r
887                         if (DataType != typeof (DateTime))\r
888                                 return true;\r
889 \r
890                         if (DateTimeMode == col.DateTimeMode)\r
891                                 return true;\r
892 \r
893                         if (DateTimeMode == DataSetDateTime.Local || DateTimeMode == DataSetDateTime.Utc)\r
894                                 return false;\r
895 \r
896                         if (col.DateTimeMode == DataSetDateTime.Local || col.DateTimeMode == DataSetDateTime.Utc)\r
897                                 return false;\r
898 #endif\r
899                         return true;\r
900                 }\r
901 \r
902                 internal static object GetDefaultValueForType (Type type)\r
903                 {\r
904 #if NET_2_0\r
905                         if (type == null)\r
906                                 return DBNull.Value;\r
907                         if (type.Namespace == "System.Data.SqlTypes" && type.Assembly == typeof (DataColumn).Assembly) {\r
908                                 // For SqlXxx types, set SqlXxx.Null instead of DBNull.Value.\r
909                                 if (type == typeof (SqlBinary))\r
910                                         return SqlBinary.Null;\r
911                                 if (type == typeof (SqlBoolean))\r
912                                         return SqlBoolean.Null;\r
913                                 if (type == typeof (SqlByte))\r
914                                         return SqlByte.Null;\r
915                                 if (type == typeof (SqlBytes))\r
916                                         return SqlBytes.Null;\r
917                                 if (type == typeof (SqlChars))\r
918                                         return SqlChars.Null;\r
919                                 if (type == typeof (SqlDateTime))\r
920                                         return SqlDateTime.Null;\r
921                                 if (type == typeof (SqlDecimal))\r
922                                         return SqlDecimal.Null;\r
923                                 if (type == typeof (SqlDouble))\r
924                                         return SqlDouble.Null;\r
925                                 if (type == typeof (SqlGuid))\r
926                                         return SqlGuid.Null;\r
927                                 if (type == typeof (SqlInt16))\r
928                                         return SqlInt16.Null;\r
929                                 if (type == typeof (SqlInt32))\r
930                                         return SqlInt32.Null;\r
931                                 if (type == typeof (SqlInt64))\r
932                                         return SqlInt64.Null;\r
933                                 if (type == typeof (SqlMoney))\r
934                                         return SqlMoney.Null;\r
935                                 if (type == typeof (SqlSingle))\r
936                                         return SqlSingle.Null;\r
937                                 if (type == typeof (SqlString))\r
938                                         return SqlString.Null;\r
939                                 if (type == typeof (SqlXml))\r
940                                         return SqlXml.Null;\r
941                         }\r
942 #endif\r
943                         return DBNull.Value;\r
944                 }\r
945 \r
946                 #endregion // Methods\r
947         }\r
948 }\r