Package.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Base / System / IO / Packaging / Package.cs / 1 / Package.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is a base abstract class for Package. This is a part of the 
//  Packaging Layer.
// 
// History:
//  01/03/2004: SarjanaS: Initial creation. [Stubs only]
//  03/01/2004: SarjanaS: Implemented the functionality for all the members.
//  03/17/2004: BruceMac: Initial implementation or PackageRelationship methods 
//
//----------------------------------------------------------------------------- 
 
using System;
using System.IO; 
using System.Collections;
using System.Collections.Generic;   // For SortedList<>
using System.Windows;               // For Exception strings - SRID
using System.Diagnostics;           // For Debug.Assert 
using MS.Internal.IO.Packaging;
using MS.Internal.WindowsBase;      // for [FriendAccessAllowed] 
 
using MS.Internal;                  // For Invariant.Assert
using MS.Utility; 

namespace System.IO.Packaging
{
    ///  
    /// Abstract Base class for the Package.
    /// This is a part of the Packaging Layer APIs 
    ///  
    public abstract class Package : IDisposable
    { 

        //-----------------------------------------------------
        //
        //  Public Constructors 
        //
        //----------------------------------------------------- 
 
        #region Protected Constructor
 
        /// 
        /// Protected constructor for the abstract Base class.
        /// This is the current contract between the subclass and the base class
        /// If we decide some registration mechanism then this might change 
        /// 
        ///  
        /// If FileAccess enumeration does not have one of the valid values 
        protected Package(FileAccess openFileAccess)
            : this(openFileAccess, false /* not streaming by default */) 
        {
        }

        ///  
        /// Protected constructor for the abstract Base class.
        /// This is the current contract between the subclass and the base class 
        /// If we decide some registration mechanism then this might change 
        /// 
        ///  
        /// Whether the package is being opened for streaming.
        /// If FileAccess enumeration does not have one of the valid values
        protected Package(FileAccess openFileAccess, bool streaming)
        { 
            ThrowIfFileAccessInvalid(openFileAccess);
 
            _openFileAccess      = openFileAccess; 

            //PackUriHelper.ValidatedPartUri implements the IComparable interface. 
            _partList            = new SortedList(); // initial default is zero
            _partCollection      = null;
            _disposed            = false;
            _inStreamingCreation = (openFileAccess == FileAccess.Write && streaming); 
        }
 
        #endregion Protected Constructor 

        //------------------------------------------------------ 
        //
        //  Public Properties
        //
        //----------------------------------------------------- 

        #region Public Properties 
 
        /// 
        /// Gets the FileAccess with which the package was opened. This is a read only property. 
        /// This property gets set when the package is opened.
        /// 
        /// FileAccess
        /// If this Package object has been disposed 
        public FileAccess FileOpenAccess
        { 
            get 
            {
                ThrowIfObjectDisposed(); 
                return _openFileAccess;
            }
        }
 
        /// 
        /// The package properties are a subset of the standard OLE property sets 
        /// SummaryInformation and DocumentSummaryInformation, and include such properties 
        /// as Title and Subject.
        ///  
        /// If this Package object has been disposed
        public PackageProperties PackageProperties
        {
            get 
            {
                ThrowIfObjectDisposed(); 
 
                if (_packageProperties == null)
                    _packageProperties = new PartBasedPackageProperties(this); 
                return _packageProperties;
            }
        }
 
        #endregion Public Properties
 
        //------------------------------------------------------ 
        //
        //  Public Methods 
        //
        //------------------------------------------------------

        #region Public Methods 

        #region OpenOnFileMethods 
 
        /// 
        /// Opens a package at the specified Path. This method calls the overload which accepts all the parameters 
        /// with the following defaults -
        /// FileMode - FileMode.OpenOrCreate,
        /// FileAccess - FileAccess.ReadWrite
        /// FileShare  - FileShare.None 
        /// 
        /// Path to the package 
        /// Package 
        /// If path parameter is null
        public static Package Open(string path) 
        {
            return Open(path, _defaultFileMode, _defaultFileAccess, _defaultFileShare);
        }
 
        /// 
        /// Opens a package at the specified Path in the given mode. This method calls the overload which 
        /// accepts all the parameters with the following defaults - 
        /// FileAccess - FileAccess.ReadWrite
        /// FileShare  - FileShare.None 
        /// 
        /// Path to the package
        /// FileMode in which the package should be opened
        /// Package 
        /// If path parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        public static Package Open(string path, FileMode packageMode) 
        {
            return Open(path, packageMode, _defaultFileAccess, _defaultFileShare); 
        }

        /// 
        /// Opens a package at the specified Path in the given mode with the specified access. This method calls 
        /// the overload which accepts all the parameters with the following defaults -
        /// FileShare  - FileShare.None 
        ///  
        /// Path to the package
        /// FileMode in which the package should be opened 
        /// FileAccess with which the package should be opened
        /// Package
        /// If path parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        public static Package Open(string path, FileMode packageMode, FileAccess packageAccess) 
        { 
            return Open(path, packageMode, packageAccess, _defaultFileShare);
        } 

        /// 
        /// Opens the package with the specified parameters.
        /// 
        /// Note:-
        /// Since currently there is no plan to implement a generic registration mechanism, this method 
        /// has some hard coded knowledge about the sub classes. This might change later if we come up 
        /// with a registration mechanism.
        /// There is no caching mechanism in case the FileShare is specified to ReadWrite/Write. 
        /// We do not have any refresh mechanism, to make sure that the cached parts reflect the actual parts,
        /// in the case where there might be more than one processes writing to the same underlying package.
        /// 
        /// Path to the package 
        /// FileMode in which the package should be opened
        /// FileAccess with which the package should be opened 
        /// FileShare with which the package is opened. 
        /// Package
        /// InvalidArgumentException - If the combination of the FileMode, 
        /// FileAccess and FileShare parameters is not meaningful.
        /// If path parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values 
        public static Package Open(string path, FileMode packageMode, FileAccess packageAccess, FileShare packageShare)
        { 
            return Open(path, packageMode, packageAccess, packageShare, false /* not in streaming mode */); 
        }
 
        #endregion OpenOnFileMethods

        #region OpenOnStreamMethods
 
        /// 
        /// Open a package on this stream. This method calls the overload which accepts all the parameters 
        /// with the following defaults - 
        /// FileMode - FileMode.Open
        /// FileAccess - FileAccess.Read 
        /// 
        /// Stream on which the package is to be opened
        /// Package
        /// If stream parameter is null 
        /// If package to be created should have readwrite/read access and underlying stream is write only
        /// If package to be created should have readwrite/write access and underlying stream is read only 
        public static Package Open(Stream stream) 
        {
            return Open(stream, _defaultStreamMode, _defaultStreamAccess); 
        }

        /// 
        /// Open a package on this stream. This method calls the overload which accepts all the parameters 
        /// with the following defaults -
        /// FileAccess - FileAccess.ReadWrite 
        ///  
        /// Stream on which the package is to be opened
        /// FileMode in which the package should be opened. 
        /// Package
        /// If stream parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values
        /// If package to be created should have readwrite/read access and underlying stream is write only 
        /// If package to be created should have readwrite/write access and underlying stream is read only
        public static Package Open(Stream stream, FileMode packageMode) 
        { 
            //If the user is providing a FileMode, in all the modes, except FileMode.Open,
            //its most likely that the user intends to write to the stream. 
            return Open(stream, packageMode, _defaultFileAccess);
        }

        ///  
        /// Opens a package on this stream. The package is opened in the specified mode and with the access
        /// specified. 
        ///  
        /// Stream on which the package is created
        /// FileMode in which the package is to be opened 
        /// FileAccess on the package that is opened
        /// Package
        /// If stream parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        /// If package to be created should have readwrite/read access and underlying stream is write only 
        /// If package to be created should have readwrite/write access and underlying stream is read only 
        public static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess)
        { 
            return Open(stream, packageMode, packageAccess, false /* not in streaming mode */);
        }
        #endregion OpenOnStreamMethods
 
        #region PackagePart Methods
 
        ///  
        /// Creates a new part in the package. An empty stream corresponding to this part will be created in the
        /// package. If a part with the specified uri already exists then we throw an exception. 
        /// This methods will call the CreatePartCore method which will create the actual PackagePart in the package.
        /// 
        /// Uri of the PackagePart that is to be added
        /// ContentType of the stream to be added 
        /// 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified 
        /// If partUri parameter is null
        /// If contentType parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax
        /// If a PackagePart with the given partUri already exists in the Package
        public PackagePart CreatePart(Uri partUri, string contentType)
        { 
            return CreatePart(partUri, contentType, CompressionOption.NotCompressed);
        } 
 
        /// 
        /// Creates a new part in the package. An empty stream corresponding to this part will be created in the 
        /// package. If a part with the specified uri already exists then we throw an exception.
        /// This methods will call the CreatePartCore method which will create the actual PackagePart in the package.
        /// 
        /// Uri of the PackagePart that is to be added 
        /// ContentType of the stream to be added
        /// CompressionOption  describing compression configuration 
        /// for the new part. This compression apply only to the part, it doesn't affect relationship parts or related parts. 
        /// This parameter is optional. 
        ///  
        /// If this Package object has been disposed
        /// If the package is readonly, it cannot be modified
        /// If partUri parameter is null
        /// If contentType parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax
        /// If CompressionOption enumeration [compressionOption] does not have one of the valid values 
        /// If a PackagePart with the given partUri already exists in the Package 
        public PackagePart CreatePart(Uri partUri,
                            string contentType, 
                            CompressionOption compressionOption)
        {
            ThrowIfObjectDisposed();
            ThrowIfReadOnly(); 

            if (partUri == null) 
                throw new ArgumentNullException("partUri"); 

            if (contentType == null) 
                throw new ArgumentNullException("contentType");

            ThrowIfCompressionOptionInvalid(compressionOption);
 
            PackUriHelper.ValidatedPartUri validatedPartUri = PackUriHelper.ValidatePartUri(partUri);
 
            if (_partList.ContainsKey(validatedPartUri)) 
                throw new InvalidOperationException(SR.Get(SRID.PartAlreadyExists));
 
            // Add the part to the _partList if there is no prefix collision
            // Note: This is the only place where we pass a null to this method for the part and if the
            // methods returns successfully then we replace the null with an actual part.
            AddIfNoPrefixCollisionDetected(validatedPartUri, null /* since we don't have a part yet */); 

            PackagePart addedPart = CreatePartCore(validatedPartUri, 
                                                            contentType, 
                                                            compressionOption);
 
            //Set the entry for this Uri with the actual part
            _partList[validatedPartUri] = addedPart;

            return addedPart; 
        }
 
 

        ///  
        /// Returns a part that already exists in the package. If the part
        /// Corresponding to the URI does not exist in the package then an exception is
        /// thrown. The method calls the GetPartCore method which actually fetches the part.
        ///  
        /// 
        ///  
        /// If this Package object has been disposed 
        /// If the package is write only, information cannot be retrieved from it
        /// If partUri parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax
        /// If the requested part does not exists in the Package
        public PackagePart GetPart(Uri partUri)
        { 
            PackagePart returnedPart = GetPartHelper(partUri);
            if (returnedPart == null) 
                throw new InvalidOperationException(SR.Get(SRID.PartDoesNotExist)); 
            else
                return returnedPart; 
        }


