whitespace cleanups
[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         {\r
64 #region Events\r
65                 EventHandlerList _eventHandlers = new EventHandlerList ();\r
66 \r
67                 //used for constraint validation\r
68                 //if an exception is fired during this event the change should be canceled\r
69                 [MonoTODO]\r
70                 internal event DelegateColumnValueChange ValidateColumnValueChange;\r
71 \r
72                 //used for FK Constraint Cascading rules\r
73                 [MonoTODO]\r
74                 internal event DelegateColumnValueChange ColumnValueChanging;\r
75 \r
76                 static readonly object _propertyChangedKey = new object ();\r
77                 internal event PropertyChangedEventHandler PropertyChanged {\r
78                         add { _eventHandlers.AddHandler (_propertyChangedKey, value); }\r
79                         remove { _eventHandlers.RemoveHandler (_propertyChangedKey, value); }\r
80                 }\r
81 \r
82                 #endregion //Events\r
83 \r
84                 #region Fields\r
85 \r
86                 private bool _allowDBNull = true;\r
87                 private bool _autoIncrement;\r
88                 private long _autoIncrementSeed;\r
89                 private long _autoIncrementStep = 1;\r
90                 private long _nextAutoIncrementValue;\r
91                 private string _caption;\r
92                 private MappingType _columnMapping;\r
93                 private string _columnName = String.Empty;\r
94                 private object _defaultValue = GetDefaultValueForType (null);\r
95                 private string _expression;\r
96                 private IExpression _compiledExpression;\r
97                 private PropertyCollection _extendedProperties = new PropertyCollection ();\r
98                 private int _maxLength = -1; //-1 represents no length limit\r
99                 private string _nameSpace;\r
100                 private int _ordinal = -1; //-1 represents not part of a collection\r
101                 private string _prefix = String.Empty;\r
102                 private bool _readOnly;\r
103                 private DataTable _table;\r
104                 private bool _unique;\r
105                 private DataContainer _dataContainer;\r
106 \r
107                 #endregion // Fields\r
108 \r
109                 #region Constructors\r
110 \r
111                 public DataColumn() : 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): this(columnName, typeof (string), String.Empty, MappingType.Element)\r
117                 {\r
118                 }\r
119 \r
120                 public DataColumn(string columnName, Type dataType): this(columnName, dataType, String.Empty, MappingType.Element)\r
121                 {\r
122                 }\r
123 \r
124                 public DataColumn( string columnName, Type dataType,\r
125                         string expr): this(columnName, dataType, expr, MappingType.Element)\r
126                 {\r
127                 }\r
128 \r
129                 public DataColumn(string columnName, Type dataType,\r
130                         string expr, MappingType type)\r
131                 {\r
132                         ColumnName = (columnName == null ? String.Empty : columnName);\r
133 \r
134                         if (dataType == null)\r
135                                 throw new ArgumentNullException("dataType");\r
136 \r
137                         DataType = dataType;\r
138                         Expression = expr == null ? String.Empty : expr;\r
139                         ColumnMapping = type;\r
140                 }\r
141                 #endregion\r
142 \r
143                 #region Properties\r
144 \r
145                 internal object this[int index] {\r
146                         get {\r
147                                 return DataContainer[index];\r
148                         }\r
149                         set {\r
150                                 if ( !(value == null && AutoIncrement) ) {\r
151                                         try {\r
152                                                 DataContainer[index] = value;\r
153                                         }\r
154                                         catch(Exception e) {\r
155                                                 throw new ArgumentException(String.Format("{0}. Couldn't store <{1}> in Column named '{2}'. Expected type is {3}.",\r
156                                                         e.Message, value, ColumnName, DataType.Name), e);\r
157                                         }\r
158                                 }\r
159 \r
160                                 if ( AutoIncrement && !DataContainer.IsNull(index) ) {\r
161                                         long value64 = Convert.ToInt64(value);\r
162                                         UpdateAutoIncrementValue(value64);\r
163                                 }\r
164                         }\r
165                 }\r
166 \r
167 #if NET_2_0\r
168                 DataSetDateTime _datetimeMode = DataSetDateTime.UnspecifiedLocal;\r
169                 [DefaultValue (DataSetDateTime.UnspecifiedLocal)]\r
170                 [RefreshProperties (RefreshProperties.All)]\r
171                 public DataSetDateTime DateTimeMode {\r
172                         get { return _datetimeMode; }\r
173                         set {\r
174                                 if (DataType != typeof (DateTime))\r
175                                         throw new InvalidOperationException ("The DateTimeMode can be set only on DataColumns of type DateTime.");\r
176 \r
177                                 if (!Enum.IsDefined (typeof (DataSetDateTime), value))\r
178                                         throw new InvalidEnumArgumentException (\r
179                                                 string.Format (CultureInfo.InvariantCulture,\r
180                                                 "The {0} enumeration value, {1}, is invalid",\r
181                                                 typeof (DataSetDateTime).Name, value));\r
182 \r
183                                 if (_datetimeMode == value)\r
184                                         return;\r
185                                 if (_table == null || _table.Rows.Count == 0) {\r
186                                         _datetimeMode = value;\r
187                                         return;\r
188                                 }\r
189                                 if ((_datetimeMode == DataSetDateTime.Unspecified || _datetimeMode == DataSetDateTime.UnspecifiedLocal)\r
190                                         && (value == DataSetDateTime.Unspecified || value == DataSetDateTime.UnspecifiedLocal)) {\r
191                                         _datetimeMode = value;\r
192                                         return;\r
193                                 }\r
194 \r
195                                 throw new InvalidOperationException ( String.Format (\r
196                                                                         "Cannot change DateTimeMode from '{0}' to '{1}' " +\r
197                                                                         "once the table has data.",_datetimeMode, value));\r
198                         }\r
199                 }\r
200 #endif\r
201 \r
202                 [DataCategory ("Data")]\r
203 #if !NET_2_0\r
204                 [DataSysDescription ("Indicates whether null values are allowed in this column.")]\r
205 #endif\r
206                 [DefaultValue (true)]\r
207                 public bool AllowDBNull\r
208                 {\r
209                         get {\r
210                                 return _allowDBNull;\r
211                         }\r
212                         set {\r
213                                 //TODO: If we are a part of the table and this value changes\r
214                                 //we need to validate that all the existing values conform to the new setting\r
215 \r
216                                 if (true == value)\r
217                                 {\r
218                                         _allowDBNull = true;\r
219                                         return;\r
220                                 }\r
221 \r
222                                 //if Value == false case\r
223                                 if (null != _table)\r
224                                 {\r
225                                         if (_table.Rows.Count > 0)\r
226                                         {\r
227                                                 bool nullsFound = false;\r
228                                                 for(int r = 0; r < _table.Rows.Count; r++) {\r
229                                                         DataRow row = _table.Rows[r];\r
230                                                         DataRowVersion version = row.HasVersion (DataRowVersion.Default) ?\r
231                                                                 DataRowVersion.Default : DataRowVersion.Original;\r
232                                                         if(row.IsNull(this, version)) {\r
233                                                                 nullsFound = true;\r
234                                                                 break;\r
235                                                         }\r
236                                                 }\r
237 \r
238                                                 if (nullsFound)\r
239                                                         throw new DataException("Column '" + ColumnName + "' has null values in it.");\r
240                                                 //TODO: Validate no null values exist\r
241                                                 //do we also check different versions of the row??\r
242                                         }\r
243                                 }\r
244 \r
245                                 _allowDBNull = value;\r
246                         }\r
247                 }\r
248 \r
249                 /// <summary>\r
250                 /// Gets or sets a value indicating whether the column automatically increments the value of the column for new rows added to the table.\r
251                 /// </summary>\r
252                 /// <remarks>\r
253                 ///             If the type of this column is not Int16, Int32, or Int64 when this property is set,\r
254                 ///             the DataType property is coerced to Int32. An exception is generated if this is a computed column\r
255                 ///             (that is, the Expression property is set.) The incremented value is used only if the row's value for this column,\r
256                 ///             when added to the columns collection, is equal to the default value.\r
257                 ///     </remarks>\r
258                 [DataCategory ("Data")]\r
259 #if !NET_2_0\r
260                 [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
261 #endif\r
262                 [DefaultValue (false)]\r
263                 [RefreshProperties (RefreshProperties.All)]\r
264                 public bool AutoIncrement\r
265                 {\r
266                         get {\r
267                                 return _autoIncrement;\r
268                         }\r
269                         set {\r
270                                 if(value == true)\r
271                                 {\r
272                                         //Can't be true if this is a computed column\r
273                                         if (Expression != string.Empty)\r
274                                         {\r
275                                                 throw new ArgumentException("Can not Auto Increment a computed column.");\r
276                                         }\r
277 \r
278                                         if ( DefaultValue != DBNull.Value ) {\r
279                                                 throw new ArgumentException("Can not set AutoIncrement while" +\r
280                                                         " default value exists for this column.");\r
281                                         }\r
282 \r
283                                         if(!CanAutoIncrement(DataType))\r
284                                         {\r
285                                                 DataType = typeof(Int32);\r
286                                         }\r
287 \r
288                                         if (_table != null)\r
289                                                 _table.Columns.UpdateAutoIncrement(this,true);\r
290                                 }\r
291                                 else\r
292                                 {\r
293                                         if (_table != null)\r
294                                                 _table.Columns.UpdateAutoIncrement(this,false);\r
295                                 }\r
296                                 _autoIncrement = value;\r
297                         }\r
298                 }\r
299 \r
300                 [DataCategory ("Data")]\r
301 #if !NET_2_0\r
302                 [DataSysDescription ("Indicates the starting value for an AutoIncrement column.")]\r
303 #endif\r
304                 [DefaultValue (0)]\r
305                 public long AutoIncrementSeed\r
306                 {\r
307                         get {\r
308                                 return _autoIncrementSeed;\r
309                         }\r
310                         set {\r
311                                 _autoIncrementSeed = value;\r
312                                 _nextAutoIncrementValue = _autoIncrementSeed;\r
313                         }\r
314                 }\r
315 \r
316                 [DataCategory ("Data")]\r
317 #if !NET_2_0\r
318                 [DataSysDescription ("Indicates the increment used by an AutoIncrement column.")]\r
319 #endif\r
320                 [DefaultValue (1)]\r
321                 public long AutoIncrementStep\r
322                 {\r
323                         get {\r
324                                 return _autoIncrementStep;\r
325                         }\r
326                         set {\r
327                                 _autoIncrementStep = value;\r
328                         }\r
329                 }\r
330 \r
331                 internal void UpdateAutoIncrementValue(long value64)\r
332                 {\r
333                         if (_autoIncrementStep > 0 ) {\r
334                                 if (value64 >= _nextAutoIncrementValue) {\r
335                                         _nextAutoIncrementValue = value64;\r
336                                         AutoIncrementValue ();\r
337                                 }\r
338                         }\r
339                         else if (value64 <= _nextAutoIncrementValue) {\r
340                                 AutoIncrementValue ();\r
341                         }\r
342                 }\r
343 \r
344                 internal long AutoIncrementValue ()\r
345                 {\r
346                         long currentValue = _nextAutoIncrementValue;\r
347                         _nextAutoIncrementValue += AutoIncrementStep;\r
348                         return currentValue;\r
349                 }\r
350 \r
351                 internal long GetAutoIncrementValue ()\r
352                 {\r
353                         return _nextAutoIncrementValue;\r
354                 }\r
355 \r
356                 internal void SetDefaultValue(int index) {\r
357                         if (AutoIncrement)\r
358                                 this[index] = _nextAutoIncrementValue;\r
359                         else\r
360                                 DataContainer.CopyValue(Table.DefaultValuesRowIndex, index);\r
361                 }\r
362 \r
363                 [DataCategory ("Data")]\r
364 #if !NET_2_0\r
365                 [DataSysDescription ("Indicates the default user-interface caption for this column.")]\r
366 #endif\r
367                 public string Caption\r
368                 {\r
369                         get {\r
370                                 if(_caption == null)\r
371                                         return ColumnName;\r
372                                 else\r
373                                         return _caption;\r
374                         }\r
375                         set {\r
376                                 if (value == null)\r
377                                         value = String.Empty;\r
378 \r
379                                 _caption = value;\r
380                         }\r
381                 }\r
382 \r
383 #if !NET_2_0\r
384                 [DataSysDescription ("Indicates how this column persists in XML: as an attribute, element, simple content node, or nothing.")]\r
385 #endif\r
386                 [DefaultValue (MappingType.Element)]\r
387                 public virtual MappingType ColumnMapping\r
388                 {\r
389                         get {\r
390                                 return _columnMapping;\r
391                         }\r
392                         set {\r
393                                 _columnMapping = value;\r
394                         }\r
395                 }\r
396 \r
397                 [DataCategory ("Data")]\r
398 #if !NET_2_0\r
399                 [DataSysDescription ("Indicates the name used to look up this column in the Columns collection of a DataTable.")]\r
400 #endif\r
401                 [RefreshProperties (RefreshProperties.All)]\r
402                 [DefaultValue ("")]\r
403                 public string ColumnName\r
404                 {\r
405                         get {\r
406                                 return _columnName;\r
407                         }\r
408                         set {\r
409                                 if (value == null)\r
410                                         value = String.Empty;\r
411 \r
412                                 CultureInfo info = Table != null ? Table.Locale : CultureInfo.CurrentCulture;\r
413                                 if (String.Compare(value, _columnName, true, info) != 0) {\r
414                                         if (Table != null) {\r
415                                                 if (value.Length == 0)\r
416                                                         throw new ArgumentException("ColumnName is required when it is part of a DataTable.");\r
417 \r
418                                                 Table.Columns.RegisterName(value, this);\r
419                                                 if (_columnName.Length > 0)\r
420                                                         Table.Columns.UnregisterName(_columnName);\r
421                                         }\r
422 \r
423                                         RaisePropertyChanging("ColumnName");\r
424                                         _columnName = value;\r
425 \r
426                                         if (Table != null)\r
427                                                 Table.ResetPropertyDescriptorsCache();\r
428                                 }\r
429                                 else if (String.Compare(value, _columnName, false, info) != 0) {\r
430                                         RaisePropertyChanging("ColumnName");\r
431                                         _columnName = value;\r
432 \r
433                                         if (Table != null)\r
434                                                 Table.ResetPropertyDescriptorsCache();\r
435                                 }\r
436                         }\r
437                 }\r
438 \r
439                 [DataCategory ("Data")]\r
440 #if !NET_2_0\r
441                 [DataSysDescription ("Indicates the type of data stored in this column.")]\r
442 #endif\r
443                 [DefaultValue (typeof (string))]\r
444                 [RefreshProperties (RefreshProperties.All)]\r
445                 [TypeConverterAttribute (typeof (ColumnTypeConverter))]\r
446                 public Type DataType\r
447                 {\r
448                         get {\r
449                                 return DataContainer.Type;\r
450                         }\r
451                         set {\r
452 \r
453                                 if ( value == null )\r
454                                         return;\r
455 \r
456                                 if ( _dataContainer != null ) {\r
457                                         if ( value == _dataContainer.Type )\r
458                                                 return;\r
459 \r
460                                         // check if data already exists can we change the datatype\r
461                                         if ( _dataContainer.Capacity > 0 )\r
462                                                 throw new ArgumentException("The column already has data stored.");\r
463                                 }\r
464 \r
465                                 if (null != GetParentRelation () || null != GetChildRelation ())\r
466                                         throw new InvalidConstraintException ("Cannot change datatype, " +\r
467                                                                               "when column is part of a relation");\r
468 \r
469                                 Type prevType = _dataContainer != null ? _dataContainer.Type : null; // current\r
470 \r
471 #if NET_2_0\r
472                                 if (_dataContainer != null && _dataContainer.Type == typeof (DateTime))\r
473                                         _datetimeMode = DataSetDateTime.UnspecifiedLocal;\r
474 #endif\r
475                                 _dataContainer = DataContainer.Create (value, this);\r
476 \r
477                                 //Check AutoIncrement status, make compatible datatype\r
478                                 if(AutoIncrement == true) {\r
479                                         // we want to check that the datatype is supported?\r
480                                         TypeCode typeCode = Type.GetTypeCode(value);\r
481 \r
482                                         if(typeCode != TypeCode.Int16 &&\r
483                                            typeCode != TypeCode.Int32 &&\r
484                                            typeCode != TypeCode.Int64) {\r
485                                                 AutoIncrement = false;\r
486                                         }\r
487                                 }\r
488 \r
489                                 if (DefaultValue != GetDefaultValueForType (prevType))\r
490                                         SetDefaultValue (DefaultValue, true);\r
491 #if NET_2_0\r
492                                 else\r
493                                         _defaultValue = GetDefaultValueForType (DataType);\r
494 #endif\r
495                         }\r
496                 }\r
497 \r
498                 /// <summary>\r
499                 ///\r
500                 /// </summary>\r
501                 /// <remarks>When AutoIncrement is set to true, there can be no default value.</remarks>\r
502                 /// <exception cref="System.InvalidCastException"></exception>\r
503                 /// <exception cref="System.ArgumentException"></exception>\r
504                 [DataCategory ("Data")]\r
505 #if !NET_2_0\r
506                 [DataSysDescription ("Indicates the default column value used when adding new rows to the table.")]\r
507 #endif\r
508                 [TypeConverterAttribute (typeof (System.Data.DefaultValueTypeConverter))]\r
509                 public object DefaultValue\r
510                 {\r
511                         get {\r
512                                 return _defaultValue;\r
513                         }\r
514 \r
515                         set {\r
516                                 if (AutoIncrement) {\r
517                                         throw new ArgumentException("Can not set default value while" +\r
518                                                 " AutoIncrement is true on this column.");\r
519                                 }\r
520                                 SetDefaultValue (value, false);\r
521                         }\r
522                 }\r
523 \r
524                 void SetDefaultValue (object value, bool forcedTypeCheck)\r
525                 {\r
526                         {\r
527                                 object tmpObj;\r
528                                 if (forcedTypeCheck|| !this._defaultValue.Equals(value)) {\r
529                                         if (value == null) {\r
530                                                 tmpObj = DBNull.Value;\r
531                                         }\r
532                                         else {\r
533                                                 tmpObj = value;\r
534                                         }\r
535 \r
536                                         if (!this.DataType.IsInstanceOfType (tmpObj) && tmpObj != DBNull.Value) {\r
537                                                 try {\r
538                                                         //Casting to the new type\r
539                                                         tmpObj= Convert.ChangeType(tmpObj,this.DataType);\r
540                                                 }\r
541                                                 catch (InvalidCastException) {\r
542                                                         string msg = String.Format ("Default Value of type '{0}' is not compatible with column type '{1}'", tmpObj.GetType (), DataType);\r
543 #if NET_2_0\r
544                                                         throw new DataException(msg);\r
545 #else\r
546                                                         throw new ArgumentException(msg);\r
547 #endif\r
548                                                 }\r
549                                         }\r
550 #if NET_2_0\r
551                                         if (tmpObj == DBNull.Value)\r
552                                                 tmpObj = GetDefaultValueForType (DataType);\r
553 #endif\r
554                                         _defaultValue = tmpObj;\r
555                                 }\r
556 \r
557                                 // store default value in the table if already belongs to\r
558                                 if (Table != null && Table.DefaultValuesRowIndex != -1) {\r
559                                         DataContainer[Table.DefaultValuesRowIndex] = _defaultValue;\r
560                                 }\r
561                         }\r
562                 }\r
563 \r
564                 [DataCategory ("Data")]\r
565 #if !NET_2_0\r
566                 [DataSysDescription ("Indicates the value that this column computes for each row based on other columns instead of taking user input.")]\r
567 #endif\r
568                 [DefaultValue ("")]\r
569                 [RefreshProperties (RefreshProperties.All)]\r
570                 public string Expression\r
571                 {\r
572                         get {\r
573                                 return _expression;\r
574                         }\r
575                         set {\r
576                                 if (value == null)\r
577                                         value = String.Empty;\r
578 \r
579                                 if (value != String.Empty)\r
580                                 {\r
581 \r
582                                         if (AutoIncrement || Unique)\r
583                                                 throw new ArgumentException("Cannot create an expression on a column that has AutoIncrement or Unique.");\r
584 \r
585                                         if (Table != null)\r
586                                         {\r
587                                                 for (int i = 0; i < Table.Constraints.Count; i++)\r
588                                                 {\r
589                                                         if (Table.Constraints[i].IsColumnContained(this))\r
590                                                                 throw new ArgumentException(String.Format("Cannot set Expression property on column {0}, because it is a part of a constraint.", ColumnName));\r
591                                                 }\r
592                                         }\r
593 \r
594                                         Parser parser = new Parser ();\r
595                                         IExpression compiledExpression = parser.Compile (value);\r
596 \r
597                                         if (Table != null)\r
598                                         {\r
599                                                 if (compiledExpression.DependsOn(this))\r
600                                                         throw new ArgumentException("Cannot set Expression property due to circular reference in the expression.");\r
601                                                 // Check if expression is ok\r
602                                                 if (Table.Rows.Count == 0)\r
603                                                         compiledExpression.Eval (Table.NewRow());\r
604                                                 else\r
605                                                         compiledExpression.Eval (Table.Rows[0]);\r
606                                         }\r
607                                         ReadOnly = true;\r
608                                         _compiledExpression = compiledExpression;\r
609                                 }\r
610                                 else\r
611                                 {\r
612                                         _compiledExpression = null;\r
613                                         if (Table != null) {\r
614                                                 int defaultValuesRowIndex = Table.DefaultValuesRowIndex;\r
615                                                 if ( defaultValuesRowIndex != -1)\r
616                                                         DataContainer.FillValues(defaultValuesRowIndex);\r
617                                         }\r
618                                 }\r
619                                 _expression = value;\r
620                         }\r
621                 }\r
622 \r
623                 internal IExpression CompiledExpression {\r
624                         get { return _compiledExpression; }\r
625                 }\r
626 \r
627                 [Browsable (false)]\r
628                 [DataCategory ("Data")]\r
629 #if !NET_2_0\r
630                 [DataSysDescription ("The collection that holds custom user information.")]\r
631 #endif\r
632                 public PropertyCollection ExtendedProperties\r
633                 {\r
634                         get {\r
635                                 return _extendedProperties;\r
636                         }\r
637 #if NET_2_0\r
638                         internal set { _extendedProperties = value; }\r
639 #endif\r
640                 }\r
641 \r
642                 [DataCategory ("Data")]\r
643 #if !NET_2_0\r
644                 [DataSysDescription ("Indicates the maximum length of the value this column allows. ")]\r
645 #endif\r
646                 [DefaultValue (-1)]\r
647                 public int MaxLength\r
648                 {\r
649                         get {\r
650                                 //Default == -1 no max length\r
651                                 return _maxLength;\r
652                         }\r
653                         set {\r
654                                 if (value >= 0 &&\r
655                                         _columnMapping == MappingType.SimpleContent)\r
656                                         throw new ArgumentException (String.Format ("Cannot set MaxLength property on '{0}' column which is mapped to SimpleContent.", ColumnName));\r
657                                 //only applies to string columns\r
658                                 _maxLength = value;\r
659                         }\r
660                 }\r
661 \r
662                 [DataCategory ("Data")]\r
663 #if !NET_2_0\r
664                 [DataSysDescription ("Indicates the XML uri for elements or attributes stored in this column.")]\r
665 #endif\r
666                 public string Namespace\r
667                 {\r
668                         get {\r
669                                 if (_nameSpace != null)\r
670                                 {\r
671                                         return _nameSpace;\r
672                                 }\r
673                                 if ((Table != null) && (_columnMapping != MappingType.Attribute))\r
674                                 {\r
675                                         return Table.Namespace;\r
676                                 }\r
677                                 return String.Empty;\r
678                         }\r
679                         set {\r
680                                 _nameSpace = value;\r
681                         }\r
682                 }\r
683 \r
684                 //Need a good way to set the Ordinal when the column is added to a columnCollection.\r
685                 [Browsable (false)]\r
686                 [DataCategory ("Data")]\r
687 #if !NET_2_0\r
688                 [DataSysDescription ("Indicates the index of this column in the Columns collection.")]\r
689 #endif\r
690                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
691                 public int Ordinal {\r
692                         get { return _ordinal; }\r
693 #if NET_2_0\r
694                         internal  set { _ordinal = value; }\r
695 #endif\r
696                 }\r
697 \r
698 #if NET_2_0\r
699                 public void SetOrdinal (int ordinal)\r
700                 {\r
701                         if (_ordinal == -1)\r
702                                 throw new ArgumentException ("Column must belong to a table.");\r
703                         _table.Columns.MoveColumn (_ordinal, ordinal);\r
704                         _ordinal = ordinal;\r
705                 }\r
706 #else\r
707                 internal void SetOrdinal(int ordinal)\r
708                 {\r
709                         _ordinal = ordinal;\r
710                 }\r
711 #endif\r
712 \r
713                 [DataCategory ("Data")]\r
714 #if !NET_2_0\r
715                 [DataSysDescription ("Indicates the Prefix used for this DataColumn in xml representation.")]\r
716 #endif\r
717                 [DefaultValue ("")]\r
718                 public string Prefix\r
719                 {\r
720                         get {\r
721                                 return _prefix;\r
722                         }\r
723                         set {\r
724                                 if (value == null)\r
725                                         value = String.Empty;\r
726                                 _prefix = value;\r
727                         }\r
728                 }\r
729 \r
730                 [DataCategory ("Data")]\r
731 #if !NET_2_0\r
732                 [DataSysDescription ("Indicates whether this column allows changes once a row has been added to the table.")]\r
733 #endif\r
734                 [DefaultValue (false)]\r
735                 public bool ReadOnly\r
736                 {\r
737                         get {\r
738                                 return _readOnly;\r
739                         }\r
740                         set {\r
741                                 _readOnly = value;\r
742                         }\r
743                 }\r
744 \r
745                 [Browsable (false)]\r
746                 [DataCategory ("Data")]\r
747 #if !NET_2_0\r
748                 [DataSysDescription ("Returns the DataTable to which this column belongs.")]\r
749 #endif\r
750                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
751                 public DataTable Table\r
752                 {\r
753                         get {\r
754                                 return _table;\r
755                         }\r
756 #if NET_2_0\r
757                         internal set {\r
758                                 _table = value;\r
759                         }\r
760 #endif\r
761                 }\r
762 \r
763                 [DataCategory ("Data")]\r
764 #if !NET_2_0\r
765                 [DataSysDescription ("Indicates whether this column should restrict its values in the rows of the table to be unique.")]\r
766 #endif\r
767                 [DefaultValue (false)]\r
768                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
769                 public bool Unique\r
770                 {\r
771                         get {\r
772                                 return _unique;\r
773                         }\r
774                         set {\r
775 \r
776                                 if (_unique == value)\r
777                                         return;\r
778 \r
779                                 // Set the property value, so that when adding/removing the constraint\r
780                                 // we dont run into recursive issues.\r
781                                 _unique = value;\r
782 \r
783                                 if (_table == null)\r
784                                         return;\r
785 \r
786                                 try {\r
787                                         if (value) {\r
788                                                 if (Expression != null && Expression != String.Empty)\r
789                                                         throw new ArgumentException("Cannot change Unique property for the expression column.");\r
790 \r
791                                                 _table.Constraints.Add(null, this, false);\r
792                                         } else {\r
793 \r
794                                                 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet (_table.Constraints,\r
795                                                                                                 new DataColumn[] {this});\r
796                                                 _table.Constraints.Remove (uc);\r
797                                         }\r
798                                 } catch (Exception e) {\r
799                                         _unique = !value;\r
800                                         throw e;\r
801                                 }\r
802                         }\r
803                 }\r
804 \r
805                 internal DataContainer DataContainer {\r
806                         get {\r
807                                 return _dataContainer;\r
808                         }\r
809                 }\r
810 \r
811                 internal static bool CanAutoIncrement(Type type) {\r
812                         switch (Type.GetTypeCode(type)) {\r
813                                 case TypeCode.Int16:\r
814                                 case TypeCode.Int32:\r
815                                 case TypeCode.Int64:\r
816                                 case TypeCode.Decimal:\r
817                                         return true;\r
818                         }\r
819 \r
820                         return false;\r
821                 }\r
822 \r
823                 #endregion // Properties\r
824 \r
825                 #region Methods\r
826 \r
827                 [MonoTODO]\r
828                 internal DataColumn Clone() {\r
829                         DataColumn copy = new DataColumn ();\r
830 \r
831                         // Copy all the properties of column\r
832                         copy._allowDBNull = _allowDBNull;\r
833                         copy._autoIncrement = _autoIncrement;\r
834                         copy._autoIncrementSeed = _autoIncrementSeed;\r
835                         copy._autoIncrementStep = _autoIncrementStep;\r
836                         copy._caption = _caption;\r
837                         copy._columnMapping = _columnMapping;\r
838                         copy._columnName = _columnName;\r
839                         //Copy.Container\r
840                         copy.DataType = DataType;\r
841                         copy._defaultValue = _defaultValue;\r
842                         copy._expression = _expression;\r
843                         //Copy.ExtendedProperties\r
844                         copy._maxLength = _maxLength;\r
845                         copy._nameSpace = _nameSpace;\r
846                         copy._prefix = _prefix;\r
847                         copy._readOnly = _readOnly;\r
848                         //Copy.Site\r
849                         //we do not copy the unique value - it will be copyied when copying the constraints.\r
850                         //Copy.Unique = Column.Unique;\r
851 #if NET_2_0\r
852                         if (DataType == typeof (DateTime))\r
853                                 copy.DateTimeMode = _datetimeMode;\r
854 #endif\r
855 \r
856                         return copy;\r
857                 }\r
858 \r
859                 /// <summary>\r
860                 ///  Sets unique true whithout creating Constraint\r
861                 /// </summary>\r
862                 internal void SetUnique()\r
863                 {\r
864                         _unique = true;\r
865                 }\r
866 \r
867                 [MonoTODO]\r
868                 internal void AssertCanAddToCollection()\r
869                 {\r
870                         //Check if Default Value is set and AutoInc is set\r
871                 }\r
872 \r
873                 [MonoTODO]\r
874                 protected internal void CheckNotAllowNull ()\r
875                 {\r
876                         throw new NotImplementedException ();\r
877                 }\r
878 \r
879                 [MonoTODO]\r
880                 protected void CheckUnique ()\r
881                 {\r
882                         throw new NotImplementedException ();\r
883                 }\r
884 \r
885                 protected internal virtual void\r
886                 OnPropertyChanging (PropertyChangedEventArgs pcevent) {\r
887                         PropertyChangedEventHandler eh = _eventHandlers [_propertyChangedKey] as PropertyChangedEventHandler;\r
888 \r
889                         if (eh != null)\r
890                                 eh (this, pcevent);\r
891                 }\r
892 \r
893                 protected internal void RaisePropertyChanging(string name) {\r
894                         PropertyChangedEventArgs e = new PropertyChangedEventArgs (name);\r
895                         OnPropertyChanging (e);\r
896                 }\r
897 \r
898                 /// <summary>\r
899                 /// Gets the Expression of the column, if one exists.\r
900                 /// </summary>\r
901                 /// <returns>The Expression value, if the property is set;\r
902                 /// otherwise, the ColumnName property.</returns>\r
903                 public override string ToString()\r
904                 {\r
905                         if (_expression != string.Empty)\r
906                                 return ColumnName + " + " + _expression;\r
907 \r
908                         return ColumnName;\r
909                 }\r
910 \r
911                 internal void SetTable (DataTable table) {\r
912                         if(_table != null) { // serves as double check while adding to a table\r
913                                 throw new ArgumentException ("The column already belongs to a different table");\r
914                         }\r
915                         _table = table;\r
916                         // this will get called by DataTable\r
917                         // and DataColumnCollection\r
918                         if(_unique) {\r
919                                 // if the DataColumn is marked as Unique and then\r
920                                 // added to a DataTable , then a UniqueConstraint\r
921                                 // should be created\r
922                                 UniqueConstraint uc = new UniqueConstraint(this);\r
923                                 _table.Constraints.Add (uc);\r
924                         }\r
925 \r
926                         // allocate space in the column data container\r
927                         DataContainer.Capacity = _table.RecordCache.CurrentCapacity;\r
928 \r
929                         int defaultValuesRowIndex = _table.DefaultValuesRowIndex;\r
930                         if ( defaultValuesRowIndex != -1) {\r
931                                 // store default value in the table\r
932                                 DataContainer[defaultValuesRowIndex] = _defaultValue;\r
933                                 // Set all the values in data container to default\r
934                                 // it's cheaper that raise event on each row.\r
935                                 DataContainer.FillValues(defaultValuesRowIndex);\r
936                         }\r
937                 }\r
938 \r
939                 // Returns true if all the same collumns are in columnSet and compareSet\r
940                 internal static bool AreColumnSetsTheSame(DataColumn[] columnSet, DataColumn[] compareSet)\r
941                 {\r
942                         if (null == columnSet && null == compareSet)\r
943                                 return true;\r
944 \r
945                         if (null == columnSet || null == compareSet)\r
946                                 return false;\r
947 \r
948                         if (columnSet.Length != compareSet.Length)\r
949                                 return false;\r
950 \r
951                         foreach (DataColumn col in columnSet) {\r
952                                 bool matchFound = false;\r
953                                 foreach (DataColumn compare in compareSet) {\r
954                                         if (col == compare)\r
955                                                 matchFound = true;\r
956                                 }\r
957                                 if (!matchFound)\r
958                                         return false;\r
959                         }\r
960                         return true;\r
961                 }\r
962 \r
963 \r
964                 internal int CompareValues (int index1, int index2)\r
965                 {\r
966                         return DataContainer.CompareValues(index1, index2);\r
967                 }\r
968 \r
969                 /// <summary>\r
970                 ///     Returns the data relation, which contains this column.\r
971                 ///     This searches in current table's parent relations.\r
972                 /// <summary>\r
973                 /// <returns>\r
974                 ///     DataRelation if found otherwise null.\r
975                 /// </returns>\r
976         private DataRelation GetParentRelation ()\r
977                 {\r
978                         if (_table == null)\r
979                                 return null;\r
980                         foreach (DataRelation rel in _table.ParentRelations)\r
981                                 if (rel.Contains (this))\r
982                                         return rel;\r
983                         return null;\r
984                 }\r
985 \r
986 \r
987                 /// <summary>\r
988                 ///     Returns the data relation, which contains this column.\r
989                 ///     This searches in current table's child relations.\r
990                 /// <summary>\r
991                 /// <returns>\r
992                 ///     DataRelation if found otherwise null.\r
993                 /// </returns>\r
994         private DataRelation GetChildRelation ()\r
995                 {\r
996                         if (_table == null)\r
997                                 return null;\r
998                         foreach (DataRelation rel in _table.ChildRelations)\r
999                                 if (rel.Contains (this))\r
1000                                         return rel;\r
1001                         return null;\r
1002                 }\r
1003 \r
1004                 internal void ResetColumnInfo ()\r
1005                 {\r
1006                         _ordinal = -1;\r
1007                         _table = null;\r
1008                         if (_compiledExpression != null)\r
1009                                 _compiledExpression.ResetExpression ();\r
1010                 }\r
1011 \r
1012                 internal bool DataTypeMatches (DataColumn col)\r
1013                 {\r
1014                         if (DataType != col.DataType)\r
1015                                 return false;\r
1016 #if NET_2_0\r
1017                         if (DataType != typeof (DateTime))\r
1018                                 return true;\r
1019 \r
1020                         if (DateTimeMode == col.DateTimeMode)\r
1021                                 return true;\r
1022 \r
1023                         if (DateTimeMode == DataSetDateTime.Local || DateTimeMode == DataSetDateTime.Utc)\r
1024                                 return false;\r
1025 \r
1026                         if (col.DateTimeMode == DataSetDateTime.Local || col.DateTimeMode == DataSetDateTime.Utc)\r
1027                                 return false;\r
1028 #endif\r
1029                         return true;\r
1030                 }\r
1031 \r
1032                 internal static object GetDefaultValueForType (Type type)\r
1033                 {\r
1034 #if NET_2_0\r
1035                         if (type == null)\r
1036                                 return DBNull.Value;\r
1037                         if (type.Namespace == "System.Data.SqlTypes" && type.Assembly == typeof (DataColumn).Assembly) {\r
1038                                 // For SqlXxx types, set SqlXxx.Null instead of DBNull.Value.\r
1039                                 if (type == typeof (SqlBinary))\r
1040                                         return SqlBinary.Null;\r
1041                                 if (type == typeof (SqlBoolean))\r
1042                                         return SqlBoolean.Null;\r
1043                                 if (type == typeof (SqlByte))\r
1044                                         return SqlByte.Null;\r
1045                                 if (type == typeof (SqlBytes))\r
1046                                         return SqlBytes.Null;\r
1047                                 if (type == typeof (SqlChars))\r
1048                                         return SqlChars.Null;\r
1049                                 if (type == typeof (SqlDateTime))\r
1050                                         return SqlDateTime.Null;\r
1051                                 if (type == typeof (SqlDecimal))\r
1052                                         return SqlDecimal.Null;\r
1053                                 if (type == typeof (SqlDouble))\r
1054                                         return SqlDouble.Null;\r
1055                                 if (type == typeof (SqlGuid))\r
1056                                         return SqlGuid.Null;\r
1057                                 if (type == typeof (SqlInt16))\r
1058                                         return SqlInt16.Null;\r
1059                                 if (type == typeof (SqlInt32))\r
1060                                         return SqlInt32.Null;\r
1061                                 if (type == typeof (SqlInt64))\r
1062                                         return SqlInt64.Null;\r
1063                                 if (type == typeof (SqlMoney))\r
1064                                         return SqlMoney.Null;\r
1065                                 if (type == typeof (SqlSingle))\r
1066                                         return SqlSingle.Null;\r
1067                                 if (type == typeof (SqlString))\r
1068                                         return SqlString.Null;\r
1069                                 if (type == typeof (SqlXml))\r
1070                                         return SqlXml.Null;\r
1071                         }\r
1072 #endif\r
1073                         return DBNull.Value;\r
1074                 }\r
1075 \r
1076                 #endregion // Methods\r
1077         }\r
1078 }\r