crashanalysercmd/Libraries/Engine/CrashItemLib/Crash/Base/CIElement.cs
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:50:58 +0200
changeset 0 818e61de6cd1
permissions -rw-r--r--
Add initial version of Crash Analyser cmdline under EPL

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
* 
* Description:
*
*/
using System;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using SymbianUtils;
using SymbianDebugLib.Engine;
using CrashItemLib.Engine;
using CrashItemLib.Crash;
using CrashItemLib.Crash.Messages;
using CrashItemLib.Crash.Container;
using CrashItemLib.Crash.Base.DataBinding;

namespace CrashItemLib.Crash.Base
{
	public abstract class CIElement : IEnumerable<CIElement>
    {
        #region Delegates & events
        public delegate void CIElementEventHandler( CIElement aElement );
        public delegate void CIMultiElementEventHandler( CIElement aSelf, CIElement aOther );

        public event CIMultiElementEventHandler ChildAdded = null;
        #endregion

        #region Enumerations
        internal enum TAutoPopulateType
        {
            EAutoPopulateDisabled = 0,
            EAutoPopulateEnabled
        }

        public enum TChildSearchType
        {
            EDirectChildrenOnly = 0,
            EEntireHierarchy
        }
        #endregion

        #region Constructors
        internal CIElement( int aId )
            : this( aId, false )
        {
            // NB: to be called only by the container itself since everything else
            // must specify a valid container object
            System.Diagnostics.Debug.Assert( this is CIContainer );
        }

        internal CIElement( CIContainer aContainer )
            : this( aContainer, TAutoPopulateType.EAutoPopulateDisabled )
		{
		}

        internal CIElement( CIContainer aContainer, CIElement aParent )
            : this( aContainer )
        {
            iParent = aParent;
        }

        internal CIElement( CIContainer aContainer, TAutoPopulateType aDataBindingAutoPopulate )
            : this( aContainer.GetNextElementId(), aDataBindingAutoPopulate == TAutoPopulateType.EAutoPopulateEnabled )
        {
            iContainer = aContainer;
        }

        internal CIElement( CIContainer aContainer, CIElement aParent, TAutoPopulateType aDataBindingAutoPopulate )
            : this( aContainer, aDataBindingAutoPopulate )
        {
            iParent = aParent;
        }
        
        private CIElement( long aId, bool aDataBindingAutoPopulate )
        {
            iId = new CIElementId( aId );
            iDataBindingModel = new CIDBModel( this, aDataBindingAutoPopulate );
        }
        #endregion

        #region API - Framework Properties
        public virtual string Name
        {
            get { return string.Empty; }
            set { }
        }

        public virtual CIElementId Id
        {
            get { return iId; }
            set 
            { 
                iId = value;
                IsIdExplicit = true;
            }
        }
        #endregion

        #region API - Children
        public virtual int Count
        {
            get 
            {
                int ret = 0;
                //
                lock ( iSyncLock )
                {
                    if ( iChildren != null )
                    {
                        ret = iChildren.Count;
                    }
                }
                //
                return ret;
            }
        }

        public bool HasChildren
        {
            get
            {
                bool ret = false;
                //
                lock ( iSyncLock )
                {
                    if ( iChildren != null )
                    {
                        ret = ( iChildren.Count > 0 );
                    }
                }
                //
                return ret;
            }
        }

        public virtual void AddChild( CIElement aChild )
        {
            if ( aChild != null )
            {
                // If we have been restricted to a specific 
                // type of child element, then check aChild against
                // it...
                Type t = aChild.GetType();
                ValidateChildType( t );

                lock ( iSyncLock )
                {
                    if ( iChildren == null )
                    {
                        iChildren = new CIElementList<CIElement>( Container );
                        iChildren.IsInContainer = this.IsInContainer;
                    }

                    if ( aChild.Parent == null )
                    {
                        aChild.Parent = this;
                    }

                    iChildren.Add( aChild );
                }

                OnElementAddedToSelf( aChild );
            }
        }

        public virtual void AddChildren( CIElement[] aChildren )
        {
            for ( int i = aChildren.Length - 1; i >= 0; i-- )
            {
                AddChild( aChildren[ i ] );
            }
        }

        public virtual void RemoveChild( CIElement aChild )
        {
            if ( aChild == null )
            {
                throw new ArgumentException( "Child is null" );
            }

            if ( HasChildren && iChildren.Count > 0 )
            {
                lock ( iSyncLock )
                {
                    iChildren.Remove( aChild );
                }
            }
        }

