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