diff -r 000000000000 -r 818e61de6cd1 crashanalysercmd/Libraries/Engine/CrashItemLib/Crash/Base/CIElement.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crashanalysercmd/Libraries/Engine/CrashItemLib/Crash/Base/CIElement.cs Thu Feb 11 15:50:58 2010 +0200 @@ -0,0 +1,795 @@ +/* +* 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 + { + #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( 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 ChildrenByType() where T: CIElement + { + CIElementList ret = ChildrenByType( TChildSearchType.EDirectChildrenOnly ); + return ret; + } + + public CIElementList ChildrenByType( Predicate aPredicate ) where T : CIElement + { + CIElementList ret = ChildrenByType( TChildSearchType.EDirectChildrenOnly, aPredicate ); + return ret; + } + + public CIElementList ChildrenByType( TChildSearchType aType ) where T : CIElement + { + return ChildrenByType( aType, null ); + } + + public CIElementList ChildrenByType( TChildSearchType aType, Predicate aPredicate ) where T : CIElement + { + CIElementList ret = new CIElementList( Container ); + GetChildrenByType( ret, aType, aPredicate ); + return ret; + } + + internal CIElementList Children + { + get + { + CIElementList ret = null; + // + lock ( iSyncLock ) + { + ret = iChildren; + // + if ( ret == null ) + { + ret = new CIElementList( 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(); + } + 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( CIElementList aList, TChildSearchType aType, Predicate 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( 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 + /// + /// 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. + /// + /// + internal virtual void OnFinalize( CIElementFinalizationParameters aParams ) + { + } + + internal void DoFinalizeElements( CIElementFinalizationParameters aParams, Queue aCallBackLast, bool aForceFinalize, IEnumerable aElements ) + { + foreach( CIElement element in aElements ) + { + if ( element.IsToBeFinalizedLast ) + { + aCallBackLast.Enqueue( element ); + } + // + element.DoFinalize( aParams, aCallBackLast, aForceFinalize ); + } + } + + internal virtual void DoFinalize( CIElementFinalizationParameters aParams, Queue 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; + } + } + } + + /// + /// 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). + /// + 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 + public IEnumerator GetEnumerator() + { + lock ( iSyncLock ) + { + CIElementList children = iChildren; + if ( iChildren == null ) + { + children = new CIElementList( Container ); + } + return children.GetEnumerator(); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + lock ( iSyncLock ) + { + CIElementList children = iChildren; + if ( iChildren == null ) + { + children = new CIElementList( Container ); + } + return children.GetEnumerator(); + } + } + #endregion + + #region Data members + private readonly CIDBModel iDataBindingModel; + private object iSyncLock = new object(); + private CIElement iParent = null; + private List iSupportedChildTypes = null; + private CIContainer iContainer; + private CIElementList iChildren; + private CIElementId iId = new CIElementId(); + private TFlags iFlags = TFlags.EFlagsNone; + #endregion + } +}