File: Base\System\IO\Packaging\PackagePart.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//
// <copyright file="PackagePart.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//  This is a base abstract class for PackagePart. 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.Windows;               // For Exception strings - SRID
using System.Collections.Generic;   // For List <>
using MS.Internal.IO.Packaging;
using System.Diagnostics;           // For Debug.Assert
using MS.Internal;                  // For Invariant.Assert
using MS.Internal.WindowsBase;
 
#pragma warning disable 1634, 1691  // suppressing PreSharp warnings
 
namespace System.IO.Packaging
{
    /// <summary>
    /// This class represents the a PackagePart within a container.
    /// This is a part of the Packaging Layer APIs
    /// </summary>
    public abstract class PackagePart
    {
        //------------------------------------------------------
        //
        //  Public Constructors
        //
        //------------------------------------------------------
 
        #region Protected Constructor
 
        /// <summary>
        /// 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
        /// 
        /// You should use this constructor in the rare case when you do not have
        /// the content type information related to this part and would prefer to 
        /// obtain it later as required. 
        /// 
        /// These parts have the CompressionOption as NotCompressed by default.
        /// 
        /// NOTE : If you are using this constructor from your subclass or passing a null
        /// for the content type parameter, be sure to implement the GetContentTypeCore 
        /// method, as that will be called to get the content type value. This is provided 
        /// to enable lazy initialization of the ContentType property.
        /// 
        /// </summary>
        /// <param name="package">Package in which this part is being created</param>
        /// <param name="partUri">uri of the part</param>
        /// <exception cref="ArgumentNullException">If parameter "package" is null</exception>
        /// <exception cref="ArgumentNullException">If parameter "partUri" is null</exception>
        protected PackagePart(Package package, Uri partUri)
            :this(package, partUri, null, CompressionOption.NotCompressed)
        {
        }
 
        /// <summary>
        /// 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
        /// 
        /// These parts have the CompressionOption as NotCompressed by default.
        /// 
        /// NOTE : If you are using this constructor from your subclass or passing a null
        /// for the content type parameter, be sure to implement the GetContentTypeCore 
        /// method, as that will be called to get the content type value. This is provided 
        /// to enable lazy initialization of the ContentType property.
        /// 
        /// </summary>
        /// <param name="package">Package in which this part is being created</param>
        /// <param name="partUri">uri of the part</param>
        /// <param name="contentType">Content Type of the part, can be null if the value
        /// is unknown at the time of construction. However the value has to be made 
        /// available anytime the ContentType property is called. A null value only indicates
        /// that the value will be provided later. Every PackagePart must have a valid 
        /// Content Type</param>
        /// <exception cref="ArgumentNullException">If parameter "package" is null</exception>
        /// <exception cref="ArgumentNullException">If parameter "partUri" is null</exception>
        /// <exception cref="ArgumentException">If parameter "partUri" does not conform to the valid partUri syntax</exception>
        protected PackagePart(Package package, Uri partUri, string contentType)
            :this(package, partUri, contentType, CompressionOption.NotCompressed)
        {
        }
 
 
        /// <summary>
        /// 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
        /// 
        /// NOTE : If you are using this constructor from your subclass or passing a null
        /// for the content type parameter, be sure to implement the GetContentTypeCore 
        /// method, as that will be called to get the content type value. This is provided 
        /// to enable lazy initialization of the ContentType property.
        /// 
        /// </summary>
        /// <param name="package">Package in which this part is being created</param>
        /// <param name="partUri">uri of the part</param>
        /// <param name="contentType">Content Type of the part, can be null if the value
        /// is unknown at the time of construction. However the value has to be made 
        /// available anytime the ContentType property is called. A null value only indicates
        /// that the value will be provided later. Every PackagePart must have a valid 
        /// Content Type</param>
        /// <param name="compressionOption">compression option for this part</param>
        /// <exception cref="ArgumentNullException">If parameter "package" is null</exception>
        /// <exception cref="ArgumentNullException">If parameter "partUri" is null</exception>
        /// <exception cref="ArgumentOutOfRangeException">If CompressionOption enumeration [compressionOption] does not have one of the valid values</exception>
        /// <exception cref="ArgumentException">If parameter "partUri" does not conform to the valid partUri syntax</exception>
        protected PackagePart(Package package, 
                                Uri partUri, 
                                string contentType, 
                                CompressionOption compressionOption)
        {
            if (package == null)
                throw new ArgumentNullException("package");
 
            if (partUri == null)
                throw new ArgumentNullException("partUri");
 
            Package.ThrowIfCompressionOptionInvalid(compressionOption);
 
            _uri = PackUriHelper.ValidatePartUri(partUri);
            _container = package;
 
            if (contentType == null)
                _contentType = null;
            else
                _contentType = new ContentType(contentType);
 
            _requestedStreams = null;
            _compressionOption = compressionOption;
            _isRelationshipPart = PackUriHelper.IsRelationshipPartUri(partUri);
        }
 
