File: src\Framework\MS\Internal\IO\Packaging\CorePropertiesFilter.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//---------------------------------------------------------------------------
//
// <copyright file="CorePropertiesFilter.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//              Implements indexing filters for core properties.
//              Invoked by PackageFilter and EncryptedPackageFilter
//              for filtering core properties in Package and
//              EncryptedPackageEnvelope respectively.
//
// History:
//  07/18/2005: ArindamB: Initial implementation
//---------------------------------------------------------------------------
 
using System;
using System.Windows;
using System.IO.Packaging;
using MS.Internal.Interop;
using System.Runtime.InteropServices;
using System.Globalization;
 
namespace MS.Internal.IO.Packaging
{
    #region CorePropertiesFilter
 
    /// <summary>
    /// Class for indexing filters for core properties.
    /// Implements IManagedFilter to extract property chunks and values
    /// from CoreProperties. 
    /// </summary>
    internal class CorePropertiesFilter : IManagedFilter
    {
        #region Nested types
 
        /// <summary>
        /// Represents a property chunk.
        /// </summary>
        private class PropertyChunk : ManagedChunk
        {
            internal PropertyChunk(uint chunkId, Guid guid, uint propId)
                : base(
                    chunkId,
                    CHUNK_BREAKTYPE.CHUNK_EOS,
                    new ManagedFullPropSpec(guid, propId),
                    (uint)CultureInfo.InvariantCulture.LCID,
                    CHUNKSTATE.CHUNK_VALUE
                )
            {
            }
        }
 
        #endregion Nested types
 
        #region Constructor
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="coreProperties">core properties to filter</param>
        internal CorePropertiesFilter(PackageProperties coreProperties)
        {
            if (coreProperties == null)
            {
                throw new ArgumentNullException("coreProperties");
            }
 
            _coreProperties = coreProperties;
        }
 
        #endregion Constructor
 
        #region IManagedFilter methods
 
        /// <summary>
        /// Initialzes the session for this filter.
        /// </summary>
        /// <param name="grfFlags">usage flags</param>
        /// <param name="aAttributes">array of Managed FULLPROPSPEC structs to restrict responses</param>
        /// <returns>IFILTER_FLAGS_NONE. Return value is effectively ignored by the caller.</returns>
        public IFILTER_FLAGS Init(IFILTER_INIT grfFlags, ManagedFullPropSpec[] aAttributes)
        {
            // NOTE: Methods parameters have already been validated by XpsFilter.
 
            _grfFlags = grfFlags;
            _aAttributes = aAttributes;
 
            // Each call to Init() creates a new enumerator
            // with parameters corresponding to current Init() call.
            _corePropertyEnumerator = new CorePropertyEnumerator(
                _coreProperties, _grfFlags, _aAttributes);
 
            return IFILTER_FLAGS.IFILTER_FLAGS_NONE;
        }
 
        /// <summary>
        /// Returns description of the next chunk.
        /// </summary>
        /// <returns>Chunk descriptor if there is a next chunk, else null.</returns>
        public ManagedChunk GetChunk()
        {
            // No GetValue() call pending from this point on.
            _pendingGetValue = false;
 
            //
            // Move to the next core property that exists and has a value
            // and create a chunk descriptor out of it.
            //
 
            if (!CorePropertyEnumerator.MoveNext())
            {
                // End of chunks.
                return null;
            }
 
            ManagedChunk chunk = new PropertyChunk(
                AllocateChunkID(),
                CorePropertyEnumerator.CurrentGuid,
                CorePropertyEnumerator.CurrentPropId
                );
 
            // GetValue() call pending from this point on
            // for the current GetChunk call.
            _pendingGetValue = true;
 
            return chunk;
        }
 
        /// <summary>
        /// Gets text content corresponding to current chunk.
        /// </summary>
        /// <param name="bufferCharacterCount"></param>
        /// <returns></returns>
        /// <remarks>Not supported in indexing of core properties.</remarks>
        public string GetText(int bufferCharacterCount)
        {
            throw new COMException(SR.Get(SRID.FilterGetTextNotSupported),
                (int)FilterErrorCode.FILTER_E_NO_TEXT);
        }
 
        /// <summary>
        /// Gets the property value corresponding to current chunk.
        /// </summary>
        /// <returns>Property value</returns>
        public object GetValue()
        {
            // If GetValue() is already called for current chunk,
            // return error with FILTER_E_NO_MORE_VALUES.
            if (!_pendingGetValue)
            {
                throw new COMException(SR.Get(SRID.FilterGetValueAlreadyCalledOnCurrentChunk),
                    (int)FilterErrorCode.FILTER_E_NO_MORE_VALUES);
            }
 
            // No GetValue() call pending from this point on
            // until another call to GetChunk() is made successfully.
            _pendingGetValue = false;
 