        ///  
        /// This is a convenient method to check whether a given part exists in the
        /// package. This will have a default implementation that will try to retrieve 
        /// the part and then if successful, it will return true. 
        /// If the custom file format has an easier way to do this, they can override this method
        /// to get this information in a more efficient way. 
        /// 
        /// 
        /// 
        /// If this Package object has been disposed 
        /// If the package is write only, information cannot be retrieved from it
        /// If partUri parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax 
        public virtual bool PartExists(Uri partUri)
        { 
            return (GetPartHelper(partUri) != null);
        }

 
        /// 
        /// This method will do all the house keeping required when a part is deleted 
        /// Then the DeletePartCore method will be called which will have the actual logic to 
        /// do the work specific to the underlying file format and will actually delete the
        /// stream corresponding to this part. This method does not throw if the specified 
        /// part does not exist. This is in conformance with the FileInfo.Delete call.
        /// 
        /// 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified
        /// If partUri parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax 
        public void DeletePart(Uri partUri)
        { 
            ThrowIfObjectDisposed();
            ThrowIfReadOnly();
            ThrowIfInStreamingCreation("DeletePart");
 
            if (partUri == null)
                throw new ArgumentNullException("partUri"); 
 
            PackUriHelper.ValidatedPartUri validatedPartUri = (PackUriHelper.ValidatedPartUri)PackUriHelper.ValidatePartUri(partUri);
 
            if (_partList.ContainsKey(validatedPartUri))
            {
                //This will get the actual casing of the part that
                //is stored in the partList which is equivalent to the 
                //partUri provided by the user
                validatedPartUri = (PackUriHelper.ValidatedPartUri)_partList[validatedPartUri].Uri; 
                _partList[validatedPartUri].IsDeleted = true; 
                _partList[validatedPartUri].Close();
 
                //Call the Subclass to delete the part

                //!!Important Note: The order of this call is important as one of the
                //sub-classes - ZipPackage relies upon the abstract layer to be 
                //able to provide the ZipPackagePart in order to do the proper
                //clean up and delete operation. 
                //The dependency is in ZipPackagePart.DeletePartCore method. 
                //Ideally we would have liked to avoid this kind of a restriction
                //but due to the current class interfaces and data structure ownerships 
                //between these objects, it tough to re-design at this point.
                DeletePartCore(validatedPartUri);

                //Finally remove it from the list of parts in the cache 
                _partList.Remove(validatedPartUri);
            } 
            else 
                //If the part is not in memory we still call the underlying layer
                //to delete the part if it exists 
                DeletePartCore(validatedPartUri);

            if (PackUriHelper.IsRelationshipPartUri(validatedPartUri))
            { 
                //We clear the in-memory data structure corresponding to that relationship part
                //This will ensure that the intention of the user to delete the part, is respected. 
                //And thus we will not try to recreate it just in case there was some data in the 
                //memory structure.
 
                Uri owningPartUri = PackUriHelper.GetSourcePartUriFromRelationshipPartUri(validatedPartUri);
                //Package-level relationships in /_rels/.rels
                if (Uri.Compare(owningPartUri, PackUriHelper.PackageRootUri, UriComponents.SerializationInfoString, UriFormat.UriEscaped, StringComparison.Ordinal)==0)
                { 
                    //Clear any data in memory
                    this.ClearRelationships(); 
                } 
                else
                { 
                    //Clear any data in memory
                    if (this.PartExists(owningPartUri))
                    {
                        PackagePart owningPart = this.GetPart(owningPartUri); 
                        owningPart.ClearRelationships();
                    } 
                } 
            }
            else 
            {
                // remove any relationship part
                DeletePart(PackUriHelper.GetRelationshipPartUri(validatedPartUri));
            } 
        }
 
        ///  
        /// This returns a collection of all the Parts within the package.
        ///  
        /// 
        /// If this Package object has been disposed
        /// If the package is writeonly, no information can be retrieved from it
        public PackagePartCollection GetParts() 
        {
            ThrowIfObjectDisposed(); 
            ThrowIfWriteOnly(); 

            //Ideally we should decide whether we should query the underlying layer for parts based on the 
            //FileShare enum. But since we do not have that information, currently the design is to just
            //query the underlying layer once.
            //Note:
            //Currently the incremental behavior for GetPart method is not consistent with the GetParts method 
            //which just queries the underlying layer once.
            if (_partCollection == null) 
            { 
                PackagePart[] parts = GetPartsCore();
 
                //making sure that we get a valid array
                Debug.Assert((parts != null),
                    "Subclass is expected to return an array [an empty one if there are no parts] as a result of GetPartsCore method call. ");
 
                PackUriHelper.ValidatedPartUri partUri;
 
                //We need this dictionary to detect any collisions that might be present in the 
                //list of parts that was given to us from the underlying physical layer, as more than one
                //partnames can be mapped to the same normalized part. 
                //Note: We cannot use the _partList member variable, as that gets updated incrementally and so its
                //not possible to find the collisions using that list.
                //PackUriHelper.ValidatedPartUri implements the IComparable interface.
                Dictionary seenPartUris = new Dictionary(parts.Length); 

                for (int i = 0; i < parts.Length; i++) 
                { 
                    partUri = (PackUriHelper.ValidatedPartUri)parts[i].Uri;
 
                    if (seenPartUris.ContainsKey(partUri))
                        throw new FileFormatException(SR.Get(SRID.BadPackageFormat));
                    else
                    { 
                        // Add the part to the list of URIs that we have already seen
                        seenPartUris.Add(partUri, parts[i]); 
 
                        if (!_partList.ContainsKey(partUri))
                        { 
                            // Add the part to the _partList if there is no prefix collision
                            AddIfNoPrefixCollisionDetected(partUri, parts[i]);
                        }
                    } 
                }
                _partCollection = new PackagePartCollection(_partList); 
            } 
            return _partCollection;
        } 

        #endregion PackagePart Methods

        #region IDisposable Methods 

        ///  
        /// Member of the IDisposable interface. This method will clean up all the resources. 
        /// It calls the Flush method to make sure that all the changes made get persisted.
        /// Note - subclasses should only override Dispose(bool) if they have resources to release. 
        /// See the Design Guidelines for the Dispose() pattern.
        /// 
        void IDisposable.Dispose()
        { 
            if (!_disposed)
            { 
                try 
                {
                    // put our house in order before involving the subclass 

                    // close core properties
                    // This method will write out the core properties to the stream
                    // In non-streaming mode - These will get flushed to the disk as a part of the DoFlush operation 
                    if (_packageProperties != null)
                        _packageProperties.Close(); 
 
                    // flush relationships
                    if (InStreamingCreation) 
                        ClosePackageRelationships();
                    else
                        FlushRelationships();
 
                    //Write out the Relationship XML for the parts
                    //These streams will get flushed in the DoClose operation. 
                    DoOperationOnEachPart(DoCloseRelationshipsXml); 

                    // Close all the parts that are currently open 
                    DoOperationOnEachPart(DoClose);

                    // start the dispose chain
                    Dispose(true); 
                }
                finally 
                { 
                    // do this no matter what (handles case of poorly behaving subclass that doesn't call back into Dispose(bool)
                    _disposed = true; 
                }

                //Since all the resources we care about are freed at this point.
                GC.SuppressFinalize(this); 
            }
        } 
 
        #endregion IDisposable Methods
 
        #region Other Methods

        /// 
        /// Closes the package and all the underlying parts and relationships. 
        /// Calls the Dispose Method, since they have the same semantics
        ///  
        public void Close() 
        {
            ((IDisposable)this).Dispose(); 
        }

        /// 
        /// Flushes the contents of the parts and the relationships to the package. 
        /// This method will call the FlushCore method which will do the actual flushing of contents.
        ///  
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified
        public void Flush() 
        {
            ThrowIfObjectDisposed();
            ThrowIfReadOnly();
 
            // Flush core properties (in streaming production, has to be done before parts get flushed).
            // Write core properties (in streaming production, has to be done before parts get flushed). 
            // This call will write out the xml for the core properties to the stream 
            // In non-streaming mode - These properties will get flushed to disk as a part of the DoFlush operation
            if (_packageProperties != null) 
                _packageProperties.Flush();

            // Write package relationships XML to the relationship part stream.
            // These will get flushed to disk as a part of the DoFlush operation 
            if (InStreamingCreation)
                FlushPackageRelationships(); // Create a piece. 
            else 
                FlushRelationships(); // Flush into .rels part.
 
            //Write out the Relationship XML for the parts
            //These streams will get flushed in the DoFlush operation.
            DoOperationOnEachPart(DoWriteRelationshipsXml);
 
            // Flush all the parts that are currently open.
            // This will flush part relationships. 
            DoOperationOnEachPart(DoFlush); 

            FlushCore(); 
        }

        #endregion Other Methods
 
        #region PackageRelationship Methods
 
