File: Base\System\IO\Packaging\CompoundFile\DataSpaceManager.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//
// <copyright file="DataSpaceManager.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//   The object for manipulating data spaces within the WPP Package.
//
// History:
//  06/04/2002: RogerCh: Initial creation of class & method stubs.
//  06/17/2002: RogerCh: Initial implementation.
//  05/20/2003: RogerCh: Ported to WCP tree.
//  05/28/2003: RogerCh: Added long name support
//
//-----------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.Diagnostics;           // For Debug.Assert
using System.Globalization;
using System.IO;
using System.Reflection;            // For finding transform objects & their constructor
using System.Security.Permissions;  // For StrongNameIdentityPermission
using System.Collections.Generic;
 
using System.Windows;               // ExceptionStringTable
using MS.Internal.IO.Packaging;
using MS.Internal.IO.Packaging.CompoundFile;
using CU = MS.Internal.IO.Packaging.CompoundFile.ContainerUtilities;
using MS.Internal.WindowsBase;
 
namespace System.IO.Packaging
{
/// <summary>
/// This class is used to manipulate the data spaces within a specific instance
/// of the Avalon container.  This is how data transform modules are plugged
/// into the container to enable features like data compression and data
/// encryption.
/// </summary>
internal class DataSpaceManager
{
    /***********************************************************************/
    // Constants
 
    // The header bytes that this version understands and supports
    const int KnownBytesInMapTableHeader = 8; // Two Int32s == 8 bytes
    const int KnownBytesInDataSpaceDefinitionHeader = 8;
    const int KnownBytesInTransformDefinitionHeader = 8;
    const int AllowedExtraDataMaximumSize = 8192; // 8K
 
    // Names for streams and storages within the container
    const string DataSpaceStorageName = "\x0006DataSpaces";
    const string DataSpaceVersionName = "Version";
    const string DataSpaceMapTableName= "DataSpaceMap";
    const string DataSpaceDefinitionsStorageName = "DataSpaceInfo";
    const string TransformDefinitions = "TransformInfo";
    const string TransformPrimaryInfo = "\x0006Primary";
 
    // The string used in FormatVersion
    private static readonly string DataSpaceVersionIdentifier = "Microsoft.Container.DataSpaces";
 
    // Version Writer - 1.0, Reader - 1.0, Updater - 1.0
    private static readonly VersionPair DataSpaceCurrentWriterVersion  = new VersionPair(1 /*major*/, 0 /*minor*/);
    private static readonly VersionPair DataSpaceCurrentReaderVersion  = new VersionPair(1 /*major*/, 0 /*minor*/);
    private static readonly VersionPair DataSpaceCurrentUpdaterVersion = new VersionPair(1 /*major*/, 0 /*minor*/);
 
    // The version information we read from the file
    private FormatVersion _fileFormatVersion;
 
    private bool _dirtyFlag;
 
    /***********************************************************************/
    // Data space manager instance values
 
    /// <summary>
    /// There is only one data space manager per container instance.  This
    /// points back to "our" reference.
    /// </summary>
    StorageRoot _associatedStorage;
 
    /// <summary>
    /// Maps container references to data spaces  
    /// 
    /// Keys into this list are CompoundFileReference instances, each 
    /// representing a subset of the container that is encoded with a 
    /// particular data space.  
    /// 
    /// Values are strings, which are data space labels and can be used
    /// as keys into _dataSpaceDefinitions for more details
    /// </summary>
    SortedList _dataSpaceMap;
 
    /// <summary>
    /// Extra data in the data space mapping table header is preserved
    /// in this byte array.
    /// </summary>
    byte[] _mapTableHeaderPreservation;
 
    /// <summary>
    /// Maps a data space name to a string array of transform names.
    /// 
    /// Keys into this hash table are strings, each a unique label for
    /// a data space.
    /// 
    /// Values from this hash table are ArrayLists, each an array of
    /// strings.  Each string is a data space label.  This transform
    /// stack is stored in bottom-up order.  The first transform listed
    /// is the first to get the raw bytes from disk.
    /// </summary>
    Hashtable _dataSpaceDefinitions;
 
 
    /// <summary>
    /// Maps a transform name to an instance of transform handle class
    /// 
    /// Keys into this hash table are strings, each a unique label for
    /// a transform object instance.
    /// 
    /// Values from this hash table are references to the TransformInstance
    /// class defined below, each of which contains information for a 
    /// particular transform instance.
    /// </summary>
    Hashtable _transformDefinitions;
 
    /// <summary>
    /// When shutting down, we need to flush each open transformed stream in
    /// order to ensure that all encoding data has been propagated through
    /// the transform stack before we shut things down.  Otherwise we may leave
    /// data in a state where it could not be written out because parts of the
    /// transform stack has already been disposed.
    /// </summary>
    ArrayList _transformedStreams;
 
    /// <summary>
    /// Table of "well-known" -- that is, "built-in" -- transforms. The keys are
    /// the TransformClassName identifier strings for the well-known transforms,
    /// such as encryption and compression. The values are the assembly-qualified
    /// .NET class names of the classes that implement the transforms.
    /// </summary>
    static readonly Hashtable _transformLookupTable;
 
    /***********************************************************************/
    // Private class for tracking individual transform instances
 
    private class TransformInstance
    {
        // When we only know the CLR type name
        internal TransformInstance( int classType, string name ) : this(classType, name, null, null, null, null ) {;}
 
        // When we also have an actual object in memory and its associated
        //  environment object
        internal TransformInstance( 
            int classType,
            string name, 
            IDataTransform instance, 
            TransformEnvironment environment ) : this(classType, name, instance, environment, null, null ) {;}
 
        // When we know everything to put into a TransformInstance.
        internal TransformInstance( 
            int classType,
            string name, 
            IDataTransform instance, 
            TransformEnvironment environment, 
            Stream primaryStream, 
            StorageInfo storage )
        {
            typeName = name;
            transformReference = instance;
            transformEnvironment = environment;
            transformPrimaryStream = primaryStream;
            transformStorage = storage;
            _classType = classType;
        }
 
        internal byte[] ExtraData
        {
            get
            {
                return _extraData;
            }
 
            set
            {
                _extraData = value;
            }
        }
 
        internal int ClassType
        {
            get
            {
                return _classType;
            }
        }
 
        /// <summary>
        /// This is the CLR name used to define this transform.  Keep in
        /// mind that this is not necessarily what we retrieve when we
        /// call Type.FullName or Type.AssemblyQualifiedName.  This is
        /// the name that needs to be persisted in the file because it is
        /// what the caller told us to use to find the type.
        /// </summary>
        internal string          typeName;
 
        /// <summary>
        /// If we have actually created the transform object, we keep
        /// a reference to it here.  This may be null if we haven't
        /// had a need to create the transform object.
        /// </summary>
        internal IDataTransform  transformReference;
 
        /// <summary>
        /// The instance of TransformEnvironment that we created and
        /// handed off to the transform object to tell us about things.
        /// This can also be null, but only when transformReference is
        /// null.  It is not valid for only one of these two to be null,
        /// either they're both null or they're both non-null.
        /// </summary>
        internal TransformEnvironment transformEnvironment;
 
        /// <summary>
        /// The stream that is the primary data storage for this instance
        /// of the transform object.
        /// </summary>
        internal Stream transformPrimaryStream;
 
        /// <summary>
        /// The storage that is available if the transform object requires
        /// more than the primary stream
        /// </summary>
        internal StorageInfo transformStorage;
 
        private int _classType;
        private byte[] _extraData;
    }
 
