New tests, updates
[mono.git] / mcs / class / System.Data / System.Data / DataViewManager.cs
1 //\r
2 // System.Data.DataViewManager\r
3 //\r
4 // Author:\r
5 //   Rodrigo Moya (rodrigo@ximian.com)\r
6 //   Tim Coleman (tim@timcoleman.com)\r
7 //   Atsushi Enomoto (atsushi@ximian.com)\r
8 //   Ivan N. Zlatev (contact@i-nz.net)\r
9 //\r
10 // (C) Ximian, Inc. 2002\r
11 // Copyright (C) Tim Coleman, 2002\r
12 // Copyright (C) 2005 Novell Inc,\r
13 //\r
14 \r
15 //\r
16 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
17 //\r
18 // Permission is hereby granted, free of charge, to any person obtaining\r
19 // a copy of this software and associated documentation files (the\r
20 // "Software"), to deal in the Software without restriction, including\r
21 // without limitation the rights to use, copy, modify, merge, publish,\r
22 // distribute, sublicense, and/or sell copies of the Software, and to\r
23 // permit persons to whom the Software is furnished to do so, subject to\r
24 // the following conditions:\r
25 // \r
26 // The above copyright notice and this permission notice shall be\r
27 // included in all copies or substantial portions of the Software.\r
28 // \r
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
36 //\r
37 \r
38 using System;\r
39 using System.Collections;\r
40 using System.ComponentModel;\r
41 using System.IO;\r
42 using System.Xml;\r
43 \r
44 namespace System.Data\r
45 {\r
46         /// <summary>\r
47         /// Contains a default DataViewSettingCollection for each DataTable in a DataSet.\r
48         /// </summary>\r
49         //[Designer]\r
50         [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.DataViewManagerDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]\r
51         public class DataViewManager : MarshalByValueComponent, IBindingList, ICollection, IList, ITypedList, IEnumerable\r
52         {\r
53                 #region Fields\r
54 \r
55                 DataSet dataSet;\r
56                 DataViewManagerListItemTypeDescriptor descriptor;\r
57                 DataViewSettingCollection settings;\r
58                 string xml;\r
59 \r
60                 #endregion // Fields\r
61 \r
62                 #region Constructors\r
63 \r
64                 public DataViewManager ()\r
65                         : this (null)\r
66                 {\r
67                 }\r
68 \r
69                 public DataViewManager (DataSet dataSet)\r
70                 {\r
71                         // Null argument is allowed here.\r
72                         SetDataSet (dataSet);\r
73                 }\r
74 \r
75                 #endregion // Constructors\r
76 \r
77                 #region Properties\r
78 \r
79 #if !NET_2_0\r
80                 [DataSysDescription ("Indicates the source of data for this DataViewManager.")]\r
81 #endif\r
82                 [DefaultValue (null)]\r
83                 public DataSet DataSet {\r
84                         get { return dataSet; }\r
85                         set {\r
86                                 if (value == null)\r
87                                         throw new DataException ("Cannot set null DataSet.");\r
88                                 SetDataSet (value);\r
89                         }\r
90                 }\r
91 \r
92                 public string DataViewSettingCollectionString {\r
93                         get { return xml; }\r
94                         set {\r
95                                 try {\r
96                                         ParseSettingString (value);\r
97                                         xml = BuildSettingString ();\r
98                                 } catch (XmlException ex) {\r
99                                         throw new DataException ("Cannot set DataViewSettingCollectionString.", ex);\r
100                                 }\r
101                         }\r
102                 }\r
103 \r
104 #if !NET_2_0\r
105                 [DataSysDescription ("Indicates the sorting/filtering/state settings for any table in the corresponding DataSet.")]\r
106 #endif\r
107                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]\r
108                 public DataViewSettingCollection DataViewSettings {\r
109                         get { return settings; }\r
110                 }\r
111 \r
112                 int ICollection.Count {\r
113                         get { return 1; }\r
114                 }\r
115 \r
116                 bool ICollection.IsSynchronized {\r
117                         get { return false; }\r
118                 }\r
119 \r
120                 object ICollection.SyncRoot {\r
121                         get { return this; }\r
122                 }\r
123 \r
124                 bool IList.IsFixedSize {\r
125                         get { return true; }\r
126                 }\r
127 \r
128                 bool IList.IsReadOnly {\r
129                         get { return true; }\r
130                 }\r
131 \r
132                 object IList.this [int index] {\r
133                         get { \r
134                                 if (descriptor == null)\r
135                                         descriptor = new DataViewManagerListItemTypeDescriptor (this);\r
136 \r
137                                 return descriptor;\r
138                         }\r
139 \r
140                         set { throw new ArgumentException ("Not modifiable"); }\r
141                 }\r
142 \r
143                 bool IBindingList.AllowEdit {\r
144                         get { return false; }\r
145                 }\r
146 \r
147                 bool IBindingList.AllowNew {\r
148                         get { return false; }\r
149                 }\r
150 \r
151                 bool IBindingList.AllowRemove {\r
152                         get { return false; }\r
153                 }\r
154 \r
155                 bool IBindingList.IsSorted {\r
156                         get { throw new NotSupportedException (); }\r
157                 }\r
158 \r
159                 ListSortDirection IBindingList.SortDirection {\r
160                         get { throw new NotSupportedException (); }\r
161                 }\r
162 \r
163                 PropertyDescriptor IBindingList.SortProperty {\r
164                         get { throw new NotSupportedException (); }\r
165                 }\r
166 \r
167                 bool IBindingList.SupportsChangeNotification {\r
168                         get { return true; }\r
169                 }\r
170 \r
171                 bool IBindingList.SupportsSearching {\r
172                         get { return false; }\r
173                 }\r
174 \r
175                 bool IBindingList.SupportsSorting {\r
176                         get { return false; }\r
177                 }\r
178 \r
179                 #endregion // Properties\r
180 \r
181                 #region Methods\r
182 \r
183                 private void SetDataSet (DataSet ds)\r
184                 {\r
185                         if (dataSet != null) {\r
186                                 dataSet.Tables.CollectionChanged -= new CollectionChangeEventHandler (TableCollectionChanged);\r
187                                 dataSet.Relations.CollectionChanged -= new CollectionChangeEventHandler (RelationCollectionChanged);\r
188                         }\r
189 \r
190                         dataSet = ds;\r
191                         settings = new DataViewSettingCollection (this);\r
192                         xml = BuildSettingString ();\r
193 \r
194                         if (dataSet != null) {\r
195                                 dataSet.Tables.CollectionChanged += new CollectionChangeEventHandler (TableCollectionChanged);\r
196                                 dataSet.Relations.CollectionChanged += new CollectionChangeEventHandler (RelationCollectionChanged);\r
197                         }\r
198                 }\r
199 \r
200                 private void ParseSettingString (string source)\r
201                 {\r
202                         XmlTextReader xtr = new XmlTextReader (source,\r
203                                 XmlNodeType.Element, null);\r
204 \r
205                         xtr.Read ();\r
206                         if (xtr.Name != "DataViewSettingCollectionString")\r
207                                 // easy way to throw the expected exception ;-)\r
208                         xtr.ReadStartElement ("DataViewSettingCollectionString");\r
209                         if (xtr.IsEmptyElement)\r
210                                 return; // MS does not change the value.\r
211 \r
212                         xtr.Read ();\r
213                         do {\r
214                                 xtr.MoveToContent ();\r
215                                 if (xtr.NodeType == XmlNodeType.EndElement)\r
216                                         break;\r
217                                 if (xtr.NodeType == XmlNodeType.Element)\r
218                                         ReadTableSetting (xtr);\r
219                                 else\r
220                                         xtr.Skip ();\r
221                         } while (!xtr.EOF);\r
222                         if (xtr.NodeType == XmlNodeType.EndElement)\r
223                                 xtr.ReadEndElement ();\r
224                 }\r
225 \r
226                 private void ReadTableSetting (XmlReader reader)\r
227                 {\r
228                         // Namespace is ignored BTW.\r
229                         DataTable dt = DataSet.Tables [XmlConvert.DecodeName (\r
230                                 reader.LocalName)];\r
231                         // The code below might result in NullReference error.\r
232                         DataViewSetting s = settings [dt];\r
233                         string sort = reader.GetAttribute ("Sort");\r
234                         if (sort != null)\r
235                                 s.Sort = sort.Trim ();\r
236                         string ads = reader.GetAttribute ("ApplyDefaultSort");\r
237                         if (ads != null && ads.Trim () == "true")\r
238                                 s.ApplyDefaultSort = true;\r
239                         string rowFilter = reader.GetAttribute ("RowFilter");\r
240                         if (rowFilter != null)\r
241                                 s.RowFilter = rowFilter.Trim ();\r
242                         string rsf = reader.GetAttribute ("RowStateFilter");\r
243                         if (rsf != null)\r
244                                 s.RowStateFilter = (DataViewRowState)\r
245                                         Enum.Parse (typeof (DataViewRowState), \r
246                                         rsf.Trim ());\r
247                         reader.Skip ();\r
248                 }\r
249 \r
250                 private string BuildSettingString ()\r
251                 {\r
252                         if (dataSet == null)\r
253                                 return String.Empty;\r
254 \r
255                         StringWriter sw = new StringWriter ();\r
256                         sw.Write ('<');\r
257                         sw.Write ("DataViewSettingCollectionString>");\r
258                         foreach (DataViewSetting s in DataViewSettings) {\r
259                                 sw.Write ('<');\r
260                                 sw.Write (XmlConvert.EncodeName (\r
261                                                 s.Table.TableName));\r
262                                 sw.Write (" Sort=\"");\r
263                                 sw.Write (Escape (s.Sort));\r
264                                 sw.Write ('"');\r
265                                 // LAMESPEC: MS.NET does not seem to handle this property as expected.\r
266                                 if (s.ApplyDefaultSort)\r
267                                         sw.Write (" ApplyDefaultSort=\"true\"");\r
268                                 sw.Write (" RowFilter=\"");\r
269                                 sw.Write (Escape (s.RowFilter));\r
270                                 sw.Write ("\" RowStateFilter=\"");\r
271                                 sw.Write (s.RowStateFilter.ToString ());\r
272                                 sw.Write ("\"/>");\r
273                         }\r
274                         sw.Write ("</DataViewSettingCollectionString>");\r
275                         return sw.ToString ();\r
276                 }\r
277 \r
278                 private string Escape (string s)\r
279                 {\r
280                         return s.Replace ("&", "&amp;")\r
281                                 .Replace ("\"", "&quot;")\r
282                                 .Replace ("\'", "&apos;")\r
283                                 .Replace ("<", "&lt;")\r
284                                 .Replace (">", "&gt;");\r
285                 }\r
286 \r
287                 public DataView CreateDataView (DataTable table)\r
288                 {\r
289                         if (settings [table] != null) {\r
290                                 DataViewSetting s = settings [table];\r
291                                 return new DataView (table, this, s.Sort, s.RowFilter, s.RowStateFilter);\r
292                         } else {\r
293                                 return new DataView (table);\r
294                         }\r
295                 }\r
296 \r
297                 void IBindingList.AddIndex (PropertyDescriptor property)\r
298                 {\r
299                 }\r
300         \r
301                 object IBindingList.AddNew ()\r
302                 {\r
303                         throw new NotSupportedException ();\r
304                 }\r
305         \r
306                 void IBindingList.ApplySort (PropertyDescriptor property, ListSortDirection direction)\r
307                 {\r
308                         throw new NotSupportedException ();\r
309                 }\r
310         \r
311                 int IBindingList.Find (PropertyDescriptor property, object key)\r
312                 {\r
313                         throw new NotSupportedException ();\r
314                 }\r
315         \r
316                 void IBindingList.RemoveIndex (PropertyDescriptor property)\r
317                 {\r
318                 }\r
319         \r
320                 void IBindingList.RemoveSort ()\r
321                 {\r
322                         throw new NotSupportedException ();\r
323                 }\r
324         \r
325                 void ICollection.CopyTo (Array array, int index)\r
326                 {\r
327                         array.SetValue (descriptor, index);\r
328                 }\r
329         \r
330                 IEnumerator IEnumerable.GetEnumerator ()\r
331                 {\r
332                         DataViewManagerListItemTypeDescriptor[] array = new DataViewManagerListItemTypeDescriptor[((ICollection)this).Count];\r
333                         ((ICollection)this).CopyTo (array, 0);\r
334                         return array.GetEnumerator ();\r
335                 }\r
336         \r
337                 int IList.Add (object value)\r
338                 {\r
339                         throw new ArgumentException ("Not modifiable");\r
340                 }\r
341         \r
342                 void IList.Clear ()\r
343                 {\r
344                         throw new ArgumentException ("Not modifiable");\r
345                 }\r
346         \r
347                 bool IList.Contains (object value)\r
348                 {\r
349                         return value == descriptor;\r
350                 }\r
351         \r
352                 int IList.IndexOf (object value)\r
353                 {\r
354                         if (value == descriptor)\r
355                                 return 0;\r
356                         return -1;\r
357                 }\r
358         \r
359                 void IList.Insert (int index, object value)\r
360                 {\r
361                         throw new ArgumentException ("Not modifiable");\r
362                 }\r
363         \r
364                 void IList.Remove (object value)\r
365                 {\r
366                         throw new ArgumentException ("Not modifiable");\r
367                 }\r
368         \r
369                 void IList.RemoveAt (int index)\r
370                 {\r
371                         throw new ArgumentException ("Not modifiable");\r
372                 }\r
373         \r
374                 [MonoLimitation("Supported only empty list of listAccessors")]\r
375                 PropertyDescriptorCollection ITypedList.GetItemProperties (PropertyDescriptor[] listAccessors)\r
376                 {\r
377                         if (dataSet == null)\r
378                                 throw new DataException ("dataset is null");\r
379 \r
380                         if (listAccessors == null || listAccessors.Length == 0) {\r
381                                 ICustomTypeDescriptor desc = new DataViewManagerListItemTypeDescriptor (this);\r
382                                 return desc.GetProperties ();\r
383                         }\r
384                                 \r
385                         throw new NotImplementedException ();\r
386                 }\r
387         \r
388                 string ITypedList.GetListName (PropertyDescriptor[] listAccessors)\r
389                 {\r
390                         if (dataSet != null) {\r
391                                 if (listAccessors == null || listAccessors.Length == 0)\r
392                                         return  dataSet.DataSetName;\r
393                         }\r
394                         \r
395                         return string.Empty;\r
396                 }\r
397         \r
398                 protected virtual void OnListChanged (ListChangedEventArgs e)\r
399                 {\r
400                         if (ListChanged != null)\r
401                                 ListChanged (this, e);\r
402                 }\r
403 \r
404                 protected virtual void RelationCollectionChanged (object sender, CollectionChangeEventArgs e)\r
405                 {\r
406                         this.OnListChanged (CollectionToListChangeEventArgs (e));\r
407                 }\r
408 \r
409                 protected virtual void TableCollectionChanged (object sender, CollectionChangeEventArgs e)\r
410                 {\r
411                         this.OnListChanged (CollectionToListChangeEventArgs (e));\r
412                 }\r
413 \r
414                 private ListChangedEventArgs CollectionToListChangeEventArgs (CollectionChangeEventArgs e)\r
415                 {\r
416                         ListChangedEventArgs args;\r
417 \r
418                         if (e.Action == CollectionChangeAction.Remove)\r
419                                 args = null;\r
420                         else if (e.Action == CollectionChangeAction.Refresh)\r
421                                 args = new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, null);\r
422                         else if (e.Action == CollectionChangeAction.Add)\r
423                                 args = new ListChangedEventArgs(ListChangedType.PropertyDescriptorAdded, new DataRelationPropertyDescriptor(((DataRelation) e.Element)));\r
424                         else\r
425                                 args = new ListChangedEventArgs(ListChangedType.PropertyDescriptorDeleted, new DataRelationPropertyDescriptor(((DataRelation) e.Element)));\r
426 \r
427                         return args;\r
428                 }\r
429 \r
430                 #endregion // Methods\r
431 \r
432                 #region Events\r
433 \r
434                 public event ListChangedEventHandler ListChanged;\r
435 \r
436                 #endregion // Events\r
437         }\r
438 }\r