        ///  
        /// Creates a relationship at the Package level with the Target PackagePart specified as the Uri
        ///  
        /// Target's URI
        /// Enumeration indicating the base uri for the target uri
        /// PackageRelationship type, having uri like syntax that is used to
        /// uniquely identify the role of the relationship 
        /// 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified 
        /// If parameter "targetUri" is null
        /// If parameter "relationshipType" is null 
        /// If parameter "targetMode" enumeration does not have a valid value
        /// If TargetMode is TargetMode.Internal and the targetUri is an absolute Uri 
        /// If relationship is being targeted to a relationship part
        public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType) 
        {
            return CreateRelationship(targetUri, targetMode, relationshipType, null); 
        } 

        ///  
        /// Creates a relationship at the Package level with the Target PackagePart specified as the Uri
        /// 
        /// Target's URI
        /// Enumeration indicating the base uri for the target uri 
        /// PackageRelationship type, having uri like syntax that is used to
        /// uniquely identify the role of the relationship 
        /// String that conforms to the xsd:ID datatype. Unique across the source's 
        /// relationships. Null is OK (ID will be generated). An empty string is an invalid XML ID.
        ///  
        /// If this Package object has been disposed
        /// If the package is readonly, it cannot be modified
        /// If parameter "targetUri" is null
        /// If parameter "relationshipType" is null 
        /// If parameter "targetMode" enumeration does not have a valid value
        /// If TargetMode is TargetMode.Internal and the targetUri is an absolute Uri  
        /// If relationship is being targeted to a relationship part 
        /// If parameter "id" is not a valid Xsd Id
        /// If an id is provided in the method, and its not unique 
        public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType, String id)
        {
            ThrowIfObjectDisposed();
            ThrowIfReadOnly(); 
            EnsureRelationships();
            //All parameter validation is done in the following call 
            return _relationships.Add(targetUri, targetMode, relationshipType, id); 
        }
 
        /// 
        /// Deletes a relationship from the Package. This is done based on the
        /// relationship's ID. The target PackagePart is not affected by this operation.
        ///  
        /// The ID of the relationship to delete. An invalid ID will not
        /// throw an exception, but nothing will be deleted. 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified
        /// If parameter "id" is null 
        /// If parameter "id" is not a valid Xsd Id
        public void DeleteRelationship(String id)
        {
            ThrowIfObjectDisposed(); 
            ThrowIfReadOnly();
            ThrowIfInStreamingCreation("DeleteRelationship"); 
 
            if (id == null)
                throw new ArgumentNullException("id"); 

            InternalRelationshipCollection.ThrowIfInvalidXsdId(id);

            EnsureRelationships(); 
            _relationships.Delete(id);
        } 
 
        /// 
        /// Returns a collection of all the Relationships that are 
        /// owned by the package
        /// 
        /// 
        /// If this Package object has been disposed 
        /// If the package is write only, no information can be retrieved from it
        public PackageRelationshipCollection GetRelationships() 
        { 
            //All the validations for dispose and file access are done in the
            //GetRelationshipsHelper method. 
            return GetRelationshipsHelper(null);
        }

        ///  
        /// Returns a collection of filtered Relationships that are
        /// owned by the package 
        /// The filter string is compared with the type of the relationships 
        /// in a case sensitive and culture ignorant manner.
        ///  
        /// 
        /// If this Package object has been disposed
        /// If the package is write only, no information can be retrieved from it
        /// If parameter "relationshipType" is null 
        /// If parameter "relationshipType" is an empty string
        public PackageRelationshipCollection GetRelationshipsByType(string relationshipType) 
        { 
            //These checks are made in the GetRelationshipsHelper as well, but we make them
            //here as we need to perform parameter validation 
            ThrowIfObjectDisposed();
            ThrowIfWriteOnly();

            if (relationshipType == null) 
                throw new ArgumentNullException("relationshipType");
 
            InternalRelationshipCollection.ThrowIfInvalidRelationshipType(relationshipType); 

            return GetRelationshipsHelper(relationshipType); 
        }

        /// 
        /// Retrieve a relationship per ID. 
        /// 
        /// The relationship ID. 
        /// The relationship with ID 'id' or throw an exception if not found. 
        /// If this Package object has been disposed
        /// If the package is write only, no information can be retrieved from it 
        /// If parameter "id" is null
        /// If parameter "id" is not a valid Xsd Id
        /// If the requested relationship does not exist in the Package
        public PackageRelationship GetRelationship(string id) 
        {
            //All the validations for dispose and file access are done in the 
            //GetRelationshipHelper method. 

            PackageRelationship returnedRelationship = GetRelationshipHelper(id); 
            if (returnedRelationship == null)
                throw new InvalidOperationException(SR.Get(SRID.PackageRelationshipDoesNotExist));
            else
                return returnedRelationship; 
        }
 
        ///  
        /// Returns whether there is a relationship with the specified ID.
        ///  
        /// The relationship ID.
        /// true iff a relationship with ID 'id' is defined on this source.
        /// If this Package object has been disposed
        /// If the package is write only, no information can be retrieved from it 
        /// If parameter "id" is null
        /// If parameter "id" is not a valid Xsd Id 
        public bool RelationshipExists(string id) 
        {
            //All the validations for dispose and file access are done in the 
            //GetRelationshipHelper method.

            return (GetRelationshipHelper(id) != null);
        } 

        #endregion PackageRelationship Methods 
 
        #endregion Public Methods
 
        #region Protected Abstract Methods

        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This method will actually add a new part to the package. An empty part should be
        /// created as a result of this call. 
        ///  
        /// 
        ///  
        /// 
        /// 
        protected abstract PackagePart CreatePartCore(Uri partUri,
                                                            string contentType, 
                                                            CompressionOption compressionOption);
 
        ///  
        /// This method is for custom implementation corresponding to the underlying file format.
        /// This method will actually return the part after reading the actual physical bits. 
        /// If the PackagePart does not exists in the underlying package then this method should return a null.
        /// This method must not throw an exception if a part does not exist.
        /// 
        ///  
        /// 
        protected abstract PackagePart GetPartCore(Uri partUri); 
 
        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This method will actually delete the part from the underlying package.
        /// This method should not throw if the specified part does not exist.
        /// This is in conformance with the FileInfo.Delete call.
        ///  
        /// 
        protected abstract void DeletePartCore(Uri partUri); 
 
        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This is the method that knows how to get the actual parts. If there are no parts,
        /// this method should return an empty array.
        /// 
        ///  
        protected abstract PackagePart[] GetPartsCore();
 
        ///  
        /// This method is for custom implementation corresponding to the underlying file format.
        /// This method should be used to dispose the resources that are specific to the file format. 
        /// Also everything should be flushed to the disc before closing the package.
        /// 
        /// Subclasses that manage non-memory resources should override this method and free these resources.
        /// Any override should be careful to always call base.Dispose(disposing) to ensure orderly cleanup. 
        protected virtual void Dispose(bool disposing)
        { 
            if (!_disposed && disposing) 
            {
                _partList.Clear(); 

                if (_packageProperties != null)
                {
                    _packageProperties.Dispose(); 
                    _packageProperties = null;
                } 
 
                //release objects
                _partList = null; 
                _partCollection = null;
                _relationships = null;

                _disposed = true; 
            }
        } 
 
        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This method flushes the contents of the package to the disc.
        /// 
        protected abstract void FlushCore();
 
        #endregion Protected Abstract Methods
 
        //----------------------------------------------------- 
        //
        //  Internal Constructors 
        //
        //------------------------------------------------------
        // None
        //----------------------------------------------------- 

        //----------------------------------------------------- 
        // 
        //   Internal Properties
        // 
        //-----------------------------------------------------

        #region Internal Properties
 
        /// 
        /// true iff the package was opened for streaming. 
        ///  
        internal bool InStreamingCreation
        { 
            get
            {
                return _inStreamingCreation;
            } 
        }
 
        #endregion Internal Properties 

        //------------------------------------------------------ 
        //
        //  Internal Methods
        //
        //----------------------------------------------------- 

        #region Internal Methods 
        // Some operations are not supported while producing a package in streaming mode. 
        internal void ThrowIfInStreamingCreation(string methodName)
        { 
            if (_inStreamingCreation)
                throw new IOException(SR.Get(SRID.OperationIsNotSupportedInStreamingProduction, methodName));
        }
 
        // Some operations are supported only while producing a package in streaming mode.
        internal void ThrowIfNotInStreamingCreation(string methodName) 
        { 
            if (!InStreamingCreation)
                throw new IOException(SR.Get(SRID.MethodAvailableOnlyInStreamingCreation, methodName)); 
        }

        //If the container is readonly then we cannot add/delete to it
        internal void ThrowIfReadOnly() 
        {
            if (_openFileAccess == FileAccess.Read) 
                throw new IOException(SR.Get(SRID.CannotModifyReadOnlyContainer)); 
        }
 
        // If the container is writeonly, parts cannot be retrieved from it
        internal void ThrowIfWriteOnly()
        {
            if (_openFileAccess == FileAccess.Write) 
                throw new IOException(SR.Get(SRID.CannotRetrievePartsOfWriteOnlyContainer));
        } 
 
        // return true to continue
        internal delegate bool PartOperation(PackagePart p); 

        internal static void ThrowIfFileModeInvalid(FileMode mode)
        {
            //We do the enum check as suggested by the following condition for performance reasons. 
            if (mode < FileMode.CreateNew || mode > FileMode.Append)
                throw new ArgumentOutOfRangeException("mode"); 
        } 

        internal static void ThrowIfFileAccessInvalid(FileAccess access) 
        {
            //We do the enum check as suggested by the following condition for performance reasons.
            if (access < FileAccess.Read || access > FileAccess.ReadWrite)
                throw new ArgumentOutOfRangeException("access"); 
        }
 
        internal static void ThrowIfCompressionOptionInvalid(CompressionOption compressionOption) 
        {
            //We do the enum check as suggested by the following condition for performance reasons. 
            if (compressionOption < CompressionOption.NotCompressed || compressionOption > CompressionOption.SuperFast)
                throw new ArgumentOutOfRangeException("compressionOption");
        }
 
        #region Write-time streaming API
 
        ///  
        /// This method gives the possibility of opening a package in streaming mode.
        /// When 'streaming' is true, the only allowed file modes are Create and CreateNew, 
        /// the only allowed file access is Write and the only allowed FileShare values are
        /// Null and Read.
        /// 
        /// Path to the package. 
        /// FileMode in which the package should be opened.
        /// FileAccess with which the package should be opened. 
        /// FileShare with which the package is opened. 
        /// Whether to allow the creation of part pieces while enforcing write-once access.
        /// Package 
        /// If path parameter is null
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        /// If FileMode enumeration [packageMode] does not have one of the valid values
        internal static Package Open( 
            string path,
            FileMode packageMode, 
            FileAccess packageAccess, 
            FileShare packageShare,
            bool streaming) 
        {
            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.StartEvent);

            if (path == null) 
                throw new ArgumentNullException("path");
 
            ThrowIfFileModeInvalid(packageMode); 
            ThrowIfFileAccessInvalid(packageAccess);
 
            ValidateStreamingAccess(packageMode, packageAccess, packageShare, streaming);

            //Note: FileShare enum is not being verfied at this stage, as we do not interpret the flag in this
            //code at all and just pass it on to the next layer, where the necessary validation can be 
            //performed. Also, there is no meaningful way to check this parameter at this layer, as the
            //FileShare enumeration is a set of flags and flags/Bit-fields can be combined using a 
            //bitwise OR operation to create different values, and validity of these values is specific to 
            //the actual physical implementation.
 
            //Verify if this is valid for filenames
            FileInfo packageFileInfo = new FileInfo(path);

            Package package = new ZipPackage(packageFileInfo.FullName, packageMode, packageAccess, packageShare, streaming); 

            if (!package._inStreamingCreation) // No read operation in streaming production. 
            { 
                //We need to get all the parts if any exists from the underlying file
                //so that we have the names in the Normalized form in our in-memory 
                //data structures.
                //Note: If ever this call is removed, each individual call to GetPartCore,
                //may result in undefined behavior as the underlying ZipArchive, maintains the
                //files list as being case-sensitive. 
                if (package.FileOpenAccess == FileAccess.ReadWrite || package.FileOpenAccess == FileAccess.Read)
                    package.GetParts(); 
            } 

            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.EndEvent); 

            return package;
        }
 
        /// 
        /// This method gives the possibility of opening a package in streaming mode. 
        /// When 'streaming' is true, the only allowed file modes are Create and CreateNew, 
        /// and the only allowed file access is Write.
        ///  
        /// Stream on which the package is created
        /// FileMode in which the package is to be opened
        /// FileAccess on the package that is opened
        /// Whether to allow the creation of part pieces while enforcing write-once access. 
        /// Package
        /// If stream parameter is null 
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        /// If package to be created should have readwrite/read access and underlying stream is write only 
        /// If package to be created should have readwrite/write access and underlying stream is read only
        [FriendAccessAllowed]
        internal static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess, bool streaming)
        { 
            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.StartEvent);
 
            if (stream == null) 
                throw new ArgumentNullException("stream");
 
            ValidateStreamingAccess(packageMode, packageAccess, null /* no FileShare info */, streaming);

            //FileMode and FileAccess Enums are validated in the following call
            Stream ensuredStream = ValidateModeAndAccess(stream, packageMode, packageAccess); 

            Package package; 
 
            // Today the Open(Stream) method is purely used for streams of Zip file format as
            // that is the default underlying file format mapper implemented. 
            package = new ZipPackage(ensuredStream, packageMode, packageAccess, streaming);

            if (!package._inStreamingCreation) // No read operation in streaming production.
            { 
                //We need to get all the parts if any exists from the underlying file
                //so that we have the names in the Normalized form in our in-memory 
                //data structures. 
                //Note: If ever this call is removed, each individual call to GetPartCore,
                //may result in undefined behavior as the underlying ZipArchive, maintains the 
                //files list as being case-sensitive.
                if(package.FileOpenAccess == FileAccess.ReadWrite || package.FileOpenAccess == FileAccess.Read)
                    package.GetParts();
            } 

            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.EndEvent); 
 
            return package;
        } 

        /// 
        /// Write a nonterminal piece for /_rels/.rels.
        ///  
        internal void FlushPackageRelationships()
        { 
            ThrowIfNotInStreamingCreation("FlushPackageRelationships"); 

            if (_relationships == null) 
                return; // nothing to flush

            _relationships.Flush();
        } 

        ///  
        /// Write a terminal piece for /_rels/.rels. 
        /// 
        internal void ClosePackageRelationships() 
        {
            ThrowIfNotInStreamingCreation("ClosePackageRelationships");

            if (_relationships == null) 
                return; // no relationship part
 
            _relationships.CloseInStreamingCreationMode(); 
        }
 
        #endregion Write-time streaming API

        #endregion Internal Methods
 
        //------------------------------------------------------
        // 
        //  Internal Events 
        //
        //------------------------------------------------------ 
        // None
        //-----------------------------------------------------
        //
        //  Private Methods 
        //
        //------------------------------------------------------ 
 
        #region Private Methods
 

        // This method is only when new part is added to the Package object.
        // This method will throw an exception if the name of the part being added is a
        // prefix of the name of an existing part. 
        // Example - Say the following parts exist in the package
        // 1. /abc.xaml 
        // 2. /xyz/pqr/a.jpg 
        // As an example - Adding any of the following parts will throw an exception -
        // 1. /abc.xaml/new.xaml 
        // 2. /xyz/pqr
        private void AddIfNoPrefixCollisionDetected(PackUriHelper.ValidatedPartUri partUri, PackagePart part)
        {
 
            //Add the Normalized Uri to the sorted _partList tentatively to see where it will get inserted
            _partList.Add(partUri, part); 
 
            //Get the index of the entry at which this part was added
            int index = _partList.IndexOfKey(partUri); 

            Invariant.Assert(index >= 0, "Given uri must be present in the dictionary");

            string normalizedPartName = partUri.NormalizedPartUriString; 
            string precedingPartName = null;
            string followingPartName = null; 
 
            if (index > 0)
            { 
                precedingPartName = _partList.Keys[index - 1].NormalizedPartUriString;
            }

            if (index < _partList.Count - 1) 
            {
                followingPartName = _partList.Keys[index + 1].NormalizedPartUriString; 
            } 

            if ((precedingPartName != null 
                && normalizedPartName.StartsWith(precedingPartName, StringComparison.Ordinal)
                && normalizedPartName.Length > precedingPartName.Length
                && normalizedPartName[precedingPartName.Length] == PackUriHelper.ForwardSlashChar) ||
                (followingPartName != null 
                && followingPartName.StartsWith(normalizedPartName, StringComparison.Ordinal)
                && followingPartName.Length > normalizedPartName.Length 
                && followingPartName[normalizedPartName.Length] == PackUriHelper.ForwardSlashChar)) 
            {
                //Removing the invalid entry from the _partList. 
                _partList.Remove(partUri);

                throw new InvalidOperationException(SR.Get(SRID.PartNamePrefixExists));
            } 
        }
 
        // Test consistency of file opening parameters with the value of 'streaming', and record 
        // whether the streaming mode is for consumption or production.
        // 
        private static void ValidateStreamingAccess(
            FileMode packageMode,
            FileAccess packageAccess,
            Nullable packageShare, 
            bool streaming)
        { 
            if (streaming) 
            {
                if (packageMode == FileMode.Create || packageMode == FileMode.CreateNew) 
                {
                    if (packageAccess != FileAccess.Write)
                        throw new IOException(SR.Get(SRID.StreamingPackageProductionImpliesWriteOnlyAccess));
                    if (   packageShare != null 
                        && packageShare != FileShare.Read && packageShare != FileShare.None)
                        throw new IOException(SR.Get(SRID.StreamingPackageProductionRequiresSingleWriter)); 
                } 
                else
                { 
                    // Blanket exception pending design of streaming consumption.
                    throw new NotSupportedException(SR.Get(SRID.StreamingModeNotSupportedForConsumption));
                }
            } 
        }
 
 
        //Checking if the mode and access parameters are compatible with the provided stream.
        private static Stream ValidateModeAndAccess(Stream s, FileMode mode, FileAccess access) 
        {
            ThrowIfFileModeInvalid(mode);
            ThrowIfFileAccessInvalid(access);
 
            //asking for more permissions than the underlying stream.
            // Stream cannot write, but package to be created should have write access 
            if (!s.CanWrite && (access == FileAccess.ReadWrite || access == FileAccess.Write)) 
                throw new IOException(SR.Get(SRID.IncompatibleModeOrAccess));
 
            //asking for more permissions than the underlying stream.
            // Stream cannot read, but the package to be created should have read access
            if (!s.CanRead && (access == FileAccess.ReadWrite || access == FileAccess.Read))
                throw new IOException(SR.Get(SRID.IncompatibleModeOrAccess)); 

            //asking for less restricted access to the underlying stream 
            //stream is ReadWrite but the package is either readonly, or writeonly 
            if ((s.CanRead && s.CanWrite) && (access == FileAccess.Read || access == FileAccess.Write))
            { 
                return new RestrictedStream(s, access);
            }
            else
                return s; 
        }
 
        //Throw if the object is in a disposed state 
        private void ThrowIfObjectDisposed()
        { 
            if (_disposed == true)
                throw new ObjectDisposedException(null, SR.Get(SRID.ObjectDisposed));
        }
 
        private void EnsureRelationships()
        { 
            // once per package 
            if (_relationships == null)
            { 
                _relationships = new InternalRelationshipCollection(this);
            }
        }
 
        //Delete All Package-level Relationships
        private void ClearRelationships() 
        { 
            if(_relationships!=null)
                _relationships.Clear(); 
        }

        //Flush the relationships at package level
        private void FlushRelationships() 
        {
            // flush relationships 
            if (_relationships != null && _openFileAccess != FileAccess.Read) 
            {
                _relationships.Flush(); 
            }
        }

        //We do the close or the flush operation per part 
        private void DoOperationOnEachPart(PartOperation operation)
        { 
            //foreach (PackagePart p in _partList.Values) 
            //    p.Close();  - this throws
            // Make local copy of part names to prevent exception during enumeration when 
            // a new relationship part gets created (flushing relationships can cause part creation).
            // This code throws in such a case:
            //
            //            foreach (PackagePart p in _partList.Values) 
            //                p.Flush();
            // 
            if (_partList.Count > 0) 
            {
                int partCount = 0; 
                PackUriHelper.ValidatedPartUri[] partKeys = new PackUriHelper.ValidatedPartUri[_partList.Keys.Count];

                foreach (PackUriHelper.ValidatedPartUri uri in _partList.Keys)
                { 
                    partKeys[partCount++] = uri;
                } 
 
                // this throws an exception in certain cases (when a part has been deleted)
                // 
                //     _partList.Keys.CopyTo(keys, 0);

                for (int i = 0; i < _partList.Keys.Count; i++)
                { 
                    // Some of these may disappear during above close because the list contains "relationship parts"
                    // and these are removed if their parts' relationship collection is empty 
                    // This fails: 
                    //                _partList[keys[i]].Flush();
 
                    PackagePart p;
                    if (_partList.TryGetValue(partKeys[i], out p))
                    {
                        if (!operation(p)) 
                            break;
                    } 
                } 
            }
        } 

        //We needed to separate the rels parts from the other parts
        //because if a rels part for a part occured earlier than the part itself in the array,
        //the rels part would be closed and then when close the part and try to persist the relationships 
        //for the particular part, it would throw an exception
        private bool DoClose(PackagePart p) 
        { 
            if (!p.IsClosed)
            { 
                if (PackUriHelper.IsRelationshipPartUri(p.Uri) && PackUriHelper.ComparePartUri(p.Uri, PackageRelationship.ContainerRelationshipPartName) != 0)
                {
                    //First we close the source part.
                    //Note - we can safely do this as DoClose is being called on all parts. So ultimately we will end up 
                    //closing the source part as well.
                    //This logic only takes care of out of order parts. 
                    PackUriHelper.ValidatedPartUri owningPartUri = 
                        (PackUriHelper.ValidatedPartUri)PackUriHelper.GetSourcePartUriFromRelationshipPartUri(p.Uri);
                    //If the source part for this rels part exists then we close it. 
                    PackagePart sourcePart;
                    if (_partList.TryGetValue(owningPartUri, out sourcePart))
                        sourcePart.Close();
                } 
                p.Close();
            } 
            return true; 
        }
 
        private bool DoFlush(PackagePart p)
        {
            p.Flush();
            return true; 
        }
 
        private bool DoWriteRelationshipsXml(PackagePart p) 
        {
            if (!p.IsRelationshipPart) 
            {
                p.FlushRelationships();
            }
            return true; 
        }
 
        private bool DoCloseRelationshipsXml(PackagePart p) 
        {
            if (!p.IsRelationshipPart) 
            {
                p.CloseRelationships();
            }
            return true; 
        }
 
        private PackagePart GetPartHelper(Uri partUri) 
        {
            ThrowIfObjectDisposed(); 
            ThrowIfWriteOnly();

            if (partUri == null)
                throw new ArgumentNullException("partUri"); 

            PackUriHelper.ValidatedPartUri validatePartUri = PackUriHelper.ValidatePartUri(partUri); 
 
            if (_partList.ContainsKey(validatePartUri))
                return _partList[validatePartUri]; 
            else
            {
                //Ideally we should decide whether we should query the underlying layer for the part based on the
                //FileShare enum. But since we do not have that information, currently the design is to always 
                //ask the underlying layer, this allows for incremental access to the package.
                //Note: 
                //Currently this incremental behavior for GetPart is not consistent with the GetParts method 
                //which just queries the underlying layer once.
                PackagePart returnedPart = GetPartCore(validatePartUri); 

                if (returnedPart != null)
                {
                    // Add the part to the _partList if there is no prefix collision 
                    AddIfNoPrefixCollisionDetected(validatePartUri, returnedPart);
                } 
 
                return returnedPart;
            } 
        }

        /// 
        /// Retrieve a relationship per ID. 
        /// 
        /// The relationship ID. 
        /// The relationship with ID 'id' or null if not found. 
        private PackageRelationship GetRelationshipHelper(string id)
        { 
            ThrowIfObjectDisposed();
            ThrowIfWriteOnly();

            if (id == null) 
                throw new ArgumentNullException("id");
 
            InternalRelationshipCollection.ThrowIfInvalidXsdId(id); 

            EnsureRelationships(); 
            return _relationships.GetRelationship(id);

        }
 
        /// 
        /// Returns a collection of all the Relationships that are 
        /// owned by the package based on the filter string. 
        /// 
        ///  
        private PackageRelationshipCollection GetRelationshipsHelper(string filterString)
        {
            ThrowIfObjectDisposed();
            ThrowIfWriteOnly(); 
            EnsureRelationships();
 
            //Internally null is used to indicate that no filter string was specified and 
            //and all the relationships should be returned.
            return new PackageRelationshipCollection(_relationships, filterString); 
        }

        #endregion Private Methods
 
        //-----------------------------------------------------
        // 
        //  Private Fields 
        //
        //----------------------------------------------------- 

        #region Private Members

        // Default values for the Package.Open method overloads 
        private static readonly FileMode        _defaultFileMode            = FileMode.OpenOrCreate;
        private static readonly FileAccess      _defaultFileAccess          = FileAccess.ReadWrite; 
        private static readonly FileShare       _defaultFileShare           = FileShare.None; 

        private static readonly FileMode        _defaultStreamMode          = FileMode.Open; 
        private static readonly FileAccess      _defaultStreamAccess        = FileAccess.Read;

        private bool                            _inStreamingCreation;       // false by default
        private FileAccess                      _openFileAccess; 
        private bool                            _disposed;
        private SortedList _partList; 
        private PackagePartCollection           _partCollection; 
        private InternalRelationshipCollection  _relationships;
        private PartBasedPackageProperties      _packageProperties; 


        #endregion Private Members
 
        //-----------------------------------------------------
        // 
        //  Private Class 
        //
        //------------------------------------------------------ 

        #region Private Class: Restricted Stream

        ///  
        /// This implementation of Stream class is a simple wrapper to restrict the
        /// read or write access to the underlying stream, as per user request. 
        /// No validation for the stream method calls is done in this wrapper, the calls 
        /// are passed onto the underlying stream object, which should do the
        /// validation as required. 
        /// 
        private sealed class RestrictedStream : Stream
        {
            #region Constructor 

            ///  
            /// Constructor 
            /// 
            ///  
            /// 
            internal RestrictedStream(Stream stream, FileAccess access)
            {
                if (stream == null) 
                    throw new ArgumentNullException("stream");
 
                //Verifying if the FileAccess enum is valid 
                //This constructor will never be called with FileAccess.ReadWrite
                Debug.Assert(access==FileAccess.Read || access == FileAccess.Write, 
                    "The constructor of this private class is expected to be called with FileAccess.Read or FileAccess.Write");

                _stream = stream;
 
                if (access == FileAccess.Read)
                { 
                    _canRead = true; 
                    _canWrite = false;
                } 
                else
                if (access == FileAccess.Write)
                {
                    _canRead = false; 
                    _canWrite = true;
                } 
            } 

            #endregion Constructor 

            #region Properties

            ///  
            /// Member of the abstract Stream class
            ///  
            /// Bool, true if the stream can be read from, else false 
            public override bool CanRead
            { 
                get
                {
                    if(!_disposed)
                        return _canRead; 
                    else
                        return false; 
                } 
            }
 
            /// 
            /// Member of the abstract Stream class
            /// 
            /// Bool, true if the stream can be seeked, else false 
            public override bool CanSeek
            { 
                get 
                {
                    if(!_disposed) 
                        return _stream.CanSeek;
                    else
                        return false;
                } 
            }
 
            ///  
            /// Member of the abstract Stream class
            ///  
            /// Bool, true if the stream can be written to, else false
            public override bool CanWrite
            {
                get 
                {
                    if(!_disposed) 
                        return _canWrite; 
                    else
                        return false; 
                }
            }

            ///  
            /// Member of the abstract Stream class
            ///  
            /// Long value indicating the length of the stream 
            public override long Length
            { 
                get
                {
                    ThrowIfStreamDisposed();
                    return _stream.Length; 
                }
            } 
 
            /// 
            /// Member of the abstract Stream class 
            /// 
            /// Long value indicating the current position in the stream
            public override long Position
            { 
                get
                { 
                    ThrowIfStreamDisposed(); 
                    return _stream.Position;
                } 
                set
                {
                    ThrowIfStreamDisposed();
                    _stream.Position = value; 
                }
            } 
 
            #endregion Properties
 
            #region Methods

            /// 
            /// Member of the abstract Stream class 
            /// 
            /// only zero is supported 
            /// only SeekOrigin.Begin is supported 
            /// zero
            public override long Seek(long offset, SeekOrigin origin) 
            {
                ThrowIfStreamDisposed();
                return _stream.Seek(offset, origin);
            } 

            ///  
            /// Member of the abstract Stream class 
            /// 
            ///  
            public override void SetLength(long newLength)
            {
                ThrowIfStreamDisposed();
                if (_canWrite) 
                    _stream.SetLength(newLength);
                else 
                    throw new NotSupportedException(SR.Get(SRID.ReadOnlyStream)); 
            }
 
            /// 
            /// Member of the abstract Stream class
            /// 
            ///  
            /// 
            ///  
            ///  
            /// 
            /// The standard Stream.Read semantics, and in particular the restoration of the current 
            /// position in case of an exception, is implemented by the underlying stream.
            /// 
            public override int Read(byte[] buffer, int offset, int count)
            { 
                ThrowIfStreamDisposed();
                if (_canRead) 
                    return _stream.Read(buffer, offset, count); 
                else
                    throw new NotSupportedException(SR.Get(SRID.WriteOnlyStream)); 
            }

            /// 
            /// Member of the abstract Stream class 
            /// 
            ///  
            ///  
            /// 
            public override void Write(byte[] buf, int offset, int count) 
            {
                ThrowIfStreamDisposed();
                if (_canWrite)
                    _stream.Write(buf, offset, count); 
                else
                    throw new NotSupportedException(SR.Get(SRID.ReadOnlyStream)); 
            } 

            ///  
            /// Member of the abstract Stream class
            /// 
            public override void Flush()
            { 
                ThrowIfStreamDisposed();
                if (_canWrite) 
                    _stream.Flush(); 
            }
            #endregion Methods 

            //-----------------------------------------------------
            //
            //  Protected Methods 
            //
            //------------------------------------------------------ 
            ///  
            /// Dispose(bool)
            ///  
            /// 
            protected override void Dispose(bool disposing)
            {
                try 
                {
                    if (disposing) 
                    { 
                        if (!_disposed)
                        { 
                            _stream.Close();
                        }
                    }
                } 
                finally
                { 
                    _disposed = true; 
                    base.Dispose(disposing);
                } 
            }


            #region Private Methods 

            private void ThrowIfStreamDisposed() 
            { 
                if (_disposed)
                    throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed)); 
            }

            #endregion Private Methods
 
            #region Private Variables
 
            private Stream _stream; 
            private bool _canRead;
            private bool _canWrite; 
            private bool _disposed;

            #endregion Private Variables
 
        }
 
        #endregion Private Class: Restricted Stream 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is a base abstract class for Package. This is a part of the 