    private class DirtyStateTrackingStream:  Stream
    {
 
        ////////////////////////////////////
        // Stream section  
        /////////////////////////////////
        public override bool CanRead
        {
            get
            {
                return (_baseStream != null && _baseStream.CanRead);
            }
        }
 
        public override bool CanSeek
        {
            get
            {
                return (_baseStream != null && _baseStream.CanSeek);
            }
        }
 
        public override bool CanWrite
        {
            get
            {
                return (_baseStream != null && _baseStream.CanWrite);
            }
        }
 
        public override long Length
        {
            get
            {
                CheckDisposed();
 
                return  _baseStream.Length;
            }
        }
 
        public override long Position
        {
            get
            {
                CheckDisposed();
                
                return _baseStream.Position;
            }
            
            set
            {
                CheckDisposed();
 
                _baseStream.Position = value;
            }
        }
 
        public override void SetLength(long newLength)
        {
            CheckDisposed(); 
            
            if (newLength != _baseStream.Length)
            {
                _dirty = true;
            }
 
            _baseStream.SetLength(newLength);
        }
 
        public override long Seek(long offset, SeekOrigin origin)
        {
            CheckDisposed();
 
            return _baseStream.Seek(offset, origin);
        }        
 
        public override int Read(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
 
            return _baseStream.Read(buffer, offset, count);
        }
 
        public override void Write(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
 
            _baseStream.Write(buffer, offset, count);
            _dirty = true;
        }
 
        public override void Flush()
        {
            CheckDisposed();
 
            _baseStream.Flush();
        }
 
        /////////////////////////////
        // Internal Constructor
        /////////////////////////////        
        internal  DirtyStateTrackingStream(Stream baseStream) 
        {
            Debug.Assert(baseStream != null);
 
            _baseStream = baseStream;
        }
 
        internal bool DirtyFlag
        {
            get
            {
                return (_baseStream != null && _dirty);
            }
        }
 
        internal Stream BaseStream
        {
            get
            {
                return _baseStream;
            }
        }
 