        public virtual void RemoveChildren( Type aType )
        {
            if ( HasChildren && iChildren.Count > 0 )
            {
                lock ( iSyncLock )
                {
                    int count = iChildren.Count;
                    for ( int i = count - 1; i >= 0; i-- )
                    {
                        CIElement child = iChildren[ i ];
                        if ( aType.IsAssignableFrom( aType ) )
                        {
                            iChildren.Remove( child );
                        }
                    }
                }
            }
        }

        public virtual void Clear()
        {
            lock ( iSyncLock )
            {
                if ( iChildren != null )
                {
                    iChildren.Clear();
                }
            }
        }

        public virtual bool Contains( CIElement aElement )
        {
            return Contains( aElement.Id );
        }

        public virtual bool Contains( CIElementId aId )
        {
            bool ret = false;
            //
            lock ( iSyncLock )
            {
                if ( iChildren != null )
                {
                    ret = iChildren.Contains( aId );
                }
            }
            //
            return ret;
        }

        public CIElement this[ CIElementId aId ]
        {
            get
            {
                CIElement ret = null;
                //
                lock ( iSyncLock )
                {
                    if ( Contains( aId ) )
                    {
                        System.Diagnostics.Debug.Assert( iChildren != null );
                        ret = iChildren[ aId ];
                    }
                }
                //
                return ret;
            }
        }

        public CIElement this[ int aIndex ]
        {
            get
            {
                CIElement ret = null;
                //
                lock ( iSyncLock )
                {
                    if ( iChildren != null )
                    {
                        ret = iChildren[ aIndex ];
                    }
                }
                //
                return ret;
            }
        }
        
        public CIElement ChildByType( Type aType )
        {
            CIElement ret = null;
            //
            lock ( iSyncLock )
            {
                foreach ( CIElement element in this )
                {
                    if ( aType.IsAssignableFrom( element.GetType() ) )
                    {
                        ret = element;
                        break;
                    }
                }
            }
            //
            return ret;
        }

        public CIElementList<T> ChildrenByType<T>() where T: CIElement
        {
            CIElementList<T> ret = ChildrenByType<T>( TChildSearchType.EDirectChildrenOnly );
            return ret;
        }

        public CIElementList<T> ChildrenByType<T>( Predicate<T> aPredicate ) where T : CIElement
        {
            CIElementList<T> ret = ChildrenByType<T>( TChildSearchType.EDirectChildrenOnly, aPredicate );
            return ret;
        }

        public CIElementList<T> ChildrenByType<T>( TChildSearchType aType ) where T : CIElement
        {
            return ChildrenByType<T>( aType, null );
        }

        public CIElementList<T> ChildrenByType<T>( TChildSearchType aType, Predicate<T> aPredicate ) where T : CIElement
        {
            CIElementList<T> ret = new CIElementList<T>( Container );
            GetChildrenByType<T>( ret, aType, aPredicate );
            return ret;
        }

        internal CIElementList<CIElement> Children
        {
            get
            {
                CIElementList<CIElement> ret = null;
                //
                lock ( iSyncLock )
                {
                    ret = iChildren;
                    //
                    if ( ret == null )
                    {
                        ret = new CIElementList<CIElement>( Container );
                    }
                }
                //
                return ret;
            }
        }
        #endregion

        #region API - Data Binding framework
        public virtual void PrepareColumns()
        {
            DataBindingModel.ClearColumns();
            DataBindingModel.TryAutoPopulateColumns( this );
        }

        public virtual void PrepareRows()
        {
            DataBindingModel.ClearRows();
            DataBindingModel.TryAutoPopulateCells( this );
        }
        #endregion

        #region API - Parentage
        public bool HasParent
        {
            get
            {
                lock ( iSyncLock )
                {
                    return iParent != null;
                }
            }
        }

        public virtual CIElement Parent
        {
            get
            {
                lock ( iSyncLock )
                {
                    return iParent;
                }
            }
            internal set 
            {
                lock ( iSyncLock )
                {
                    iParent = value;
                }
            }
        }
        #endregion

        #region Properties
        public virtual bool IsInContainer
        {
            get
            {
                lock ( iSyncLock )
                {
                    return ( iFlags & TFlags.EFlagsIsInContainer ) == TFlags.EFlagsIsInContainer;
                }
            }
            internal set
            {
                // Don't allow it to change if we're locked down.
                if ( IsFinalized == false )
                {
                    bool oldValue = IsInContainer;
                    if ( oldValue != value )
                    {
                        // Set new flag value
                        lock ( iSyncLock )
                        {
                            if ( value )
                            {
                                iFlags |= TFlags.EFlagsIsInContainer;
                            }
                            else
                            {
                                iFlags &= ~TFlags.EFlagsIsInContainer;
                            }
                        }

                        // Fire internal call that reflects new state. This notifies the
                        // container about our registration status and in turn, the container
                        // can notify it's observers about our presence/remove within container.
                        OnIsInContainerChanged();

                        // Ensure children are also in/out of container
                        lock ( iSyncLock )
                        {
                            if ( iChildren != null )
                            {
                                iChildren.IsInContainer = value;
                            }
                        }
                    }
                }
            }
        }