//  Packaging Layer.
// 
// History:
//  01/03/2004: SarjanaS: Initial creation. [Stubs only]
//  03/01/2004: SarjanaS: Implemented the functionality for all the members.
//  03/17/2004: BruceMac: Initial implementation or PackageRelationship methods 
//
//----------------------------------------------------------------------------- 
 
using System;
using System.IO; 
using System.Collections;
using System.Collections.Generic;   // For SortedList<>
using System.Windows;               // For Exception strings - SRID
using System.Diagnostics;           // For Debug.Assert 
using MS.Internal.IO.Packaging;
using MS.Internal.WindowsBase;      // for [FriendAccessAllowed] 
 
using MS.Internal;                  // For Invariant.Assert
using MS.Utility; 

namespace System.IO.Packaging
{
    ///  
    /// Abstract Base class for the Package.
    /// This is a part of the Packaging Layer APIs 
    ///  
    public abstract class Package : IDisposable
    { 

        //-----------------------------------------------------
        //
        //  Public Constructors 
        //
        //----------------------------------------------------- 
 
        #region Protected Constructor
 
        /// 
        /// Protected constructor for the abstract Base class.
        /// This is the current contract between the subclass and the base class
        /// If we decide some registration mechanism then this might change 
        /// 
        ///  
        /// If FileAccess enumeration does not have one of the valid values 
        protected Package(FileAccess openFileAccess)
            : this(openFileAccess, false /* not streaming by default */) 
        {
        }

