File: Serialization\Manager\ReachSerializableProperties.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\ReachFramework\ReachFramework.csproj (ReachFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
/*++                                                                      
    Abstract:
        Defining some classes encapsulating some information about 
        the properties contained within an object and used in the 
        serialization process 
                                                                     
--*/
 
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Markup;
 
namespace System.Windows.Xps.Serialization
{
    internal class SerializablePropertyCollection :
                   IEnumerable,
                   IEnumerator
    {
        #region Contstructor
 
        /// <summary>
        /// Intantiate a SerializablePropertyCollection
        /// </summary>
        internal 
        SerializablePropertyCollection(
            PackageSerializationManager manager, 
            object                      targetObject
            )
        {
            this._simplePropertyCollection  = null;
            this._complexPropertyCollection = null;
            this._simplePropertiesIndex     = -1;
            this._complexPropertiesIndex    = -1;
            this._queueEnumerator           = null;
            this._serializationManager      = manager;
            this._target                    = targetObject;
            this._isSimpleMode              = true;
            
            Initialize(manager, _target);
        }
 
        #endregion Constructor
 
        #region IEnumerable implementation
 
        /// <summary>
        /// return the Enumerator for the collection
        /// </summary>
        public
        IEnumerator 
        GetEnumerator(
            )
        {
            return this;
        }
 
        #endregion IEnumerable implementation
 
        #region IEnumerator implementation
 
        /// <summary>
        /// Current object in the collection
        /// </summary>
        public 
        object
        Current
        {
            get 
            {
                return _queueEnumerator.Current;
            }
        }
 
        /// <summary>
        /// Move to the next PropertyContext
        /// </summary>
        public 
        bool 
        MoveNext()
        {
            bool canMoveToNext = false;
 
            if(_isSimpleMode)
            {
                if(_simplePropertiesIndex == -1)
                {
                    _queueEnumerator = _simplePropertyCollection.GetEnumerator();
                }
                if(_simplePropertiesIndex < _simplePropertyCollection.Count-1)
                {
                    _simplePropertiesIndex++;
                    _queueEnumerator.MoveNext();
                    canMoveToNext = true;
                }
                else
                {
                    _isSimpleMode = false;
                }
            }
 
            if(!_isSimpleMode)
            {
                if(_complexPropertiesIndex == -1)
                {
                    _queueEnumerator = _complexPropertyCollection.GetEnumerator();
                }
                if(_complexPropertiesIndex < _complexPropertyCollection.Count-1)
                {
                    _complexPropertiesIndex++;
                    _queueEnumerator.MoveNext();
                    canMoveToNext = true;
                }
            }
 
            return canMoveToNext;
        }
 
        /// <summary>
        /// Reset all necessary indices and pointers to 
        /// the beginning of the collection
        /// </summary>
        public
        void 
        Reset(
            )
        {
            this._simplePropertiesIndex     = -1;
            this._complexPropertiesIndex    = -1;
            this._queueEnumerator           = null;
            this._isSimpleMode              = true;
        }
        
        #endregion IEnumerator implementation
 
        #region Internal Methods
 
        /// <summary>
        /// Initialize this instance
        /// </summary>
        internal 
        void 
        Initialize(
            PackageSerializationManager serializationManager, 
            object                      targetObject
            )
        {
            this._serializationManager = serializationManager;
            this._target               = targetObject;
 
            //
            // Collect all serializable properties on the
            // current object instance. Those could be
            // o clr properties
            // o dependency properties
            //
            if (_simplePropertyCollection == null)
            {
                _simplePropertyCollection = new Queue();
            }
 
            if (_complexPropertyCollection == null)
            {
                _complexPropertyCollection = new Queue();
            }
 
            //
            // Collecting information about the CLR properties
            //
            InitializeSerializableClrProperties();
            
            //
            // Now that we are done with the clr serializable properties we need to 
            // iterate through locally set dependency properties on this instance
            // that have not been serialized already. 
            // Note: Dependency Properties can only be set on Dependency Objects
            //
            InitializeSerializableDependencyProperties();
 
            //
            // Reset the enumerator 
            // to begin enumerating 
            // from the start of the 
            // properties collection            
            //
            Reset();
        }
 
        /// <summary>
        /// Clear all references for reuse without
        /// instantiation. This is a way of caching
        /// already instantiated through recycling
        /// for future use.
        /// </summary>
        internal void Clear()
        {
            this._simplePropertyCollection.Clear();
            this._complexPropertyCollection.Clear();
            this._simplePropertiesIndex     = -1;
            this._complexPropertiesIndex    = -1;
            this._queueEnumerator           = null;
            this._serializationManager      = null;
            this._target                    = null;
            this._isSimpleMode              = true;
        }
 
        #endregion Internal Methods
        
        #region Private Methods
 
        /// <summary>
        /// Fill out the portion of the collection which
        /// points out to the CLR properties
        /// </summary>
        private
        void
        InitializeSerializableClrProperties(
            )
        {
            TypePropertyCache[] clrProperties = _serializationManager.CacheManager.GetClrSerializableProperties(_target);
 
            if(clrProperties!=null)
            {
                for (int indexInClrSerializableProperties=0;
                     indexInClrSerializableProperties < clrProperties.Length; 
                     indexInClrSerializableProperties++)
                {
                    TypePropertyCache propertyCache = clrProperties[indexInClrSerializableProperties];
 
                    //
                    // Create SerializablePropertyContext out of the cache retrieved
                    //
                    SerializablePropertyContext propertyContext = new SerializablePropertyContext(_target, 
                                                                                                  propertyCache);
 
                    propertyContext.Name = propertyContext.TypePropertyCache.PropertyInfo.Name;
 
                    //
                    // We have to differentiate between simple properties and complex properties.
                    // o Simple properties would be considered as attributes within the markup and
                    //   would not require new writers
                    // o Complex properties would be considered elements within the markup and might
                    //   require a new writer
                    //
                    if(propertyContext.IsComplexProperty(_serializationManager))
                    {
                        propertyContext.IsComplex = true;
                        _complexPropertyCollection.Enqueue(propertyContext);
                    }
                    else
                    {
                        propertyContext.IsComplex = false;
                        _simplePropertyCollection.Enqueue(propertyContext);
                    }                    
                }
            }
        }
 
        /// <summary>
        /// Fill out the portion of the collection
        /// that points out to the dependency properties
        /// </summary>
        private
        void
        InitializeSerializableDependencyProperties(
            )
        {
            TypeDependencyPropertyCache[] dependencyProperties = _serializationManager.
                                                                 CacheManager.
                                                                 GetSerializableDependencyProperties(_target);
 
            if(dependencyProperties!=null)
            {
                for (int indexInSerializableDependencyProperties=0;
                     indexInSerializableDependencyProperties < dependencyProperties.Length; 
                     indexInSerializableDependencyProperties++)
                {
                    TypeDependencyPropertyCache dependencyPropertyCache = 
                    dependencyProperties[indexInSerializableDependencyProperties];
 
                    //
                    // Create SerializableDependencyPropertyContext out of the cache retrieved
                    //
                    SerializableDependencyPropertyContext dependencyPropertyContext = 
                    new SerializableDependencyPropertyContext(_target, 
                                                              dependencyPropertyCache);
 
                    dependencyPropertyContext.Name = 
                    ((DependencyProperty)((TypeDependencyPropertyCache)dependencyPropertyContext.TypePropertyCache).
                    DependencyProperty).Name;
 
                    //
                    // We have to differentiate between simple properties and complex properties.
                    // o Simple properties would be considered as attributes within the markup and
                    //   would not require new writers
                    // o Complex properties would be considered elements within the markup and might
                    //   require a new writer
                    //
                    if(dependencyPropertyContext.IsComplexProperty(_serializationManager))
                    {
                        dependencyPropertyContext.IsComplex = true;
                        _complexPropertyCollection.Enqueue(dependencyPropertyContext);
                    }
                    else
                    {
                        dependencyPropertyContext.IsComplex = false;
                        _simplePropertyCollection.Enqueue(dependencyPropertyContext);
                    }                    
                }
            }
        }
 
        
        #endregion Private Methods
        
        #region Private Data
 
        private 
        PackageSerializationManager _serializationManager;
        private 
        object                      _target;
        private 
        bool                        _isSimpleMode;
        private                 
        int                         _simplePropertiesIndex;
        private 
        Queue                       _simplePropertyCollection;
        private 
        int                         _complexPropertiesIndex;
        private 
        Queue                       _complexPropertyCollection;
        private 
        IEnumerator                 _queueEnumerator;
 
        #endregion Private Data
    };
 
 
    /// <summary>
    /// A class defining a context for the serializable CLR property
    /// </summary>
    internal class SerializablePropertyContext : 
                   BasicContext
    {
        #region Constructor
 
        /// <summary>
        /// Constructor for SerializablePropertyContext
        /// </summary>
        public 
        SerializablePropertyContext(
            string            name, 
            string            prefix, 
            object            target, 
            TypePropertyCache propertyCache) : 
        base(name, prefix)
        {
            //
            // Validate Input Arguments
            //
            ArgumentNullException.ThrowIfNull(target);
 
            ArgumentNullException.ThrowIfNull(propertyCache);
 
            this._targetObject = target;
            this._propertyInfo = propertyCache;
            this._isComplex    = false;
        }
 
        /// <summary>
        /// Constructor for SerializablePropertyContext
        /// </summary>
        internal 
        SerializablePropertyContext(
            object            target, 
            TypePropertyCache propertyCache) : 
        base()
        {
            //
            // Validate Input Arguments
            //
            ArgumentNullException.ThrowIfNull(target);
            ArgumentNullException.ThrowIfNull(propertyCache);
 
            _targetObject   = target;
            _propertyInfo   = propertyCache;
            this._isComplex = false;
        }
 
        #endregion Constructor
        
        #region Public Methods
 
        /// <summary>
        /// Detect whether it is a complex property or not.
        /// </summary>
        virtual
        public
        bool
        IsComplexProperty(
            PackageSerializationManager serializationManager
            )
        {
            bool isComplex = false;
 
            //
            // Null property value is always serialized 
            // in simple attribute="*null" notation
            //
            if (Value != null)
            {
                //
                // If the property has a DesignerSerializationOptions.SerializeAsAttribute 
                // then obviously we do not use complex notation
                //
                if(!(DesignerSerializationOptionsAttribute != null && 
                     (DesignerSerializationOptionsAttribute.DesignerSerializationOptions == 
                      DesignerSerializationOptions.SerializeAsAttribute)))
 
                {
                    //
                    // String space preservation is honoured by System.Xml only for contents of a tag not within 
                    // an attribute value. Hence we always emit strings as content within a tag
                    //
                    Type valueType = Value.GetType();
 
                    if (valueType == typeof(string) && 
                        ((string)Value) != string.Empty)
                    {
                        isComplex = true;
                    }
                    else
                    {
                        bool canConvert;
                        isComplex = IsComplexValue(serializationManager,
                                                   out canConvert);
                    }
                }
            }
 
            return isComplex;
        }
 
        virtual
        public
        bool 
        IsComplexValue(
                PackageSerializationManager manager, 
            out bool                        canConvert
            )
        {
            bool isComplex = true;
 
            canConvert = true;
            
            if(SerializerType!=null)
            {
                isComplex = true;
            }
            else
            {
                TypeConverter converter = this.TypeConverter;
                
                canConvert = converter.CanConvertTo(null,typeof(string)) &&
                             converter.CanConvertFrom(null,typeof(string));
 
                if(canConvert)
                {
                    isComplex = false;
                }
            }
 
            return isComplex;
        }
 
        #endregion Public Methods
        
        #region Public Properties
 
        /// <summary>
        /// Qyery the Target Object
        /// </summary>
        public
        object 
        TargetObject
        {
            get 
            { 
                return _targetObject; 
            }
        }
        
        /// <summary>
        /// Qyery the CLR propertyInfo structure of this property
        /// </summary>
        public 
        PropertyInfo 
        PropertyInfo
        {
            get 
            { 
                PropertyInfo info = null;
 
                if (_propertyInfo != null)
                {
                    info = (PropertyInfo)_propertyInfo.PropertyInfo;
                }
                
                return info; 
            }
        }
 
        // <summary>
        // Query / Set whether the property is Complex / Simple
        // </summary>
        public
        bool
        IsComplex
        {
            get
            {
                return _isComplex;
            }
 
            set
            {
                _isComplex = value;
            }
        }
 
 
        /// <summary>
        /// Query the visibility of the property
        /// </summary>
        public
        DesignerSerializationVisibility 
        Visibility
        {
            get 
            { 
                DesignerSerializationVisibility visibility = DesignerSerializationVisibility.Visible;
 
                if (_propertyInfo != null)
                {
                    visibility = _propertyInfo.Visibility;
                }
                
                return visibility; 
            }
        }
 
        /// <summary>
        /// Queries the Serializer type used to serialize this
        /// property if it happens to be a complex property
        /// </summary>
        public
        Type 
        SerializerType
        {
            get 
            { 
                Type type = null;
 
                if (_propertyInfo != null)
                {
                    type =  _propertyInfo.SerializerTypeForProperty;
                }
                
                return type; 
            }
        }
 
        /// <summary>
        /// Queries the TypeConverter used to convert this property
        /// to some equivalent string
        /// </summary>
        public
        TypeConverter 
        TypeConverter
        {
            get 
            { 
                TypeConverter converter = null;
 
                if (_propertyInfo != null)
                {
                    converter = _propertyInfo.TypeConverterForProperty;
                }
                
                return converter; 
            }
        }
 
        /// <summary>
        /// Query the default value attribute
        /// </summary>
        public
        DefaultValueAttribute
        DefaultValueAttribute
        {
            get 
            { 
                DefaultValueAttribute defValAttr = null;
 
                if (_propertyInfo != null)
                {
                    defValAttr = _propertyInfo.DefaultValueAttr;
                }
                
                return defValAttr; 
            }
        }
 
        /// <summary>
        /// Query the serialization attributes
        /// </summary>
        public
        DesignerSerializationOptionsAttribute
        DesignerSerializationOptionsAttribute
        {
            get 
            { 
                DesignerSerializationOptionsAttribute designerSerFlagAttr = null;
 
                if (_propertyInfo != null)
                {
                    designerSerFlagAttr = _propertyInfo.DesignerSerializationOptionsAttr;
                }
                
                return designerSerFlagAttr; 
            }
        }
 
 
        /// <summary>
        /// Query whether this is a read only or a read/write property    
        /// </summary>
        public
        bool 
        IsReadOnly
        {
            get 
            { 
                bool isReadOnly = false;
 
                if ( (_propertyInfo != null) &&
                     (((PropertyInfo)_propertyInfo.PropertyInfo) != null) )
                {
                    isReadOnly = !((PropertyInfo)_propertyInfo.PropertyInfo).CanWrite;
                }
                
                return isReadOnly; 
            }
        }
 
        /// <summary>
        /// Query / set the value of this property
        /// </summary>
        public 
        object 
        Value
        {
            get 
            { 
                return _propertyInfo.PropertyValue; 
            }
 
            set 
            {
                 _propertyInfo.PropertyValue = value; 
            }
        }
 
        /// <summary>
        /// This is the cache information about the property. Each
        /// property discovered is saved for performance optimization
        /// </summary>
        public 
        TypePropertyCache 
        TypePropertyCache
        {
            get 
            { 
                return _propertyInfo; 
            }
        }
 
        #endregion Public Properties
        
        #region Private Data
 
        private 
        object              _targetObject;
        private 
        TypePropertyCache   _propertyInfo;
        private
        bool                _isComplex;
 
        #endregion Private Data
    };
 
    /// <summary>
    /// A class defining context for serializable Dependency Properties
    /// </summary>
    internal class SerializableDependencyPropertyContext : 
                   SerializablePropertyContext
    {
        #region Constructor
 
        /// <summary>
        /// Constructor for SerializableDependencyPropertyContext
        /// </summary>
        public 
        SerializableDependencyPropertyContext(
            string                      name, 
            string                      prefix, 
            object                      target, 
            TypeDependencyPropertyCache propertyCache) : 
        base(name, prefix,target,propertyCache)
        {
        }
 
        /// <summary>
        /// Constructor for SerializableDependencyPropertyContext
        /// </summary>
        public 
        SerializableDependencyPropertyContext(
            object                      target, 
            TypeDependencyPropertyCache propertyCache) : 
        base(target,propertyCache)
        {
        }
 
        #endregion Constructor
 
 
        #region Public Methods
        
        /// <summary>
        /// Detects whether this property is complex or simple
        /// </summary>
        public
        override
        bool
        IsComplexProperty(
            PackageSerializationManager serializationManager
            )
        {
            bool isComplex = false;
 
            //
            // Null property value is always serialized 
            // in simple attribute="*null" notation
            //
            if (Value != null)
            {
                //
                // If the property has a DesignerSerializationOptions.SerializeAsAttribute 
                // then obviously we do not use complex notation
                //
                if(!(DesignerSerializationOptionsAttribute != null && 
                     (DesignerSerializationOptionsAttribute.DesignerSerializationOptions == 
                      DesignerSerializationOptions.SerializeAsAttribute)))
 
                {
                    //
                    // String space preservation is honored by System.Xml only for contents of a tag not within 
                    // an attribute value. Hence we always emit strings as content within a tag
                    //
                    Type valueType = Value.GetType();
 
                    if (valueType == typeof(string) && 
                        ((string)Value) != string.Empty)
                    {
                        isComplex = true;
                    }
                    else
                    {
                        bool canConvert;
                        isComplex = IsComplexValue(serializationManager,
                                                   out canConvert);
 
                        if (!canConvert)
                        {
                            Expression expr = this.Value as Expression;
 
                            if (expr != null)
                            {
                                this.Value = ((DependencyObject)this.TargetObject).GetValue((DependencyProperty)this.DependencyProperty);
                                isComplex = this.IsComplexProperty(serializationManager);
                            }
                        }
                    }
                }
            }
 
            return isComplex;
        }
 
        public
        override
        bool 
        IsComplexValue(
                PackageSerializationManager manager, 
            out bool                        canConvert
            )
        {
            bool isComplex = true;
 
            canConvert = true;
            
            if(SerializerType!=null)
            {
                isComplex = true;
            }
            else
            {
                TypeConverter converter = this.TypeConverter;
                
                canConvert = converter.CanConvertTo(null,typeof(string)) &&
                             converter.CanConvertFrom(null,typeof(string));
 
                if(canConvert)
                {
                    isComplex = false;
                }
            }
 
            return isComplex;
        }
 
        #endregion Public Methods
        
        #region Public Properties
 
        /// <summary>
        /// return info about the dependency property
        /// </summary>
        public 
        MemberInfo 
        MemberInfo
        {
            get 
            { 
                MemberInfo memberInfo = null;
 
                if (this.PropertyInfo != null)
                {
                    memberInfo = ((TypeDependencyPropertyCache)TypePropertyCache).MemberInfo;
                }
                
                return memberInfo; 
            }
        }
 
        /// <summary>
        /// return the dependency property
        /// </summary>
        public 
        Object 
        DependencyProperty
        {
            get 
            { 
                Object dependencyProperty = null;
 
                if (this.PropertyInfo != null)
                {
                    dependencyProperty = ((TypeDependencyPropertyCache)TypePropertyCache).DependencyProperty;
                }
                
                return dependencyProperty; 
            }
        }
 
        #endregion Public Properties
    };
}