            return CorePropertyEnumerator.CurrentValue;
        }
 
        #endregion IManagedFilter methods
 
        #region Private methods
 
        /// <summary>
        /// Generates unique and legal chunk ID.
        /// To be called prior to returning a chunk.
        /// </summary>
        /// <remarks>
        /// 0 is an illegal value, so this function never returns 0.
        /// After the counter reaches UInt32.MaxValue, it wraps around to 1.
        /// </remarks>
        private uint AllocateChunkID()
        {
            if (_chunkID == UInt32.MaxValue)
            {
                _chunkID = 1;
            }
            else
            {
                _chunkID++;
            }
            return _chunkID;
        }
 
        #endregion
 
        #region Properties
 
        /// <summary>
        /// Enumerates core properties based on the
        /// core properties and parameters passed in to Init() call.
        /// </summary>
        private CorePropertyEnumerator CorePropertyEnumerator
        {
            get
            {
                if (_corePropertyEnumerator == null)
                {
                    _corePropertyEnumerator = new CorePropertyEnumerator(
                        _coreProperties, _grfFlags, _aAttributes);
                }
 
                return _corePropertyEnumerator;
            }
        }
 
        #endregion Properties
 
        #region Fields
 
        /// <summary>
        /// IFilter.Init parameters.
        /// Used to initialize CorePropertyEnumerator.
        /// </summary>
        IFILTER_INIT _grfFlags = 0;
        ManagedFullPropSpec[] _aAttributes = null;
 
        /// <summary>
        /// Chunk ID for the current chunk. Incremented for
        /// every next chunk.
        /// </summary>
        private uint _chunkID = 0;
 
        /// <summary>
        /// Indicates if GetValue() call is pending
        /// for the current chunk queried using GetChunk().
        /// If not, GetValue() returns FILTER_E_NO_MORE_VALUES.
        /// </summary>
        private bool _pendingGetValue = false;
 
        /// <summary>
        /// Enumerator used to iterate over the
        /// core properties and create chunk out of them. 
        /// </summary>
        private CorePropertyEnumerator _corePropertyEnumerator = null;
 
        /// <summary>
        /// Core properties being filtered.
        /// Could be PackageCoreProperties or EncryptedPackageCoreProperties.
        /// </summary>
        private PackageProperties _coreProperties = null;
 
        #endregion Fields
    }
 
    #endregion CorePropertiesFilter
 
    #region CorePropertyEnumerator
 