        ///  
        /// Protected constructor for the abstract Base class.
        /// This is the current contract between the subclass and the base class 
        /// If we decide some registration mechanism then this might change 
        /// 
        ///  
        /// Whether the package is being opened for streaming.
        /// If FileAccess enumeration does not have one of the valid values
        protected Package(FileAccess openFileAccess, bool streaming)
        { 
            ThrowIfFileAccessInvalid(openFileAccess);
 
            _openFileAccess      = openFileAccess; 

            //PackUriHelper.ValidatedPartUri implements the IComparable interface. 
            _partList            = new SortedList(); // initial default is zero
            _partCollection      = null;
            _disposed            = false;
            _inStreamingCreation = (openFileAccess == FileAccess.Write && streaming); 
        }
 
        #endregion Protected Constructor 

        //------------------------------------------------------ 
        //
        //  Public Properties
        //
        //----------------------------------------------------- 

        #region Public Properties 
 
        /// 
        /// Gets the FileAccess with which the package was opened. This is a read only property. 
        /// This property gets set when the package is opened.
        /// 
        /// FileAccess
        /// If this Package object has been disposed 
        public FileAccess FileOpenAccess
        { 
            get 
            {
                ThrowIfObjectDisposed(); 
                return _openFileAccess;
            }
        }
 
        /// 
        /// The package properties are a subset of the standard OLE property sets 
        /// SummaryInformation and DocumentSummaryInformation, and include such properties 
        /// as Title and Subject.
        ///  
        /// If this Package object has been disposed
        public PackageProperties PackageProperties
        {
            get 
            {
                ThrowIfObjectDisposed(); 
 
                if (_packageProperties == null)
                    _packageProperties = new PartBasedPackageProperties(this); 
                return _packageProperties;
            }
        }
 
        #endregion Public Properties
 
        //------------------------------------------------------ 
        //
        //  Public Methods 
        //
        //------------------------------------------------------

        #region Public Methods 

        #region OpenOnFileMethods 
 
        /// 
        /// Opens a package at the specified Path. This method calls the overload which accepts all the parameters 
        /// with the following defaults -
        /// FileMode - FileMode.OpenOrCreate,
        /// FileAccess - FileAccess.ReadWrite
        /// FileShare  - FileShare.None 
        /// 
        /// Path to the package 
        /// Package 
        /// If path parameter is null
        public static Package Open(string path) 
        {
            return Open(path, _defaultFileMode, _defaultFileAccess, _defaultFileShare);
        }
 
        /// 
        /// Opens a package at the specified Path in the given mode. This method calls the overload which 
        /// accepts all the parameters with the following defaults - 
        /// FileAccess - FileAccess.ReadWrite
        /// FileShare  - FileShare.None 
        /// 
        /// Path to the package
        /// FileMode in which the package should be opened
        /// Package 
        /// If path parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        public static Package Open(string path, FileMode packageMode) 
        {
            return Open(path, packageMode, _defaultFileAccess, _defaultFileShare); 
        }

        /// 
        /// Opens a package at the specified Path in the given mode with the specified access. This method calls 
        /// the overload which accepts all the parameters with the following defaults -
        /// FileShare  - FileShare.None 
        ///  
        /// Path to the package
        /// FileMode in which the package should be opened 
        /// FileAccess with which the package should be opened
        /// Package
        /// If path parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        public static Package Open(string path, FileMode packageMode, FileAccess packageAccess) 
        { 
            return Open(path, packageMode, packageAccess, _defaultFileShare);
        } 

        /// 
        /// Opens the package with the specified parameters.
        /// 
        /// Note:-
        /// Since currently there is no plan to implement a generic registration mechanism, this method 
        /// has some hard coded knowledge about the sub classes. This might change later if we come up 
        /// with a registration mechanism.
        /// There is no caching mechanism in case the FileShare is specified to ReadWrite/Write. 
        /// We do not have any refresh mechanism, to make sure that the cached parts reflect the actual parts,
        /// in the case where there might be more than one processes writing to the same underlying package.
        /// 
        /// Path to the package 
        /// FileMode in which the package should be opened
        /// FileAccess with which the package should be opened 
        /// FileShare with which the package is opened. 
        /// Package
        /// InvalidArgumentException - If the combination of the FileMode, 
        /// FileAccess and FileShare parameters is not meaningful.
        /// If path parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values 
        public static Package Open(string path, FileMode packageMode, FileAccess packageAccess, FileShare packageShare)
        { 
            return Open(path, packageMode, packageAccess, packageShare, false /* not in streaming mode */); 
        }
 
        #endregion OpenOnFileMethods

        #region OpenOnStreamMethods
 
        /// 
        /// Open a package on this stream. This method calls the overload which accepts all the parameters 
        /// with the following defaults - 
        /// FileMode - FileMode.Open
        /// FileAccess - FileAccess.Read 
        /// 
        /// Stream on which the package is to be opened
        /// Package
        /// If stream parameter is null 
        /// If package to be created should have readwrite/read access and underlying stream is write only
        /// If package to be created should have readwrite/write access and underlying stream is read only 
        public static Package Open(Stream stream) 
        {
            return Open(stream, _defaultStreamMode, _defaultStreamAccess); 
        }

        /// 
        /// Open a package on this stream. This method calls the overload which accepts all the parameters 
        /// with the following defaults -
        /// FileAccess - FileAccess.ReadWrite 
        ///  
        /// Stream on which the package is to be opened
        /// FileMode in which the package should be opened. 
        /// Package
        /// If stream parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values
        /// If package to be created should have readwrite/read access and underlying stream is write only 
        /// If package to be created should have readwrite/write access and underlying stream is read only
        public static Package Open(Stream stream, FileMode packageMode) 
        { 
            //If the user is providing a FileMode, in all the modes, except FileMode.Open,
            //its most likely that the user intends to write to the stream. 
            return Open(stream, packageMode, _defaultFileAccess);
        }

        ///  
        /// Opens a package on this stream. The package is opened in the specified mode and with the access
        /// specified. 
        ///  
        /// Stream on which the package is created
        /// FileMode in which the package is to be opened 
        /// FileAccess on the package that is opened
        /// Package
        /// If stream parameter is null
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        /// If package to be created should have readwrite/read access and underlying stream is write only 
        /// If package to be created should have readwrite/write access and underlying stream is read only 
        public static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess)
        { 
            return Open(stream, packageMode, packageAccess, false /* not in streaming mode */);
        }
        #endregion OpenOnStreamMethods
 
        #region PackagePart Methods
 
        ///  
        /// Creates a new part in the package. An empty stream corresponding to this part will be created in the
        /// package. If a part with the specified uri already exists then we throw an exception. 
        /// This methods will call the CreatePartCore method which will create the actual PackagePart in the package.
        /// 
        /// Uri of the PackagePart that is to be added
        /// ContentType of the stream to be added 
        /// 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified 
        /// If partUri parameter is null
        /// If contentType parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax
        /// If a PackagePart with the given partUri already exists in the Package
        public PackagePart CreatePart(Uri partUri, string contentType)
        { 
            return CreatePart(partUri, contentType, CompressionOption.NotCompressed);
        } 
 
        /// 
        /// Creates a new part in the package. An empty stream corresponding to this part will be created in the 
        /// package. If a part with the specified uri already exists then we throw an exception.
        /// This methods will call the CreatePartCore method which will create the actual PackagePart in the package.
        /// 
        /// Uri of the PackagePart that is to be added 
        /// ContentType of the stream to be added
        /// CompressionOption  describing compression configuration 
        /// for the new part. This compression apply only to the part, it doesn't affect relationship parts or related parts. 
        /// This parameter is optional. 
        ///  
        /// If this Package object has been disposed
        /// If the package is readonly, it cannot be modified
        /// If partUri parameter is null
        /// If contentType parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax
        /// If CompressionOption enumeration [compressionOption] does not have one of the valid values 
        /// If a PackagePart with the given partUri already exists in the Package 
        public PackagePart CreatePart(Uri partUri,
                            string contentType, 
                            CompressionOption compressionOption)
        {
            ThrowIfObjectDisposed();
            ThrowIfReadOnly(); 

            if (partUri == null) 
                throw new ArgumentNullException("partUri"); 

            if (contentType == null) 
                throw new ArgumentNullException("contentType");

            ThrowIfCompressionOptionInvalid(compressionOption);
 
            PackUriHelper.ValidatedPartUri validatedPartUri = PackUriHelper.ValidatePartUri(partUri);
 
            if (_partList.ContainsKey(validatedPartUri)) 
                throw new InvalidOperationException(SR.Get(SRID.PartAlreadyExists));
 
            // Add the part to the _partList if there is no prefix collision
            // Note: This is the only place where we pass a null to this method for the part and if the
            // methods returns successfully then we replace the null with an actual part.
            AddIfNoPrefixCollisionDetected(validatedPartUri, null /* since we don't have a part yet */); 

            PackagePart addedPart = CreatePartCore(validatedPartUri, 
                                                            contentType, 
                                                            compressionOption);
 
            //Set the entry for this Uri with the actual part
            _partList[validatedPartUri] = addedPart;

            return addedPart; 
        }
 
 

        ///  
        /// Returns a part that already exists in the package. If the part
        /// Corresponding to the URI does not exist in the package then an exception is
        /// thrown. The method calls the GetPartCore method which actually fetches the part.
        ///  
        /// 
        ///  
        /// If this Package object has been disposed 
        /// If the package is write only, information cannot be retrieved from it
        /// If partUri parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax
        /// If the requested part does not exists in the Package
        public PackagePart GetPart(Uri partUri)
        { 
            PackagePart returnedPart = GetPartHelper(partUri);
            if (returnedPart == null) 
                throw new InvalidOperationException(SR.Get(SRID.PartDoesNotExist)); 
            else
                return returnedPart; 
        }


        ///  
        /// This is a convenient method to check whether a given part exists in the
        /// package. This will have a default implementation that will try to retrieve 
        /// the part and then if successful, it will return true. 
        /// If the custom file format has an easier way to do this, they can override this method
        /// to get this information in a more efficient way. 
        /// 
        /// 
        /// 
        /// If this Package object has been disposed 
        /// If the package is write only, information cannot be retrieved from it
        /// If partUri parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax 
        public virtual bool PartExists(Uri partUri)
        { 
            return (GetPartHelper(partUri) != null);
        }

 
        /// 
        /// This method will do all the house keeping required when a part is deleted 
        /// Then the DeletePartCore method will be called which will have the actual logic to 
        /// do the work specific to the underlying file format and will actually delete the
        /// stream corresponding to this part. This method does not throw if the specified 
        /// part does not exist. This is in conformance with the FileInfo.Delete call.
        /// 
        /// 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified
        /// If partUri parameter is null 
        /// If partUri parameter does not conform to the valid partUri syntax 
        public void DeletePart(Uri partUri)
        { 
            ThrowIfObjectDisposed();
            ThrowIfReadOnly();
            ThrowIfInStreamingCreation("DeletePart");
 
            if (partUri == null)
                throw new ArgumentNullException("partUri"); 
 
            PackUriHelper.ValidatedPartUri validatedPartUri = (PackUriHelper.ValidatedPartUri)PackUriHelper.ValidatePartUri(partUri);
 
            if (_partList.ContainsKey(validatedPartUri))
            {
                //This will get the actual casing of the part that
                //is stored in the partList which is equivalent to the 
                //partUri provided by the user
                validatedPartUri = (PackUriHelper.ValidatedPartUri)_partList[validatedPartUri].Uri; 
                _partList[validatedPartUri].IsDeleted = true; 
                _partList[validatedPartUri].Close();
 
                //Call the Subclass to delete the part

                //!!Important Note: The order of this call is important as one of the
                //sub-classes - ZipPackage relies upon the abstract layer to be 
                //able to provide the ZipPackagePart in order to do the proper
                //clean up and delete operation. 
                //The dependency is in ZipPackagePart.DeletePartCore method. 
                //Ideally we would have liked to avoid this kind of a restriction
                //but due to the current class interfaces and data structure ownerships 
                //between these objects, it tough to re-design at this point.
                DeletePartCore(validatedPartUri);

                //Finally remove it from the list of parts in the cache 
                _partList.Remove(validatedPartUri);
            } 
            else 
                //If the part is not in memory we still call the underlying layer
                //to delete the part if it exists 
                DeletePartCore(validatedPartUri);

            if (PackUriHelper.IsRelationshipPartUri(validatedPartUri))
            { 
                //We clear the in-memory data structure corresponding to that relationship part
                //This will ensure that the intention of the user to delete the part, is respected. 
                //And thus we will not try to recreate it just in case there was some data in the 
                //memory structure.
 
                Uri owningPartUri = PackUriHelper.GetSourcePartUriFromRelationshipPartUri(validatedPartUri);
                //Package-level relationships in /_rels/.rels
                if (Uri.Compare(owningPartUri, PackUriHelper.PackageRootUri, UriComponents.SerializationInfoString, UriFormat.UriEscaped, StringComparison.Ordinal)==0)
                { 
                    //Clear any data in memory
                    this.ClearRelationships(); 
                } 
                else
                { 
                    //Clear any data in memory
                    if (this.PartExists(owningPartUri))
                    {
                        PackagePart owningPart = this.GetPart(owningPartUri); 
                        owningPart.ClearRelationships();
                    } 
                } 
            }
            else 
            {
                // remove any relationship part
                DeletePart(PackUriHelper.GetRelationshipPartUri(validatedPartUri));
            } 
        }
 
