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