        #endregion Protected Constructor
 
        //------------------------------------------------------
        //
        //  Public Properties
        //
        //------------------------------------------------------
 
        #region Public Properties
 
        /// <summary>
        /// The Uri for this PackagePart. It is always relative to the Package Root
        /// The PackagePart properties can not be accessed if the parent container is closed.
        /// </summary>
        /// <value></value>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        public Uri Uri
        {
            get
            {
                CheckInvalidState();
                return _uri;
            }
        }
 
        /// <summary>
        /// The Content type of the stream that is represented by this part.
        /// The PackagePart properties can not be accessed if the parent container is closed.
        /// The content type value can be provided by the underlying physical format 
        /// implementation at the time of creation of the Part object ( constructor ) or 
        /// We can initialize it in a lazy manner when the ContentType property is called 
        /// called for the first time by calling the GetContentTypeCore method.  
        /// Note: This method GetContentTypeCore() is only for lazy initialization of the Content 
        /// type value and will only be called once. There is no way to change the content type of 
        /// the part once it has been assigned. 
        /// </summary>
        /// <value>Content Type of the Part [can never return null] </value>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="InvalidOperationException">If the subclass fails to provide a non-null content type value.</exception>
        public string ContentType
        {
            get
            {
                CheckInvalidState();
                if (_contentType == null)
                {
                    //Lazy initialization for the content type
                    string contentType = GetContentTypeCore();
 
                    if (contentType == null)
                    {
                        // From sarjanas:
                        //
                        // We have seen this bug in the past and have said that this should be
                        // treated as exception. If we get a null content type, it’s an error.
                        // We want to throw this exception so that anyone sub-classing this class
                        // should not be setting the content type to null. Its like any other
                        // parameter validation. This is the only place we can validate it. We
                        // throw an ArgumentNullException, when the content type is set to null
                        // in the constructor.
                        //
                        // We cannot get rid of this exception. At most, we can change it to
                        // Invariant.Assert. But then client code will see an Assert if they make
                        // a mistake and that is also not desirable.
                        //
                        // PackagePart is a public API.
                        #pragma warning suppress 6503
                        throw new InvalidOperationException(SR.Get(SRID.NullContentTypeProvided));
                    }
                    _contentType = new ContentType(contentType);
                }
                return _contentType.ToString();
            }
        }
 
 
        /// <summary>
        /// The parent container for this PackagePart
        /// The PackagePart properties can not be accessed if the parent container is closed.
        /// </summary>
        /// <value></value>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        public Package Package
        {
            get
            {
                CheckInvalidState();
                return _container;
            }
        }
 
        /// <summary>
        /// CompressionOption class that was provided as a parameter during the original CreatePart call.
        /// The PackagePart properties can not be accessed if the parent container is closed.
        /// </summary>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        public CompressionOption CompressionOption
        {
            get
            {
                CheckInvalidState();
                return _compressionOption;
            } 
        }
 
        #endregion Public Properties    
        
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------        
 
        #region Public Methods
 
        #region Content Type Method
 
