| File: MS\Internal\Documents\Application\Document.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationUI\PresentationUI.csproj (PresentationUI) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Description: // Defines the common contract for all underlying documents in XpsViewer. using System; using System.IO; namespace MS.Internal.Documents.Application { /// <summary> /// Defines the common contract for all underlying documents in XpsViewer. /// </summary> /// <remarks> /// Responsibility: /// This class imposes a contract which provides for: /// /// - chaining dependencies /// - expose stream which providers may manipulate at each level /// - disposing of resources in order of dependency /// /// Design Comments: /// Packages are dependent on EncryptedPackages who are dependent on FileStreams /// however all these classes are very different in function. However they /// share a need for understanding dependencies and all use streams (some /// consuming, some publshing and others both). /// /// Providing a chain ensures dependent operations are executed in order. /// /// The design of exsiting components also requires us to define three common /// types of streams (Source - the original data, Workspace - a type of change /// log, Destination - the place to save changes). /// /// Examples: /// - FileController need to substitue streams as as we can not edit in /// place and may not be able to re-open files on some OSes (hence /// IsRebindNeed). /// /// - Protected documents need to decrypt the underlying FileStream before /// passing them up to the PackageDocument. (hence Source). /// /// - As Package does not allow us to discard changes we need a seperate stream /// for working on packages (hence Workspace). /// /// - When Protected documents have a key change (PublishLicense) they need /// to read from the source, and write to the destination at the same time /// (hence Destination & IsFileCopySafe). /// </remarks> internal abstract class Document : IChainOfDependenciesNode<Document>, IDisposable { #region Constructors //-------------------------------------------------------------------------- // Constructors //-------------------------------------------------------------------------- /// <summary> /// Constructor allows the chaining of dependencies. /// </summary> /// <param name="dependency">A document we are dependent on.</param> internal Document(Document dependency) { _dependency = dependency; } #endregion Constructors #region Finalizers //-------------------------------------------------------------------------- // Finalizers //-------------------------------------------------------------------------- ~Document() { #if DEBUG // our code should be explicitly disposing this is to catch bad code AssertIfNotDisposed(); #endif this.Dispose(true); } #endregion #region IChainOfDependenciesNode<Document> Members //-------------------------------------------------------------------------- // IChainOfDependenciesNode<Document> Members //-------------------------------------------------------------------------- /// <summary> /// <see cref="MS.Internal.Documents.Application.IChainOfDependenciesNode<T>"/> /// </summary> Document IChainOfDependenciesNode<Document>.Dependency { get { #if DEBUG ThrowIfDisposed(); #endif return _dependency; } } #endregion IChainOfDependenciesNode<Document> Members #region IDisposable Members //-------------------------------------------------------------------------- // IDisposable Members //-------------------------------------------------------------------------- /// <summary> /// <see cref="System.IDisposable"/> /// </summary> public void Dispose() { Trace.SafeWrite( Trace.File, "{0}.Dispose called.", this); #if DEBUG Trace.SafeWriteIf( !_isDisposed, Trace.File, "{0} document is being disposed.", this); #endif this.Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// <see cref="System.IDisposable"/> /// </summary> protected virtual void Dispose(bool disposing) { if (disposing) { #if DEBUG ThrowIfDisposed(); _isDisposed = true; #endif _dependency?.Dispose(); _dependency = null; } } #endregion IDisposable Members #region Internal Properties //-------------------------------------------------------------------------- // Internal Properties //-------------------------------------------------------------------------- /// <summary> /// A stream with the data for the source document. /// </summary> internal abstract Stream Source { get; } /// <summary> /// A stream to store working information for edits. /// </summary> internal abstract Stream Workspace { get; } /// <summary> /// A stream to publish the changes to when saving. /// </summary> internal abstract Stream Destination { get; } /// <summary> /// An underlying document we are dependent on. /// </summary> internal Document Dependency { get { #if DEBUG ThrowIfDisposed(); #endif return _dependency; } } /// <summary> /// When true copying the data at the file level is valid. /// </summary> internal bool IsFileCopySafe { get { #if DEBUG ThrowIfDisposed(); #endif return ChainOfDependencies<Document>.GetLast(this)._isCopySafe; } set { #if DEBUG ThrowIfDisposed(); #endif ChainOfDependencies<Document>.GetLast(this)._isCopySafe = value; } } /// <summary> /// When true, the destination file is exactly identical to the source file /// byte-for-byte. /// </summary> internal bool IsDestinationIdenticalToSource { get { #if DEBUG ThrowIfDisposed(); #endif return ChainOfDependencies<Document>.GetLast(this)._isDestinationIdenticalToSource; } set { #if DEBUG ThrowIfDisposed(); #endif ChainOfDependencies<Document>.GetLast(this)._isDestinationIdenticalToSource = value; } } /// <summary> /// When true the source data has changed and we should rebind. /// </summary> internal bool IsRebindNeeded { get { #if DEBUG ThrowIfDisposed(); #endif return ChainOfDependencies<Document>.GetLast(this)._isRebindNeeded; } set { #if DEBUG ThrowIfDisposed(); #endif ChainOfDependencies<Document>.GetLast(this)._isRebindNeeded = value; } } /// <summary> /// When true the source location has changed and we should reload. /// </summary> internal bool IsReloadNeeded { get { #if DEBUG ThrowIfDisposed(); #endif return ChainOfDependencies<Document>.GetLast(this)._isReloadNeeded; } set { #if DEBUG ThrowIfDisposed(); #endif ChainOfDependencies<Document>.GetLast(this)._isReloadNeeded = value; } } /// <summary> /// The location of this document. /// </summary> internal Uri Uri { get { #if DEBUG ThrowIfDisposed(); #endif return ChainOfDependencies<Document>.GetLast(this)._uri; } set { #if DEBUG ThrowIfDisposed(); #endif ChainOfDependencies<Document>.GetLast(this)._uri = value; } } #endregion Internal Properties #region Private Methods //-------------------------------------------------------------------------- // Private Methods //-------------------------------------------------------------------------- #if DEBUG /// <summary> /// Will assert if any documents are left undisposed in the application /// domain. /// </summary> private void AssertIfNotDisposed() { Invariant.Assert( _isDisposed, string.Format( System.Globalization.CultureInfo.CurrentCulture, "{0} was not disposed.", this)); } /// <summary> /// Will throw if the object has been disposed. /// </summary> /// <exception cref="System.ObjectDisposedException"/> private void ThrowIfDisposed() { ObjectDisposedException.ThrowIf(_isDisposed, this); } #endif #endregion Private Methods #region Private Fields //-------------------------------------------------------------------------- // Private Fields //-------------------------------------------------------------------------- private Uri _uri; private bool _isCopySafe = true; private bool _isDestinationIdenticalToSource; private bool _isRebindNeeded; private bool _isReloadNeeded; private Document _dependency; #if DEBUG private bool _isDisposed; #endif #endregion Private Fields } }