        public bool IsIdExplicit
        {
            get
            {
                lock ( iSyncLock )
                {
                    return ( iFlags & TFlags.EFlagsIdWasExplicitlySet ) == TFlags.EFlagsIdWasExplicitlySet;
                }
            }
            private set 
            {
                lock ( iSyncLock )
                {
                    if ( value )
                    {
                        iFlags |= TFlags.EFlagsIdWasExplicitlySet;
                    }
                    else
                    {
                        iFlags &= ~TFlags.EFlagsIdWasExplicitlySet;
                    }
                }
            }
        }

        public virtual CIEngine Engine
        {
            get { return Container.Engine; }
        }

        public CIContainer Container
        {
            get
            {
                lock ( iSyncLock )
                {
                    return iContainer;
                }
            }
            internal set 
            {
                lock ( iSyncLock )
                {
                    iContainer = value;
                }
            }
        }

        public CIDBModel DataBindingModel
        {
            get { return iDataBindingModel; }
        }

        protected bool IsToBeFinalizedLast
        {
            get
            {
                lock ( iSyncLock )
                {
                    return ( iFlags & TFlags.EFlagsIsToBeFinalizedLast ) == TFlags.EFlagsIsToBeFinalizedLast;
                }
            }
            set
            {
                lock ( iSyncLock )
                {
                    if ( value )
                    {
                        iFlags |= TFlags.EFlagsIsToBeFinalizedLast;
                    }
                    else
                    {
                        iFlags &= ~TFlags.EFlagsIsToBeFinalizedLast;
                    }
                }
            }
        }

        internal bool IsFinalized
        {
            get
            {
                lock ( iSyncLock )
                {
                    return ( iFlags & TFlags.EFlagsIsFinalized ) == TFlags.EFlagsIsFinalized;
                }
            }
            set
            {
                lock ( iSyncLock )
                {
                    if ( value )
                    {
                        iFlags |= TFlags.EFlagsIsFinalized;
                    }
                    else
                    {
                        iFlags &= ~TFlags.EFlagsIsFinalized;
                    }
                }
            }
        }
        #endregion

        #region Internal methods
        internal void Trace( string aMessage )
        {
            Container.Engine.Trace( aMessage );
        }

        internal void Trace( string aFormat, params object[] aParams )
        {
            Container.Engine.Trace( aFormat, aParams );
        }

        protected void AddSupportedChildType( Type aType )
        {
            lock ( iSyncLock )
            {
                if ( iSupportedChildTypes == null )
                {
                    iSupportedChildTypes = new List<Type>();
                }
                iSupportedChildTypes.Add( aType );
            }
        }

        private void ValidateChildType( Type aType )
        {
            lock ( iSyncLock )
            {
                if ( iSupportedChildTypes != null )
                {
                    StringBuilder typeNames = new StringBuilder();
                    //
                    foreach ( Type t in iSupportedChildTypes )
                    {
                        typeNames.Append( t.ToString() + ", " );
                        //
                        if ( aType == t || aType.IsSubclassOf( t ) )
                        {
                            return;
                        }
                    }
                    //
                    string names = typeNames.ToString();
                    if ( names.Length > 2 )
                    {
                        names = names.Substring( 0, names.Length - 2 );
                    }
                    //
                    throw new ArgumentException( "Child is not of type: [" + names + "]" );
                }
            }
        }

        internal virtual void GetChildrenByType<T>( CIElementList<T> aList, TChildSearchType aType, Predicate<T> aPredicate ) where T : CIElement
        {
            // Get all direct children, and if recusion enabled, then fetch the
            // entire tree.
            Type t = typeof( T );
            foreach ( CIElement element in Children )
            {
                if ( t.IsAssignableFrom( element.GetType() ) )
                {
                    // Get entry of correct type
                    T objectEntry = (T) ( (object) element );

                    // Check whether it is suitable for inclusion via our predicate
                    bool addEntry = true;
                    if ( aPredicate != null )
                    {
                        addEntry = aPredicate( objectEntry );
                    }
                    
                    // Is it okay to take the entry?
                    if ( addEntry )
                    {
                        aList.Add( objectEntry );
                    }
                }

                // Get the element's children
                if ( aType == TChildSearchType.EEntireHierarchy )
                {
                    element.GetChildrenByType<T>( aList, aType, aPredicate );
                }
            }
        }
        #endregion