        ///  
        /// This returns a collection of all the Parts within the package.
        ///  
        /// 
        /// If this Package object has been disposed
        /// If the package is writeonly, no information can be retrieved from it
        public PackagePartCollection GetParts() 
        {
            ThrowIfObjectDisposed(); 
            ThrowIfWriteOnly(); 

            //Ideally we should decide whether we should query the underlying layer for parts based on the 
            //FileShare enum. But since we do not have that information, currently the design is to just
            //query the underlying layer once.
            //Note:
            //Currently the incremental behavior for GetPart method is not consistent with the GetParts method 
            //which just queries the underlying layer once.
            if (_partCollection == null) 
            { 
                PackagePart[] parts = GetPartsCore();
 
                //making sure that we get a valid array
                Debug.Assert((parts != null),
                    "Subclass is expected to return an array [an empty one if there are no parts] as a result of GetPartsCore method call. ");
 
                PackUriHelper.ValidatedPartUri partUri;
 
                //We need this dictionary to detect any collisions that might be present in the 
                //list of parts that was given to us from the underlying physical layer, as more than one
                //partnames can be mapped to the same normalized part. 
                //Note: We cannot use the _partList member variable, as that gets updated incrementally and so its
                //not possible to find the collisions using that list.
                //PackUriHelper.ValidatedPartUri implements the IComparable interface.
                Dictionary seenPartUris = new Dictionary(parts.Length); 

                for (int i = 0; i < parts.Length; i++) 
                { 
                    partUri = (PackUriHelper.ValidatedPartUri)parts[i].Uri;
 
                    if (seenPartUris.ContainsKey(partUri))
                        throw new FileFormatException(SR.Get(SRID.BadPackageFormat));
                    else
                    { 
                        // Add the part to the list of URIs that we have already seen
                        seenPartUris.Add(partUri, parts[i]); 
 
                        if (!_partList.ContainsKey(partUri))
                        { 
                            // Add the part to the _partList if there is no prefix collision
                            AddIfNoPrefixCollisionDetected(partUri, parts[i]);
                        }
                    } 
                }
                _partCollection = new PackagePartCollection(_partList); 
            } 
            return _partCollection;
        } 

        #endregion PackagePart Methods

        #region IDisposable Methods 

        ///  
        /// Member of the IDisposable interface. This method will clean up all the resources. 
        /// It calls the Flush method to make sure that all the changes made get persisted.
        /// Note - subclasses should only override Dispose(bool) if they have resources to release. 
        /// See the Design Guidelines for the Dispose() pattern.
        /// 
        void IDisposable.Dispose()
        { 
            if (!_disposed)
            { 
                try 
                {
                    // put our house in order before involving the subclass 

                    // close core properties
                    // This method will write out the core properties to the stream
                    // In non-streaming mode - These will get flushed to the disk as a part of the DoFlush operation 
                    if (_packageProperties != null)
                        _packageProperties.Close(); 
 
                    // flush relationships
                    if (InStreamingCreation) 
                        ClosePackageRelationships();
                    else
                        FlushRelationships();
 
                    //Write out the Relationship XML for the parts
                    //These streams will get flushed in the DoClose operation. 
                    DoOperationOnEachPart(DoCloseRelationshipsXml); 

                    // Close all the parts that are currently open 
                    DoOperationOnEachPart(DoClose);

                    // start the dispose chain
                    Dispose(true); 
                }
                finally 
                { 
                    // do this no matter what (handles case of poorly behaving subclass that doesn't call back into Dispose(bool)
                    _disposed = true; 
                }

                //Since all the resources we care about are freed at this point.
                GC.SuppressFinalize(this); 
            }
        } 
 
        #endregion IDisposable Methods
 
        #region Other Methods

        /// 
        /// Closes the package and all the underlying parts and relationships. 
        /// Calls the Dispose Method, since they have the same semantics
        ///  
        public void Close() 
        {
            ((IDisposable)this).Dispose(); 
        }

        /// 
        /// Flushes the contents of the parts and the relationships to the package. 
        /// This method will call the FlushCore method which will do the actual flushing of contents.
        ///  
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified
        public void Flush() 
        {
            ThrowIfObjectDisposed();
            ThrowIfReadOnly();
 
            // Flush core properties (in streaming production, has to be done before parts get flushed).
            // Write core properties (in streaming production, has to be done before parts get flushed). 
            // This call will write out the xml for the core properties to the stream 
            // In non-streaming mode - These properties will get flushed to disk as a part of the DoFlush operation
            if (_packageProperties != null) 
                _packageProperties.Flush();

            // Write package relationships XML to the relationship part stream.
            // These will get flushed to disk as a part of the DoFlush operation 
            if (InStreamingCreation)
                FlushPackageRelationships(); // Create a piece. 
            else 
                FlushRelationships(); // Flush into .rels part.
 
            //Write out the Relationship XML for the parts
            //These streams will get flushed in the DoFlush operation.
            DoOperationOnEachPart(DoWriteRelationshipsXml);
 
            // Flush all the parts that are currently open.
            // This will flush part relationships. 
            DoOperationOnEachPart(DoFlush); 

            FlushCore(); 
        }

        #endregion Other Methods
 
        #region PackageRelationship Methods
 