        /// <summary>
        /// Custom Implementation for the GetContentType Method
        /// This method should only be implemented by those physical format implementors where
        /// the value for the content type cannot be provided at the time of construction of 
        /// Part object and if calculating the content type value is a non-trivial or costly 
        /// operation. The return value has to be a valid ContentType. This method will be used in 
        /// real corner cases. The most common usage should be to provide the content type in the 
        /// constructor.
        /// This method is only for lazy initialization of the Content type value and will only
        /// be called once. There is no way to change the content type of the part once it is 
        /// assigned. 
        /// </summary>        
        /// <returns>Content type for the Part</returns>
        /// <exception cref="NotSupportedException">By default, this method throws a NotSupportedException. If a subclass wants to 
        /// initialize the content type for a PackagePart in a lazy manner they must override this method.</exception>
        protected virtual string GetContentTypeCore()
        {
           throw new NotSupportedException(SR.Get(SRID.GetContentTypeCoreNotImplemented));
        }
 
 
        #endregion Content Type Method
 
        #region Stream Methods
 
        /// <summary>
        /// Returns the underlying stream that is represented by this part
        /// with the default FileMode and FileAccess
        /// Note: If you are requesting a stream for a relationship part and 
        /// at the same time using relationship APIs to manipulate relationships,
        /// the final persisted data will depend on which data gets flushed last.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the subclass fails to provide a non-null stream object</exception>
        public Stream GetStream()
        {
            CheckInvalidState();
            return GetStream(FileMode.OpenOrCreate, _container.FileOpenAccess);
        }
 
        /// <summary>
        /// Returns the underlying stream in the specified mode and the 
        /// default FileAccess
        /// Note: If you are requesting a stream for a relationship part for editing 
        /// and at the same time using relationship APIs to manipulate relationships,
        /// the final persisted data will depend on which data gets flushed last.
        /// </summary>
        /// <param name="mode"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="ArgumentOutOfRangeException">If FileMode enumeration [mode] does not have one of the valid values</exception>
        /// <exception cref="IOException">If FileAccess.Read is provided and FileMode values are any of the following - 
        /// FileMode.Create, FileMode.CreateNew, FileMode.Truncate, FileMode.Append</exception>
        /// <exception cref="IOException">If the mode and access for the Package and the Stream are not compatible</exception>
        /// <exception cref="IOException">If the subclass fails to provide a non-null stream object</exception>
        public Stream GetStream(FileMode mode)
        {
            CheckInvalidState();
            return GetStream(mode, _container.FileOpenAccess);
        }
 
        /// <summary>
        /// Returns the underlying stream that is represented by this part 
        /// in the specified mode with the access.
        /// Note: If you are requesting a stream for a relationship part and 
        /// at the same time using relationship APIs to manipulate relationships,
        /// the final persisted data will depend on which data gets flushed last.
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="access"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="ArgumentOutOfRangeException">If FileMode enumeration [mode] does not have one of the valid values</exception>
        /// <exception cref="ArgumentOutOfRangeException">If FileAccess enumeration [access] does not have one of the valid values</exception>
        /// <exception cref="IOException">If FileAccess.Read is provided and FileMode values are any of the following - 
        /// FileMode.Create, FileMode.CreateNew, FileMode.Truncate, FileMode.Append</exception>
        /// <exception cref="IOException">If the mode and access for the Package and the Stream are not compatible</exception>
        /// <exception cref="IOException">If the subclass fails to provide a non-null stream object</exception>
        public Stream GetStream(FileMode mode, FileAccess access)
        {
            CheckInvalidState();
            ThrowIfOpenAccessModesAreIncompatible(mode, access);
 
            Stream s = GetStreamCore(mode, access);
 
            if (s == null)
                throw new IOException(SR.Get(SRID.NullStreamReturned));
 
            //Detect if any stream implementations are returning all three
            //properties - CanSeek, CanWrite and CanRead as false. Such a 
            //stream should be pretty much useless. And as per current programming
            //practice, these properties are all false, when the stream has been
            //disposed.
            Debug.Assert(!IsStreamClosed(s));
 
            //Lazy init
            if(_requestedStreams==null)
                _requestedStreams = new List<Stream>(); //Default capacity is 4
 
            //Delete all the closed streams from the _requestedStreams list.
            //Each time a new stream is handed out, we go through the list
            //to clean up streams that were handed out and have been closed.
            //Thus those stream can be garbage collected and we will avoid 
            //keeping around stream objects that have been disposed
            CleanUpRequestedStreamsList();
 
            _requestedStreams.Add(s);
            
            return s;
        }
 
        /// <summary>
        /// Custom Implementation for the GetSream Method
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="access"></param>
        /// <returns></returns>
        protected abstract Stream GetStreamCore(FileMode mode, FileAccess access);
 
        #endregion Stream Methods
 
        #region PackageRelationship Methods
        /// <summary>
        /// Adds a relationship to this PackagePart with the Target PackagePart specified as the Uri
        /// Initial and trailing spaces in the name of the PackageRelationship are trimmed.
        /// </summary>
        /// <param name="targetUri"></param>
        /// <param name="targetMode">Enumeration indicating the base uri for the target uri</param>
        /// <param name="relationshipType">PackageRelationship type, having uri like syntax that is used to 
        /// uniquely identify the role of the relationship</param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the package is readonly, it cannot be modified</exception>
        /// <exception cref="ArgumentNullException">If parameter "targetUri" is null</exception>
        /// <exception cref="ArgumentNullException">If parameter "relationshipType" is null</exception>
        /// <exception cref="ArgumentOutOfRangeException">If parameter "targetMode" enumeration does not have a valid value</exception>
        /// <exception cref="ArgumentException">If TargetMode is TargetMode.Internal and the targetUri is an absolute Uri </exception>
        /// <exception cref="ArgumentException">If relationship is being targeted to a relationship part</exception>
        public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType)
        {
            return CreateRelationship(targetUri, targetMode, relationshipType, null);
        }
 
