**** Merged from MCS ****
[mono-project.git] / mcs / class / System.Data / System.Data / DataTable.cs
blob5f4930b2908070704fb2f0cb20f61efecab44b53
1 //
2 // System.Data.DataTable.cs
3 //
4 // Author:
5 // Franklin Wise <gracenote@earthlink.net>
6 // Christopher Podurgiel (cpodurgiel@msn.com)
7 // Daniel Morgan <danmorg@sc.rr.com>
8 // Rodrigo Moya <rodrigo@ximian.com>
9 // Tim Coleman (tim@timcoleman.com)
10 // Ville Palo <vi64pa@koti.soon.fi>
12 // (C) Chris Podurgiel
13 // (C) Ximian, Inc 2002
14 // Copyright (C) Tim Coleman, 2002-2003
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.Data.Common;
43 using System.Collections;
44 using System.ComponentModel;
45 using System.Globalization;
46 using System.IO;
47 using System.Runtime.Serialization;
48 using System.Xml;
49 using System.Xml.Schema;
50 using Mono.Data.SqlExpressions;
52 namespace System.Data {
53 //[Designer]
54 [ToolboxItem (false)]
55 [DefaultEvent ("RowChanging")]
56 [DefaultProperty ("TableName")]
57 [DesignTimeVisible (false)]
58 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
59 [Serializable]
60 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
62 #region Fields
64 internal DataSet dataSet;
66 private bool _caseSensitive;
67 private DataColumnCollection _columnCollection;
68 private ConstraintCollection _constraintCollection;
69 private DataView _defaultView;
71 private string _displayExpression;
72 private PropertyCollection _extendedProperties;
73 private bool _hasErrors;
74 private CultureInfo _locale;
75 private int _minimumCapacity;
76 private string _nameSpace;
77 private DataRelationCollection _childRelations;
78 private DataRelationCollection _parentRelations;
79 private string _prefix;
80 private DataColumn[] _primaryKey;
81 private DataRowCollection _rows;
82 private ISite _site;
83 private string _tableName;
84 private bool _containsListCollection;
85 private string _encodedTableName;
86 internal bool _duringDataLoad;
87 internal bool _nullConstraintViolationDuringDataLoad;
88 private bool dataSetPrevEnforceConstraints;
89 private bool dataTablePrevEnforceConstraints;
90 private bool enforceConstraints = true;
91 private DataRowBuilder _rowBuilder;
92 private ArrayList _indexes;
93 private RecordCache _recordCache;
94 private int _defaultValuesRowIndex = -1;
95 protected internal bool fInitInProgress;
97 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
98 // CaseSensitive property. So when you lost you virginity it's gone for ever
99 private bool _virginCaseSensitive = true;
101 #endregion //Fields
103 private delegate void PostEndInit();
105 /// <summary>
106 /// Initializes a new instance of the DataTable class with no arguments.
107 /// </summary>
108 public DataTable ()
110 dataSet = null;
111 _columnCollection = new DataColumnCollection(this);
112 _constraintCollection = new ConstraintCollection(this);
113 _extendedProperties = new PropertyCollection();
114 _tableName = "";
115 _nameSpace = null;
116 _caseSensitive = false; //default value
117 _displayExpression = null;
118 _primaryKey = null;
119 _site = null;
120 _rows = new DataRowCollection (this);
121 _indexes = new ArrayList();
122 _recordCache = new RecordCache(this);
124 //LAMESPEC: spec says 25 impl does 50
125 _minimumCapacity = 50;
127 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
128 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
130 _defaultView = new DataView(this);
133 /// <summary>
134 /// Intitalizes a new instance of the DataTable class with the specified table name.
135 /// </summary>
136 public DataTable (string tableName) : this ()
138 _tableName = tableName;
141 /// <summary>
142 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
143 /// </summary>
144 [MonoTODO]
145 protected DataTable (SerializationInfo info, StreamingContext context)
146 : this ()
148 string schema = info.GetString ("XmlSchema");
149 string data = info.GetString ("XmlDiffGram");
151 DataSet ds = new DataSet ();
152 ds.ReadXmlSchema (new StringReader (schema));
153 ds.Tables [0].CopyProperties (this);
154 ds = new DataSet ();
155 ds.Tables.Add (this);
156 ds.ReadXml (new StringReader (data), XmlReadMode.DiffGram);
157 ds.Tables.Remove (this);
158 /* keeping for a while. With the change above, we shouldn't have to consider
159 * DataTable mode in schema inference/read.
160 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
161 XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
162 mapper.Read (xtr);
164 XmlDiffLoader loader = new XmlDiffLoader (this);
165 xtr = new XmlTextReader(new StringReader (data));
166 loader.Load (xtr);
170 #if NET_2_0
171 public DataTable (string tableName, string tbNamespace)
172 : this (tableName)
174 _nameSpace = tbNamespace;
176 #endif
178 /// <summary>
179 /// Indicates whether string comparisons within the table are case-sensitive.
180 /// </summary>
181 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
182 public bool CaseSensitive {
183 get {
184 if (_virginCaseSensitive && dataSet != null)
185 return dataSet.CaseSensitive;
186 else
187 return _caseSensitive;
189 set {
190 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
191 throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
193 _virginCaseSensitive = false;
194 _caseSensitive = value;
198 internal bool VirginCaseSensitive {
199 get { return _virginCaseSensitive; }
200 set { _virginCaseSensitive = value; }
203 internal ArrayList Indexes{
204 get { return _indexes; }
207 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
209 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
210 OnColumnChanged(e);
213 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
215 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
216 OnColumnChanging (e);
219 internal void DeletedDataRow (DataRow dr, DataRowAction action)
221 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
222 OnRowDeleted (e);
225 internal void DeletingDataRow (DataRow dr, DataRowAction action)
227 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
228 OnRowDeleting(e);
231 internal void ChangedDataRow (DataRow dr, DataRowAction action)
233 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
234 OnRowChanged (e);
237 internal void ChangingDataRow (DataRow dr, DataRowAction action)
239 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
240 OnRowChanging (e);
243 /// <summary>
244 /// Gets the collection of child relations for this DataTable.
245 /// </summary>
246 [Browsable (false)]
247 [DataSysDescription ("Returns the child relations for this table.")]
248 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
249 public DataRelationCollection ChildRelations {
250 get {
251 return _childRelations;
255 /// <summary>
256 /// Gets the collection of columns that belong to this table.
257 /// </summary>
258 [DataCategory ("Data")]
259 [DataSysDescription ("The collection that holds the columns for this table.")]
260 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
261 public DataColumnCollection Columns {
262 get { return _columnCollection; }
265 /// <summary>
266 /// Gets the collection of constraints maintained by this table.
267 /// </summary>
268 [DataCategory ("Data")]
269 [DataSysDescription ("The collection that holds the constraints for this table.")]
270 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
271 public ConstraintCollection Constraints {
272 get { return _constraintCollection; }
275 /// <summary>
276 /// Gets the DataSet that this table belongs to.
277 /// </summary>
278 [Browsable (false)]
279 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
280 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
281 public DataSet DataSet {
282 get { return dataSet; }
287 /// <summary>
288 /// Gets a customized view of the table which may
289 /// include a filtered view, or a cursor position.
290 /// </summary>
291 [MonoTODO]
292 [Browsable (false)]
293 [DataSysDescription ("This is the default DataView for the table.")]
294 public DataView DefaultView {
295 get { return _defaultView; }
299 /// <summary>
300 /// Gets or sets the expression that will return
301 /// a value used to represent this table in the user interface.
302 /// </summary>
303 [DataCategory ("Data")]
304 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
305 [DefaultValue ("")]
306 public string DisplayExpression {
307 get { return _displayExpression == null ? "" : _displayExpression; }
308 set { _displayExpression = value; }
311 /// <summary>
312 /// Gets the collection of customized user information.
313 /// </summary>
314 [Browsable (false)]
315 [DataCategory ("Data")]
316 [DataSysDescription ("The collection that holds custom user information.")]
317 public PropertyCollection ExtendedProperties {
318 get { return _extendedProperties; }
321 /// <summary>
322 /// Gets a value indicating whether there are errors in
323 /// any of the_rows in any of the tables of the DataSet to
324 /// which the table belongs.
325 /// </summary>
326 [Browsable (false)]
327 [DataSysDescription ("Returns whether the table has errors.")]
328 public bool HasErrors {
329 get {
330 // we can not use the _hasError flag because we do not know when to turn it off!
331 for (int i = 0; i < _rows.Count; i++)
333 if (_rows[i].HasErrors)
334 return true;
336 return false;
340 /// <summary>
341 /// Gets or sets the locale information used to
342 /// compare strings within the table.
343 /// </summary>
344 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
345 public CultureInfo Locale {
346 get {
347 // if the locale is null, we check for the DataSet locale
348 // and if the DataSet is null we return the current culture.
349 // this way if DataSet locale is changed, only if there is no locale for
350 // the DataTable it influece the Locale get;
351 if (_locale != null)
352 return _locale;
353 if (DataSet != null)
354 return DataSet.Locale;
355 return CultureInfo.CurrentCulture;
357 set {
358 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
359 throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
361 if (_locale == null || !_locale.Equals(value))
362 _locale = value;
366 /// <summary>
367 /// Gets or sets the initial starting size for this table.
368 /// </summary>
369 [DataCategory ("Data")]
370 [DataSysDescription ("Indicates an initial starting size for this table.")]
371 [DefaultValue (50)]
372 public int MinimumCapacity {
373 get { return _minimumCapacity; }
374 set { _minimumCapacity = value; }
377 /// <summary>
378 /// Gets or sets the namespace for the XML represenation
379 /// of the data stored in the DataTable.
380 /// </summary>
381 [DataCategory ("Data")]
382 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
383 public string Namespace {
384 get {
385 if (_nameSpace != null)
386 return _nameSpace;
387 else if (dataSet != null)
388 return dataSet.Namespace;
389 return String.Empty;
391 set { _nameSpace = value; }
394 /// <summary>
395 /// Gets the collection of parent relations for
396 /// this DataTable.
397 /// </summary>
398 [Browsable (false)]
399 [DataSysDescription ("Returns the parent relations for this table.")]
400 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
401 public DataRelationCollection ParentRelations {
402 get {
403 return _parentRelations;
407 /// <summary>
408 /// Gets or sets the namespace for the XML represenation
409 /// of the data stored in the DataTable.
410 /// </summary>
411 [DataCategory ("Data")]
412 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
413 [DefaultValue ("")]
414 public string Prefix {
415 get { return _prefix == null ? "" : _prefix; }
416 set {
417 // Prefix cannot contain any special characters other than '_' and ':'
418 for (int i = 0; i < value.Length; i++) {
419 if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
420 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
422 _prefix = value;
426 /// <summary>
427 /// Gets or sets an array of columns that function as
428 /// primary keys for the data table.
429 /// </summary>
430 [DataCategory ("Data")]
431 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
432 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
433 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
434 public DataColumn[] PrimaryKey {
435 get {
436 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
437 if (null == uc) return new DataColumn[] {};
438 return uc.Columns;
440 set {
441 UniqueConstraint oldPKConstraint = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
443 // first check if value is the same as current PK.
444 if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))
445 return;
447 // remove PK Constraint
448 if(oldPKConstraint != null) {
449 Constraints.Remove(oldPKConstraint);
452 if (value != null) {
453 //Does constraint exist for these columns
454 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
455 this.Constraints, (DataColumn[]) value);
457 //if constraint doesn't exist for columns
458 //create new unique primary key constraint
459 if (null == uc) {
460 foreach (DataColumn Col in (DataColumn[]) value) {
462 if (Col.Table == null)
463 break;
465 if (Columns.IndexOf (Col) < 0)
466 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
470 uc = new UniqueConstraint( (DataColumn[]) value, true);
472 Constraints.Add (uc);
474 else {
475 //set existing constraint as the new primary key
476 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
479 // if we really supplied some columns fo primary key -
480 // rebuild indexes fo all foreign key constraints
481 if(value.Length > 0) {
482 foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
483 constraint.AssertConstraint();
487 else {
488 // if primary key is null now - drop all the indexes for foreign key constraints
489 foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
490 Index index = constraint.Index;
491 constraint.Index = null;
492 this.DropIndex(index);
499 /// <summary>
500 /// Gets the collection of_rows that belong to this table.
501 /// </summary>
502 [Browsable (false)]
503 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
504 public DataRowCollection Rows {
505 get { return _rows; }
508 /// <summary>
509 /// Gets or sets an System.ComponentModel.ISite
510 /// for the DataTable.
511 /// </summary>
512 [Browsable (false)]
513 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
514 public override ISite Site {
515 get { return _site; }
516 set { _site = value; }
519 /// <summary>
520 /// Gets or sets the name of the the DataTable.
521 /// </summary>
522 [DataCategory ("Data")]
523 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
524 [DefaultValue ("")]
525 [RefreshProperties (RefreshProperties.All)]
526 public string TableName {
527 get { return _tableName == null ? "" : _tableName; }
528 set { _tableName = value; }
531 bool IListSource.ContainsListCollection {
532 get {
533 // the collection is a DataView
534 return false;
538 internal RecordCache RecordCache {
539 get {
540 return _recordCache;
544 private bool EnforceConstraints {
545 get { return enforceConstraints; }
546 set {
547 if (value != enforceConstraints) {
548 if (value) {
549 // first assert all unique constraints
550 foreach (UniqueConstraint uc in this.Constraints.UniqueConstraints)
551 uc.AssertConstraint ();
552 // then assert all foreign keys
553 foreach (ForeignKeyConstraint fk in this.Constraints.ForeignKeyConstraints)
554 fk.AssertConstraint ();
556 enforceConstraints = value;
561 internal bool RowsExist(DataColumn[] columns, DataColumn[] relatedColumns,DataRow row)
563 int curIndex = row.IndexFromVersion(DataRowVersion.Default);
564 int tmpRecord = RecordCache.NewRecord();
566 try {
567 for (int i = 0; i < relatedColumns.Length; i++) {
568 // according to MSDN: the DataType value for both columns must be identical.
569 columns[i].DataContainer.CopyValue(relatedColumns[i].DataContainer, curIndex, tmpRecord);
572 return RowsExist(columns, tmpRecord, relatedColumns.Length);
574 finally {
575 RecordCache.DisposeRecord(tmpRecord);
579 bool RowsExist(DataColumn[] columns, int index, int length)
581 bool rowsExist = false;
582 Index indx = this.GetIndexByColumns (columns);
584 if (indx != null) { // lookup for a row in index
585 rowsExist = (indx.FindSimple (index, length, false) != null);
588 if(indx == null || rowsExist == false) {
589 // no index or rowExist= false, we have to perform full-table scan
590 // check that there is a parent for this row.
591 foreach (DataRow thisRow in this.Rows) {
592 if (thisRow.RowState != DataRowState.Deleted) {
593 bool match = true;
594 // check if the values in the columns are equal
595 int thisIndex = thisRow.IndexFromVersion(DataRowVersion.Current);
596 foreach (DataColumn column in columns) {
597 if (column.DataContainer.CompareValues(thisIndex, index) != 0) {
598 match = false;
599 break;
602 if (match) {// there is a row with columns values equals to those supplied.
603 rowsExist = true;
604 break;
609 return rowsExist;
612 /// <summary>
613 /// Commits all the changes made to this table since the
614 /// last time AcceptChanges was called.
615 /// </summary>
616 public void AcceptChanges ()
618 //FIXME: Do we need to validate anything here or
619 //try to catch any errors to deal with them?
621 // we do not use foreach because if one of the rows is in Delete state
622 // it will be romeved from Rows and we get an exception.
623 DataRow myRow;
624 for (int i = 0; i < Rows.Count; )
626 myRow = Rows[i];
627 myRow.AcceptChanges();
629 // if the row state is Detached it meens that it was removed from row list (Rows)
630 // so we should not increase 'i'.
631 if (myRow.RowState != DataRowState.Detached)
632 i++;
636 /// <summary>
637 /// Begins the initialization of a DataTable that is used
638 /// on a form or used by another component. The initialization
639 /// occurs at runtime.
640 /// </summary>
641 public virtual void BeginInit ()
643 fInitInProgress = true;
646 /// <summary>
647 /// Turns off notifications, index maintenance, and
648 /// constraints while loading data.
649 /// </summary>
650 [MonoTODO]
651 public void BeginLoadData ()
653 if (!this._duringDataLoad)
655 //duringDataLoad is important to EndLoadData and
656 //for not throwing unexpected exceptions.
657 this._duringDataLoad = true;
658 this._nullConstraintViolationDuringDataLoad = false;
660 if (this.dataSet != null)
662 //Saving old Enforce constraints state for later
663 //use in the EndLoadData.
664 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
665 this.dataSet.EnforceConstraints = false;
667 else {
668 //if table does not belong to any data set use EnforceConstraints of the table
669 this.dataTablePrevEnforceConstraints = this.EnforceConstraints;
670 this.EnforceConstraints = false;
673 return;
676 /// <summary>
677 /// Clears the DataTable of all data.
678 /// </summary>
679 public void Clear () {
680 // Foriegn key constraints are checked in _rows.Clear method
681 _rows.Clear ();
684 /// <summary>
685 /// Clones the structure of the DataTable, including
686 /// all DataTable schemas and constraints.
687 /// </summary>
688 public virtual DataTable Clone ()
690 // Use Activator so we can use non-public constructors.
691 DataTable Copy = (DataTable) Activator.CreateInstance(GetType(), true);
692 CopyProperties (Copy);
693 return Copy;
696 /// <summary>
697 /// Computes the given expression on the current_rows that
698 /// pass the filter criteria.
699 /// </summary>
700 [MonoTODO]
701 public object Compute (string expression, string filter)
703 // expression is an aggregate function
704 // filter is an expression used to limit rows
706 DataRow[] rows = Select(filter);
708 Parser parser = new Parser (rows);
709 IExpression expr = parser.Compile (expression);
710 object obj = expr.Eval (rows[0]);
712 return obj;
715 /// <summary>
716 /// Copies both the structure and data for this DataTable.
717 /// </summary>
718 public DataTable Copy ()
720 DataTable copy = Clone();
722 copy._duringDataLoad = true;
723 foreach (DataRow row in Rows) {
724 DataRow newRow = copy.NewNotInitializedRow();
725 copy.Rows.Add(newRow);
726 CopyRow(row,newRow);
728 copy._duringDataLoad = false;
729 return copy;
732 internal void CopyRow(DataRow fromRow,DataRow toRow)
734 fromRow.CopyState(toRow);
736 if (fromRow.HasVersion(DataRowVersion.Original)) {
737 toRow._original = toRow.Table.RecordCache.CopyRecord(this,fromRow._original,-1);
740 if (fromRow.HasVersion(DataRowVersion.Current)) {
741 toRow._current = toRow.Table.RecordCache.CopyRecord(this,fromRow._current,-1);
745 private void CopyProperties (DataTable Copy)
748 Copy.CaseSensitive = CaseSensitive;
749 Copy.VirginCaseSensitive = VirginCaseSensitive;
751 // Copy.ChildRelations
752 // Copy.Constraints
753 // Copy.Container
754 // Copy.DefaultView
755 // Copy.DesignMode
756 Copy.DisplayExpression = DisplayExpression;
757 if(ExtendedProperties.Count > 0) {
758 // Cannot copy extended properties directly as the property does not have a set accessor
759 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
760 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
761 for (int i=0; i < ExtendedProperties.Count; i++)
762 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
764 Copy.Locale = Locale;
765 Copy.MinimumCapacity = MinimumCapacity;
766 Copy.Namespace = Namespace;
767 // Copy.ParentRelations
768 Copy.Prefix = Prefix;
769 Copy.Site = Site;
770 Copy.TableName = TableName;
774 // Copy columns
775 foreach (DataColumn Column in Columns) {
776 // When cloning a table, the columns may be added in the default
777 // constructor.
778 if (!Copy.Columns.Contains(Column.ColumnName)) Copy.Columns.Add (CopyColumn (Column));
781 CopyConstraints(Copy);
782 // add primary key to the copy
783 if (PrimaryKey.Length > 0)
785 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
786 for (int i = 0; i < pColumns.Length; i++)
787 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
789 Copy.PrimaryKey = pColumns;
793 private void CopyConstraints(DataTable copy)
795 UniqueConstraint origUc;
796 UniqueConstraint copyUc;
797 for (int i = 0; i < this.Constraints.Count; i++)
799 if (this.Constraints[i] is UniqueConstraint)
801 origUc = (UniqueConstraint)this.Constraints[i];
802 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
803 for (int j = 0; j < columns.Length; j++)
804 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
806 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
807 copy.Constraints.Add(copyUc);
811 /// <summary>
812 /// Ends the initialization of a DataTable that is used
813 /// on a form or used by another component. The
814 /// initialization occurs at runtime.
815 /// </summary>
816 [MonoTODO]
817 public virtual void EndInit ()
819 fInitInProgress = false;
820 // Add the constraints
821 PostEndInit _postEndInit = new PostEndInit (_constraintCollection.PostEndInit);
822 _postEndInit();
825 /// <summary>
826 /// Turns on notifications, index maintenance, and
827 /// constraints after loading data.
828 /// </summary>
829 public void EndLoadData()
831 int i = 0;
832 if (this._duringDataLoad)
835 if (this.dataSet !=null)
837 //Getting back to previous EnforceConstraint state
838 this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
840 else {
841 //Getting back to the table's previous EnforceConstraint state
842 this.EnforceConstraints = this.dataTablePrevEnforceConstraints;
845 if(this._nullConstraintViolationDuringDataLoad) {
846 this._nullConstraintViolationDuringDataLoad = false;
847 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
850 //Returning from loading mode, raising exceptions as usual
851 this._duringDataLoad = false;
857 /// <summary>
858 /// Gets a copy of the DataTable that contains all
859 /// changes made to it since it was loaded or
860 /// AcceptChanges was last called.
861 /// </summary>
862 public DataTable GetChanges()
864 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
867 /// <summary>
868 /// Gets a copy of the DataTable containing all
869 /// changes made to it since it was last loaded, or
870 /// since AcceptChanges was called, filtered by DataRowState.
871 /// </summary>
872 public DataTable GetChanges(DataRowState rowStates)
874 DataTable copyTable = null;
876 IEnumerator rowEnumerator = Rows.GetEnumerator();
877 while (rowEnumerator.MoveNext()) {
878 DataRow row = (DataRow)rowEnumerator.Current;
879 // The spec says relationship constraints may cause Unchanged parent rows to be included but
880 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.
881 if (row.IsRowChanged(rowStates)) {
882 if (copyTable == null)
883 copyTable = Clone();
884 DataRow newRow = copyTable.NewRow();
885 row.CopyValuesToRow(newRow);
886 copyTable.Rows.Add (newRow);
890 return copyTable;
893 #if NET_2_0
894 [MonoTODO]
895 public DataTableReader GetDataReader ()
897 throw new NotImplementedException ();
899 #endif
901 /// <summary>
902 /// Gets an array of DataRow objects that contain errors.
903 /// </summary>
904 public DataRow[] GetErrors ()
906 ArrayList errors = new ArrayList();
907 for (int i = 0; i < _rows.Count; i++)
909 if (_rows[i].HasErrors)
910 errors.Add(_rows[i]);
913 return (DataRow[]) errors.ToArray(typeof(DataRow));
916 /// <summary>
917 /// This member is only meant to support Mono's infrastructure
918 /// </summary>
919 protected virtual DataTable CreateInstance ()
921 return Activator.CreateInstance (this.GetType (), true) as DataTable;
924 /// <summary>
925 /// This member is only meant to support Mono's infrastructure
926 /// </summary>
927 protected virtual Type GetRowType ()
929 return typeof (DataRow);
932 /// <summary>
933 /// This member is only meant to support Mono's infrastructure
934 ///
935 /// Used for Data Binding between System.Web.UI. controls
936 /// like a DataGrid
937 /// or
938 /// System.Windows.Forms controls like a DataGrid
939 /// </summary>
940 IList IListSource.GetList ()
942 IList list = (IList) _defaultView;
943 return list;
946 /// <summary>
947 /// Copies a DataRow into a DataTable, preserving any
948 /// property settings, as well as original and current values.
949 /// </summary>
950 public void ImportRow (DataRow row)
952 DataRow newRow = NewRow();
953 Rows.Add(newRow);
954 row.CopyValuesToRow(newRow);
958 internal int DefaultValuesRowIndex
960 get {
961 return _defaultValuesRowIndex;
965 /// <summary>
966 /// This member is only meant to support Mono's infrastructure
967 /// </summary>
968 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
970 DataSet dset;
971 if (dataSet != null)
972 dset = dataSet;
973 else {
974 dset = new DataSet ("tmpDataSet");
975 dset.Tables.Add (this);
978 StringWriter sw = new StringWriter ();
979 XmlTextWriter tw = new XmlTextWriter (sw);
980 dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
981 tw.Close ();
983 StringWriter sw2 = new StringWriter ();
984 DataTableCollection tables = new DataTableCollection (dset);
985 tables.Add (this);
986 XmlSchema schema = dset.BuildSchema (tables, null);
987 schema.Write (sw2);
988 sw2.Close ();
990 info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
991 info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
994 #if NET_2_0
995 [MonoTODO]
996 public void Load (IDataReader reader)
998 throw new NotImplementedException ();
1001 [MonoTODO]
1002 public void Load (IDataReader reader, LoadOption loadOption)
1004 throw new NotImplementedException ();
1006 #endif
1008 /// <summary>
1009 /// Finds and updates a specific row. If no matching row
1010 /// is found, a new row is created using the given values.
1011 /// </summary>
1012 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
1014 DataRow row = null;
1015 if (PrimaryKey.Length == 0) {
1016 row = Rows.Add (values);
1017 if (fAcceptChanges)
1018 row.AcceptChanges ();
1020 else {
1021 bool hasPrimaryValues = true;
1022 // initiate an array that has the values of the primary keys.
1023 object[] keyValues = new object[PrimaryKey.Length];
1024 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
1026 if(PrimaryKey[i].Ordinal < values.Length)
1027 keyValues[i] = values[PrimaryKey[i].Ordinal];
1028 else
1029 hasPrimaryValues = false;
1032 if (hasPrimaryValues){
1033 // find the row in the table.
1034 row = Rows.Find(keyValues);
1037 if (row == null)
1038 row = Rows.Add (values);
1039 else
1040 row.ItemArray = values;
1042 if (fAcceptChanges)
1043 row.AcceptChanges ();
1046 return row;
1049 internal DataRow LoadDataRow(IDataRecord record, int[] mapping, bool fAcceptChanges)
1051 DataRow row = null;
1052 if (PrimaryKey.Length == 0) {
1053 row = NewRow();
1054 row.SetValuesFromDataRecord(record, mapping);
1055 Rows.Add (row);
1057 if (fAcceptChanges) {
1058 row.AcceptChanges();
1061 else {
1062 bool hasPrimaryValues = true;
1063 int tmpRecord = this.RecordCache.NewRecord();
1064 try {
1065 for (int i = 0; i < PrimaryKey.Length && hasPrimaryValues; i++) {
1066 DataColumn primaryKeyColumn = PrimaryKey[i];
1067 int ordinal = primaryKeyColumn.Ordinal;
1068 if(ordinal < mapping.Length) {
1069 primaryKeyColumn.DataContainer.SetItemFromDataRecord(tmpRecord,record,mapping[ordinal]);
1071 else {
1072 hasPrimaryValues = false;
1076 if (hasPrimaryValues) {
1077 // find the row in the table.
1078 row = Rows.Find(tmpRecord,PrimaryKey.Length);
1081 finally {
1082 this.RecordCache.DisposeRecord(tmpRecord);
1085 if (row == null) {
1086 row = NewRow();
1087 row.SetValuesFromDataRecord(record, mapping);
1088 Rows.Add (row);
1090 else {
1091 row.SetValuesFromDataRecord(record, mapping);
1094 if (fAcceptChanges) {
1095 row.AcceptChanges();
1098 return row;
1101 #if NET_2_0
1102 [MonoTODO]
1103 public DataRow LoadDataRow (object[] values, LoadOption loadOption)
1105 throw new NotImplementedException ();
1108 [MonoTODO]
1109 public void Merge (DataTable table)
1111 throw new NotImplementedException ();
1114 [MonoTODO]
1115 public void Merge (DataTable table, bool preserveChanges)
1117 throw new NotImplementedException ();
1120 [MonoTODO]
1121 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
1123 throw new NotImplementedException ();
1125 #endif
1127 /// <summary>
1128 /// Creates a new DataRow with the same schema as the table.
1129 /// </summary>
1130 public DataRow NewRow ()
1132 // initiate only one row builder.
1133 if (_rowBuilder == null)
1134 _rowBuilder = new DataRowBuilder (this, 0, 0);
1136 // new row get id -1.
1137 _rowBuilder._rowId = -1;
1139 // initialize default values row for the first time
1140 if ( _defaultValuesRowIndex == -1 ) {
1141 _defaultValuesRowIndex = RecordCache.NewRecord();
1142 foreach(DataColumn column in Columns) {
1143 column.DataContainer[_defaultValuesRowIndex] = column.DefaultValue;
1147 return this.NewRowFromBuilder (_rowBuilder);
1150 /// <summary>
1151 /// This member supports the .NET Framework infrastructure
1152 /// and is not intended to be used directly from your code.
1153 /// </summary>
1154 protected internal DataRow[] NewRowArray (int size)
1156 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
1159 /// <summary>
1160 /// Creates a new row from an existing row.
1161 /// </summary>
1162 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
1164 return new DataRow (builder);
1167 internal DataRow NewNotInitializedRow()
1169 return new DataRow(this,-1);
1172 #if NET_2_0
1173 [MonoTODO]
1174 XmlReadMode ReadXml (Stream stream)
1176 throw new NotImplementedException ();
1179 public void ReadXmlSchema (Stream stream)
1181 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1182 mapper.Read (new XmlTextReader(stream));
1185 public void ReadXmlSchema (TextReader reader)
1187 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1188 mapper.Read (new XmlTextReader(reader));
1191 public void ReadXmlSchema (string fileName)
1193 StreamReader reader = new StreamReader (fileName);
1194 ReadXmlSchema (reader);
1195 reader.Close ();
1198 public void ReadXmlSchema (XmlReader reader)
1200 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1201 mapper.Read (reader);
1203 #endif
1205 /// <summary>
1206 /// Rolls back all changes that have been made to the
1207 /// table since it was loaded, or the last time AcceptChanges
1208 /// was called.
1209 /// </summary>
1210 public void RejectChanges ()
1212 for (int i = _rows.Count - 1; i >= 0; i--) {
1213 DataRow row = _rows [i];
1214 if (row.RowState != DataRowState.Unchanged)
1215 _rows [i].RejectChanges ();
1219 /// <summary>
1220 /// Resets the DataTable to its original state.
1221 /// </summary>
1222 public virtual void Reset ()
1224 Clear();
1225 while (ParentRelations.Count > 0)
1227 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
1228 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
1231 while (ChildRelations.Count > 0)
1233 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
1234 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
1236 Constraints.Clear();
1237 Columns.Clear();
1240 /// <summary>
1241 /// Gets an array of all DataRow objects.
1242 /// </summary>
1243 public DataRow[] Select ()
1245 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
1248 /// <summary>
1249 /// Gets an array of all DataRow objects that match
1250 /// the filter criteria in order of primary key (or
1251 /// lacking one, order of addition.)
1252 /// </summary>
1253 public DataRow[] Select (string filterExpression)
1255 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
1258 /// <summary>
1259 /// Gets an array of all DataRow objects that
1260 /// match the filter criteria, in the the
1261 /// specified sort order.
1262 /// </summary>
1263 public DataRow[] Select (string filterExpression, string sort)
1265 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
1268 /// <summary>
1269 /// Gets an array of all DataRow objects that match
1270 /// the filter in the order of the sort, that match
1271 /// the specified state.
1272 /// </summary>
1273 [MonoTODO]
1274 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
1276 if (filterExpression == null)
1277 filterExpression = String.Empty;
1279 IExpression filter = null;
1280 if (filterExpression != String.Empty) {
1281 Parser parser = new Parser ();
1282 filter = parser.Compile (filterExpression);
1285 ArrayList rowList = new ArrayList();
1286 int recordStateFilter = GetRowStateFilter(recordStates);
1287 foreach (DataRow row in Rows) {
1288 if (((int)row.RowState & recordStateFilter) != 0) {
1289 if (filter != null && !(bool)filter.Eval (row))
1290 continue;
1291 rowList.Add (row);
1295 DataRow[] dataRows = (DataRow[])rowList.ToArray(typeof(DataRow));
1297 if (sort != null && !sort.Equals(String.Empty))
1299 SortableColumn[] sortableColumns = null;
1301 sortableColumns = ParseTheSortString (sort);
1302 if (sortableColumns == null)
1303 throw new Exception ("sort expression result is null");
1304 if (sortableColumns.Length == 0)
1305 throw new Exception("sort expression result is 0");
1307 RowSorter rowSorter = new RowSorter (this, dataRows, sortableColumns);
1308 dataRows = rowSorter.SortRows ();
1310 sortableColumns = null;
1311 rowSorter = null;
1315 return dataRows;
1318 internal void AddIndex (Index index)
1320 if (_indexes == null)
1321 _indexes = new ArrayList();
1323 _indexes.Add (index);
1326 internal void RemoveIndex (Index indx)
1328 _indexes.Remove (indx);
1331 internal Index GetIndexByColumns (DataColumn[] columns)
1333 return GetIndexByColumns(columns,false,false);
1336 // internal Index GetIndexByColumnsExtended(DataColumn[] columns)
1337 // {
1338 // DataColumn[] pkColumns = this.PrimaryKey;
1339 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1340 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1341 // Array.Copy(columns,0,cols,0,columns.Length);
1342 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1344 // return _getIndexByColumns(cols,false,false);
1345 // } else {
1346 // return null;
1347 // }
1348 // }
1350 internal Index GetIndexByColumns (DataColumn[] columns, bool unique)
1352 return GetIndexByColumns(columns,unique,true);
1355 // internal Index GetIndexByColumnsExtended(DataColumn[] columns, bool unique)
1356 // {
1357 // DataColumn[] pkColumns = this.PrimaryKey;
1358 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1359 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1360 // Array.Copy(columns,0,cols,0,columns.Length);
1361 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1363 // return _getIndexByColumns(cols,unique,true);
1364 // } else {
1365 // return null;
1366 // }
1367 // }
1369 internal Index GetIndexByColumns(DataColumn[] columns, bool unique, bool useUnique)
1371 if (_indexes != null) {
1372 foreach (Index indx in _indexes) {
1373 bool found = false;
1374 if ((!useUnique) || ((useUnique)&& (indx.IsUnique))) {
1375 found = DataColumn.AreColumnSetsTheSame (indx.Columns, columns);
1377 if (found)
1378 return indx;
1382 return null;
1385 internal void DeleteRowFromIndexes (DataRow row)
1387 if (_indexes != null) {
1388 foreach (Index indx in _indexes) {
1389 indx.Delete (row);
1394 private static int GetRowStateFilter(DataViewRowState recordStates)
1396 int flag = 0;
1398 if ((recordStates & DataViewRowState.Added) != 0)
1399 flag |= (int)DataRowState.Added;
1400 if ((recordStates & DataViewRowState.Deleted) != 0)
1401 flag |= (int)DataRowState.Deleted;
1402 if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
1403 flag |= (int)DataRowState.Modified;
1404 if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
1405 flag |= (int)DataRowState.Modified;
1406 if ((recordStates & DataViewRowState.Unchanged) != 0)
1407 flag |= (int)DataRowState.Unchanged;
1409 return flag;
1412 /// <summary>
1413 /// Gets the TableName and DisplayExpression, if
1414 /// there is one as a concatenated string.
1415 /// </summary>
1416 public override string ToString()
1418 //LAMESPEC: spec says concat the two. impl puts a
1419 //plus sign infront of DisplayExpression
1420 string retVal = TableName;
1421 if(DisplayExpression != null && DisplayExpression != "")
1422 retVal += " + " + DisplayExpression;
1423 return retVal;
1426 #if NET_2_0
1427 [MonoTODO]
1428 public void WriteXml (Stream stream)
1430 throw new NotImplementedException ();
1433 [MonoTODO]
1434 public void WriteXml (TextWriter writer)
1436 throw new NotImplementedException ();
1439 [MonoTODO]
1440 public void WriteXml (XmlWriter writer)
1442 throw new NotImplementedException ();
1445 [MonoTODO]
1446 public void WriteXml (string fileName)
1448 throw new NotImplementedException ();
1451 [MonoTODO]
1452 public void WriteXml (Stream stream, XmlWriteMode mode)
1454 throw new NotImplementedException ();
1457 [MonoTODO]
1458 public void WriteXml (TextWriter writer, XmlWriteMode mode)
1460 throw new NotImplementedException ();
1463 [MonoTODO]
1464 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
1466 throw new NotImplementedException ();
1469 [MonoTODO]
1470 public void WriteXml (string fileName, XmlWriteMode mode)
1472 throw new NotImplementedException ();
1475 [MonoTODO]
1476 public void WriteXmlSchema (Stream stream)
1478 throw new NotImplementedException ();
1481 [MonoTODO]
1482 public void WriteXmlSchema (TextWriter writer)
1484 throw new NotImplementedException ();
1487 [MonoTODO]
1488 public void WriteXmlSchema (XmlWriter writer)
1490 throw new NotImplementedException ();
1493 [MonoTODO]
1494 public void WriteXmlSchema (string fileName)
1496 throw new NotImplementedException ();
1498 #endif
1500 #region Events
1502 /// <summary>
1503 /// Raises the ColumnChanged event.
1504 /// </summary>
1505 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
1506 if (null != ColumnChanged) {
1507 ColumnChanged (this, e);
1511 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e) {
1512 OnColumnChanged(e);
1515 /// <summary>
1516 /// Raises the ColumnChanging event.
1517 /// </summary>
1518 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
1519 if (null != ColumnChanging) {
1520 ColumnChanging (this, e);
1524 /// <summary>
1525 /// Raises the PropertyChanging event.
1526 /// </summary>
1527 [MonoTODO]
1528 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
1529 // if (null != PropertyChanging)
1530 // {
1531 // PropertyChanging (this, e);
1532 // }
1535 /// <summary>
1536 /// Notifies the DataTable that a DataColumn is being removed.
1537 /// </summary>
1538 [MonoTODO]
1539 protected internal virtual void OnRemoveColumn (DataColumn column) {
1543 /// <summary>
1544 /// Raises the RowChanged event.
1545 /// </summary>
1546 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
1547 if (null != RowChanged) {
1548 RowChanged(this, e);
1553 /// <summary>
1554 /// Raises the RowChanging event.
1555 /// </summary>
1556 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
1557 if (null != RowChanging) {
1558 RowChanging(this, e);
1562 /// <summary>
1563 /// Raises the RowDeleted event.
1564 /// </summary>
1565 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
1566 if (null != RowDeleted) {
1567 RowDeleted(this, e);
1571 /// <summary>
1572 /// Raises the RowDeleting event.
1573 /// </summary>
1574 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
1575 if (null != RowDeleting) {
1576 RowDeleting(this, e);
1580 [MonoTODO]
1581 private DataColumn CopyColumn (DataColumn Column) {
1582 DataColumn Copy = new DataColumn ();
1584 // Copy all the properties of column
1585 Copy.AllowDBNull = Column.AllowDBNull;
1586 Copy.AutoIncrement = Column.AutoIncrement;
1587 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
1588 Copy.AutoIncrementStep = Column.AutoIncrementStep;
1589 Copy.Caption = Column.Caption;
1590 Copy.ColumnMapping = Column.ColumnMapping;
1591 Copy.ColumnName = Column.ColumnName;
1592 //Copy.Container
1593 Copy.DataType = Column.DataType;
1594 Copy.DefaultValue = Column.DefaultValue;
1595 Copy.Expression = Column.Expression;
1596 //Copy.ExtendedProperties
1597 Copy.MaxLength = Column.MaxLength;
1598 Copy.Namespace = Column.Namespace;
1599 Copy.Prefix = Column.Prefix;
1600 Copy.ReadOnly = Column.ReadOnly;
1601 //Copy.Site
1602 //we do not copy the unique value - it will be copyied when copying the constraints.
1603 //Copy.Unique = Column.Unique;
1605 return Copy;
1608 /// <summary>
1609 /// Occurs when after a value has been changed for
1610 /// the specified DataColumn in a DataRow.
1611 /// </summary>
1612 [DataCategory ("Data")]
1613 [DataSysDescription ("Occurs when a value has been changed for this column.")]
1614 public event DataColumnChangeEventHandler ColumnChanged;
1616 /// <summary>
1617 /// Occurs when a value is being changed for the specified
1618 /// DataColumn in a DataRow.
1619 /// </summary>
1620 [DataCategory ("Data")]
1621 [DataSysDescription ("Occurs when a value has been submitted for this column. The user can modify the proposed value and should throw an exception to cancel the edit.")]
1622 public event DataColumnChangeEventHandler ColumnChanging;
1624 /// <summary>
1625 /// Occurs after a DataRow has been changed successfully.
1626 /// </summary>
1627 [DataCategory ("Data")]
1628 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
1629 public event DataRowChangeEventHandler RowChanged;
1631 /// <summary>
1632 /// Occurs when a DataRow is changing.
1633 /// </summary>
1634 [DataCategory ("Data")]
1635 [DataSysDescription ("Occurs when the row is being changed so that the event handler can modify or cancel the change. The user can modify values in the row and should throw an exception to cancel the edit.")]
1636 public event DataRowChangeEventHandler RowChanging;
1638 /// <summary>
1639 /// Occurs after a row in the table has been deleted.
1640 /// </summary>
1641 [DataCategory ("Data")]
1642 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
1643 public event DataRowChangeEventHandler RowDeleted;
1645 /// <summary>
1646 /// Occurs before a row in the table is about to be deleted.
1647 /// </summary>
1648 [DataCategory ("Data")]
1649 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
1650 public event DataRowChangeEventHandler RowDeleting;
1652 #endregion // Events
1654 /// <summary>
1655 /// Removes all UniqueConstraints
1656 /// </summary>
1657 private void RemoveUniqueConstraints ()
1659 foreach (Constraint Cons in Constraints) {
1661 if (Cons is UniqueConstraint) {
1662 Constraints.Remove (Cons);
1663 break;
1667 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
1670 // to parse the sort string for DataTable:Select(expression,sort)
1671 // into sortable columns (think ORDER BY,
1672 // such as, "customer ASC, price DESC" )
1673 internal SortableColumn[] ParseTheSortString (string sort)
1675 SortableColumn[] sortColumns = null;
1676 ArrayList columns = null;
1678 if (sort != null && !sort.Equals ("")) {
1679 columns = new ArrayList ();
1680 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
1682 for (int c = 0; c < columnExpression.Length; c++) {
1683 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
1685 string columnName = columnSortInfo[0].Trim ();
1686 string sortOrder = "ASC";
1687 if (columnSortInfo.Length > 1)
1688 sortOrder = columnSortInfo[1].Trim ().ToUpper (Locale);
1690 ListSortDirection sortDirection = ListSortDirection.Ascending;
1691 switch (sortOrder) {
1692 case "ASC":
1693 sortDirection = ListSortDirection.Ascending;
1694 break;
1695 case "DESC":
1696 sortDirection = ListSortDirection.Descending;
1697 break;
1698 default:
1699 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
1701 Int32 ord = 0;
1702 try {
1703 ord = Int32.Parse (columnName);
1705 catch (FormatException) {
1706 ord = -1;
1708 DataColumn dc = null;
1709 if (ord == -1)
1710 dc = _columnCollection[columnName];
1711 else
1712 dc = _columnCollection[ord];
1713 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
1714 columns.Add (sortCol);
1716 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
1718 return sortColumns;
1721 internal class SortableColumn
1723 private DataColumn col;
1724 private ListSortDirection dir;
1726 internal SortableColumn (DataColumn column,
1727 ListSortDirection direction)
1729 col = column;
1730 dir = direction;
1733 public DataColumn Column {
1734 get {
1735 return col;
1739 public ListSortDirection SortDirection {
1740 get {
1741 return dir;
1746 private class RowSorter : IComparer
1748 private DataTable table;
1749 private SortableColumn[] sortColumns;
1750 private DataRow[] rowsToSort;
1752 internal RowSorter(DataTable table,
1753 DataRow[] unsortedRows,
1754 SortableColumn[] sortColumns)
1756 this.table = table;
1757 this.sortColumns = sortColumns;
1758 this.rowsToSort = unsortedRows;
1761 public SortableColumn[] SortColumns {
1762 get {
1763 return sortColumns;
1767 public DataRow[] SortRows ()
1769 Array.Sort (rowsToSort, this);
1770 return rowsToSort;
1773 int IComparer.Compare (object x, object y)
1775 if(x == null)
1776 throw new Exception ("Object to compare is null: x");
1777 if(y == null)
1778 throw new Exception ("Object to compare is null: y");
1779 if(!(x is DataRow))
1780 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
1781 if(!(y is DataRow))
1782 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
1784 DataRow rowx = (DataRow) x;
1785 DataRow rowy = (DataRow) y;
1787 for(int i = 0; i < sortColumns.Length; i++) {
1788 SortableColumn sortColumn = sortColumns[i];
1789 DataColumn dc = sortColumn.Column;
1791 int result = dc.CompareValues(
1792 rowx.IndexFromVersion(DataRowVersion.Default),
1793 rowy.IndexFromVersion(DataRowVersion.Default));
1795 if (result != 0) {
1796 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
1797 return result;
1799 else {
1800 return -result;
1804 return 0;
1808 /// <summary>
1809 /// Creates new index for a table
1810 /// </summary>
1811 internal Index CreateIndex(string name, DataColumn[] columns, bool unique)
1813 // first check whenever index exists on the columns
1814 Index idx = this.GetIndexByColumns(columns);
1815 if(idx != null) {
1816 // if index on this columns already exists - return it
1817 return idx;
1820 // create new index
1821 Index newIndex = new Index(name,this,columns,unique);
1823 //InitializeIndex (newIndex);
1825 // add new index to table indexes
1826 this.AddIndex(newIndex);
1827 return newIndex;
1830 // /// <summary>
1831 // /// Creates new extended index for a table
1832 // /// </summary>
1833 // internal Index CreateIndexExtended(string name, DataColumn[] columns, bool unique)
1834 // {
1835 // // first check whenever extended index exists on the columns
1836 // Index idx = this.GetIndexByColumnsExtended(columns);
1837 // if(idx != null) {
1838 // // if extended index on this columns already exists - return it
1839 // return idx;
1840 // }
1842 // DataColumn[] pkColumns = this.PrimaryKey;
1843 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1844 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1845 // Array.Copy(columns,0,cols,0,columns.Length);
1846 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1847 // return this.CreateIndex(name, cols, unique);
1848 // }
1849 // else {
1850 // throw new InvalidOperationException("Can not create extended index if the primary key is null or primary key does not contains any row");
1851 // }
1852 // }
1854 // /// <summary>
1855 // /// Drops extended index if it is not referenced anymore
1856 // /// by any of table constraints
1857 // /// </summary>
1858 // internal void DropIndexExtended(DataColumn[] columns)
1859 // {
1860 // // first check whenever extended index exists on the columns
1861 // Index index = this.GetIndexByColumnsExtended(columns);
1862 // if(index == null) {
1863 // // if no extended index on this columns exists - do nothing
1864 // return;
1865 // }
1866 // this.DropIndex(index);
1867 // }
1869 /// <summary>
1870 /// Drops index specified by columns if it is not referenced anymore
1871 /// by any of table constraints
1872 /// </summary>
1873 internal void DropIndex(DataColumn[] columns)
1875 // first check whenever index exists for the columns
1876 Index index = this.GetIndexByColumns(columns);
1877 if(index == null) {
1878 // if no index on this columns already exists - do nothing
1879 return;
1881 this.DropIndex(index);
1884 internal void DropIndex(Index index)
1886 // loop through table constraints and checks
1887 foreach(Constraint constraint in Constraints) {
1888 // if we found another reference to the index we do not remove the index.
1889 if (index == constraint.Index)
1890 return;
1893 this.RemoveIndex(index);
1896 internal void InitializeIndex (Index indx)
1898 DataRow[] rows = new DataRow[this.Rows.Count];
1899 this.Rows.CopyTo (rows, 0);
1900 indx.Root = null;
1901 // fill index with table rows
1902 foreach(DataRow row in this.Rows) {
1903 if(row.RowState != DataRowState.Deleted) {
1904 indx.Insert(new Node(row), DataRowVersion.Default);