        ///  
        /// Creates a relationship at the Package level with the Target PackagePart specified as the Uri
        ///  
        /// Target's URI
        /// Enumeration indicating the base uri for the target uri
        /// PackageRelationship type, having uri like syntax that is used to
        /// uniquely identify the role of the relationship 
        /// 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified 
        /// If parameter "targetUri" is null
        /// If parameter "relationshipType" is null 
        /// If parameter "targetMode" enumeration does not have a valid value
        /// If TargetMode is TargetMode.Internal and the targetUri is an absolute Uri 
        /// If relationship is being targeted to a relationship part
        public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType) 
        {
            return CreateRelationship(targetUri, targetMode, relationshipType, null); 
        } 

        ///  
        /// Creates a relationship at the Package level with the Target PackagePart specified as the Uri
        /// 
        /// Target's URI
        /// Enumeration indicating the base uri for the target uri 
        /// PackageRelationship type, having uri like syntax that is used to
        /// uniquely identify the role of the relationship 
        /// String that conforms to the xsd:ID datatype. Unique across the source's 
        /// relationships. Null is OK (ID will be generated). An empty string is an invalid XML ID.
        ///  
        /// If this Package object has been disposed
        /// If the package is readonly, it cannot be modified
        /// If parameter "targetUri" is null
        /// If parameter "relationshipType" is null 
        /// If parameter "targetMode" enumeration does not have a valid value
        /// If TargetMode is TargetMode.Internal and the targetUri is an absolute Uri  
        /// If relationship is being targeted to a relationship part 
        /// If parameter "id" is not a valid Xsd Id
        /// If an id is provided in the method, and its not unique 
        public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType, String id)
        {
            ThrowIfObjectDisposed();
            ThrowIfReadOnly(); 
            EnsureRelationships();
            //All parameter validation is done in the following call 
            return _relationships.Add(targetUri, targetMode, relationshipType, id); 
        }
 
        /// 
        /// Deletes a relationship from the Package. This is done based on the
        /// relationship's ID. The target PackagePart is not affected by this operation.
        ///  
        /// The ID of the relationship to delete. An invalid ID will not
        /// throw an exception, but nothing will be deleted. 
        /// If this Package object has been disposed 
        /// If the package is readonly, it cannot be modified
        /// If parameter "id" is null 
        /// If parameter "id" is not a valid Xsd Id
        public void DeleteRelationship(String id)
        {
            ThrowIfObjectDisposed(); 
            ThrowIfReadOnly();
            ThrowIfInStreamingCreation("DeleteRelationship"); 
 
            if (id == null)
                throw new ArgumentNullException("id"); 

            InternalRelationshipCollection.ThrowIfInvalidXsdId(id);

            EnsureRelationships(); 
            _relationships.Delete(id);
        } 
 
        /// 
        /// Returns a collection of all the Relationships that are 
        /// owned by the package
        /// 
        /// 
        /// If this Package object has been disposed 
        /// If the package is write only, no information can be retrieved from it
        public PackageRelationshipCollection GetRelationships() 
        { 
            //All the validations for dispose and file access are done in the
            //GetRelationshipsHelper method. 
            return GetRelationshipsHelper(null);
        }

        ///  
        /// Returns a collection of filtered Relationships that are
        /// owned by the package 
        /// The filter string is compared with the type of the relationships 
        /// in a case sensitive and culture ignorant manner.
        ///  
        /// 
        /// If this Package object has been disposed
        /// If the package is write only, no information can be retrieved from it
        /// If parameter "relationshipType" is null 
        /// If parameter "relationshipType" is an empty string
        public PackageRelationshipCollection GetRelationshipsByType(string relationshipType) 
        { 
            //These checks are made in the GetRelationshipsHelper as well, but we make them
            //here as we need to perform parameter validation 
            ThrowIfObjectDisposed();
            ThrowIfWriteOnly();

            if (relationshipType == null) 
                throw new ArgumentNullException("relationshipType");
 
            InternalRelationshipCollection.ThrowIfInvalidRelationshipType(relationshipType); 

            return GetRelationshipsHelper(relationshipType); 
        }

        /// 
        /// Retrieve a relationship per ID. 
        /// 
        /// The relationship ID. 
        /// The relationship with ID 'id' or throw an exception if not found. 
        /// If this Package object has been disposed
        /// If the package is write only, no information can be retrieved from it 
        /// If parameter "id" is null
        /// If parameter "id" is not a valid Xsd Id
        /// If the requested relationship does not exist in the Package
        public PackageRelationship GetRelationship(string id) 
        {
            //All the validations for dispose and file access are done in the 
            //GetRelationshipHelper method. 

            PackageRelationship returnedRelationship = GetRelationshipHelper(id); 
            if (returnedRelationship == null)
                throw new InvalidOperationException(SR.Get(SRID.PackageRelationshipDoesNotExist));
            else
                return returnedRelationship; 
        }
 
        ///  
        /// Returns whether there is a relationship with the specified ID.
        ///  
        /// The relationship ID.
        /// true iff a relationship with ID 'id' is defined on this source.
        /// If this Package object has been disposed
        /// If the package is write only, no information can be retrieved from it 
        /// If parameter "id" is null
        /// If parameter "id" is not a valid Xsd Id 
        public bool RelationshipExists(string id) 
        {
            //All the validations for dispose and file access are done in the 
            //GetRelationshipHelper method.

            return (GetRelationshipHelper(id) != null);
        } 

        #endregion PackageRelationship Methods 
 
        #endregion Public Methods
 
        #region Protected Abstract Methods

        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This method will actually add a new part to the package. An empty part should be
        /// created as a result of this call. 
        ///  
        /// 
        ///  
        /// 
        /// 
        protected abstract PackagePart CreatePartCore(Uri partUri,
                                                            string contentType, 
                                                            CompressionOption compressionOption);
 
        ///  
        /// This method is for custom implementation corresponding to the underlying file format.
        /// This method will actually return the part after reading the actual physical bits. 
        /// If the PackagePart does not exists in the underlying package then this method should return a null.
        /// This method must not throw an exception if a part does not exist.
        /// 
        ///  
        /// 
        protected abstract PackagePart GetPartCore(Uri partUri); 
 
        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This method will actually delete the part from the underlying package.
        /// This method should not throw if the specified part does not exist.
        /// This is in conformance with the FileInfo.Delete call.
        ///  
        /// 
        protected abstract void DeletePartCore(Uri partUri); 
 
        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This is the method that knows how to get the actual parts. If there are no parts,
        /// this method should return an empty array.
        /// 
        ///  
        protected abstract PackagePart[] GetPartsCore();
 
        ///  
        /// This method is for custom implementation corresponding to the underlying file format.
        /// This method should be used to dispose the resources that are specific to the file format. 
        /// Also everything should be flushed to the disc before closing the package.
        /// 
        /// Subclasses that manage non-memory resources should override this method and free these resources.
        /// Any override should be careful to always call base.Dispose(disposing) to ensure orderly cleanup. 
        protected virtual void Dispose(bool disposing)
        { 
            if (!_disposed && disposing) 
            {
                _partList.Clear(); 

                if (_packageProperties != null)
                {
                    _packageProperties.Dispose(); 
                    _packageProperties = null;
                } 
 
                //release objects
                _partList = null; 
                _partCollection = null;
                _relationships = null;

                _disposed = true; 
            }
        } 
 
        /// 
        /// This method is for custom implementation corresponding to the underlying file format. 
        /// This method flushes the contents of the package to the disc.
        /// 
        protected abstract void FlushCore();
 
        #endregion Protected Abstract Methods
 
        //----------------------------------------------------- 
        //
        //  Internal Constructors 
        //
        //------------------------------------------------------
        // None
        //----------------------------------------------------- 

        //----------------------------------------------------- 
        // 
        //   Internal Properties
        // 
        //-----------------------------------------------------

        #region Internal Properties
 
        /// 
        /// true iff the package was opened for streaming. 
        ///  
        internal bool InStreamingCreation
        { 
            get
            {
                return _inStreamingCreation;
            } 
        }
 
        #endregion Internal Properties 

        //------------------------------------------------------ 
        //
        //  Internal Methods
        //
        //----------------------------------------------------- 

        #region Internal Methods 
        // Some operations are not supported while producing a package in streaming mode. 
        internal void ThrowIfInStreamingCreation(string methodName)
        { 
            if (_inStreamingCreation)
                throw new IOException(SR.Get(SRID.OperationIsNotSupportedInStreamingProduction, methodName));
        }
 
        // Some operations are supported only while producing a package in streaming mode.
        internal void ThrowIfNotInStreamingCreation(string methodName) 
        { 
            if (!InStreamingCreation)
                throw new IOException(SR.Get(SRID.MethodAvailableOnlyInStreamingCreation, methodName)); 
        }

        //If the container is readonly then we cannot add/delete to it
        internal void ThrowIfReadOnly() 
        {
            if (_openFileAccess == FileAccess.Read) 
                throw new IOException(SR.Get(SRID.CannotModifyReadOnlyContainer)); 
        }
 
        // If the container is writeonly, parts cannot be retrieved from it
        internal void ThrowIfWriteOnly()
        {
            if (_openFileAccess == FileAccess.Write) 
                throw new IOException(SR.Get(SRID.CannotRetrievePartsOfWriteOnlyContainer));
        } 
 
        // return true to continue
        internal delegate bool PartOperation(PackagePart p); 

        internal static void ThrowIfFileModeInvalid(FileMode mode)
        {
            //We do the enum check as suggested by the following condition for performance reasons. 
            if (mode < FileMode.CreateNew || mode > FileMode.Append)
                throw new ArgumentOutOfRangeException("mode"); 
        } 

        internal static void ThrowIfFileAccessInvalid(FileAccess access) 
        {
            //We do the enum check as suggested by the following condition for performance reasons.
            if (access < FileAccess.Read || access > FileAccess.ReadWrite)
                throw new ArgumentOutOfRangeException("access"); 
        }
 
        internal static void ThrowIfCompressionOptionInvalid(CompressionOption compressionOption) 
        {
            //We do the enum check as suggested by the following condition for performance reasons. 
            if (compressionOption < CompressionOption.NotCompressed || compressionOption > CompressionOption.SuperFast)
                throw new ArgumentOutOfRangeException("compressionOption");
        }
 
        #region Write-time streaming API
 
        ///  
        /// This method gives the possibility of opening a package in streaming mode.
        /// When 'streaming' is true, the only allowed file modes are Create and CreateNew, 
        /// the only allowed file access is Write and the only allowed FileShare values are
        /// Null and Read.
        /// 
        /// Path to the package. 
        /// FileMode in which the package should be opened.
        /// FileAccess with which the package should be opened. 
        /// FileShare with which the package is opened. 
        /// Whether to allow the creation of part pieces while enforcing write-once access.
        /// Package 
        /// If path parameter is null
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        /// If FileMode enumeration [packageMode] does not have one of the valid values
        internal static Package Open( 
            string path,
            FileMode packageMode, 
            FileAccess packageAccess, 
            FileShare packageShare,
            bool streaming) 
        {
            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.StartEvent);

            if (path == null) 
                throw new ArgumentNullException("path");
 
            ThrowIfFileModeInvalid(packageMode); 
            ThrowIfFileAccessInvalid(packageAccess);
 
            ValidateStreamingAccess(packageMode, packageAccess, packageShare, streaming);

            //Note: FileShare enum is not being verfied at this stage, as we do not interpret the flag in this
            //code at all and just pass it on to the next layer, where the necessary validation can be 
            //performed. Also, there is no meaningful way to check this parameter at this layer, as the
            //FileShare enumeration is a set of flags and flags/Bit-fields can be combined using a 
            //bitwise OR operation to create different values, and validity of these values is specific to 
            //the actual physical implementation.
 
            //Verify if this is valid for filenames
            FileInfo packageFileInfo = new FileInfo(path);

            Package package = new ZipPackage(packageFileInfo.FullName, packageMode, packageAccess, packageShare, streaming); 

            if (!package._inStreamingCreation) // No read operation in streaming production. 
            { 
                //We need to get all the parts if any exists from the underlying file
                //so that we have the names in the Normalized form in our in-memory 
                //data structures.
                //Note: If ever this call is removed, each individual call to GetPartCore,
                //may result in undefined behavior as the underlying ZipArchive, maintains the
                //files list as being case-sensitive. 
                if (package.FileOpenAccess == FileAccess.ReadWrite || package.FileOpenAccess == FileAccess.Read)
                    package.GetParts(); 
            } 

            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.EndEvent); 

            return package;
        }
 
        /// 
        /// This method gives the possibility of opening a package in streaming mode. 
        /// When 'streaming' is true, the only allowed file modes are Create and CreateNew, 
        /// and the only allowed file access is Write.
        ///  
        /// Stream on which the package is created
        /// FileMode in which the package is to be opened
        /// FileAccess on the package that is opened
        /// Whether to allow the creation of part pieces while enforcing write-once access. 
        /// Package
        /// If stream parameter is null 
        /// If FileMode enumeration [packageMode] does not have one of the valid values 
        /// If FileAccess enumeration [packageAccess] does not have one of the valid values
        /// If package to be created should have readwrite/read access and underlying stream is write only 
        /// If package to be created should have readwrite/write access and underlying stream is read only
        [FriendAccessAllowed]
        internal static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess, bool streaming)
        { 
            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.StartEvent);
 
            if (stream == null) 
                throw new ArgumentNullException("stream");
 
            ValidateStreamingAccess(packageMode, packageAccess, null /* no FileShare info */, streaming);

            //FileMode and FileAccess Enums are validated in the following call
            Stream ensuredStream = ValidateModeAndAccess(stream, packageMode, packageAccess); 

            Package package; 
 
            // Today the Open(Stream) method is purely used for streams of Zip file format as
            // that is the default underlying file format mapper implemented. 
            package = new ZipPackage(ensuredStream, packageMode, packageAccess, streaming);

            if (!package._inStreamingCreation) // No read operation in streaming production.
            { 
                //We need to get all the parts if any exists from the underlying file
                //so that we have the names in the Normalized form in our in-memory 
                //data structures. 
                //Note: If ever this call is removed, each individual call to GetPartCore,
                //may result in undefined behavior as the underlying ZipArchive, maintains the 
                //files list as being case-sensitive.
                if(package.FileOpenAccess == FileAccess.ReadWrite || package.FileOpenAccess == FileAccess.Read)
                    package.GetParts();
            } 

            EventTrace.NormalTraceEvent(EventTraceGuidId.DRXOPENPACKAGEGUID, EventType.EndEvent); 
 
            return package;
        } 

        /// 
        /// Write a nonterminal piece for /_rels/.rels.
        ///  
        internal void FlushPackageRelationships()
        { 
            ThrowIfNotInStreamingCreation("FlushPackageRelationships"); 

            if (_relationships == null) 
                return; // nothing to flush

            _relationships.Flush();
        } 

        ///  
        /// Write a terminal piece for /_rels/.rels. 
        /// 
        internal void ClosePackageRelationships() 
        {
            ThrowIfNotInStreamingCreation("ClosePackageRelationships");

            if (_relationships == null) 
                return; // no relationship part
 
            _relationships.CloseInStreamingCreationMode(); 
        }
 
        #endregion Write-time streaming API

        #endregion Internal Methods
 
        //------------------------------------------------------
        // 
        //  Internal Events 
        //
        //------------------------------------------------------ 
        // None
        //-----------------------------------------------------
        //
        //  Private Methods 
        //
        //------------------------------------------------------ 
 
        #region Private Methods
 

        // This method is only when new part is added to the Package object.
        // This method will throw an exception if the name of the part being added is a
        // prefix of the name of an existing part. 
        // Example - Say the following parts exist in the package
        // 1. /abc.xaml 
        // 2. /xyz/pqr/a.jpg 
        // As an example - Adding any of the following parts will throw an exception -
        // 1. /abc.xaml/new.xaml 
        // 2. /xyz/pqr
        private void AddIfNoPrefixCollisionDetected(PackUriHelper.ValidatedPartUri partUri, PackagePart part)
        {
 
            //Add the Normalized Uri to the sorted _partList tentatively to see where it will get inserted
            _partList.Add(partUri, part); 
 
            //Get the index of the entry at which this part was added
            int index = _partList.IndexOfKey(partUri); 

            Invariant.Assert(index >= 0, "Given uri must be present in the dictionary");

            string normalizedPartName = partUri.NormalizedPartUriString; 
            string precedingPartName = null;
            string followingPartName = null; 
 
            if (index > 0)
            { 
                precedingPartName = _partList.Keys[index - 1].NormalizedPartUriString;
            }

            if (index < _partList.Count - 1) 
            {
                followingPartName = _partList.Keys[index + 1].NormalizedPartUriString; 
            } 

            if ((precedingPartName != null 
                && normalizedPartName.StartsWith(precedingPartName, StringComparison.Ordinal)
                && normalizedPartName.Length > precedingPartName.Length
                && normalizedPartName[precedingPartName.Length] == PackUriHelper.ForwardSlashChar) ||
                (followingPartName != null 
                && followingPartName.StartsWith(normalizedPartName, StringComparison.Ordinal)
                && followingPartName.Length > normalizedPartName.Length 
                && followingPartName[normalizedPartName.Length] == PackUriHelper.ForwardSlashChar)) 
            {
                //Removing the invalid entry from the _partList. 
                _partList.Remove(partUri);

                throw new InvalidOperationException(SR.Get(SRID.PartNamePrefixExists));
            } 
        }
 
        // Test consistency of file opening parameters with the value of 'streaming', and record 
        // whether the streaming mode is for consumption or production.
        // 
        private static void ValidateStreamingAccess(
            FileMode packageMode,
            FileAccess packageAccess,
            Nullable packageShare, 
            bool streaming)
        { 
            if (streaming) 
            {
                if (packageMode == FileMode.Create || packageMode == FileMode.CreateNew) 
                {
                    if (packageAccess != FileAccess.Write)
                        throw new IOException(SR.Get(SRID.StreamingPackageProductionImpliesWriteOnlyAccess));
                    if (   packageShare != null 
                        && packageShare != FileShare.Read && packageShare != FileShare.None)
                        throw new IOException(SR.Get(SRID.StreamingPackageProductionRequiresSingleWriter)); 
                } 
                else
                { 
                    // Blanket exception pending design of streaming consumption.
                    throw new NotSupportedException(SR.Get(SRID.StreamingModeNotSupportedForConsumption));
                }
            } 
        }
 
 
        //Checking if the mode and access parameters are compatible with the provided stream.
        private static Stream ValidateModeAndAccess(Stream s, FileMode mode, FileAccess access) 
        {
            ThrowIfFileModeInvalid(mode);
            ThrowIfFileAccessInvalid(access);
 
            //asking for more permissions than the underlying stream.
            // Stream cannot write, but package to be created should have write access 
            if (!s.CanWrite && (access == FileAccess.ReadWrite || access == FileAccess.Write)) 
                throw new IOException(SR.Get(SRID.IncompatibleModeOrAccess));
 
            //asking for more permissions than the underlying stream.
            // Stream cannot read, but the package to be created should have read access
            if (!s.CanRead && (access == FileAccess.ReadWrite || access == FileAccess.Read))
                throw new IOException(SR.Get(SRID.IncompatibleModeOrAccess)); 

            //asking for less restricted access to the underlying stream 
            //stream is ReadWrite but the package is either readonly, or writeonly 
            if ((s.CanRead && s.CanWrite) && (access == FileAccess.Read || access == FileAccess.Write))
            { 
                return new RestrictedStream(s, access);
            }
            else
                return s; 
        }
 
        //Throw if the object is in a disposed state 
        private void ThrowIfObjectDisposed()
        { 
            if (_disposed == true)
                throw new ObjectDisposedException(null, SR.Get(SRID.ObjectDisposed));
        }
 
        private void EnsureRelationships()
        { 
            // once per package 
            if (_relationships == null)
            { 
                _relationships = new InternalRelationshipCollection(this);
            }
        }
 
        //Delete All Package-level Relationships
        private void ClearRelationships() 
        { 
            if(_relationships!=null)
                _relationships.Clear(); 
        }

        //Flush the relationships at package level
        private void FlushRelationships() 
        {
            // flush relationships 
            if (_relationships != null && _openFileAccess != FileAccess.Read) 
            {
                _relationships.Flush(); 
            }
        }

        //We do the close or the flush operation per part 
        private void DoOperationOnEachPart(PartOperation operation)
        { 
            //foreach (PackagePart p in _partList.Values) 
            //    p.Close();  - this throws
            // Make local copy of part names to prevent exception during enumeration when 
            // a new relationship part gets created (flushing relationships can cause part creation).
            // This code throws in such a case:
            //
            //            foreach (PackagePart p in _partList.Values) 
            //                p.Flush();
            // 
            if (_partList.Count > 0) 
            {
                int partCount = 0; 
                PackUriHelper.ValidatedPartUri[] partKeys = new PackUriHelper.ValidatedPartUri[_partList.Keys.Count];

                foreach (PackUriHelper.ValidatedPartUri uri in _partList.Keys)
                { 
                    partKeys[partCount++] = uri;
                } 
 
                // this throws an exception in certain cases (when a part has been deleted)
                // 
                //     _partList.Keys.CopyTo(keys, 0);

                for (int i = 0; i < _partList.Keys.Count; i++)
                { 
                    // Some of these may disappear during above close because the list contains "relationship parts"
                    // and these are removed if their parts' relationship collection is empty 
                    // This fails: 
                    //                _partList[keys[i]].Flush();
 
                    PackagePart p;
                    if (_partList.TryGetValue(partKeys[i], out p))
                    {
                        if (!operation(p)) 
                            break;
                    } 
                } 
            }
        } 

        //We needed to separate the rels parts from the other parts
        //because if a rels part for a part occured earlier than the part itself in the array,
        //the rels part would be closed and then when close the part and try to persist the relationships 
        //for the particular part, it would throw an exception
        private bool DoClose(PackagePart p) 
        { 
            if (!p.IsClosed)
            { 
                if (PackUriHelper.IsRelationshipPartUri(p.Uri) && PackUriHelper.ComparePartUri(p.Uri, PackageRelationship.ContainerRelationshipPartName) != 0)
                {
                    //First we close the source part.
                    //Note - we can safely do this as DoClose is being called on all parts. So ultimately we will end up 
                    //closing the source part as well.
                    //This logic only takes care of out of order parts. 
                    PackUriHelper.ValidatedPartUri owningPartUri = 
                        (PackUriHelper.ValidatedPartUri)PackUriHelper.GetSourcePartUriFromRelationshipPartUri(p.Uri);
                    //If the source part for this rels part exists then we close it. 
                    PackagePart sourcePart;
                    if (_partList.TryGetValue(owningPartUri, out sourcePart))
                        sourcePart.Close();
                } 
                p.Close();
            } 
            return true; 
        }
 
        private bool DoFlush(PackagePart p)
        {
            p.Flush();
            return true; 
        }
 
        private bool DoWriteRelationshipsXml(PackagePart p) 
        {
            if (!p.IsRelationshipPart) 
            {
                p.FlushRelationships();
            }
            return true; 
        }
 
        private bool DoCloseRelationshipsXml(PackagePart p) 
        {
            if (!p.IsRelationshipPart) 
            {
                p.CloseRelationships();
            }
            return true; 
        }
 
        private PackagePart GetPartHelper(Uri partUri) 
        {
            ThrowIfObjectDisposed(); 
            ThrowIfWriteOnly();

            if (partUri == null)
                throw new ArgumentNullException("partUri"); 

            PackUriHelper.ValidatedPartUri validatePartUri = PackUriHelper.ValidatePartUri(partUri); 
 
            if (_partList.ContainsKey(validatePartUri))
                return _partList[validatePartUri]; 
            else
            {
                //Ideally we should decide whether we should query the underlying layer for the part based on the
                //FileShare enum. But since we do not have that information, currently the design is to always 
                //ask the underlying layer, this allows for incremental access to the package.
                //Note: 
                //Currently this incremental behavior for GetPart is not consistent with the GetParts method 
                //which just queries the underlying layer once.
                PackagePart returnedPart = GetPartCore(validatePartUri); 

                if (returnedPart != null)
                {
                    // Add the part to the _partList if there is no prefix collision 
                    AddIfNoPrefixCollisionDetected(validatePartUri, returnedPart);
                } 
 
                return returnedPart;
            } 
        }

        /// 
        /// Retrieve a relationship per ID. 
        /// 
        /// The relationship ID. 
        /// The relationship with ID 'id' or null if not found. 
        private PackageRelationship GetRelationshipHelper(string id)
        { 
            ThrowIfObjectDisposed();
            ThrowIfWriteOnly();

            if (id == null) 
                throw new ArgumentNullException("id");
 
            InternalRelationshipCollection.ThrowIfInvalidXsdId(id); 

            EnsureRelationships(); 
            return _relationships.GetRelationship(id);

        }
 
        /// 
        /// Returns a collection of all the Relationships that are 
        /// owned by the package based on the filter string. 
        /// 
        ///  
        private PackageRelationshipCollection GetRelationshipsHelper(string filterString)
        {
            ThrowIfObjectDisposed();
            ThrowIfWriteOnly(); 
            EnsureRelationships();
 
            //Internally null is used to indicate that no filter string was specified and 
            //and all the relationships should be returned.
            return new PackageRelationshipCollection(_relationships, filterString); 
        }

        #endregion Private Methods
 
        //-----------------------------------------------------
        // 
        //  Private Fields 
        //
        //----------------------------------------------------- 

        #region Private Members

        // Default values for the Package.Open method overloads 
        private static readonly FileMode        _defaultFileMode            = FileMode.OpenOrCreate;
        private static readonly FileAccess      _defaultFileAccess          = FileAccess.ReadWrite; 
        private static readonly FileShare       _defaultFileShare           = FileShare.None; 

        private static readonly FileMode        _defaultStreamMode          = FileMode.Open; 
        private static readonly FileAccess      _defaultStreamAccess        = FileAccess.Read;

        private bool                            _inStreamingCreation;       // false by default
        private FileAccess                      _openFileAccess; 
        private bool                            _disposed;
        private SortedList _partList; 
        private PackagePartCollection           _partCollection; 
        private InternalRelationshipCollection  _relationships;
        private PartBasedPackageProperties      _packageProperties; 


        #endregion Private Members
 
        //-----------------------------------------------------
        // 
        //  Private Class 
        //
        //------------------------------------------------------ 

        #region Private Class: Restricted Stream

        ///  
        /// This implementation of Stream class is a simple wrapper to restrict the
        /// read or write access to the underlying stream, as per user request. 
        /// No validation for the stream method calls is done in this wrapper, the calls 
        /// are passed onto the underlying stream object, which should do the
        /// validation as required. 
        /// 
        private sealed class RestrictedStream : Stream
        {
            #region Constructor 

            ///  
            /// Constructor 
            /// 
            ///  
            /// 
            internal RestrictedStream(Stream stream, FileAccess access)
            {
                if (stream == null) 
                    throw new ArgumentNullException("stream");
 
                //Verifying if the FileAccess enum is valid 
                //This constructor will never be called with FileAccess.ReadWrite
                Debug.Assert(access==FileAccess.Read || access == FileAccess.Write, 
                    "The constructor of this private class is expected to be called with FileAccess.Read or FileAccess.Write");

                _stream = stream;
 
                if (access == FileAccess.Read)
                { 
                    _canRead = true; 
                    _canWrite = false;
                } 
                else
                if (access == FileAccess.Write)
                {
                    _canRead = false; 
                    _canWrite = true;
                } 
            } 

            #endregion Constructor 

            #region Properties

            ///  
            /// Member of the abstract Stream class
            ///  
            /// Bool, true if the stream can be read from, else false 
            public override bool CanRead
            { 
                get
                {
                    if(!_disposed)
                        return _canRead; 
                    else
                        return false; 
                } 
            }
 
            /// 
            /// Member of the abstract Stream class
            /// 
            /// Bool, true if the stream can be seeked, else false 
            public override bool CanSeek
            { 
                get 
                {
                    if(!_disposed) 
                        return _stream.CanSeek;
                    else
                        return false;
                } 
            }
 
            ///  
            /// Member of the abstract Stream class
            ///  
            /// Bool, true if the stream can be written to, else false
            public override bool CanWrite
            {
                get 
                {
                    if(!_disposed) 
                        return _canWrite; 
                    else
                        return false; 
                }
            }

            ///  
            /// Member of the abstract Stream class
            ///  
            /// Long value indicating the length of the stream 
            public override long Length
            { 
                get
                {
                    ThrowIfStreamDisposed();
                    return _stream.Length; 
                }
            } 
 
            /// 
            /// Member of the abstract Stream class 
            /// 
            /// Long value indicating the current position in the stream
            public override long Position
            { 
                get
                { 
                    ThrowIfStreamDisposed(); 
                    return _stream.Position;
                } 
                set
                {
                    ThrowIfStreamDisposed();
                    _stream.Position = value; 
                }
            } 
 
            #endregion Properties
 
            #region Methods

            /// 
            /// Member of the abstract Stream class 
            /// 
            /// only zero is supported 
            /// only SeekOrigin.Begin is supported 
            /// zero
            public override long Seek(long offset, SeekOrigin origin) 
            {
                ThrowIfStreamDisposed();
                return _stream.Seek(offset, origin);
            } 

            ///  
            /// Member of the abstract Stream class 
            /// 
            ///  
            public override void SetLength(long newLength)
            {
                ThrowIfStreamDisposed();
                if (_canWrite) 
                    _stream.SetLength(newLength);
                else 
                    throw new NotSupportedException(SR.Get(SRID.ReadOnlyStream)); 
            }
 
            /// 
            /// Member of the abstract Stream class
            /// 
            ///  
            /// 
            ///  
            ///  
            /// 
            /// The standard Stream.Read semantics, and in particular the restoration of the current 
            /// position in case of an exception, is implemented by the underlying stream.
            /// 
            public override int Read(byte[] buffer, int offset, int count)
            { 
                ThrowIfStreamDisposed();
                if (_canRead) 
                    return _stream.Read(buffer, offset, count); 
                else
                    throw new NotSupportedException(SR.Get(SRID.WriteOnlyStream)); 
            }

            /// 
            /// Member of the abstract Stream class 
            /// 
            ///  
            ///  
            /// 
            public override void Write(byte[] buf, int offset, int count) 
            {
                ThrowIfStreamDisposed();
                if (_canWrite)
                    _stream.Write(buf, offset, count); 
                else
                    throw new NotSupportedException(SR.Get(SRID.ReadOnlyStream)); 
            } 

            ///  
            /// Member of the abstract Stream class
            /// 
            public override void Flush()
            { 
                ThrowIfStreamDisposed();
                if (_canWrite) 
                    _stream.Flush(); 
            }
            #endregion Methods 

            //-----------------------------------------------------
            //
            //  Protected Methods 
            //
            //------------------------------------------------------ 
            ///  
            /// Dispose(bool)
            ///  
            /// 
            protected override void Dispose(bool disposing)
            {
                try 
                {
                    if (disposing) 
                    { 
                        if (!_disposed)
                        { 
                            _stream.Close();
                        }
                    }
                } 
                finally
                { 
                    _disposed = true; 
                    base.Dispose(disposing);
                } 
            }


            #region Private Methods 

            private void ThrowIfStreamDisposed() 
            { 
                if (_disposed)
                    throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed)); 
            }

            #endregion Private Methods
 
            #region Private Variables
 
            private Stream _stream; 
            private bool _canRead;
            private bool _canWrite; 
            private bool _disposed;

            #endregion Private Variables
 
        }
 
        #endregion Private Class: Restricted Stream 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK