PackagePart.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / System / IO / Packaging / PackagePart.cs / 1305600 / PackagePart.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 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
{
    ///  
    /// This class represents the a PackagePart within a container.
    /// This is a part of the Packaging Layer APIs 
    ///  
    public abstract class PackagePart
    { 
        //-----------------------------------------------------
        //
        //  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
        /// 
        /// 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.
        /// 
        ///  
        /// Package in which this part is being created
        /// uri of the part 
        /// If parameter "package" is null
        /// If parameter "partUri" is null
        protected PackagePart(Package package, Uri partUri)
            :this(package, partUri, null, CompressionOption.NotCompressed) 
        {
        } 
 
        /// 
        /// 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. 
        ///
        /// 
        /// Package in which this part is being created
        /// uri of the part 
        /// 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 
        /// If parameter "package" is null
        /// If parameter "partUri" is null
        /// If parameter "partUri" does not conform to the valid partUri syntax
        protected PackagePart(Package package, Uri partUri, string contentType) 
            :this(package, partUri, contentType, CompressionOption.NotCompressed)
        { 
        } 

 
        /// 
        /// 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. 
        ///
        /// 
        /// Package in which this part is being created
        /// uri of the part 
        /// 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 
        /// compression option for this part
        /// If parameter "package" is null
        /// If parameter "partUri" is null
        /// If CompressionOption enumeration [compressionOption] does not have one of the valid values 
        /// If parameter "partUri" does not conform to the valid partUri syntax
        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 

        ///  
        /// 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.
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or disposed
        public Uri Uri 
        {
            get 
            { 
                CheckInvalidState();
                return _uri; 
            }
        }

        ///  
        /// 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. 
        /// 
        /// Content Type of the Part [can never return null]  
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed
        /// If the subclass fails to provide a non-null content type value. 
        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(); 
            } 
        }
 

        /// 
        /// The parent container for this PackagePart
        /// The PackagePart properties can not be accessed if the parent container is closed. 
        /// 
        ///  
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed
        public Package Package 
        {
            get
            {
                CheckInvalidState(); 
                return _container;
            } 
        } 

        ///  
        /// 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.
        /// 
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed
        public CompressionOption CompressionOption 
        { 
            get
            { 
                CheckInvalidState();
                return _compressionOption;
            }
        } 

        #endregion Public Properties 
 
        //------------------------------------------------------
        // 
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
 
        #region Content Type Method 

        ///  
        /// 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.
        /// 
        /// Content type for the Part
        /// 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.
        protected virtual string GetContentTypeCore() 
        { 
           throw new NotSupportedException(SR.Get(SRID.GetContentTypeCoreNotImplemented));
        } 


        #endregion Content Type Method
 
        #region Stream Methods
 
        ///  
        /// 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.
        ///  
        /// 
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed 
        /// If the subclass fails to provide a non-null stream object
        public Stream GetStream() 
        {
            CheckInvalidState();
            return GetStream(FileMode.OpenOrCreate, _container.FileOpenAccess);
        } 

        ///  
        /// 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.
        /// 
        ///  
        /// 
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed 
        /// If FileMode enumeration [mode] does not have one of the valid values
        /// If FileAccess.Read is provided and FileMode values are any of the following - 
        /// FileMode.Create, FileMode.CreateNew, FileMode.Truncate, FileMode.Append
        /// If the mode and access for the Package and the Stream are not compatible
        /// If the subclass fails to provide a non-null stream object
        public Stream GetStream(FileMode mode) 
        {
            CheckInvalidState(); 
            return GetStream(mode, _container.FileOpenAccess); 
        }
 
        /// 
        /// 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. 
        ///  
        /// 
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or disposed
        /// If FileMode enumeration [mode] does not have one of the valid values 
        /// If FileAccess enumeration [access] does not have one of the valid values
        /// If FileAccess.Read is provided and FileMode values are any of the following - 
        /// FileMode.Create, FileMode.CreateNew, FileMode.Truncate, FileMode.Append 
        /// If the mode and access for the Package and the Stream are not compatible
        /// If the subclass fails to provide a non-null stream object 
        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(); //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; 
        }
 
        ///  
        /// Custom Implementation for the GetSream Method
        ///  
        /// 
        /// 
        /// 
        protected abstract Stream GetStreamCore(FileMode mode, FileAccess access); 

        #endregion Stream Methods 
 
        #region PackageRelationship Methods
        ///  
        /// 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.
        /// 
        ///  
        /// 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 part has been deleted 
        /// If the parent package has been closed or 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);
        }

        ///  
        /// 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. 
        ///  
        /// 
        /// 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 part has been deleted 
        /// If the parent package has been closed or 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) 
        {
            CheckInvalidState(); 
            _container.ThrowIfReadOnly();
            EnsureRelationships();
            //All parameter validation is done in the following method
            return _relationships.Add(targetUri, targetMode, relationshipType, id); 
        }
 
        ///  
        /// Deletes a relationship from the PackagePart. 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 part has been deleted 
        /// If the parent package has been closed or 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) 
        {
            CheckInvalidState();
            _container.ThrowIfReadOnly();
            _container.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 this PackagePart
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or 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 this PackagePart 
        /// The relationshipType string is compared with the type of the relationships 
        /// in a case sensitive and culture ignorant manner.
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or 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
            CheckInvalidState();
            _container.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 part has been deleted 
        /// If the parent package has been closed or 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.PackagePartRelationshipDoesNotExist)); 
            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 part has been deleted
        /// If the parent package has been closed or 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

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

        ///  
        /// This property returns the content type of the part
        /// as a validated strongly typed ContentType object
        /// 
        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 

        ///  
        /// According to the access mode (streaming or not), 
        /// write the whole relationships part or just a nonterminal
        /// nonterminal piece for the relationships part. 
        /// 
        /// 
        /// The behavior of _relationships.Flush, which gets invoked here, will vary
        /// according as the package was opened for streaming production or not. 
        /// 
        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(); 
                }
            } 
        } 

 
        /// 
        /// Write a terminal piece for folderPath/_rels/baseName.rels
        /// 
        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)); 
        } 

        ///  
        /// Retrieve a relationship per ID.
        /// 
        /// The relationship ID.
        /// The relationship with ID 'id' or null if not found. 
        private PackageRelationship GetRelationshipHelper(string id)
        { 
            CheckInvalidState(); 
            _container.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 this PackagePart, based on the filter string
        ///  
        /// 
        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                    _requestedStreams;
        private InternalRelationshipCollection  _relationships;
        private CompressionOption               _compressionOption = CompressionOption.NotCompressed;
        private bool                            _disposed; 
        private bool                            _deleted;
        private bool                            _isRelationshipPart; 
 
        #endregion Private Members
    } 
}

// 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 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
{
    ///  
    /// This class represents the a PackagePart within a container.
    /// This is a part of the Packaging Layer APIs 
    ///  
    public abstract class PackagePart
    { 
        //-----------------------------------------------------
        //
        //  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
        /// 
        /// 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.
        /// 
        ///  
        /// Package in which this part is being created
        /// uri of the part 
        /// If parameter "package" is null
        /// If parameter "partUri" is null
        protected PackagePart(Package package, Uri partUri)
            :this(package, partUri, null, CompressionOption.NotCompressed) 
        {
        } 
 
        /// 
        /// 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. 
        ///
        /// 
        /// Package in which this part is being created
        /// uri of the part 
        /// 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 
        /// If parameter "package" is null
        /// If parameter "partUri" is null
        /// If parameter "partUri" does not conform to the valid partUri syntax
        protected PackagePart(Package package, Uri partUri, string contentType) 
            :this(package, partUri, contentType, CompressionOption.NotCompressed)
        { 
        } 

 
        /// 
        /// 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. 
        ///
        /// 
        /// Package in which this part is being created
        /// uri of the part 
        /// 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 
        /// compression option for this part
        /// If parameter "package" is null
        /// If parameter "partUri" is null
        /// If CompressionOption enumeration [compressionOption] does not have one of the valid values 
        /// If parameter "partUri" does not conform to the valid partUri syntax
        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 

        ///  
        /// 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.
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or disposed
        public Uri Uri 
        {
            get 
            { 
                CheckInvalidState();
                return _uri; 
            }
        }

        ///  
        /// 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. 
        /// 
        /// Content Type of the Part [can never return null]  
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed
        /// If the subclass fails to provide a non-null content type value. 
        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(); 
            } 
        }
 

        /// 
        /// The parent container for this PackagePart
        /// The PackagePart properties can not be accessed if the parent container is closed. 
        /// 
        ///  
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed
        public Package Package 
        {
            get
            {
                CheckInvalidState(); 
                return _container;
            } 
        } 

        ///  
        /// 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.
        /// 
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed
        public CompressionOption CompressionOption 
        { 
            get
            { 
                CheckInvalidState();
                return _compressionOption;
            }
        } 

        #endregion Public Properties 
 
        //------------------------------------------------------
        // 
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
 
        #region Content Type Method 

        ///  
        /// 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.
        /// 
        /// Content type for the Part
        /// 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.
        protected virtual string GetContentTypeCore() 
        { 
           throw new NotSupportedException(SR.Get(SRID.GetContentTypeCoreNotImplemented));
        } 


        #endregion Content Type Method
 
        #region Stream Methods
 
        ///  
        /// 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.
        ///  
        /// 
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed 
        /// If the subclass fails to provide a non-null stream object
        public Stream GetStream() 
        {
            CheckInvalidState();
            return GetStream(FileMode.OpenOrCreate, _container.FileOpenAccess);
        } 

        ///  
        /// 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.
        /// 
        ///  
        /// 
        /// If this part has been deleted 
        /// If the parent package has been closed or disposed 
        /// If FileMode enumeration [mode] does not have one of the valid values
        /// If FileAccess.Read is provided and FileMode values are any of the following - 
        /// FileMode.Create, FileMode.CreateNew, FileMode.Truncate, FileMode.Append
        /// If the mode and access for the Package and the Stream are not compatible
        /// If the subclass fails to provide a non-null stream object
        public Stream GetStream(FileMode mode) 
        {
            CheckInvalidState(); 
            return GetStream(mode, _container.FileOpenAccess); 
        }
 
        /// 
        /// 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. 
        ///  
        /// 
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or disposed
        /// If FileMode enumeration [mode] does not have one of the valid values 
        /// If FileAccess enumeration [access] does not have one of the valid values
        /// If FileAccess.Read is provided and FileMode values are any of the following - 
        /// FileMode.Create, FileMode.CreateNew, FileMode.Truncate, FileMode.Append 
        /// If the mode and access for the Package and the Stream are not compatible
        /// If the subclass fails to provide a non-null stream object 
        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(); //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; 
        }
 
        ///  
        /// Custom Implementation for the GetSream Method
        ///  
        /// 
        /// 
        /// 
        protected abstract Stream GetStreamCore(FileMode mode, FileAccess access); 

        #endregion Stream Methods 
 
        #region PackageRelationship Methods
        ///  
        /// 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.
        /// 
        ///  
        /// 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 part has been deleted 
        /// If the parent package has been closed or 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);
        }

        ///  
        /// 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. 
        ///  
        /// 
        /// 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 part has been deleted 
        /// If the parent package has been closed or 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) 
        {
            CheckInvalidState(); 
            _container.ThrowIfReadOnly();
            EnsureRelationships();
            //All parameter validation is done in the following method
            return _relationships.Add(targetUri, targetMode, relationshipType, id); 
        }
 
        ///  
        /// Deletes a relationship from the PackagePart. 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 part has been deleted 
        /// If the parent package has been closed or 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) 
        {
            CheckInvalidState();
            _container.ThrowIfReadOnly();
            _container.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 this PackagePart
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or 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 this PackagePart 
        /// The relationshipType string is compared with the type of the relationships 
        /// in a case sensitive and culture ignorant manner.
        ///  
        /// 
        /// If this part has been deleted
        /// If the parent package has been closed or 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
            CheckInvalidState();
            _container.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 part has been deleted 
        /// If the parent package has been closed or 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.PackagePartRelationshipDoesNotExist)); 
            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 part has been deleted
        /// If the parent package has been closed or 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

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

        ///  
        /// This property returns the content type of the part
        /// as a validated strongly typed ContentType object
        /// 
        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 

        ///  
        /// According to the access mode (streaming or not), 
        /// write the whole relationships part or just a nonterminal
        /// nonterminal piece for the relationships part. 
        /// 
        /// 
        /// The behavior of _relationships.Flush, which gets invoked here, will vary
        /// according as the package was opened for streaming production or not. 
        /// 
        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(); 
                }
            } 
        } 

 
        /// 
        /// Write a terminal piece for folderPath/_rels/baseName.rels
        /// 
        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)); 
        } 

        ///  
        /// Retrieve a relationship per ID.
        /// 
        /// The relationship ID.
        /// The relationship with ID 'id' or null if not found. 
        private PackageRelationship GetRelationshipHelper(string id)
        { 
            CheckInvalidState(); 
            _container.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 this PackagePart, based on the filter string
        ///  
        /// 
        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                    _requestedStreams;
        private InternalRelationshipCollection  _relationships;
        private CompressionOption               _compressionOption = CompressionOption.NotCompressed;
        private bool                            _disposed; 
        private bool                            _deleted;
        private bool                            _isRelationshipPart; 
 
        #endregion Private Members
    } 
}

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