PackagePart.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Base / System / IO / Packaging / PackagePart.cs / 1 / 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 
 
#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 
 
#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