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