File: MS\Internal\Documents\Application\Document.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationUI\PresentationUI_wgljbex5_wpftmp.csproj (PresentationUI)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// 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
            if (_dependency != null)
            {
                _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
}
}