e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
731 lines
27 KiB
C#
731 lines
27 KiB
C#
//------------------------------------------------------------------------------
|
|
// <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
|
|
}
|
|
}
|
|
|