|
// 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
}
}
|