        #region Internal flags
        [Flags]
        private enum TFlags : short
        {
            EFlagsNone = 0,
            EFlagsIsInContainer = 1,
            EFlagsIdWasExplicitlySet = 2,
            EFlagsIsReadOnly = 4,
            EFlagsIsToBeFinalizedLast = 8,
            EFlagsIsFinalized = 16,
        }
        #endregion

        #region Internal framework methods
        /// <summary>
        /// Called when the element is to finish its construction. At this point
        /// it is assumed that the crash container data structure is largely fully
        /// populated. This function call should typically perform any final 
        /// post-processing, such as looking up symbols etc.
        /// </summary>
        /// <param name="aParams"></param>
        internal virtual void OnFinalize( CIElementFinalizationParameters aParams )
        {
        }

        internal void DoFinalizeElements( CIElementFinalizationParameters aParams, Queue<CIElement> aCallBackLast, bool aForceFinalize, IEnumerable<CIElement> aElements )
        {
            foreach( CIElement element in aElements )
            {
                if ( element.IsToBeFinalizedLast )
                {
                    aCallBackLast.Enqueue( element );
                }
                //
                element.DoFinalize( aParams, aCallBackLast, aForceFinalize );
            }
        }

        internal virtual void DoFinalize( CIElementFinalizationParameters aParams, Queue<CIElement> aCallBackLast, bool aForceFinalize )
        {
            lock ( iSyncLock )
            {
                // Finalize children
                if ( iChildren != null )
                {
                    DoFinalizeElements( aParams, aCallBackLast, aForceFinalize, iChildren );
                }
            }

            // Finalize ourself
            if ( ( aForceFinalize || IsToBeFinalizedLast == false ) && IsFinalized == false )
            {
                try
                {
                    OnFinalize( aParams );
                }
                finally
                {
                    IsFinalized = true;
                }
            }
        }

        /// <summary>
        /// Called by CIElement.AddChild() whenever a new element is
        /// added as a child of this element.
        /// 
        /// Notify the container that a child entry was added
        /// so that it can notify any elements that listen to
        /// all additions to the container (direct or indirect).
        /// </summary>
        protected virtual void OnElementAddedToSelf( CIElement aElement )
        {
            // aElement inherits our container state
            bool inContainer = IsInContainer;

            // Doing this will fire an added/removed event in accordance
            // with what our state currently is and what it has just become.
            aElement.IsInContainer = inContainer;

            // Report event
            if ( ChildAdded != null )
            {
                ChildAdded( this, aElement );
            }
        }

        protected virtual void OnIsInContainerChanged()
        {
            // If we're now in the container, either directly or indirectly (we don't care)
            // then make sure we notify the container so that it can inform other elements
            // that may be listening.
            //
            // Because "IsInContainer" cascades changes to our children, they will also
            // notify the container themselves in due course.
            //
            // Obviously don't cascade when the container itself changes its state.
            if ( this != Container )
            {
                if ( IsInContainer )
                {
                    Container.OnContainerElementRegistered( this );
                }
                else
                {
                    Container.OnContainerElementUnregistered( this );
                }
            }
        }
        #endregion

        #region From System.Object
        public override string ToString()
        {
            return Name;
        }
        #endregion

        #region From IEnumerable<CIElement>
        public IEnumerator<CIElement> GetEnumerator()
        {
            lock ( iSyncLock )
            {
                CIElementList<CIElement> children = iChildren;
                if ( iChildren == null )
                {
                    children = new CIElementList<CIElement>( Container );
                }
                return children.GetEnumerator();
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            lock ( iSyncLock )
            {
                CIElementList<CIElement> children = iChildren;
                if ( iChildren == null )
                {
                    children = new CIElementList<CIElement>( Container );
                }
                return children.GetEnumerator();
            }
        }
        #endregion

        #region Data members
        private readonly CIDBModel iDataBindingModel;
        private object iSyncLock = new object();
        private CIElement iParent = null;
        private List<Type> iSupportedChildTypes = null;
        private CIContainer iContainer;
        private CIElementList<CIElement> iChildren;
        private CIElementId iId = new CIElementId();
        private TFlags iFlags = TFlags.EFlagsNone;
		#endregion
    }
}