File: XmlDataSource.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (730 lines) | stat: -rw-r--r-- 27,360 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
//------------------------------------------------------------------------------
// <copyright file="XmlDataSource.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace System.Web.UI.WebControls {

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Web;
    using System.Web.Caching;
    using System.Web.Hosting;
    using System.Web.UI;
    using System.Web.Util;
    using System.Xml;
    using System.Xml.Xsl;



    /// <devdoc>
    /// Represents an XML file as both an IDataSource and an IHierarchicalDataSource.
    /// The XML data is retrieved either from a file specified by the DataFile property
    /// or by inline XML content in the Data property.
    /// </devdoc>
    [
    DefaultEvent("Transforming"),
    DefaultProperty("DataFile"),
    Designer("System.Web.UI.Design.WebControls.XmlDataSourceDesigner, " + AssemblyRef.SystemDesign),
    ParseChildren(true),
    PersistChildren(false),
    ToolboxBitmap(typeof(XmlDataSource)),
    WebSysDescription(SR.XmlDataSource_Description),
    WebSysDisplayName(SR.XmlDataSource_DisplayName)
    ]
    public class XmlDataSource : HierarchicalDataSourceControl, IDataSource, IListSource {

        private static readonly object EventTransforming = new object();
        private const string DefaultViewName = "DefaultView";

        private DataSourceCache _cache;
        private bool _cacheLookupDone;
        private bool _disallowChanges;
        private XsltArgumentList _transformArgumentList;
        private ICollection _viewNames;
        private XmlDocument _xmlDocument;
        private string _writeableDataFile;

        private string _data;
        private string _dataFile;
        private string _transform;
        private string _transformFile;
        private string _xPath;


        /// <devdoc>
        /// Specifies the cache settings for this data source.
        /// </devdoc>
        private DataSourceCache Cache {
            get {
                if (_cache == null) {
                    _cache = new DataSourceCache();
                    _cache.Enabled = true;
                }
                return _cache;
            }
        }


        /// <devdoc>
        /// The duration, in seconds, of the expiration. The expiration policy is specified by the CacheExpirationPolicy property.
        /// </devdoc>
        [
        DefaultValue(DataSourceCache.Infinite),
        TypeConverterAttribute(typeof(DataSourceCacheDurationConverter)),
        WebCategory("Cache"),
        WebSysDescription(SR.DataSourceCache_Duration),
        ]
        public virtual int CacheDuration {
            get {
                return Cache.Duration;
            }
            set {
                Cache.Duration = value;
            }
        }

        /// <devdoc>
        /// The expiration policy of the cache. The duration for the expiration is specified by the CacheDuration property.
        /// </devdoc>
        [
        DefaultValue(DataSourceCacheExpiry.Absolute),
        WebCategory("Cache"),
        WebSysDescription(SR.DataSourceCache_ExpirationPolicy),
        ]
        public virtual DataSourceCacheExpiry CacheExpirationPolicy {
            get {
                return Cache.ExpirationPolicy;
            }
            set {
                Cache.ExpirationPolicy = value;
            }
        }

        /// <devdoc>
        /// Indicates an arbitrary cache key to make this cache entry depend on. This allows
        /// the user to further customize when this cache entry will expire.
        /// </devdoc>
        [
        DefaultValue(""),
        WebCategory("Cache"),
        WebSysDescription(SR.DataSourceCache_KeyDependency),
        ]
        public virtual string CacheKeyDependency {
            get {
                return Cache.KeyDependency;
            }
            set {
                Cache.KeyDependency = value;
            }
        }

        [
        DefaultValue(""),
        WebCategory("Cache"),
        WebSysDescription(SR.XmlDataSource_CacheKeyContext),
        ]
        public virtual string CacheKeyContext {
            get {
                return (string)ViewState["CacheKeyContext "] ?? String.Empty;
            }
            set {
                ViewState["CacheKeyContext "] = value;
            }
        }

        /// <devdoc>
        /// Inline XML content.
        /// </devdoc>
        [
        DefaultValue(""),
        Editor("System.ComponentModel.Design.MultilineStringEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
        PersistenceMode(PersistenceMode.InnerProperty),
        TypeConverter("System.ComponentModel.MultilineStringConverter," + AssemblyRef.System),
        WebCategory("Data"),
        WebSysDescription(SR.XmlDataSource_Data),
        ]
        public virtual string Data {
            get {
                if (_data == null) {
                    return String.Empty;
                }
                return _data;
            }
            set {
                if (value != null) {
                    value = value.Trim();
                }
                if (Data != value) {
                    if (_disallowChanges) {
                        throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_CannotChangeWhileLoading, "Data", ID));
                    }
                    _data = value;
                    _xmlDocument = null;
                    OnDataSourceChanged(EventArgs.Empty);
                }
            }
        }

        /// <devdoc>
        /// Path to an XML file.
        /// </devdoc>
        [
        DefaultValue(""),
        Editor("System.Web.UI.Design.XmlDataFileEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
        WebCategory("Data"),
        WebSysDescription(SR.XmlDataSource_DataFile),
        ]
        public virtual string DataFile {
            get {
                if (_dataFile == null) {
                    return String.Empty;
                }
                return _dataFile;
            }
            set {
                if (DataFile != value) {
                    if (_disallowChanges) {
                        throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_CannotChangeWhileLoading, "DataFile", ID));
                    }
                    _dataFile = value;
                    _xmlDocument = null;
                    _writeableDataFile = null;
                    OnDataSourceChanged(EventArgs.Empty);
                }
            }
        }

        /// <devdoc>
        /// Whether caching is enabled for this data source.
        /// </devdoc>
        [
        DefaultValue(true),
        WebCategory("Cache"),
        WebSysDescription(SR.DataSourceCache_Enabled),
        ]
        public virtual bool EnableCaching {
            get {
                return Cache.Enabled;
            }
            set {
                Cache.Enabled = value;
            }
        }

        /// <devdoc>
        /// Indicates whether the XML data can be modified.
        /// This is also used by XmlDataSourceView to determine whether CanDelete/Insert/Update are true.
        /// </devdoc>
        internal bool IsModifiable {
            get {
                return (String.IsNullOrEmpty(TransformFile) &&
                        String.IsNullOrEmpty(Transform) &&
                        !String.IsNullOrEmpty(WriteableDataFile));
            }
        }

        /// <devdoc>
        /// Inline XSL transform.
        /// </devdoc>
        [
        DefaultValue(""),
        Editor("System.ComponentModel.Design.MultilineStringEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
        PersistenceMode(PersistenceMode.InnerProperty),
        TypeConverter("System.ComponentModel.MultilineStringConverter," + AssemblyRef.System),
        WebCategory("Data"),
        WebSysDescription(SR.XmlDataSource_Transform),
        ]
        public virtual string Transform {
            get {
                if (_transform == null) {
                    return String.Empty;
                }
                return _transform;
            }
            set {
                if (value != null) {
                    value = value.Trim();
                }
                if (Transform != value) {
                    if (_disallowChanges) {
                        throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_CannotChangeWhileLoading, "Transform", ID));
                    }
                    _transform = value;
                    _xmlDocument = null;
                    OnDataSourceChanged(EventArgs.Empty);
                }
            }
        }

        /// <devdoc>
        /// Arguments for the XSL transform.
        /// This should be populated in the Transforming event.
        /// </devdoc>
        [
        Browsable(false),
        ]
        public virtual XsltArgumentList TransformArgumentList {
            get {
                return _transformArgumentList;
            }
            set {
                _transformArgumentList = value;
            }
        }

        /// <devdoc>
        /// Path to an XSL transform file.
        /// </devdoc>
        [
        DefaultValue(""),
        Editor("System.Web.UI.Design.XslTransformFileEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
        WebCategory("Data"),
        WebSysDescription(SR.XmlDataSource_TransformFile),
        ]
        public virtual string TransformFile {
            get {
                if (_transformFile == null) {
                    return String.Empty;
                }
                return _transformFile;
            }
            set {
                if (TransformFile != value) {
                    if (_disallowChanges) {
                        throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_CannotChangeWhileLoading, "TransformFile", ID));
                    }
                    _transformFile = value;
                    _xmlDocument = null;
                    OnDataSourceChanged(EventArgs.Empty);
                }
            }
        }

        /// <devdoc>
        /// Gets a physical path of the data file that can be written to.
        /// The value is null if the path is not a writable path.
        /// </devdoc>
        private string WriteableDataFile {
            get {
                if (_writeableDataFile == null) {
                    _writeableDataFile = GetWriteableDataFile();
                }
                return _writeableDataFile;
            }
        }

        /// <devdoc>
        /// Specifies an initial XPath that is applied to the XML data.
        /// </devdoc>
        [
        DefaultValue(""),
        WebCategory("Data"),
        WebSysDescription(SR.XmlDataSource_XPath),
        ]
        public virtual string XPath {
            get {
                if (_xPath == null) {
                    return String.Empty;
                }
                return _xPath;
            }
            set {
                if (XPath != value) {
                    if (_disallowChanges) {
                        throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_CannotChangeWhileLoading, "XPath", ID));
                    }
                    _xPath = value;
                    OnDataSourceChanged(EventArgs.Empty);
                }
            }
        }


        /// <devdoc>
        /// Raised before the XSL transform is applied.
        /// </devdoc>
        [
        WebCategory("Data"),
        WebSysDescription(SR.XmlDataSource_Transforming),
        ]
        public event EventHandler Transforming {
            add {
                Events.AddHandler(EventTransforming, value);
            }
            remove {
                Events.RemoveHandler(EventTransforming, value);
            }
        }


        /// <devdoc>
        /// Creates a unique cache key for this data source's data.
        /// </devdoc>
        // Made internal for unit testing
        [SuppressMessage("Microsoft.Usage", "CA2303:FlagTypeGetHashCode", Justification = "This is specifically on XmlDataSource type which is not a com interop type.")]
        internal string CreateCacheKey()
        {
            StringBuilder sb = new StringBuilder(CacheInternal.PrefixDataSourceControl, 1024);
            sb.Append(GetType().GetHashCode().ToString(CultureInfo.InvariantCulture));

            sb.Append(CacheDuration.ToString(CultureInfo.InvariantCulture));
            sb.Append(':');
            sb.Append(((int)CacheExpirationPolicy).ToString(CultureInfo.InvariantCulture));

            bool includeUniqueID = false;

            if (!String.IsNullOrEmpty(CacheKeyContext)) {
                sb.Append(':');
                sb.Append(CacheKeyContext);
            }

            if (DataFile.Length > 0) {
                sb.Append(':');
                sb.Append(DataFile);
            }
            else {
                if (Data.Length > 0) {
                    includeUniqueID = true;
                }
            }

            if (TransformFile.Length > 0) {
                sb.Append(':');
                sb.Append(TransformFile);
            }
            else {
                if (Transform.Length > 0) {
                    includeUniqueID = true;
                }
            }

            if (includeUniqueID) {
                // If we don't have any paths, use the Page
                if (Page != null) {
                    sb.Append(':');
                    sb.Append(Page.GetType().AssemblyQualifiedName);
                }
                sb.Append(':');
                string uniqueID = UniqueID;
                if (String.IsNullOrEmpty(uniqueID)) {
                    throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_NeedUniqueIDForCache));
                }
                sb.Append(uniqueID);
            }

            return sb.ToString();
        }

        /// <devdoc>
        /// Returns a HierarchicalDataSourceView based on an XPath specified by viewPath.
        /// </devdoc>
        protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath) {
            return new XmlHierarchicalDataSourceView(this, viewPath);
        }

        /// <devdoc>
        /// Gets an XmlReader representing XML or XSL content, and optionally a cache
        /// dependency for that content.
        /// Supported paths are: Relative paths, physical paths, UNC paths, and HTTP URLs
        /// If a path is not provided, the content parameter is assumed to contain the
        /// actual content.
        /// If there is no data, null is returned.
        /// This method is fully compatible with Virtual Path Providers.
        /// </devdoc>
        private XmlReader GetReader(string path, string content, out CacheDependency cacheDependency) {
            // If a filename is specified, load from file. Otherwise load from inner content.
            if (path.Length != 0) {
                // First try to detect if it is an HTTP URL
                Uri uri;
                bool success = Uri.TryCreate(path, UriKind.Absolute, out uri);
                if (success) {
                    if (uri.Scheme == Uri.UriSchemeHttp) {
                        // Check for Web permissions for the URL we want
                        if (!HttpRuntime.HasWebPermission(uri)) {
                            throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_NoWebPermission, uri.PathAndQuery, ID));
                        }
                        // Dependencies are not supported with HTTP URLs
                        cacheDependency = null;
                        // If it is an HTTP URL and we have permissions, get a reader
                        return XmlUtils.CreateXmlReader(path);
                    }
                }

                // Now see what kind of file-based path it is
                VirtualPath virtualPath;
                string physicalPath;
                ResolvePhysicalOrVirtualPath(path, out virtualPath, out physicalPath);

                if (virtualPath != null && DesignMode) {
                    // This exception should never be thrown - the designer always maps paths
                    // before using the runtime control.
                    throw new NotSupportedException(SR.GetString(SR.XmlDataSource_DesignTimeRelativePathsNotSupported, ID));
                }

                Stream dataStream = OpenFileAndGetDependency(virtualPath, physicalPath, out cacheDependency);
                return XmlUtils.CreateXmlReader(dataStream);
            }
            else {
                // Dependencies are not supported with inline content
                cacheDependency = null;
                content = content.Trim();
                if (content.Length == 0) {
                    return null;
                }
                else {
                    return XmlUtils.CreateXmlReader(new StringReader(content));
                }
            }
        }

        /// <devdoc>
        /// Gets a path to a writeable file where we can save data to.
        /// The return value is null if a writeable path cannot be found.
        /// </devdoc>
        private string GetWriteableDataFile() {
            if (DataFile.Length != 0) {
                // First try to detect if it is an HTTP URL
                Uri uri;
                bool success = Uri.TryCreate(DataFile, UriKind.Absolute, out uri);
                if (success) {
                    if (uri.Scheme == Uri.UriSchemeHttp) {
                        // Cannot write to HTTP URLs
                        return null;
                    }
                }

                if (HostingEnvironment.UsingMapPathBasedVirtualPathProvider) {
                    // Now see what kind of file-based path it is
                    VirtualPath virtualPath;
                    string physicalPath;
                    ResolvePhysicalOrVirtualPath(DataFile, out virtualPath, out physicalPath);
                    if (physicalPath == null) {
                        physicalPath = virtualPath.MapPathInternal(this.TemplateControlVirtualDirectory, true /*allowCrossAppMapping*/);
                    }
                    return physicalPath;
                }
                else {
                    // File is coming from a custom virtual path provider, and there is no support for writing
                    return null;
                }
            }
            else {
                // Data is specified using Data property, so it is not writeable
                return null;
            }
        }

        /// <devdoc>
        /// Returns the XmlDocument representing the XML data.
        /// If necessary, the XML data will be reloaded along with the transform, if available.
        /// </devdoc>
        public XmlDocument GetXmlDocument() {

            string cacheKey = null;

            if (!_cacheLookupDone && Cache.Enabled) {
                // If caching is enabled, attempt to load from cache.
                cacheKey = CreateCacheKey();
                _xmlDocument = Cache.LoadDataFromCache(cacheKey) as XmlDocument;

                _cacheLookupDone = true;
            }

            if (_xmlDocument == null) {

                // Load up the data
                _xmlDocument = new XmlDocument();
                CacheDependency transformCacheDependency;
                CacheDependency dataCacheDependency;

                PopulateXmlDocument(_xmlDocument, out dataCacheDependency, out transformCacheDependency);

                if (cacheKey != null) {
                    Debug.Assert(Cache.Enabled);

                    // If caching is enabled, save the XmlDocument to cache.
                    CacheDependency fileDependency;
                    if (dataCacheDependency != null) {
                        if (transformCacheDependency != null) {
                            // We have both a data file as well as a transform file dependency
                            AggregateCacheDependency aggregateDependency = new AggregateCacheDependency();
                            aggregateDependency.Add(dataCacheDependency, transformCacheDependency);
                            fileDependency = aggregateDependency;
                        }
                        else {
                            // We only have a data file dependency
                            fileDependency = dataCacheDependency;
                        }
                    }
                    else {
                        // We have at most only a transform file dependency (or no dependency at all)
                        fileDependency = transformCacheDependency;
                    }

                    Cache.SaveDataToCache(cacheKey, _xmlDocument, fileDependency);
                }
            }

            return _xmlDocument;
        }

        /// <devdoc>
        /// Populates an XmlDocument with the appropriate XML data, including applying transforms.
        /// </devdoc>
        [SuppressMessage("Microsoft.Security", "MSEC1204:UseSecureXmlResolver", Justification = "Legacy code that trusts our developer input.  Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")]
        private void PopulateXmlDocument(XmlDocument document, out CacheDependency dataCacheDependency, out CacheDependency transformCacheDependency) {
            XmlReader transformReader = null;
            XmlReader dataReader = null;
            XmlReader tempDataReader = null;

            try {
                // Don't allow changes to the XmlDataSource while we are loading the document
                _disallowChanges = true;

                // Check if transform is specified.
                // If there is a transform, load the data, then the transform, and get an XmlReader from the transformation.
                transformReader = GetReader(TransformFile, Transform, out transformCacheDependency);
                if (transformReader != null) {
                    tempDataReader = GetReader(DataFile, Data, out dataCacheDependency);

                    // Now load the transform and transform the document data
#pragma warning disable 0618    // To avoid deprecation warning
                    XslTransform transform = XmlUtils.CreateXslTransform(transformReader, null);
#pragma warning restore 0618
                    if (transform != null) {
                        OnTransforming(EventArgs.Empty);

                        XmlDocument tempDocument = new XmlDocument();
                        tempDocument.Load(tempDataReader);
                        // The XmlResolver cast on the third parameter is required to eliminate an ambiguity
                        // from the compiler.
                        dataReader = transform.Transform(tempDocument, _transformArgumentList, (XmlResolver)null);
                        document.Load(dataReader);
                    }
                    else {
                        // XslCompiledTransform for some reason wants to completely re-create an internal XmlReader
                        // from scratch.  In doing so, it does not respect all the settings of XmlTextReader.  Be 100%
                        // sure that this XmlReader we are using here uses settings of XmlReader and not those
                        // introduced by XmlTextReader.
                        XslCompiledTransform compiledTransform = XmlUtils.CreateXslCompiledTransform(transformReader);

                        OnTransforming(EventArgs.Empty);

                        using (MemoryStream ms = new MemoryStream()) {
                            XmlWriter writer = XmlWriter.Create(ms);
                            compiledTransform.Transform(tempDataReader, _transformArgumentList, writer, null);
                            document.Load(XmlUtils.CreateXmlReader(ms));
                        }
                    }
                }
                else {
                    dataReader = GetReader(DataFile, Data, out dataCacheDependency);
                    document.Load(dataReader);
                }
            }
            finally {
                _disallowChanges = false;

                if (dataReader != null) {
                    dataReader.Close();
                }
                if (tempDataReader != null) {
                    tempDataReader.Close();
                }
                if (transformReader != null) {
                    transformReader.Close();
                }
            }
        }

        /// <devdoc>
        /// Called right before the XSLT transform is applied.
        /// This allows a developer to supply an XsltArgumentList in the TransformArgumentList property.
        /// </devdoc>
        protected virtual void OnTransforming(EventArgs e) {
            EventHandler handler = (EventHandler)Events[EventTransforming];
            if (handler != null) {
                handler(this, e);
            }
        }

        /// <devdoc>
        /// Saves the XML data to disk.
        /// </devdoc>
        public void Save() {
            if (!IsModifiable) {
                throw new InvalidOperationException(SR.GetString(SR.XmlDataSource_SaveNotAllowed, ID));
            }

            string writeableDataFile = WriteableDataFile;
            Debug.Assert(!String.IsNullOrEmpty(writeableDataFile), "Did not expect WriteableDataFile to be empty in Save()");

            // Check for write permissions
            HttpRuntime.CheckFilePermission(writeableDataFile, true);

            // Save the document
            GetXmlDocument().Save(writeableDataFile);
        }


        #region Implementation of IDataSource
        event EventHandler IDataSource.DataSourceChanged {
            add {
                ((IHierarchicalDataSource)this).DataSourceChanged += value;
            }
            remove {
                ((IHierarchicalDataSource)this).DataSourceChanged -= value;
            }
        }


        /// <internalonly/>
        DataSourceView IDataSource.GetView(string viewName) {
            if (viewName.Length == 0) {
                viewName = DefaultViewName;
            }
            return new XmlDataSourceView(this, viewName);
        }


        /// <internalonly/>
        ICollection IDataSource.GetViewNames() {
            if (_viewNames == null) {
                _viewNames = new string[1] { DefaultViewName };
            }
            return _viewNames;
        }
        #endregion

        #region Implementation of IListSource
        /// <internalonly/>
        bool IListSource.ContainsListCollection {
            get {
                if (DesignMode) {
                    return false;
                }
                return ListSourceHelper.ContainsListCollection(this);
            }
        }


        /// <internalonly/>
        IList IListSource.GetList() {
            if (DesignMode) {
                return null;
            }
            return ListSourceHelper.GetList(this);
        }
        #endregion
    }
}