        /// <summary>
        /// Adds a relationship to this PackagePart with the Target PackagePart specified as the Uri
        /// Initial and trailing spaces in the name of the PackageRelationship are trimmed.
        /// </summary>
        /// <param name="targetUri"></param>
        /// <param name="targetMode">Enumeration indicating the base uri for the target uri</param>
        /// <param name="relationshipType">PackageRelationship type, having uri like syntax that is used to 
        /// uniquely identify the role of the relationship</param>
        /// <param name="id">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.</param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the package is readonly, it cannot be modified</exception>
        /// <exception cref="ArgumentNullException">If parameter "targetUri" is null</exception>
        /// <exception cref="ArgumentNullException">If parameter "relationshipType" is null</exception>
        /// <exception cref="ArgumentOutOfRangeException">If parameter "targetMode" enumeration does not have a valid value</exception>
        /// <exception cref="ArgumentException">If TargetMode is TargetMode.Internal and the targetUri is an absolute Uri </exception>
        /// <exception cref="ArgumentException">If relationship is being targeted to a relationship part</exception>
        /// <exception cref="System.Xml.XmlException">If parameter "id" is not a valid Xsd Id</exception>
        /// <exception cref="System.Xml.XmlException">If an id is provided in the method, and its not unique</exception>
        public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType, String id)
        {
            CheckInvalidState();
            _container.ThrowIfReadOnly();
            EnsureRelationships();
            //All parameter validation is done in the following method
            return _relationships.Add(targetUri, targetMode, relationshipType, id);
        }
 
        /// <summary>
        /// Deletes a relationship from the PackagePart. This is done based on the 
        /// relationship's ID. The target PackagePart is not affected by this operation.
        /// </summary>
        /// <param name="id">The ID of the relationship to delete. An invalid ID will not
        /// throw an exception, but nothing will be deleted.</param>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the package is readonly, it cannot be modified</exception>
        /// <exception cref="ArgumentNullException">If parameter "id" is null</exception>
        /// <exception cref="System.Xml.XmlException">If parameter "id" is not a valid Xsd Id</exception>
        public void DeleteRelationship(string id)
        {
            CheckInvalidState();
            _container.ThrowIfReadOnly();
            _container.ThrowIfInStreamingCreation("DeleteRelationship");
 
            if (id == null)
                throw new ArgumentNullException("id");
 
            InternalRelationshipCollection.ThrowIfInvalidXsdId(id);
 
            EnsureRelationships();
            _relationships.Delete(id);
        }
 
        /// <summary>
        /// Returns a collection of all the Relationships that are
        /// owned by this PackagePart
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the package is write only, no information can be retrieved from it</exception>
        public PackageRelationshipCollection GetRelationships()
        {
            //All the validations for dispose and file access are done in the 
            //GetRelationshipsHelper method.
 
            return GetRelationshipsHelper(null);
        }
 
        /// <summary>
        /// Returns a collection of filtered Relationships that are
        /// owned by this PackagePart
        /// The relationshipType string is compared with the type of the relationships
        /// in a case sensitive and culture ignorant manner. 
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the package is write only, no information can be retrieved from it</exception>
        /// <exception cref="ArgumentNullException">If parameter "relationshipType" is null</exception>
        /// <exception cref="ArgumentException">If parameter "relationshipType" is an empty string</exception>
        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
            CheckInvalidState();
            _container.ThrowIfWriteOnly();
            
            if (relationshipType == null)
                throw new ArgumentNullException("relationshipType");
 
            InternalRelationshipCollection.ThrowIfInvalidRelationshipType(relationshipType);
           
            return GetRelationshipsHelper(relationshipType);
        }
 
        /// <summary>
        /// Retrieve a relationship per ID.
        /// </summary>
        /// <param name="id">The relationship ID.</param>
        /// <returns>The relationship with ID 'id' or throw an exception if not found.</returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the package is write only, no information can be retrieved from it</exception>
        /// <exception cref="ArgumentNullException">If parameter "id" is null</exception>
        /// <exception cref="System.Xml.XmlException">If parameter "id" is not a valid Xsd Id</exception>
        /// <exception cref="InvalidOperationException">If the requested relationship does not exist in the Package</exception>
        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.PackagePartRelationshipDoesNotExist));
            else
                return returnedRelationship;
        }
 
        /// <summary>
        /// Returns whether there is a relationship with the specified ID.
        /// </summary>
        /// <param name="id">The relationship ID.</param>
        /// <returns>true iff a relationship with ID 'id' is defined on this source.</returns>
        /// <exception cref="InvalidOperationException">If this part has been deleted</exception>
        /// <exception cref="InvalidOperationException">If the parent package has been closed or disposed</exception>
        /// <exception cref="IOException">If the package is write only, no information can be retrieved from it</exception>
        /// <exception cref="ArgumentNullException">If parameter "id" is null</exception>
        /// <exception cref="System.Xml.XmlException">If parameter "id" is not a valid Xsd Id</exception>
        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
 
        //------------------------------------------------------
        //
        //  Public Events
        //
        //------------------------------------------------------
        // None
        //------------------------------------------------------
        //
        //  Internal Constructors
        //
        //------------------------------------------------------
        // None       
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
 
        internal bool IsRelationshipPart
        {
            get
            {
                return _isRelationshipPart;
            }
        }
 
        //This property can be set to indicate if the part has been deleted
        internal bool IsDeleted
        {
            get
            {
                return _deleted;
            }
            set
            {
                _deleted = value;
            }
        }
 
        //This property can be set to indicate if the part has been deleted
        internal bool IsClosed
        {
            get
            {
                return _disposed;
            }
        }
 
        /// <summary>
        /// This property returns the content type of the part 
        /// as a validated strongly typed ContentType object
        /// </summary>
        internal ContentType ValidatedContentType
        {
            get
            {
                return _contentType;
            }
        }
 
        #endregion Internal Properties
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        //Delete all the relationships for this part
        internal void ClearRelationships()
        {
            if(_relationships!=null)
                _relationships.Clear();
        }
 
        //Flush all the streams that are currently opened for this part and the relationships for this part
        //Note: This method is never be called on a deleted part
        internal void Flush()
        {
            Debug.Assert(_deleted != true, "PackagePart.Flush should never be called on a deleted part");
 
            if (_requestedStreams!= null)
            {
                foreach (Stream s in _requestedStreams)
                {                    
                    // Streams in this list are never set to null, so we do not need to check for
                    // stream being null; However it could be closed by some external code. In that case 
                    // this property (CanWrite) will still be accessible and we can check to see 
                    // whether we can call flush or no.
                    if (s.CanWrite)
                        s.Flush();
                }
            }
 
            // Relationships for this part should have been flushed earlier in the Package.Flush method.            
        }
 
        //Close all the streams that are open for this part.
        internal void Close()
        {
            if (!_disposed)
            {
                try
                {
                    if (_requestedStreams != null)
                    {
                        //Adding this extra check here to optimize delete operation
                        //Everytime we delete a part we close it before deleting to 
                        //ensure that its deleted in a valid state. However, we do not
                        //need to persist any changes if the part is being deleted.
                        if (!_deleted)
                        {
                            foreach (Stream s in _requestedStreams)
                            {
                                s.Close();
                            }
                        }
                        _requestedStreams.Clear();
                    }
 
                    // In streaming creation, if the part has had no stream opened on it,
                    // it has not been physically added to the archive yet.
                    else if (_container.InStreamingCreation)
                    {
                        // Create an interleaving stream to make sure a physical item gets created.
                        GetStream(FileMode.CreateNew, _container.FileOpenAccess).Close();
                    }
 
                    // Relationships for this part should have been flushed/closed earlier in the Package.Close method. 
                }
                finally
                {                    
                    _requestedStreams = null;
                 
                    //InternalRelationshipCollection is not required any more
                    _relationships = null;
 
                    //Once the container is closed there is no way to get to the stream or any other part 
                    //in the container.
                    _container = null;
 
                    //We do not need to explicitly call GC.SuppressFinalize(this)
 
                    _disposed = true;
 
                }
            }
        }
 
        #region Write-Time Streaming API
 
        /// <summary>
        /// According to the access mode (streaming or not),
        /// write the whole relationships part or just a nonterminal
        /// nonterminal piece for the relationships part.
        /// </summary>
        /// <remarks>
        /// The behavior of _relationships.Flush, which gets invoked here, will vary
        /// according as the package was opened for streaming production or not.
        /// </remarks>
        internal void FlushRelationships()
        {
            Debug.Assert(_deleted != true, "PackagePart.FlushRelationsips should never be called on a deleted part");
 
            // flush relationships
            if (_relationships != null && _container.FileOpenAccess != FileAccess.Read)
            {
                _relationships.Flush();
            }
        }
 
 
        internal void CloseRelationships()
        {
            if (!_deleted)
            {
                //Flush the relationships for this part.
                if (_container.InStreamingCreation)
                {
                    // Flush unsaved relationships,
                    // close XML markup and save as the last piece.
                    CloseStreamingRelationships();
                }
                else
                {
                    FlushRelationships();
                }
            }
        }
 
        
        /// <summary>
        /// Write a terminal piece for folderPath/_rels/baseName.rels
        /// </summary>
        private void CloseStreamingRelationships()
        {
            if (!_container.InStreamingCreation)
                throw new IOException(SR.Get(SRID.MethodAvailableOnlyInStreamingCreation, "CloseRelationships"));
 
            if (_relationships == null)
                return; // No relationship has been created.
 
            _relationships.CloseInStreamingCreationMode();
        }
 
        #endregion Write-Time Streaming API
 
        #endregion Internal Methods
 
        //------------------------------------------------------
        //
        //  Internal Events
        //
        //------------------------------------------------------
        // None
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        #region Private Methods
        
        // lazy init
        private void EnsureRelationships()
        {
           if (_relationships  == null)
            {
                // check here
                ThrowIfRelationship();
                
                // obtain the relationships from the PackageRelationship part (if available)
                _relationships = new InternalRelationshipCollection(this);
            }
        }
 
        //Make sure that the access modes for the container and the part are compatible
        private void ThrowIfOpenAccessModesAreIncompatible(FileMode mode, FileAccess access)
        {
            Package.ThrowIfFileModeInvalid(mode);
            Package.ThrowIfFileAccessInvalid(access);
 
            //Creating a part using a readonly stream.
            if(access == FileAccess.Read	&& 
                (mode == FileMode.Create || mode == FileMode.CreateNew || mode == FileMode.Truncate || mode == FileMode.Append))
                throw new IOException(SR.Get(SRID.UnsupportedCombinationOfModeAccess));
 
            //Incompatible access modes between container and part stream.
            if((_container.FileOpenAccess == FileAccess.Read && access != FileAccess.Read) ||
                (_container.FileOpenAccess == FileAccess.Write && access != FileAccess.Write) )
                throw new IOException(SR.Get(SRID.ContainerAndPartModeIncompatible));
        }
 
        //Check if the part is in an invalid state
        private void CheckInvalidState()
        {
            ThrowIfPackagePartDeleted();
            ThrowIfParentContainerClosed();
        }
 
        //If the parent container is closed then the operations on this part like getting stream make no sense
        private void ThrowIfParentContainerClosed()
        {
            if (_container == null)
                throw new InvalidOperationException(SR.Get(SRID.ParentContainerClosed));
        }
 
        //If the part has been deleted then we throw
        private void ThrowIfPackagePartDeleted()
        {
            if (_deleted == true)
                throw new InvalidOperationException(SR.Get(SRID.PackagePartDeleted));
        }
 
        // some operations are invalid if we are a relationship part
        private void ThrowIfRelationship()
        {
            if (IsRelationshipPart)
                throw new InvalidOperationException(SR.Get(SRID.RelationshipPartsCannotHaveRelationships));
        }
        
        /// <summary>
        /// Retrieve a relationship per ID.
        /// </summary>
        /// <param name="id">The relationship ID.</param>
        /// <returns>The relationship with ID 'id' or null if not found.</returns>
        private PackageRelationship GetRelationshipHelper(string id)
        {
            CheckInvalidState();
            _container.ThrowIfWriteOnly();
 
            if (id == null)
                throw new ArgumentNullException("id");
 
            InternalRelationshipCollection.ThrowIfInvalidXsdId(id);
 
            EnsureRelationships();
            return _relationships.GetRelationship(id);
        }
 
        /// <summary>
        /// Returns a collection of all the Relationships that are
        /// owned by this PackagePart, based on the filter string
        /// </summary>
        /// <returns></returns>
        private PackageRelationshipCollection GetRelationshipsHelper(string filterString)
        {
            CheckInvalidState();
            _container.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);
        }
 
        //Deletes all the streams that have been closed from the _requestedStreams list.
        private void CleanUpRequestedStreamsList()
        {
            if (_requestedStreams != null)
            {
                for (int i = _requestedStreams.Count-1; i >= 0; i--)
                {
                    if (IsStreamClosed(_requestedStreams[i]))
                        _requestedStreams.RemoveAt(i);
                }
            }
        }
 
        //Detect if the stream has been closed.
        //When a stream is closed the three flags - CanSeek, CanRead and CanWrite
        //return false. These properties do not throw ObjectDisposedException.
        //So we rely on the values of these properties to determine if a stream 
        //has been closed.
        private bool IsStreamClosed(Stream s)
        {
            if (s.CanRead == false && s.CanSeek == false && s.CanWrite == false)
                return true;
            else
                return false;
        }
 
        #endregion Private Methods
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Members
 
        private PackUriHelper.ValidatedPartUri _uri;
        private Package                         _container;
        private ContentType                     _contentType;
        private List<Stream>                    _requestedStreams;
        private InternalRelationshipCollection  _relationships;
        private CompressionOption               _compressionOption = CompressionOption.NotCompressed;
        private bool                            _disposed;
        private bool                            _deleted;
        private bool                            _isRelationshipPart;
            
        #endregion Private Members
    }
}