        //------------------------------------------------------
        //
        //  Protected Methods
        //
        //------------------------------------------------------
        /// <summary>
        /// Dispose(bool)
        /// </summary>
        /// <param name="disposing"></param>
        /// <remarks>We implement this because we want a consistent experience (essentially Flush our data) if the user chooses to 
        /// call Dispose() instead of Close().</remarks>
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    if (_baseStream != null)
                        _baseStream.Close();
                }
            }
            finally
            {
                _baseStream = null;
                base.Dispose(disposing);
            }
        }
 
        /////////////////////////////
        // Private Methods
        /////////////////////////////        
 
        private void CheckDisposed()
        {
            if (_baseStream == null)
            {
                throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed));            
            }
        }
 
        private bool _dirty;
        private Stream _baseStream;
    }
 
    private struct DataSpaceDefinition
    {
        ArrayList _transformStack;
        Byte[]    _extraData;
 
        internal DataSpaceDefinition(ArrayList transformStack, Byte[] extraData)
        {
            _transformStack = transformStack;
            _extraData = extraData;
        }
 
        internal ArrayList TransformStack
        {
            get
            {
                return _transformStack;
            }
        }
 
        internal Byte[] ExtraData
        {
            get
            {
                return _extraData;
            }
        }
    }
 
    /***********************************************************************/
    // Constructors
 
    /// <summary>
    /// Static constructor that initializes the transformLookupTable (which see).
    /// </summary>
    static DataSpaceManager()
    {
        // Transform Identifier: we preserve casing, but do case-insensitive comparison
        _transformLookupTable = new Hashtable(CU.StringCaseInsensitiveComparer);
 
        _transformLookupTable[RightsManagementEncryptionTransform.ClassTransformIdentifier]
            = "System.IO.Packaging.RightsManagementEncryptionTransform";
        _transformLookupTable[CompressionTransform.ClassTransformIdentifier]
            = "System.IO.Packaging.CompressionTransform";
    }
 
    /// <summary>
    /// Internally visible constructor
    /// </summary>
    /// <param name="containerInstance">The container instance we're associated with</param>
    internal DataSpaceManager( StorageRoot containerInstance )
    {
        _associatedStorage = containerInstance;
 
        // Storage under which all data space information is stored.
        StorageInfo dataSpaceStorage = 
            new StorageInfo( _associatedStorage, DataSpaceStorageName );
 
        // Initialize internal data structures.
        //  
        _dataSpaceMap = new SortedList();
        _mapTableHeaderPreservation = new byte[0];
        _dataSpaceDefinitions = new Hashtable(CU.StringCaseInsensitiveComparer);
        _transformDefinitions = new Hashtable(CU.StringCaseInsensitiveComparer);
        _transformedStreams = new ArrayList();
 
        // Check to see if we have any data space information to read
        if (dataSpaceStorage.Exists)
        {
            // Read any existing data space mapping information from the container
            //  
            ReadDataSpaceMap();
            ReadDataSpaceDefinitions();
            ReadTransformDefinitions();
        }
        return; 
    }
 
    /// <summary>
    /// Returns the number of data spaces defined in this manager object
    /// </summary>
    internal int Count
    {
        get
        {
            CheckDisposedStatus();
            return _dataSpaceMap.Count;
        }
    }
 
    private bool DirtyFlag
    {
        get
        {
            if (_dirtyFlag)     // It is already dirty don't need to check further
                return true;
            
            foreach( string transformDef in _transformDefinitions.Keys )
            {
                TransformInstance transformInstance = GetTransformInstanceOf( transformDef );
                if (((DirtyStateTrackingStream) transformInstance.transformPrimaryStream).DirtyFlag)
                {
                    return true;
                }
            }
 
            return false;
        }
    }
 
    /// <summary>
    /// Clean up the data space information and flush all data to the container.
    /// </summary>
    public void Dispose()
    {
        CheckDisposedStatus();
        
        // Flush any outstanding data in the transformed streams
        foreach( StreamWithDictionary dataStream in _transformedStreams )
        {
            if (!dataStream.Disposed)
                dataStream.Flush();
        }
        _transformedStreams.Clear();
 
        // Now that all data have been flushed, shut down the transform
        //  objects.
        foreach( object o in _transformDefinitions.Values )
        {
            IDataTransform idt = ((TransformInstance)o).transformReference;
 
            if( null != idt &&
                null != (idt as IDisposable))
            {
                ((IDisposable)idt).Dispose();
            }
        }
 
        // Now write the tables out.
        //  
 
        if (FileAccess.Read != _associatedStorage.OpenAccess && DirtyFlag)
        {
            WriteDataSpaceMap();
            WriteDataSpaceDefinitions();
            WriteTransformDefinitions();
        }
 
        _dataSpaceMap = null;
        _dataSpaceDefinitions = null;
        _transformDefinitions = null;
            
        return;
    }
 
    /// <summary>
    /// Removes the container reference from the dataspace map. This is called when the container/substorage is getting deleted from the root storage. [DeleteSubStorage in StorageInfo]
    /// </summary>
    internal void RemoveContainerFromDataSpaceMap(CompoundFileReference target)
    {
       CheckDisposedStatus();
       if (_dataSpaceMap.Contains(target))
       {
            _dataSpaceMap.Remove(target);
            _dirtyFlag = true;
       }
    }
 
    // Check to see if the dispose method has been called.  If so, throw an
    //  ObjectDisposedException.
    internal void CheckDisposedStatus()
    {
        // First check root
        _associatedStorage.CheckRootDisposedStatus();
 
        // Check if we've been disposed
        if( null == _dataSpaceMap )
        {
            Debug.Assert( null == _dataSpaceDefinitions, 
                "Having a null data space map and a non-null data space definitions map is an inconsistent state" );
            Debug.Assert( null == _transformDefinitions,
                "Having a null data space map and a non-null transform definition map is an inconsistent state" );
 
            throw new ObjectDisposedException(null, SR.Get(SRID.DataSpaceManagerDisposed));
        }
    }
 
    /// <summary>
    /// Define a data space with the given stack of transform objects and
    /// labeled with the given name.  The transform stack is interpreted in
    /// bottom-up order.  (Clear-text transform is last.)
    /// </summary>
    /// <param name="transformStack">Transform stack</param>
    /// <param name="newDataSpaceLabel">New data space label</param>
    internal void DefineDataSpace( string[] transformStack, string newDataSpaceLabel )
    {
        CheckDisposedStatus();
        // Data space must have at least one transform
        if( null == transformStack ||
            0 == transformStack.Length )
            throw new ArgumentException(
                SR.Get(SRID.TransformStackValid));
 
        // Given label must be a non-empty string
        CU.CheckStringAgainstNullAndEmpty(newDataSpaceLabel, "newDataSpaceLabel");
 
        // Given label must not be a reserved string
        CU.CheckStringAgainstReservedName(newDataSpaceLabel, "newDataSpaceLabel");
        
        // Given label must not already be in use
        if( DataSpaceIsDefined( newDataSpaceLabel ) )
            throw new ArgumentException(
                SR.Get(SRID.DataSpaceLabelInUse));
 
        // Given transform array must include labels that have already been defined
        foreach( string transformLabel in transformStack )
        {
            CU.CheckStringAgainstNullAndEmpty( transformLabel, "Transform label" );
            
            if( !TransformLabelIsDefined( transformLabel ) )
                throw new ArgumentException(
                    SR.Get(SRID.TransformLabelUndefined));
        }
 
        // Passes all inspection, data space definition successful.
        SetDataSpaceDefinition( newDataSpaceLabel, new DataSpaceDefinition(new ArrayList(transformStack), null));
        _dirtyFlag = true;
 
        return;
    }
 
    /// <summary>
    /// Internal shortcut to check if data space is defined.  When we start
    /// doing on-demand reads of data space definitions, the "demand" could
    /// be triggered by this.
    /// </summary>
    /// <param name="dataSpaceLabel">Label to check</param>
    /// <returns>True if label is in use</returns>
    internal bool DataSpaceIsDefined( string dataSpaceLabel )
    {
        CU.CheckStringAgainstNullAndEmpty(dataSpaceLabel, "dataSpaceLabel");
 
        return _dataSpaceDefinitions.Contains( dataSpaceLabel );
    }
 
    /// <summary>
    ///     Central place to set a data space definition, centralizing the
    /// call to the name manager.
    /// </summary>
    private void SetDataSpaceDefinition( string dataSpaceLabel, DataSpaceDefinition definition )
    {
        _dataSpaceDefinitions[ dataSpaceLabel ] = definition;
    }
 
    /// <summary>
    ///     Central place to get a data space definition, centralizing the
    /// call to the name manager.
    /// </summary>
    private DataSpaceDefinition GetDataSpaceDefinition( string dataSpaceLabel )
    {
        return ((DataSpaceDefinition) _dataSpaceDefinitions[dataSpaceLabel]);
    }
    /// <summary>
    /// Internal method to retrieve the data space label corresponding to
    /// a container reference.  Returns null if no data space is associated.
    /// </summary>
    /// <param name="target">CompoundFileReference whose data space label is to be retrieved</param>
    /// <returns>Data space label</returns>
    internal string DataSpaceOf( CompoundFileReference target )
    {
        // Can't simply return _dataSpaceMap[target] because I need to cast it
        //  into a string, and if it's null the cast `blows up.
        if( _dataSpaceMap.Contains(target) )
        {
            return (string)_dataSpaceMap[target];
        }
        else
        {
            return null;
        }
    }
 
    /// <summary>
    /// This method returns all the transforms that are applied to a particular stream as an 
    /// List of IDataTransform objects.
    /// </summary>
    /// <param name="streamInfo">StreamInfo for the stream whose transforms are requested</param>
    /// <returns>A List of IDataTransform objects that are applied to the stream represented by streamInfo</returns>
    internal List<IDataTransform> GetTransformsForStreamInfo(StreamInfo streamInfo)
    {
        string dataSpaces = this.DataSpaceOf(streamInfo.StreamReference);
 
        if (dataSpaces == null) // No datas pace is associated with the stream
        {
            return new List<IDataTransform>(0);     // return an empty list
        }
 
        ArrayList transformsList = this.GetDataSpaceDefinition(dataSpaces).TransformStack;
        List<IDataTransform> dataTransforms = new List<IDataTransform>(transformsList.Count);
 
        for (int i = 0; i < transformsList.Count; i++)
        {
            dataTransforms.Add(this.GetTransformFromName(transformsList[i] as string));
        }
 
        return dataTransforms;
    }
 
    /// <summary>
    /// Define a data space with the given stack of transform objects and
    /// labeled with an auto-generated name
    /// </summary>
    /// <param name="transformStack">Transform stack</param>
    /// <returns>The auto-generated label for this data space</returns>
    internal string DefineDataSpace( string[] transformStack )
    {
        CheckDisposedStatus();
        Int64   timeSeed      = DateTime.Now.ToFileTime();
        string  generatedName = timeSeed.ToString(CultureInfo.InvariantCulture);
 
        // If there is a name collision, just keep incrementing the number
        while( DataSpaceIsDefined( generatedName ) )
        {
            timeSeed++;
            generatedName = timeSeed.ToString(CultureInfo.InvariantCulture);
        }
 
        // Submit the definition
        DefineDataSpace( transformStack, generatedName );
 
        return generatedName;
    }
 
    /// <summary>
    /// Tranform object is created. We are no longer using reflection to do this. We are supporting limited data transforms.
    /// </summary>
    private IDataTransform InstantiateDataTransformObject(int transformClassType,  string transformClassName, TransformEnvironment transformEnvironment )
    {
        object transformInstance = null;
 
        if (transformClassType != (int) TransformIdentifierTypes_PredefinedTransformName)
            throw new NotSupportedException(SR.Get(SRID.TransformTypeUnsupported));
 
        // Transform Identifier: we preserve casing, but do case-insensitive comparison
        if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(transformClassName,
                RightsManagementEncryptionTransform.ClassTransformIdentifier))
        {
            transformInstance = new RightsManagementEncryptionTransform( transformEnvironment);
        }
        else if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(transformClassName,
                CompressionTransform.ClassTransformIdentifier))
        {
             transformInstance = new CompressionTransform( transformEnvironment );
        }
        else
        {
            //this transform class is not supported. Need to change this to appropriate error.
            throw new ArgumentException(
                    SR.Get(SRID.TransformLabelUndefined));
        }
 
        if (null != transformInstance)
        {
            if( !( transformInstance is IDataTransform ) )
                throw new ArgumentException(
                    SR.Get(SRID.TransformObjectImplementIDataTransform));
            return (IDataTransform)transformInstance;
        }
 
        return null;        
    }
 
    /// <summary>
    /// Private method to check if a transform label is defined.  When we
    /// start reading transform defintions on-demand, we would probably do it 
    /// here as necessary.
    /// </summary>
    /// <param name="transformLabel">Transform label to check</param>
    /// <returns>True if label is defined in hash table</returns>
    internal bool TransformLabelIsDefined( string transformLabel )
    {
        // 
 
        
        return _transformDefinitions.Contains( transformLabel );
    }
 
    /// <summary>
    ///     Central place to set a transform definition, centralizing the
    /// call to the name manager.
    /// </summary>
    private void SetTransformDefinition( string transformLabel, TransformInstance definition )
    {
        _transformDefinitions[ transformLabel ] = definition;
    }
 
    /// <summary>
    /// Private method to get the TransformInstance class representing
    /// a transform instance.
    /// </summary>
    private TransformInstance GetTransformInstanceOf( string transformLabel )
    {
        Debug.Assert( TransformLabelIsDefined( transformLabel ),
            "Data space manager caller failed to verify transform exists before retrieving instance" );
 
        // 
 
 
        
        return _transformDefinitions[ transformLabel ] as TransformInstance;
    }
 
    /// <summary>
    /// Internal method to get a MemoryStream whose contents will be
    /// stored in the "\x0006Primary" data stream after our type identification 
    /// information
    /// </summary>
    /// <param name="transformLabel">Transform Label</param>
    /// <returns>Memory stream object for transform instance primary stream</returns>
    internal Stream GetPrimaryInstanceStreamOf( string transformLabel )
    {
        TransformInstance targetInstance = GetTransformInstanceOf( transformLabel );
 
        if( null == targetInstance.transformPrimaryStream )
        {
            //build memory stream on the byte[0] , and allow writes only if 
            // FileAccess is Write or ReadWrite
            if (_associatedStorage.OpenAccess == FileAccess.Read)
            {
                targetInstance.transformPrimaryStream =
                    new DirtyStateTrackingStream (new MemoryStream
                            (new byte[0], 
                            false /* Not writable */));
            }
            else
            {
                targetInstance.transformPrimaryStream = new DirtyStateTrackingStream (new MemoryStream());
            }
        }
 
        return targetInstance.transformPrimaryStream;
    }
 
    /// <summary>
    /// Internal method to get the StorageInfo where the transform instance
    /// data is stored.  This StorageInfo may not yet exist!
    /// </summary>
    /// <param name="transformLabel">Transform Label</param>
    /// <returns>StorageInfo pointing to transform instance data storage</returns>
    internal StorageInfo GetInstanceDataStorageOf( string transformLabel )
    {
        TransformInstance targetInstance = GetTransformInstanceOf( transformLabel );
 
        if( null == targetInstance.transformStorage )
        {
            //string name = DataSpaceStorageName + '\\' + TransformDefinitions + '\\' + transformLabel;
            
            //targetInstance.transformStorage  = new StorageInfo(_associatedStorage,name);
 
            StorageInfo dataSpaceStorage = new StorageInfo( _associatedStorage, DataSpaceStorageName );
            if (!dataSpaceStorage.Exists )
            {
                dataSpaceStorage.Create();
            }
            StorageInfo transformDefinition = new StorageInfo( dataSpaceStorage, TransformDefinitions );
            if (!transformDefinition.Exists)
            {
                transformDefinition.Create();
            }
            targetInstance.transformStorage  = new StorageInfo(transformDefinition,transformLabel);
        }
        return targetInstance.transformStorage;
    }
 
    /// <summary>
    /// Internal method to get the transform object corresponding to the specified
    /// transform instance label. The transform object is created if it does not exist.
    /// </summary>
    /// <param name="transformLabel">
    /// String that identifies the transform instance.
    /// </param>
    /// <returns>
    /// An IDataTransform interface pointer to the transform object identified by
    /// <paramref name="transformLabel"/>, or null if there is no such transform.
    /// </returns>
    internal IDataTransform GetTransformFromName(string transformLabel)
    {
        TransformInstance transformInstance = _transformDefinitions[transformLabel] as TransformInstance;
 
        if (transformInstance == null)
        {
            //
            // There is no transform instance with the specified name.
            //
            return null;
        }
 
        IDataTransform transformObject = transformInstance.transformReference;
        if (transformObject == null)
        {
            //
            // There is a transform instance with the specified name, but its transform
            // object has not yet been created. Create it now. This code is modeled on the
            // code in DefineTransform.
            //
            TransformEnvironment transformEnvironment = new TransformEnvironment(this, transformLabel);
 
            // Create the transform object.
            transformObject = InstantiateDataTransformObject(
                                    transformInstance.ClassType,
                                    transformInstance.typeName,
                                    transformEnvironment);
            transformInstance.transformReference = transformObject;
        }
 
        return transformObject;
    }
 
    /// <summary>
    /// Define a data transform object with the given object identification and
    /// labeled with the given name.
    /// </summary>
    /// <param name="transformClassName">Transform identification string</param>
    /// <param name="newTransformLabel">Label to use for new transform</param>
    internal void DefineTransform(string transformClassName, string newTransformLabel )
    {
        CheckDisposedStatus();
 
        // Check to see if transform name is obviously invalid
        CU.CheckStringAgainstNullAndEmpty( transformClassName, "Transform identifier name" );
 
        // Check to see if transform name is valid
        CU.CheckStringAgainstNullAndEmpty( newTransformLabel, "Transform label" );
 
        // Given transform name must not be a reserved string
        CU.CheckStringAgainstReservedName( newTransformLabel, "Transform label" );
        
        // Can't re-use an existing transform name
        if( TransformLabelIsDefined( newTransformLabel ) )
            throw new ArgumentException(
                SR.Get(SRID.TransformLabelInUse));
 
        // Create class the transform object will use to communicate to us
        TransformEnvironment transformEnvironment = new TransformEnvironment( this, newTransformLabel );
 
        // Create a TransformInstance object to represent this transform instance.
        TransformInstance newTransform = new TransformInstance(
            TransformIdentifierTypes_PredefinedTransformName,
            transformClassName,
            null,
            transformEnvironment );
 
        SetTransformDefinition( newTransformLabel, newTransform );
 
        // Create the transform object
        IDataTransform transformObject = InstantiateDataTransformObject(
                                                    TransformIdentifierTypes_PredefinedTransformName,
                                                    transformClassName,
                                                    transformEnvironment );
        newTransform.transformReference = transformObject;
 
        // If transform is not ready out-of-the-box, do an initialization run.
        //  Note: Transform is not required to be "ready" after this.  This is
        //  done for those transforms that need initialization work up-front.
        if( ! transformObject.IsReady )
        {
            CallTransformInitializers( 
                new TransformInitializationEventArgs(
                    transformObject,
                    null,
                    null,
                    newTransformLabel) 
                );
        }
 
        _dirtyFlag = true;
 
        return;
    }
 
    /// <summary>
    /// Define a data transform object with the given object identification and
    /// labeled with an auto-generated name.
    /// </summary>
    /// <param name="transformClassName">Transform identification string</param>
    /// <returns>The auto-generated label for this transform</returns>
    internal string DefineTransform( string transformClassName )
    {
        CheckDisposedStatus();
        Int64   timeSeed      = DateTime.Now.ToFileTime();
        string generatedName = timeSeed.ToString(CultureInfo.InvariantCulture);
 
        // If there is a name collision, just keep incrementing the number
        while( TransformLabelIsDefined( generatedName ) )
        {
            timeSeed++;
            generatedName = timeSeed.ToString(CultureInfo.InvariantCulture);
        }
 
        // Submit the definition
        DefineTransform( transformClassName, generatedName );
 
        return generatedName;
    }
 
    //+----------------------------------------------------------------------
    //  Transform initialization event/delegate/etc.
    
    /// <summary>
    ///     Delegate method for initializing transforms
    /// </summary>
    internal delegate void TransformInitializeEventHandler(
        object sender,
        TransformInitializationEventArgs e );
    
    /// <summary>
    ///     Transform initialization event
    /// </summary>
    internal event TransformInitializeEventHandler OnTransformInitialization;
 
    /// <summary>
    /// Internal shortcut to kick off all the initializers
    /// </summary>
    /// <param name="initArguments">Arguments for the initializers</param>
    internal void CallTransformInitializers( TransformInitializationEventArgs initArguments )
    {
        if( null != OnTransformInitialization )
            OnTransformInitialization( this, initArguments );
    }
 
    /// <summary>
    /// Reads a data space map from the associated container, if such a thing
    /// is written to the file.
    /// </summary>
    void ReadDataSpaceMap()
    {    
        // See if there's even a data spaces storage
        StorageInfo dataSpaceStorage = 
            new StorageInfo( _associatedStorage, DataSpaceStorageName );
        StreamInfo dataSpaceMapStreamInfo = 
            new StreamInfo( dataSpaceStorage, DataSpaceMapTableName );
 
        if( dataSpaceStorage.StreamExists(DataSpaceMapTableName) )
        {
            // There is an existing data space mapping table to read.
 
            // Read the versioning information
            ReadDataSpaceVersionInformation(dataSpaceStorage);      
     
            // Check if its the correct version for reading
            ThrowIfIncorrectReaderVersion();
 
            // Read the data space mapping table
            using(Stream dataSpaceMapStream = dataSpaceMapStreamInfo.GetStream(FileMode.Open))
            {
                using(BinaryReader dataSpaceMapReader = 
                    new BinaryReader( dataSpaceMapStream, System.Text.Encoding.Unicode ))
                {
                    int headerLength = dataSpaceMapReader.ReadInt32();
                    int entryCount = dataSpaceMapReader.ReadInt32();
 
                    if (headerLength < KnownBytesInMapTableHeader || entryCount < 0)
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                    int extraDataSize = headerLength - KnownBytesInMapTableHeader;
 
                    if( 0 < extraDataSize )
                    {
                        if (extraDataSize > AllowedExtraDataMaximumSize)
                            throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                        _mapTableHeaderPreservation = dataSpaceMapReader.ReadBytes(extraDataSize);
 
                        if (_mapTableHeaderPreservation.Length != extraDataSize)
                            throw new FileFormatException(SR.Get(SRID.CorruptedData));
                    }
 
                    _dataSpaceMap.Capacity = entryCount;
 
                    int entryLength;
                    int bytesRead;
                    int totalBytesRead;
 
                    for( int i = 0; i < entryCount; i++ )
                    {
                        entryLength = dataSpaceMapReader.ReadInt32();
 
                        if (entryLength < 0)
                            throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                        totalBytesRead = 4; // entryLength
                        // Read the container reference entry
                        CompoundFileReference entryRef = 
                            CompoundFileReference.Load( dataSpaceMapReader, out bytesRead );
                        checked { totalBytesRead += bytesRead; }
 
                        // Read data space string and add to data space mapping table
                        string label = CU.ReadByteLengthPrefixedDWordPaddedUnicodeString(dataSpaceMapReader, out bytesRead);
                        checked { totalBytesRead += bytesRead; }
 
                        _dataSpaceMap[entryRef] = label;
 
                        // Verify entryLength against what was actually read:
                        if (entryLength != totalBytesRead)
                        {
                            throw new IOException(SR.Get(SRID.DataSpaceMapEntryInvalid));
                        }
                    }
                }                    
            }
        }
    }
 
    /// <summary>
    /// Write the data space mapping table to underlying storage.
    /// </summary>
    void WriteDataSpaceMap()
    {
        ThrowIfIncorrectUpdaterVersion();
 
        StorageInfo dataSpaceStorage = 
            new StorageInfo( _associatedStorage, DataSpaceStorageName );
        StreamInfo dataSpaceMapStreamInfo = 
            new StreamInfo ( dataSpaceStorage, DataSpaceMapTableName );
 
        if( 0 < _dataSpaceMap.Count )
        {
            // Write versioning information
            StreamInfo versionStreamInfo = null;
            if (dataSpaceStorage.StreamExists( DataSpaceVersionName ) )
                versionStreamInfo = dataSpaceStorage.GetStreamInfo( DataSpaceVersionName );
            else
                versionStreamInfo = dataSpaceStorage.CreateStream( DataSpaceVersionName );
            Stream versionStream = versionStreamInfo.GetStream();
            _fileFormatVersion.SaveToStream(versionStream);
            versionStream.Close();
 
            // Create stream for write, overwrite any existing
            using(Stream dataSpaceMapStream = dataSpaceMapStreamInfo.GetStream(FileMode.Create))
            {
                using(BinaryWriter dataSpaceMapWriter =
                    new BinaryWriter( dataSpaceMapStream, System.Text.Encoding.Unicode ))
                {
                    // Write header 
 
                    // header length = our known size + preserved array size
                    dataSpaceMapWriter.Write( 
                        checked ((Int32) (KnownBytesInMapTableHeader + _mapTableHeaderPreservation.Length)));
                    // number of entries
                    dataSpaceMapWriter.Write(
                        _dataSpaceMap.Count );
                    // anything else we've preserved
                    dataSpaceMapWriter.Write(
                        _mapTableHeaderPreservation );
 
                    // Loop to write entries
                    foreach( CompoundFileReference o in _dataSpaceMap.Keys )
                    {
                        // determine the entry length
                        string label = (string)_dataSpaceMap[o];
                        int entryLength = CompoundFileReference.Save(o, null);
 
                        checked { entryLength += CU.WriteByteLengthPrefixedDWordPaddedUnicodeString(null, label); }
 
                        // length of entryLength itself
                        checked { entryLength += 4; }
 
                        // write out the entry length
                        dataSpaceMapWriter.Write((Int32) entryLength);
 
                        // Write out reference
                        CompoundFileReference.Save( o, dataSpaceMapWriter);
 
                        // Write out dataspace label
                        CU.WriteByteLengthPrefixedDWordPaddedUnicodeString(
                            dataSpaceMapWriter, label);
                    }
                }
            }
        }
        else
        {
            // data space map is empty, remove existing stream if there.
            if ( dataSpaceStorage.StreamExists( DataSpaceMapTableName ) )
                dataSpaceStorage.DeleteStream( DataSpaceMapTableName );
        }
    }
 
    /// <summary>
    /// Read all data space definitions in one chunk.  To be replaced
    /// with on-demand reading mechanism.
    /// </summary>
    void ReadDataSpaceDefinitions()
    {
        ThrowIfIncorrectReaderVersion();
 
        StorageInfo dataSpaceStorage = 
            new StorageInfo( _associatedStorage, DataSpaceStorageName );
        StorageInfo dataSpaceDefinitionsStorage =
            new StorageInfo( dataSpaceStorage, DataSpaceDefinitionsStorageName );
 
        if( dataSpaceDefinitionsStorage.Exists )
        {
            // Fill in the Data Space Definitions hash table
            foreach( StreamInfo definitionStreamInfo in dataSpaceDefinitionsStorage.GetStreams())
            {
                // Open up the stream for this data space definition
                using(Stream definitionStream = definitionStreamInfo.GetStream(FileMode.Open))
                {
                    using(BinaryReader definitionReader = new BinaryReader( definitionStream, System.Text.Encoding.Unicode ))
                    {
                        // Read data space definition stream
                        int headerLength = definitionReader.ReadInt32();
                        int transformCount = definitionReader.ReadInt32();
 
                        if (headerLength < KnownBytesInDataSpaceDefinitionHeader || transformCount < 0)
                            throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                        ArrayList transformLabels = new ArrayList(transformCount);
                        byte[] extraData = null;
                        int extraDataSize = headerLength - KnownBytesInDataSpaceDefinitionHeader;
 
                        if (extraDataSize > AllowedExtraDataMaximumSize)
                            throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                        if (extraDataSize > 0)
                        {
                            extraData = definitionReader.ReadBytes(extraDataSize);
 
                            if (extraData.Length != extraDataSize)
                                throw new FileFormatException(SR.Get(SRID.CorruptedData));
                        }
 
                        // Read the array of strings that make up the transform stack
                        for( int i = 0; i < transformCount; i++ )
                        {
                            transformLabels.Add(
                                CU.ReadByteLengthPrefixedDWordPaddedUnicodeString( definitionReader ) );
                        }
 
                        // Add data space definition to table
                        SetDataSpaceDefinition( definitionStreamInfo.Name, new DataSpaceDefinition(transformLabels, extraData));
                    }
                }
            }
        }
    }
 
    /// <summary>
    /// Write all data space definitions to underlying storage in one chunk.
    /// </summary>
    /// 
    // 
    void WriteDataSpaceDefinitions()
    {
        ThrowIfIncorrectUpdaterVersion();
 
        StorageInfo dataSpaceStorage = 
            new StorageInfo( _associatedStorage, DataSpaceStorageName );
 
        // 
 
 
 
        // Write out data space definitions
        if( 0 < _dataSpaceDefinitions.Count )
        {
            
            StorageInfo dataSpaceDefinitionsStorage =
                new StorageInfo(dataSpaceStorage, DataSpaceDefinitionsStorageName);
            dataSpaceDefinitionsStorage.Create();
 
            foreach( string name in _dataSpaceDefinitions.Keys )
            {
                StreamInfo singleDefinitionInfo =
                    new StreamInfo(dataSpaceDefinitionsStorage,name);
 
                using(Stream singleDefinition = singleDefinitionInfo.GetStream())
                {
                    using(BinaryWriter definitionWriter =new BinaryWriter( singleDefinition, System.Text.Encoding.Unicode ))
                    {
                        DataSpaceDefinition definition = (DataSpaceDefinition) _dataSpaceDefinitions[name];
 
                        int headerSize = KnownBytesInDataSpaceDefinitionHeader;
 
                        if (definition.ExtraData != null)
                        {
                            checked { headerSize += definition.ExtraData.Length; }
                        }
                        definitionWriter.Write( headerSize );
                        definitionWriter.Write( definition.TransformStack.Count );
                        if (definition.ExtraData != null)
                        {
                            definitionWriter.Write(definition.ExtraData);
                        }
 
                        foreach( object transformLabel in definition.TransformStack)
                        {
                            CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( 
                                definitionWriter, (string)transformLabel);
                        }
                    }
                }
            }
        }
    }
 
    /// <summary>
    /// Read all transform definitions in one chunk
    /// </summary>
    // 
 
    void ReadTransformDefinitions()
    {
        ThrowIfIncorrectReaderVersion();
 
        StorageInfo dataSpaceStorage = 
            new StorageInfo( _associatedStorage, DataSpaceStorageName );
        StorageInfo transformDefinitionsStorage =
            new StorageInfo( dataSpaceStorage, TransformDefinitions );
 
        if( transformDefinitionsStorage.Exists )
        {
            // Read transform definitions from file
            foreach( StorageInfo transformStorage in transformDefinitionsStorage.GetSubStorages() )
            {
                // Read from primary stream
                StreamInfo transformPrimary = new StreamInfo(
                    transformStorage, TransformPrimaryInfo );
 
                using(Stream transformDefinition = transformPrimary.GetStream(FileMode.Open))
                {
                    using(BinaryReader definitionReader = new BinaryReader( transformDefinition, System.Text.Encoding.Unicode ))
                    {
                        int headerLength = definitionReader.ReadInt32(); // We don't actually do anything with HeaderLen at the moment
                        int transformType = definitionReader.ReadInt32();
 
                        if (headerLength < 0)
                            throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                        // Create a TransformInstance class using name from file
                        TransformInstance transformInstance =
                            new TransformInstance(transformType, CU.ReadByteLengthPrefixedDWordPaddedUnicodeString( definitionReader ) );
 
                        
                        int extraDataSize = checked ((int) (headerLength - transformDefinition.Position));
 
                        if (extraDataSize < 0)
                            throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                        if (extraDataSize > 0)
                        {
                            if (extraDataSize > AllowedExtraDataMaximumSize)
                                throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                            // Preserve the fields we don't know about.
                            byte[] extraData = definitionReader.ReadBytes(extraDataSize);
 
                            if (extraData.Length != extraDataSize)
                                throw new FileFormatException(SR.Get(SRID.CorruptedData));
 
                            transformInstance.ExtraData = extraData;
                        }
 
                        if( transformDefinition.Length > transformDefinition.Position )
                        {
                            // We have additional data in the primary instance data stream
                            int instanceDataSize = checked ((int)(transformDefinition.Length - transformDefinition.Position));
                            byte[] instanceData = new byte[instanceDataSize];
                            PackagingUtilities.ReliableRead(transformDefinition, instanceData, 0, instanceDataSize);
 
                            //build memory stream on the byte[] , and allow writes only if 
                            // FileAccess is Write or ReadWrite
                            MemoryStream instanceDataStream;
                            if (_associatedStorage.OpenAccess == FileAccess.Read)
                            {
                                //  NOTE: Building MemoryStream directly on top of
                                //  instanceData byte array because we want it to be
                                //  NOT resizable and NOT writable.                    
                                instanceDataStream = new MemoryStream(instanceData, false /* Not writable */);
                            }
                            else
                            {
                                // Copy additional data into a memory stream
                                //  NOTE: Not building MemoryStream directly on top of
                                //  instanceData byte array because we want it to be
                                //  resizable.                    
                                instanceDataStream = new MemoryStream();
                                instanceDataStream.Write( instanceData, 0, instanceDataSize );
                            }
                            instanceDataStream.Seek( 0, SeekOrigin.Begin );
 
                            // Dirty state should be tracked after the original data is read in from the disk into the memory stream
                            transformInstance.transformPrimaryStream = new DirtyStateTrackingStream(instanceDataStream);
                        }
 
                        transformInstance.transformStorage = transformStorage;
 
                        SetTransformDefinition( transformStorage.Name, transformInstance );
                    }
                }
            }
        }
    }
 
    /// <summary>
    /// Write out all transform definitions all at once
    /// </summary>
    /// 
    // 
 
    void WriteTransformDefinitions()
    {
        ThrowIfIncorrectUpdaterVersion();
 
        StorageInfo dataSpaceStorage = 
            new StorageInfo( _associatedStorage, DataSpaceStorageName );
        StorageInfo transformDefinitionsStorage =
            new StorageInfo( dataSpaceStorage, TransformDefinitions );
 
        // 
 
 
 
        if( 0 < _transformDefinitions.Count )
        {
            foreach( string transformDef in _transformDefinitions.Keys )
            {
                // 'transformDef' is the normalized label.  We need to dig a
                //  bit to retrieve the original transform label.
                string transformLabel = null;
                TransformInstance transformInstance = GetTransformInstanceOf( transformDef );
                Debug.Assert( transformInstance != null, "A transform instance should be available if its name is in the transformDefinitions hashtable");
                
                if( transformInstance.transformEnvironment != null )
                {
                    // We have a transform environment object - it has the transform label.
                    transformLabel = transformInstance.transformEnvironment.TransformLabel;
                }
                else
                {
                    // We don't have a transform environment object - we'll need to do a
                    //  more expensive reverse-lookup with the name manager.
                    transformLabel = transformDef;
                }
 
                // Now use transformLabel to create the storage.
                StorageInfo singleTransformStorage =
                    new StorageInfo( transformDefinitionsStorage, transformLabel );
                StreamInfo transformPrimaryInfo =
                    new StreamInfo( singleTransformStorage, TransformPrimaryInfo );
 
                using(Stream transformPrimary = transformPrimaryInfo.GetStream())
                {
                    using(BinaryWriter transformWriter = new BinaryWriter( transformPrimary, System.Text.Encoding.Unicode ))
                    {
                        // Header length size = Known (the number itself + identifier) +
                        //  to be calculated (length of type name)
                        int headerLength = checked (KnownBytesInTransformDefinitionHeader +
                                                CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( null, transformInstance.typeName));
 
                        if (transformInstance.ExtraData != null)
                        {
                            Debug.Assert(transformInstance.ExtraData.Length > 0);
 
                            checked { headerLength += transformInstance.ExtraData.Length; }
                        }
 
                        transformWriter.Write(headerLength);
                        
                        transformWriter.Write((int)TransformIdentifierTypes_PredefinedTransformName);
                        CU.WriteByteLengthPrefixedDWordPaddedUnicodeString( 
                            transformWriter, transformInstance.typeName);
 
                        // Write out the preserved unknown data if there are some
                        if (transformInstance.ExtraData != null)
                        {
                            transformWriter.Write(transformInstance.ExtraData);
                        }
 
                        if( null != transformInstance.transformPrimaryStream )
                        {
                            byte[] memoryBuffer = ((MemoryStream) ((DirtyStateTrackingStream) transformInstance.transformPrimaryStream).BaseStream).GetBuffer();
                            transformPrimary.Write( memoryBuffer, 0, memoryBuffer.Length );
                        }
                    }
                }
            }
        }
        else // No transform definitions
        {
            if ( transformDefinitionsStorage.Exists)
            {
                dataSpaceStorage.Delete(true, TransformDefinitions);
                //transformDefinitionStorage.Delete(true);
            }
        }
    }
 
    /// <summary>
    /// Internal method to create an entry in the data space mapping table
    /// </summary>
    /// <param name="containerReference">Package reference describing the scope of the data space</param>
    /// <param name="label">Label of the data space definition</param>
    internal void CreateDataSpaceMapping( CompoundFileReference containerReference, string label )
    {
        Debug.Assert( DataSpaceIsDefined( label ),
            "Internal method illegally defining a data space reference on a data space that has not yet been defined");
        _dataSpaceMap[containerReference] = label;
        _dirtyFlag = true;
    }
 
    /// <summary>
    /// Internal method to create the actual transform stack of streams which
    /// will handle the encoding and decoding
    /// </summary>
    /// <param name="containerReference">Package reference for the data space stream</param>
    /// <param name="rawStream">Stream with raw bytes on disk</param>
    /// <returns>Stream with clear text</returns>
    internal Stream CreateDataSpaceStream( CompoundFileStreamReference containerReference , Stream rawStream )
    {
        Stream outputStream = rawStream;
        // RightsManagementEncryptionTransform and CompressionTransform do not use transformContext
        IDictionary transformContext = new Hashtable(); // Transform context object
 
        // See which data space we're creating this stream in
        string dataSpaceLabel = _dataSpaceMap[containerReference] as string;
        Debug.Assert( null != dataSpaceLabel,
            "Internal caller has asked to create a data space stream but the reference is not associated with a data space" );
 
        // Retrieve the transform object stack
        ArrayList transformStack = GetDataSpaceDefinition(dataSpaceLabel).TransformStack;
        Debug.Assert( null != transformStack,
            "Internal inconsistency - data space name does not have a transform stack definition" );
 
        // Iterate the initialization process for each transform on the stack
        foreach( string transformLabel in transformStack )
        {
            // Get information on this layer of the transform stack
            TransformInstance transformLayer = GetTransformInstanceOf( transformLabel );
            Debug.Assert( null != transformLayer, "Data space definition included an undefined transform" );
 
            // If we haven't gotten around to creating the transform object, go do it.
            if( null == transformLayer.transformReference )
            {
                transformLayer.transformEnvironment = new TransformEnvironment( this, transformLabel );
 
                transformLayer.transformReference = InstantiateDataTransformObject(
                                                        transformLayer.ClassType,
                                                        transformLayer.typeName,
                                                        transformLayer.transformEnvironment );
            }
            Debug.Assert( null != transformLayer.transformReference,
                "Failed to have a transform instance going in" );
            IDataTransform transformObject = transformLayer.transformReference;
 
            // If transform is not ready, call initializers to make it ready.
            if( ! transformObject.IsReady )
            {
                CallTransformInitializers( 
                    new TransformInitializationEventArgs(
                        transformObject,
                        dataSpaceLabel,
                        containerReference.FullName,
                        transformLabel)
                    );
 
                if( ! transformObject.IsReady ) // If STILL not ready, nobody could make it "ready".
                    throw new InvalidOperationException(
                        SR.Get(SRID.TransformObjectInitFailed));
            }
            // Everything is setup, get a transformed stream
            outputStream = transformObject.GetTransformedStream( outputStream, transformContext );
        }
 
        outputStream = new BufferedStream( outputStream ); // Add buffering layer
 
        outputStream = new StreamWithDictionary( outputStream, transformContext );
 
        _transformedStreams.Add( outputStream ); // Remember this for later use
 
        return outputStream;
    }
 
    /// <summary>
    /// When naming a transform object, the string being passed in can be
    /// interpreted in one of several ways.  This enumerated type is used
    /// to specify the semantics of the identification string.
    /// 
    /// The transform identification string is key into a table of
    ///  well-known transform definitions.
    /// </summary>
    internal const int TransformIdentifierTypes_PredefinedTransformName = 1;
 
    #region Version Methods
 
    /// <summary>
    /// Read the version information that specifies the minimum versions of the 
    /// DataSpaceManager software that can read, write, or update the data space 
    /// information in this file.
    /// </summary>
    /// <param name="dataSpaceStorage"></param>
    /// <exception cref="FileFormatException">
    /// If the format version information in the stream is corrupt.
    /// </exception>
    private void ReadDataSpaceVersionInformation(StorageInfo dataSpaceStorage)
    {      
        if (_fileFormatVersion == null)
        {
            if (dataSpaceStorage.StreamExists( DataSpaceVersionName ))
            {
                StreamInfo versionStreamInfo = dataSpaceStorage.GetStreamInfo( DataSpaceVersionName );
                using (Stream versionStream = versionStreamInfo.GetStream(FileMode.Open))
                {
                    _fileFormatVersion = FormatVersion.LoadFromStream(versionStream);
                                        
                    // Transform Identifier: we preserve casing, but do case-insensitive comparison
                    //Case-insensitive comparison. As per recommendations, we convert both strings
                    //to Upper case and then compare with StringComparison.Ordinal
                    if (!((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(_fileFormatVersion.FeatureIdentifier,
                                       DataSpaceVersionIdentifier))
                    {
                        throw new FileFormatException(
                                            SR.Get(SRID.InvalidTransformFeatureName,
                                            _fileFormatVersion.FeatureIdentifier,
                                            DataSpaceVersionIdentifier));                       
                    }
                    // If we ever write this version number out again, we will want to record
                    // the fact that it was done by the current version of the Dataspace software.
                    _fileFormatVersion.WriterVersion = DataSpaceCurrentWriterVersion;
                }
            }
        }        
    }
 
    /// <summary>
    /// If the DataSpace version information was not present in the file, we initialize it with the
    /// values for the current DataSpace software.
    /// If the information was present we should have already read it as a part of the ReadDataSpaceMap.
    /// </summary>
    private void EnsureDataSpaceVersionInformation()
    {
        if (_fileFormatVersion == null)
        {
            _fileFormatVersion = new FormatVersion(
                                        DataSpaceVersionIdentifier,
                                        DataSpaceCurrentWriterVersion,
                                        DataSpaceCurrentReaderVersion,
                                        DataSpaceCurrentUpdaterVersion
                                     );
        }
    }
      
    /// <summary>
    /// Verify that the current version of this class can read the DataSpace information in
    /// this file.
    /// </summary>
    /// <exception cref="FileFormatException">
    /// If the current version of this class cannot read the DataSpace information in this file.
    /// </exception>
    private void ThrowIfIncorrectReaderVersion()
    {
        EnsureDataSpaceVersionInformation();
      
        if (!_fileFormatVersion.IsReadableBy(DataSpaceCurrentReaderVersion))
        {
            throw new FileFormatException(
                            SR.Get(
                                SRID.ReaderVersionError,
                                _fileFormatVersion.ReaderVersion,
                                DataSpaceCurrentReaderVersion
                                )
                            );
        }
    }
 
    /// <summary>
    /// Verify that the current version of this class can update the DataSpace information in
    /// this file.
    /// </summary>
    /// <exception cref="FileFormatException">
    /// If the current version of this class cannot update the DataSpace information in this file,
    /// or if the header information in the stream is corrupt.
    /// </exception>
    private void ThrowIfIncorrectUpdaterVersion()
    {
        EnsureDataSpaceVersionInformation();
 
        if (!_fileFormatVersion.IsUpdatableBy(DataSpaceCurrentUpdaterVersion))
        {
            throw new FileFormatException(
                            SR.Get(
                                SRID.UpdaterVersionError,
                                _fileFormatVersion.UpdaterVersion,
                                DataSpaceCurrentUpdaterVersion
                                )
                            );
        }
    }
 
    #endregion Version Methods
}
 
/// <summary>
/// Interface to be implemented by all data transform objects
/// </summary>
internal interface IDataTransform
{
    /// <summary>
    /// Whether this transform is ready to perform.  If false, it needs
    /// further initialization.
    /// </summary>
    bool IsReady { get; }
 
    /// <summary>
    /// Whether this transform is in a stable state
    /// </summary>
    bool FixedSettings { get; }
 
    /// <summary>
    /// Data transform identifier object
    /// </summary>
    object TransformIdentifier { get; }
 
    /// <summary>
    /// Given a stream for storing the encoded data, return a stream for
    /// manipulating the "cleartext" data.
    /// </summary>
    Stream GetTransformedStream( Stream encodedDataStream, IDictionary transformContext );
}
 
/// <summary>
/// Internal class for passing arguments into event handlers
/// </summary>
internal class TransformInitializationEventArgs : EventArgs
{
    IDataTransform  dataInstance;
    string          dataSpaceLabel;
    string          streamPath;
    string          transformLabel;
 
    internal TransformInitializationEventArgs(
        IDataTransform instance,
        string dataSpaceInstanceLabel,
        string transformedStreamPath,
        string transformInstanceLabel )
    {
        dataInstance = instance;
        dataSpaceLabel = dataSpaceInstanceLabel;
        streamPath = transformedStreamPath;
        transformLabel = transformInstanceLabel;
    }
    /// <summary>
    /// Reference to the data object that requires initialization
    /// </summary>
    internal IDataTransform DataTransform
    {
        get
        {
            return dataInstance;
        }
    }
 
    /// <summary>
    /// Label for the data space whose initialization this is a part of
    /// </summary>
    internal string DataSpaceLabel
    {
        get
        {
            return dataSpaceLabel;
        }
    }
 
    /// <summary>
    /// The path to the stream whose use initiated this process
    /// </summary>
    internal string Path
    {
        get
        {
            return streamPath;
        }
    }
 
    /// <summary>
    /// Label for the transform object instance being initialized
    /// </summary>
    internal string TransformInstanceLabel
    {
        get
        {
            return transformLabel;
        }
    }
}
 
/// <summary>
/// An instance of this class is given to each transform object as a 
/// means for the transform object to interact with the environment 
/// provided by the data space manager.  It is not mandatory for a 
/// transform object to keep a reference on the given TransformEnvironment 
/// object  it may choose to discard it if there is no need to interact 
/// with the transform environment.
/// </summary>
internal  class TransformEnvironment
{
    DataSpaceManager transformHost;
    string  transformLabel;
 
    /// <summary>
    /// This object is only created internally by the data space manager.
    /// </summary>
    /// <param name="host">The data space manager who created this class</param>
    /// <param name="instanceLabel">Text label for this transform instance</param>
    internal TransformEnvironment( DataSpaceManager host, string instanceLabel )
    {
        transformHost = host;
        transformLabel = instanceLabel;
    }
 
    /// <summary>
    /// Whether this transform demands that all instance data of other
    /// transform above it in the transform stack be transformed too
    /// </summary>
    internal bool RequireOtherInstanceData
    {
        get
        {
            return false;
        }
        set
        {
            transformHost.CheckDisposedStatus();
            throw new NotSupportedException(
                SR.Get(SRID.NYIDefault));
        }
    }
 
    /// <summary>
    /// Whether this transform demands that its own instance data be
    /// free from other data transformation
    /// </summary>
    internal bool RequireInstanceDataUnaltered
    {
        get
        {
            return false;
        }
        set
        {
            transformHost.CheckDisposedStatus();
            throw new NotSupportedException(
                SR.Get(SRID.NYIDefault));
        }
    }
 
    /// <summary>
    /// Whether this transform requests that its own instance data be
    /// processed by other transforms below it in the stack
    /// </summary>
    internal bool DefaultInstanceDataTransform
    {
        get
        {
            return false;
        }
        set
        {
            transformHost.CheckDisposedStatus();
            throw new NotSupportedException(
                SR.Get(SRID.NYIDefault));
        }
    }
 
    /// <summary>
    ///     In the transform definition hashtable, we only have the normalized
    /// label as the key.  By digging our way to this property via the hashtable
    /// value, we can recover the original label.
    /// </summary>
    internal string TransformLabel
    {
        get
        {
            return transformLabel;
        }
    }
 
    /// <summary>
    /// Returns the stream specific to this instance of the transform
    /// object, in which it can store any instance data it needs
    /// </summary>
    /// <returns>System.IO.Stream object for storing instance data</returns>
    internal Stream GetPrimaryInstanceData()
    {
        transformHost.CheckDisposedStatus();
        return transformHost.GetPrimaryInstanceStreamOf( transformLabel );
    }
 
    /// <summary>
    /// When the primary instance data stream is insufficient, this returns
    /// a storage in which to store multiple streams that comprise the instance
    /// data that this transform object wants to store.
    /// </summary>
    /// <returns>StorageInfo pointing to the storage that this transform is free to use</returns>
    internal StorageInfo GetInstanceDataStorage()
    {
        transformHost.CheckDisposedStatus();
 
        StorageInfo storageInfo = transformHost.GetInstanceDataStorageOf( transformLabel );
        
        if (! storageInfo.Exists)
        {
            storageInfo.Create();
        }
        
        return storageInfo;
    }
}
}