(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / System.Data / System.Data / DataColumn.cs
blobb8e46eb754de61ff9ba13dd288c7fe9a2848f26f
1 //
2 // System.Data.DataColumn.cs
3 //
4 // Author:
5 // Franklin Wise (gracenote@earthlink.net)
6 // Christopher Podurgiel (cpodurgiel@msn.com)
7 // Rodrigo Moya (rodrigo@ximian.com)
8 // Daniel Morgan (danmorg@sc.rr.com)
9 // Tim Coleman (tim@timcoleman.com)
11 // (C) Copyright 2002, Franklin Wise
12 // (C) Chris Podurgiel
13 // (C) Ximian, Inc 2002
14 // Copyright (C) Tim Coleman, 2002
15 // Copyright (C) Daniel Morgan, 2002, 2003
19 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
21 // Permission is hereby granted, free of charge, to any person obtaining
22 // a copy of this software and associated documentation files (the
23 // "Software"), to deal in the Software without restriction, including
24 // without limitation the rights to use, copy, modify, merge, publish,
25 // distribute, sublicense, and/or sell copies of the Software, and to
26 // permit persons to whom the Software is furnished to do so, subject to
27 // the following conditions:
28 //
29 // The above copyright notice and this permission notice shall be
30 // included in all copies or substantial portions of the Software.
31 //
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 using System;
42 using System.ComponentModel;
43 using System.Reflection;
44 using System.Collections;
45 using System.Data.Common;
46 using Mono.Data.SqlExpressions;
48 namespace System.Data {
49 internal delegate void DelegateColumnValueChange(DataColumn column, DataRow row, object proposedValue);
51 /// <summary>
52 /// Summary description for DataColumn.
53 /// </summary>
55 [Editor]
56 [ToolboxItem (false)]
57 [DefaultProperty ("ColumnName")]
58 [DesignTimeVisible (false)]
59 public class DataColumn : MarshalByValueComponent
61 #region Events
62 [MonoTODO]
63 //used for constraint validation
64 //if an exception is fired during this event the change should be canceled
65 internal event DelegateColumnValueChange ValidateColumnValueChange;
67 //used for FK Constraint Cascading rules
68 internal event DelegateColumnValueChange ColumnValueChanging;
69 #endregion //Events
71 #region Fields
73 private bool _allowDBNull = true;
74 private bool _autoIncrement;
75 private long _autoIncrementSeed;
76 private long _autoIncrementStep = 1;
77 private long _nextAutoIncrementValue;
78 private string _caption;
79 private MappingType _columnMapping;
80 private string _columnName;
81 private object _defaultValue = DBNull.Value;
82 private string expression;
83 private IExpression compiledExpression;
84 private PropertyCollection _extendedProperties = new PropertyCollection ();
85 private int maxLength = -1; //-1 represents no length limit
86 private string nameSpace = String.Empty;
87 private int _ordinal = -1; //-1 represents not part of a collection
88 private string prefix = String.Empty;
89 private bool readOnly;
90 private DataTable _table;
91 private bool unique;
92 private AbstractDataContainer _dataContainer;
94 #endregion // Fields
96 #region Constructors
98 public DataColumn() : this(String.Empty, typeof (string), String.Empty, MappingType.Element)
102 //TODO: Ctor init vars directly
103 public DataColumn(string columnName): this(columnName, typeof (string), String.Empty, MappingType.Element)
107 public DataColumn(string columnName, Type dataType): this(columnName, dataType, String.Empty, MappingType.Element)
111 public DataColumn( string columnName, Type dataType,
112 string expr): this(columnName, dataType, expr, MappingType.Element)
116 public DataColumn(string columnName, Type dataType,
117 string expr, MappingType type)
119 ColumnName = (columnName == null ? String.Empty : columnName);
121 if(dataType == null) {
122 throw new ArgumentNullException("dataType can't be null.");
125 DataType = dataType;
126 Expression = expr == null ? String.Empty : expr;
127 ColumnMapping = type;
129 #endregion
131 #region Properties
133 internal object this[int index] {
134 get {
135 return DataContainer[index];
137 set {
138 if ( !(value == null && AutoIncrement) ) {
139 DataContainer[index] = value;
142 if ( AutoIncrement && !DataContainer.IsNull(index) ) {
143 long value64 = Convert.ToInt64(value);
144 UpdateAutoIncrementValue(value64);
149 [DataCategory ("Data")]
150 [DataSysDescription ("Indicates whether null values are allowed in this column.")]
151 [DefaultValue (true)]
152 public bool AllowDBNull
154 get {
155 return _allowDBNull;
157 set {
158 //TODO: If we are a part of the table and this value changes
159 //we need to validate that all the existing values conform to the new setting
161 if (true == value)
163 _allowDBNull = true;
164 return;
167 //if Value == false case
168 if (null != _table)
170 if (_table.Rows.Count > 0)
172 bool nullsFound = false;
173 for(int r = 0; r < _table.Rows.Count; r++) {
174 DataRow row = _table.Rows[r];
175 if(row.IsNull(this)) {
176 nullsFound = true;
177 break;
181 if (nullsFound)
182 throw new DataException("Column '" + ColumnName + "' has null values in it.");
183 //TODO: Validate no null values exist
184 //do we also check different versions of the row??
188 _allowDBNull = value;
192 /// <summary>
193 /// Gets or sets a value indicating whether the column automatically increments the value of the column for new rows added to the table.
194 /// </summary>
195 /// <remarks>
196 /// If the type of this column is not Int16, Int32, or Int64 when this property is set,
197 /// the DataType property is coerced to Int32. An exception is generated if this is a computed column
198 /// (that is, the Expression property is set.) The incremented value is used only if the row's value for this column,
199 /// when added to the columns collection, is equal to the default value.
200 /// </remarks>
201 [DataCategory ("Data")]
202 [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.")]
203 [DefaultValue (false)]
204 [RefreshProperties (RefreshProperties.All)]
205 public bool AutoIncrement
207 get {
208 return _autoIncrement;
210 set {
211 if(value == true)
213 //Can't be true if this is a computed column
214 if (Expression != string.Empty)
216 throw new ArgumentException("Can not Auto Increment a computed column.");
219 if ( DefaultValue != DBNull.Value ) {
220 throw new ArgumentException("Can not set AutoIncrement while" +
221 " default value exists for this column.");
224 //If the DataType of this Column isn't an Int
225 //Make it an int
226 TypeCode typeCode = Type.GetTypeCode(DataType);
227 if(typeCode != TypeCode.Int16 &&
228 typeCode != TypeCode.Int32 &&
229 typeCode != TypeCode.Int64)
231 DataType = typeof(Int32);
234 if (_table != null)
235 _table.Columns.UpdateAutoIncrement(this,true);
237 else
239 if (_table != null)
240 _table.Columns.UpdateAutoIncrement(this,false);
242 _autoIncrement = value;
246 [DataCategory ("Data")]
247 [DataSysDescription ("Indicates the starting value for an AutoIncrement column.")]
248 [DefaultValue (0)]
249 public long AutoIncrementSeed
251 get {
252 return _autoIncrementSeed;
254 set {
255 _autoIncrementSeed = value;
256 _nextAutoIncrementValue = _autoIncrementSeed;
260 [DataCategory ("Data")]
261 [DataSysDescription ("Indicates the increment used by an AutoIncrement column.")]
262 [DefaultValue (1)]
263 public long AutoIncrementStep
265 get {
266 return _autoIncrementStep;
268 set {
269 _autoIncrementStep = value;
273 internal void UpdateAutoIncrementValue(long value64)
275 if (_autoIncrementStep > 0 ) {
276 if (value64 >= _nextAutoIncrementValue) {
277 _nextAutoIncrementValue = value64;
278 AutoIncrementValue ();
281 else if (value64 <= _nextAutoIncrementValue) {
282 AutoIncrementValue ();
286 internal long AutoIncrementValue ()
288 long currentValue = _nextAutoIncrementValue;
289 _nextAutoIncrementValue += AutoIncrementStep;
290 return currentValue;
293 internal long GetAutoIncrementValue ()
295 return _nextAutoIncrementValue;
298 [DataCategory ("Data")]
299 [DataSysDescription ("Indicates the default user-interface caption for this column.")]
300 public string Caption
302 get {
303 if(_caption == null)
304 return ColumnName;
305 else
306 return _caption;
308 set {
309 if (value == null)
310 value = String.Empty;
312 _caption = value;
315 [DataSysDescription ("Indicates how this column persists in XML: as an attribute, element, simple content node, or nothing.")]
316 [DefaultValue (MappingType.Element)]
317 public virtual MappingType ColumnMapping
319 get {
320 return _columnMapping;
322 set {
323 _columnMapping = value;
327 [DataCategory ("Data")]
328 [DataSysDescription ("Indicates the name used to look up this column in the Columns collection of a DataTable.")]
329 [RefreshProperties (RefreshProperties.All)]
330 [DefaultValue ("")]
331 public string ColumnName
333 get {
334 return (_columnName == null ? String.Empty : _columnName);
336 set {
337 //Both are checked after the column is part of the collection
338 //TODO: Check Name duplicate
339 //TODO: check Name != null
340 _columnName = value;
344 [DataCategory ("Data")]
345 [DataSysDescription ("Indicates the type of data stored in this column.")]
346 [DefaultValue (typeof (string))]
347 [RefreshProperties (RefreshProperties.All)]
348 [TypeConverterAttribute (typeof (ColumnTypeConverter))]
349 public Type DataType
351 get {
352 return DataContainer.Type;
354 set {
355 if ( _dataContainer != null ) {
356 if ( value == _dataContainer.Type ) {
357 return;
360 // check if data already exists can we change the datatype
361 if ( _dataContainer.Capacity > 0 )
362 throw new ArgumentException("The column already has data stored.");
365 _dataContainer = AbstractDataContainer.CreateInstance(value, this);
367 //Check AutoIncrement status, make compatible datatype
368 if(AutoIncrement == true) {
369 // we want to check that the datatype is supported?
370 TypeCode typeCode = Type.GetTypeCode(value);
372 if(typeCode != TypeCode.Int16 &&
373 typeCode != TypeCode.Int32 &&
374 typeCode != TypeCode.Int64) {
375 AutoIncrement = false;
381 /// <summary>
382 ///
383 /// </summary>
384 /// <remarks>When AutoIncrement is set to true, there can be no default value.</remarks>
385 /// <exception cref="System.InvalidCastException"></exception>
386 /// <exception cref="System.ArgumentException"></exception>
387 [DataCategory ("Data")]
388 [DataSysDescription ("Indicates the default column value used when adding new rows to the table.")]
389 [TypeConverterAttribute (typeof (System.Data.DefaultValueTypeConverter))]
390 public object DefaultValue
392 get {
393 return _defaultValue;
396 set {
397 if (AutoIncrement) {
398 throw new ArgumentException("Can not set default value while" +
399 " AutoIncrement is true on this column.");
402 object tmpObj;
403 if (!this._defaultValue.Equals(value)) {
404 if (value == null) {
405 tmpObj = DBNull.Value;
407 else {
408 tmpObj = value;
411 if ((this.DataType != typeof (object))&& (tmpObj != DBNull.Value)) {
412 try {
413 //Casting to the new type
414 tmpObj= Convert.ChangeType(tmpObj,this.DataType);
416 catch (InvalidCastException) {
417 throw new InvalidCastException("Default Value type is not compatible with" +
418 " column type.");
421 _defaultValue = tmpObj;
424 // store default value in the table if already belongs to
425 if (Table != null && Table.DefaultValuesRowIndex != -1) {
426 DataContainer[Table.DefaultValuesRowIndex] = _defaultValue;
431 [DataCategory ("Data")]
432 [DataSysDescription ("Indicates the value that this column computes for each row based on other columns instead of taking user input.")]
433 [DefaultValue ("")]
434 [RefreshProperties (RefreshProperties.All)]
435 public string Expression
437 get {
438 return expression;
440 set {
441 if (value == null)
442 value = String.Empty;
444 if (value != String.Empty) {
445 Parser parser = new Parser ();
446 compiledExpression = parser.Compile (value);
447 ReadOnly = true;
449 expression = value;
453 internal IExpression CompiledExpression {
454 get { return compiledExpression; }
457 [Browsable (false)]
458 [DataCategory ("Data")]
459 [DataSysDescription ("The collection that holds custom user information.")]
460 public PropertyCollection ExtendedProperties
462 get {
463 return _extendedProperties;
467 [DataCategory ("Data")]
468 [DataSysDescription ("Indicates the maximum length of the value this column allows.")]
469 [DefaultValue (-1)]
470 public int MaxLength
472 get {
473 //Default == -1 no max length
474 return maxLength;
476 set {
477 //only applies to string columns
478 maxLength = value;
482 [DataCategory ("Data")]
483 [DataSysDescription ("Indicates the XML uri for elements stored in this column.")]
484 public string Namespace
486 get {
487 return nameSpace;
489 set {
490 if (value == null)
491 value = String.Empty;
492 nameSpace = value;
496 //Need a good way to set the Ordinal when the column is added to a columnCollection.
497 [Browsable (false)]
498 [DataCategory ("Data")]
499 [DataSysDescription ("Indicates the index of this column in the Columns collection.")]
500 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
501 public int Ordinal
503 get {
504 //value is -1 if not part of a collection
505 return _ordinal;
509 internal void SetOrdinal(int ordinal)
511 _ordinal = ordinal;
514 [DataCategory ("Data")]
515 [DataSysDescription ("Indicates the prefix used for this DataColumn in the xml representation.")]
516 [DefaultValue ("")]
517 public string Prefix
519 get {
520 return prefix;
522 set {
523 if (value == null)
524 value = String.Empty;
525 prefix = value;
529 [DataCategory ("Data")]
530 [DataSysDescription ("Indicates whether this column allows changes once a row has been added to the table.")]
531 [DefaultValue (false)]
532 public bool ReadOnly
534 get {
535 return readOnly;
537 set {
538 readOnly = value;
542 [Browsable (false)]
543 [DataCategory ("Data")]
544 [DataSysDescription ("Returns the DataTable to which this column belongs.")]
545 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
546 public DataTable Table
548 get {
549 return _table;
553 [DataCategory ("Data")]
554 [DataSysDescription ("Indicates whether this column should restrict its values in the rows of the table to be unique.")]
555 [DefaultValue (false)]
556 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
557 public bool Unique
559 get {
560 return unique;
562 set {
563 //NOTE: In .NET 1.1 the Unique property
564 //is left unchanged when it is added
565 //to a UniqueConstraint
567 if(unique != value)
569 unique = value;
571 if( value )
573 if (Expression != null && Expression != String.Empty)
574 throw new ArgumentException("Cannot change Unique property for the expression column.");
575 if( _table != null )
577 UniqueConstraint uc = new UniqueConstraint(this);
578 _table.Constraints.Add(uc);
581 else
583 if( _table != null )
585 ConstraintCollection cc = _table.Constraints;
586 //foreach (Constraint c in cc)
587 for (int i = 0; i < cc.Count; i++)
589 Constraint c = cc[i];
590 if (c is UniqueConstraint)
592 DataColumn[] cols = ((UniqueConstraint)c).Columns;
594 if (cols.Length == 1 && cols[0] == this)
596 if (!cc.CanRemove(c))
597 throw new ArgumentException("Cannot remove unique constraint '" + c.ConstraintName + "'. Remove foreign key constraint first.");
599 cc.Remove(c);
611 internal AbstractDataContainer DataContainer {
612 get {
613 return _dataContainer;
617 #endregion // Properties
619 #region Methods
621 /* ??
622 [MonoTODO]
623 protected internal void CheckNotAllowNull() {
626 [MonoTODO]
627 protected void CheckUnique() {
631 /// <summary>
632 /// Sets unique true whithout creating Constraint
633 /// </summary>
634 internal void SetUnique()
636 unique = true;
639 [MonoTODO]
640 internal void AssertCanAddToCollection()
642 //Check if Default Value is set and AutoInc is set
645 [MonoTODO]
646 protected internal void CheckNotAllowNull ()
648 throw new NotImplementedException ();
651 [MonoTODO]
652 protected void CheckUnique ()
654 throw new NotImplementedException ();
657 [MonoTODO]
658 protected internal virtual void
659 OnPropertyChanging (PropertyChangedEventArgs pcevent) {
662 [MonoTODO]
663 protected internal void RaisePropertyChanging(string name) {
666 /// <summary>
667 /// Gets the Expression of the column, if one exists.
668 /// </summary>
669 /// <returns>The Expression value, if the property is set;
670 /// otherwise, the ColumnName property.</returns>
671 public override string ToString()
673 if (expression != string.Empty)
674 return ColumnName + " + " + expression;
676 return ColumnName;
679 internal void SetTable(DataTable table) {
680 if(_table!=null) { // serves as double check while adding to a table
681 throw new ArgumentException("The column already belongs to a different table");
683 _table = table;
684 // this will get called by DataTable
685 // and DataColumnCollection
686 if(unique) {
687 // if the DataColumn is marked as Unique and then
688 // added to a DataTable , then a UniqueConstraint
689 // should be created
690 UniqueConstraint uc = new UniqueConstraint(this);
691 _table.Constraints.Add(uc);
694 // allocate space in the column data container
695 DataContainer.Capacity = _table.RecordCache.CurrentCapacity;
697 int defaultValuesRowIndex = _table.DefaultValuesRowIndex;
698 if ( defaultValuesRowIndex != -1) {
699 // store default value in the table
700 DataContainer[defaultValuesRowIndex] = _defaultValue;
701 // Set all the values in data container to default
702 // it's cheaper that raise event on each row.
703 DataContainer.FillValues(_table.DefaultValuesRowIndex);
708 // Returns true if all the same collumns are in columnSet and compareSet
709 internal static bool AreColumnSetsTheSame(DataColumn[] columnSet, DataColumn[] compareSet)
711 if (null == columnSet && null == compareSet) return true;
712 if (null == columnSet || null == compareSet) return false;
714 if (columnSet.Length != compareSet.Length) return false;
716 foreach (DataColumn col in columnSet)
718 bool matchFound = false;
719 foreach (DataColumn compare in compareSet)
721 if (col == compare)
723 matchFound = true;
726 if (! matchFound) return false;
729 return true;
732 internal int CompareValues (int index1, int index2)
734 return DataContainer.CompareValues(index1, index2);
737 #endregion // Methods