    /// <summary>
    /// Enumerator for CoreProperties. Used to iterate through the
    /// properties and obtain their property set GUID, property ID and value.
    /// </summary>
    internal class CorePropertyEnumerator
    {
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="coreProperties">CoreProperties to enumerate</param>
        /// <param name="grfFlags">
        /// if IFILTER_INIT_APPLY_INDEX_ATTRIBUTES is specified,
        /// this indicates all core properties to be returned unless
        /// the parameter aAttributes is non-empty.
        /// </param>
        /// <param name="attributes">
        /// attributes specified corresponding to the properties to filter.
        /// </param>
        internal CorePropertyEnumerator(PackageProperties coreProperties,
            IFILTER_INIT grfFlags,
            ManagedFullPropSpec[] attributes)
        {
            if (attributes != null && attributes.Length > 0)
            {
                //
                // If attruibutes list specified,
                // return core properties for only those attributes.
                //
 
                _attributes = attributes;
            }
            else if ((grfFlags & IFILTER_INIT.IFILTER_INIT_APPLY_INDEX_ATTRIBUTES)
                == IFILTER_INIT.IFILTER_INIT_APPLY_INDEX_ATTRIBUTES)
            {
                //
                // If no attributes list specified,
                // but IFILTER_INIT_APPLY_INDEX_ATTRIBUTES is present in grfFlags,
                // return all core properties.
                //
 
                _attributes = new ManagedFullPropSpec[]
                {
                    //
                    // SummaryInformation
                    //
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.Title),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.Subject),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.Creator),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.Keywords),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.Description),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.LastModifiedBy),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.Revision),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.LastPrinted),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.DateCreated),
                    new ManagedFullPropSpec(FormatId.SummaryInformation, PropertyId.DateModified),
 
                    //
                    // DocumentSummaryInformation
                    //
                    new ManagedFullPropSpec(FormatId.DocumentSummaryInformation, PropertyId.Category),
                    new ManagedFullPropSpec(FormatId.DocumentSummaryInformation, PropertyId.Identifier),
                    new ManagedFullPropSpec(FormatId.DocumentSummaryInformation, PropertyId.ContentType),
                    new ManagedFullPropSpec(FormatId.DocumentSummaryInformation, PropertyId.Language),
                    new ManagedFullPropSpec(FormatId.DocumentSummaryInformation, PropertyId.Version),
                    new ManagedFullPropSpec(FormatId.DocumentSummaryInformation, PropertyId.ContentStatus)
                };
            }
            else
            {
                // No core properties to be returned.
            }
 
            _coreProperties = coreProperties;
            _currentIndex = -1;
        }
 
        #endregion Constructors
 
        #region Internal methods
 
        /// <summary>
        /// Move to next property in the enumeration
        /// which exists and has a value.
        /// </summary>
        /// <returns></returns>
        internal bool MoveNext()
        {
            if (_attributes == null)
            {
                return false;
            }
 
            _currentIndex++;
 
            // Move to next existing property with value present.
            while (_currentIndex < _attributes.Length)
            {
                if (   _attributes[_currentIndex].Property.PropType == PropSpecType.Id
                    && CurrentValue != null)
                {
                    return true;
                }
                _currentIndex++;
            }
 
            // End of properties.
            return false;
        }
 
        #endregion Internal methods
 
        #region Internal properties
 
        /// <summary>
        /// Property set GUID for current propery.
        /// </summary>
        internal Guid CurrentGuid
        {
            get
            {
                ValidateCurrent();
                return _attributes[_currentIndex].Guid;
            }
        }
 
        /// <summary>
        /// Property ID for current property.
        /// </summary>
        internal uint CurrentPropId
        {
            get
            {
                ValidateCurrent();
                return _attributes[_currentIndex].Property.PropId;
            }
        }
 
        /// <summary>
        /// Value for current property.
        /// </summary>
        internal object CurrentValue
        {
            get
            {
                ValidateCurrent();
                return GetValue(CurrentGuid, CurrentPropId);
            }
        }
 
        #endregion Internal properties
 
        #region Private methods
 
        /// <summary>
        /// Check if the current entry in enumeration is valid.
        /// </summary>
        private void ValidateCurrent()
        {
            if (_currentIndex < 0 || _currentIndex >= _attributes.Length)
            {
                throw new InvalidOperationException(
                    SR.Get(SRID.CorePropertyEnumeratorPositionedOutOfBounds));
            }
        }
 
        #endregion
 
        #region Private properties
 
        /// <summary>
        /// Get value for the property corresponding to the
        /// property set GUID and property ID.
        /// </summary>
        /// <param name="guid">property set GUID</param>
        /// <param name="propId">property ID</param>
        /// <returns>
        /// property value which could be string or DateTime,
        /// or null if it doesn't exist.
        /// </returns>
        private object GetValue(Guid guid, uint propId)
        {
            if (guid == FormatId.SummaryInformation)
            {
                switch (propId)
                {
                    case PropertyId.Title:
                        return _coreProperties.Title;
 
                    case PropertyId.Subject:
                        return _coreProperties.Subject;
 
                    case PropertyId.Creator:
                        return _coreProperties.Creator;
 
                    case PropertyId.Keywords:
                        return _coreProperties.Keywords;
 
                    case PropertyId.Description:
                        return _coreProperties.Description;
 
                    case PropertyId.LastModifiedBy:
                        return _coreProperties.LastModifiedBy;
 
                    case PropertyId.Revision:
                        return _coreProperties.Revision;
 
                    case PropertyId.LastPrinted:
                        if (_coreProperties.LastPrinted != null)
                        {
                            return _coreProperties.LastPrinted.Value;
                        }
                        return null;
 
                    case PropertyId.DateCreated:
                        if (_coreProperties.Created != null)
                        {
                            return _coreProperties.Created.Value;
                        }
                        return null;
 
                    case PropertyId.DateModified:
                        if (_coreProperties.Modified != null)
                        {
                            return _coreProperties.Modified.Value;
                        }
                        return null;
                }
 
            }
            else if (guid == FormatId.DocumentSummaryInformation)
            {
                switch (propId)
                {
                    case PropertyId.Category:
                        return _coreProperties.Category;
 
                    case PropertyId.Identifier:
                        return _coreProperties.Identifier;
 
                    case PropertyId.ContentType:
                        return _coreProperties.ContentType;
 
                    case PropertyId.Language:
                        return _coreProperties.Language;
 
                    case PropertyId.Version:
                        return _coreProperties.Version;
 
                    case PropertyId.ContentStatus:
                        return _coreProperties.ContentStatus;
                }
            }
 
            // Property/value not found.
            return null;
        }
 
        #endregion Private properties
 
        #region Fields
 
        /// <summary>
        /// Reference to the CorePropeties to enumerate.
        /// </summary>
        private PackageProperties _coreProperties;
 
        /// <summary>
        /// Indicates the list of properties to be enumerated.
        /// </summary>
        private ManagedFullPropSpec[] _attributes = null;
 
        /// <summary>
        /// Index of the current property in enumeration.
        /// </summary>
        private int _currentIndex;
 
        #endregion Fields
    }
 
    #endregion CorePropertyEnumerator
 
}