File: Base\MS\Internal\IO\Packaging\CompoundFile\VersionedStream.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//
// <copyright file="VersionedStream.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//   This class provides file versioning support for streams provided by 
//   IDataTransform implementations.
//
// History:
//  02/21/2006: BruceMac: Initial implementation.
//
//-----------------------------------------------------------------------------
 
using System;
using System.IO;                                // for Stream
using System.Windows;                           // ExceptionStringTable
using System.Globalization;                     // for CultureInfo
using MS.Internal.WindowsBase;
 
namespace MS.Internal.IO.Packaging.CompoundFile
{
    /// <summary>
    /// Maintains a FormatVersion for this stream and any number of sibling streams that semantically
    /// share the same version information (which is only persisted in one of the streams).
    /// </summary>
    internal class VersionedStream : Stream
    {
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
        #region Stream Methods
        /// <summary>
        /// Return the bytes requested from the container
        /// </summary>
        public override int Read(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
 
            // ReadAttempt accepts an optional boolean.  If this is true, that means
            // we are expecting a legal FormatVersion to exist and that it is readable by our
            // code version.  We do not want to force this check if we are empty.
            _versionOwner.ReadAttempt(_stream.Length > 0);
            return _stream.Read(buffer, offset, count);
        }
 
        /// <summary>
        /// Write
        /// </summary>
        public override void Write(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
            _versionOwner.WriteAttempt();
            _stream.Write(buffer, offset, count);
        }
 
        /// <summary>
        /// ReadByte
        /// </summary>
        public override int ReadByte()
        {
            CheckDisposed();
 
            // ReadAttempt accepts an optional boolean.  If this is true, that means
            // we are expecting a legal FormatVersion to exist and that it is readable by our
            // code version.  We do not want to force this check if we are empty.
            _versionOwner.ReadAttempt(_stream.Length > 0);
            return _stream.ReadByte();
        }
 
        /// <summary>
        /// WriteByte
        /// </summary>
        public override void WriteByte(byte b)
        {
            CheckDisposed();
            _versionOwner.WriteAttempt();
            _stream.WriteByte(b);
        }
 
        /// <summary>
        /// Seek
        /// </summary>
        /// <param name="offset">offset</param>
        /// <param name="origin">origin</param>
        /// <returns>zero</returns>
        public override long Seek(long offset, SeekOrigin origin)
        {
            CheckDisposed();
            return _stream.Seek(offset, origin);
        }
 
        /// <summary>
        /// SetLength
        /// </summary>
        public override void SetLength(long newLength)
        {
            CheckDisposed();
 
            if (newLength < 0)
                throw new ArgumentOutOfRangeException("newLength");
 
            _versionOwner.WriteAttempt();
            _stream.SetLength(newLength);
        }
 
        /// <summary>
        /// Flush
        /// </summary>
        public override void Flush()
        {
            CheckDisposed();
            _stream.Flush();
        }
        #endregion Stream Methods
 
        //------------------------------------------------------
        //
        //  Public Properties
        //
        //------------------------------------------------------
        #region Stream Properties
        /// <summary>
        /// Current logical position within the stream
        /// </summary>
        public override long Position
        {
            get
            {
                CheckDisposed();
                return _stream.Position;
            }
            set
            {
                Seek(value, SeekOrigin.Begin);
            }
        }
 
        /// <summary>
        /// Length
        /// </summary>
        public override long Length
        {
            get
            {
                CheckDisposed();
                return _stream.Length;
            }
        }
 
        /// <summary>
        /// Is stream readable?
        /// </summary>
        /// <remarks>returns false when called on disposed stream</remarks>
        public override bool CanRead
        {
            get
            {
                return (_stream != null) && _stream.CanRead && _versionOwner.IsReadable;
            }
        }
 
        /// <summary>
        /// Is stream seekable - should be handled by our owner
        /// </summary>
        /// <remarks>returns false when called on disposed stream</remarks>
        public override bool CanSeek
        {
            get
            {
                return (_stream != null) && _stream.CanSeek && _versionOwner.IsReadable;
            }
        }
 
        /// <summary>
        /// Is stream writeable?
        /// </summary>
        /// <remarks>returns false when called on disposed stream</remarks>
        public override bool CanWrite
        {
            get
            {
                return (_stream != null) && _stream.CanWrite && _versionOwner.IsUpdatable;
            }
        }
        #endregion
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
        /// <summary>
        /// Constructor to use for any stream that shares versioning information with another stream
        /// but is not the one that houses the FormatVersion data itself.
        /// </summary>
        /// <param name="baseStream"></param>
        /// <param name="versionOwner"></param>
        internal VersionedStream(Stream baseStream, VersionedStreamOwner versionOwner)
        {
            if (baseStream == null)
                throw new ArgumentNullException("baseStream");
 
            if (versionOwner == null)
                throw new ArgumentNullException("versionOwner");
 
            _stream = baseStream;
            _versionOwner = versionOwner;
        }
 
        //------------------------------------------------------
        //
        //  Protected Methods
        //
        //------------------------------------------------------
        /// <summary>
        /// Constructor for use by our subclass VersionedStreamOwner.
        /// </summary>
        /// <param name="baseStream"></param>
        protected VersionedStream(Stream baseStream)
        {
            if (baseStream == null)
                throw new ArgumentNullException("baseStream");
 
            _stream = baseStream;
 
            // we are actually a VersionedStreamOwner
            _versionOwner = (VersionedStreamOwner)this;
        }
 
        /// <summary>
        /// Sometimes our subclass needs to read/write directly to the stream
        /// </summary>
        /// <remarks>Don't use CheckDisposed() here as we need to return null if we are disposed</remarks>
        protected Stream BaseStream
        {
            get
            {
                return _stream;
            }
        }
 
        /// <summary>
        /// Dispose(bool)
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing && (_stream != null))
                {
                    _stream.Close();
                }
            }
            finally
            {
                _stream = null;
                base.Dispose(disposing);
            }
        }
 
        /// <summary>
        /// Call this before accepting any public API call (except some Stream calls that
        /// are allowed to respond even when Closed
        /// </summary>
        protected void CheckDisposed()
        {
            if (_stream == null)
                throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed));
        }
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
        private VersionedStreamOwner    _versionOwner;
        private Stream                  _stream;            // null indicates Disposed state
    }
}