crashanalysercmd/PerfToolsSharedLibraries/Engine/SymbianImageLib/Common/Content/SIContent.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.Collections.Generic;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Threading;
using SymbianUtils;
using SymbianUtils.Tracer;
using SymbianUtils.PluginManager;
using SymbianStructuresLib.Uids;
using SymbianStructuresLib.Compression.Common;
using SymbianImageLib.Common.Image;
using SymbianImageLib.Common.Header;
using SymbianImageLib.Common.Streams;
using SymbianImageLib.Common.Factory;

namespace SymbianImageLib.Common.Content
{
    public abstract class SIContent : DisposableObject, ITracer
    {
        #region Enumerations
        public enum TDecompressionEvent
        {
            EEventDecompressionStarting = 0,
            EEventDecompressionProgress,
            EEventDecompressionComplete
        }
        #endregion

        #region Delegates & events
        public delegate void DecompressionEventHandler( TDecompressionEvent aEvent, SIContent aFile, object aData );
        public event DecompressionEventHandler DecompressionEvent;
        #endregion

        #region Constructors
        protected SIContent( SIImage aImage )
        {
            iImage = aImage;
        }
        #endregion

        #region Framework API
        public abstract TSymbianCompressionType CompressionType
        {
            get;
        }

        public abstract string FileName
        {
            get;
        }

        public abstract uint FileSize
        {
            get;
        }

        public abstract uint ContentSize
        {
            get;
        }

        public virtual bool IsRelocationSupported
        {
            get { return true; }
        }

        public virtual TCheckedUid Uid
        {
            get { return new TCheckedUid(); }
        }

        public virtual byte[] GetAllData()
        {
            return new byte[ 0 ];
        }

        public virtual uint ProvideDataUInt32( uint aAddress )
        {
            uint ret = 0;
            //
            if ( IsContentPrepared == false )
            {
                // Cannot provide data if we've not prepared it yet
            }
            else if ( iDecompressionException == false )
            {
                WaitForAsyncOperationCompletion();
                uint resolvedAddress = FixupAddress( aAddress );
                ret = DoProvideDataUInt32( resolvedAddress );
            }
            //
            return ret;
        }

        public virtual ushort ProvideDataUInt16( uint aAddress )
        {
            ushort ret = 0;
            //
            if ( IsContentPrepared == false )
            {
                // Cannot provide data if we've not prepared it yet
            }
            else if ( iDecompressionException == false )
            {
                WaitForAsyncOperationCompletion();
                uint resolvedAddress = FixupAddress( aAddress );
                ret = DoProvideDataUInt16( resolvedAddress );
            }
            //
            return ret;
        }

        public virtual bool IsCode
        {
            get { return true; }
        }

        protected virtual void OnRelocationAddressChanged( uint aOld, uint aNew )
        {
        }

        protected virtual void DoDecompress()
        {
        }

        protected virtual uint DoProvideDataUInt32( uint aTranslatedAddress )
        {
            throw new NotImplementedException();
        }

        protected virtual ushort DoProvideDataUInt16( uint aTranslatedAddress )
        {
            throw new NotImplementedException();
        }

        protected abstract bool GetIsContentPrepared();
        #endregion

        #region API
        public void PrepareContent( TSynchronicity aSynchronicity )
        {
            if ( CompressionType == TSymbianCompressionType.ENone )
            {
                // No content preparation required, so just indicate completion
                // immediately.
                ReportDecompressionEvent( TDecompressionEvent.EEventDecompressionStarting );
                ReportDecompressionEvent( TDecompressionEvent.EEventDecompressionComplete );
            }
            else
            {
                switch ( aSynchronicity )
                {
                case TSynchronicity.ESynchronous:
                    RunDecompressor();
                    break;
                case TSynchronicity.EAsynchronous:
                    // Must take the lock to either create or destroy waiter
                    lock ( iWaiterSyncRoot )
                    {
                        if ( iWaiter == null )
                        {
                            iWaiter = new AutoResetEvent( false );
                            ThreadPool.QueueUserWorkItem( new WaitCallback( RunDecompressionInBackgroundThread ), null );
                            break;
                        }
                        else
                        {
                            // Wait is active, so we are presumably busy...
                            throw new Exception( "Content is already in preparation" );
                        }
                    }
                }
            }
        }
 
        public uint RelocationAddress
        {
            get { return iRelocationAddress; }
            set
            {
                if ( value != iRelocationAddress )
                {
                    uint old = iRelocationAddress;
                    iRelocationAddress = value;
                    OnRelocationAddressChanged( old, iRelocationAddress );
                }
            }
        }

        public virtual bool IsContentPrepared
        {
            get
            {
                bool ret = true;
                //
                if ( CompressionType != TSymbianCompressionType.ENone )
                {
                    ret = GetIsContentPrepared();
                }
                //
                return ret;
            }
        }
        #endregion

        #region Properties
        public SIImage Image
        {
            get { return iImage; }
        }
        
        public SIHeader ImageHeader
        {
            get { return iImage.Header; }
        }

        internal SIStream ImageStream
        {
            get 
            {
                return iImage.Stream; 
            }
            set { iImage.Stream = value; }
        }
        #endregion

        #region Event handlers
        private void RunDecompressionInBackgroundThread( object aNotUsed )
        {
            System.Diagnostics.Debug.Assert( iWaiter != null );

            // Call derived class to do decompression. Must not throw.
            RunDecompressor();

            // Must do this last so that anybody that is waiting for code to become
            // ready is resumed.
            //
            // Also, we might have been disposed of as a result of the "completion" callback
            // so in that case, we will have already set the auto-reset-event during the cleanup.
            if ( iWaiter != null )
            {
                iWaiter.Set();
            }
        }
        #endregion

        #region Internal methods
        protected void ReportDecompressionEvent( TDecompressionEvent aEvent )
        {
            ReportDecompressionEvent( aEvent, null );
        }

        protected void ReportDecompressionEvent( TDecompressionEvent aEvent, object aData )
        {
            if ( DecompressionEvent != null )
            {
                DecompressionEvent( aEvent, this, aData );
            }
        }

        private void RunDecompressor()
        {
            Trace( "[SIImageContent] RunDecompressor() - START - this: " + FileName );
            ReportDecompressionEvent( TDecompressionEvent.EEventDecompressionStarting );
            //
            try
            {
                DoDecompress();
            }
            catch ( Exception e )
            {
                Trace( "[SIImageContent] RunDecompressor() - Exception - this: {0}, message: {1}", this.FileName, e.Message );
                Trace( "[SIImageContent] RunDecompressor() - Exception - this: {0}, stack  : {1}", this.FileName, e.StackTrace );
                iDecompressionException = true;
            }

            // Doing this might cause the disposal of the object
            ReportDecompressionEvent( TDecompressionEvent.EEventDecompressionComplete );
            Trace( "[SIImageContent] RunDecompressor() - END - this: " + FileName );
        }

        private void WaitForAsyncOperationCompletion()
        {
            // If the worker doesn't exist then we've already completed the operation
            if ( iWaiter != null )
            {
                // Just wait on the auto-reset event. If it's already been signalled, then
                // we will continue immediately.
                iWaiter.WaitOne();

                // Must take the lock to either create or destroy waiter
                lock ( iWaiterSyncRoot )
                {
                    iWaiter.Close();
                    iWaiter = null;
                }
            }
        }

        private uint FixupAddress( uint aAddress )
        {
            uint ret = aAddress - iRelocationAddress;
            return ret;
        }
        #endregion

        #region From ITracer
        public void Trace( string aMessage )
        {
            iImage.Trace( aMessage );
        }

        public void Trace( string aFormat, params object[] aParams )
        {
            iImage.Trace( aFormat, aParams );
        }
        #endregion

        #region From DisposableObject
        protected override void CleanupManagedResources()
        {
            try
            {
                base.CleanupManagedResources();
            }
            finally
            {
                iImage.Remove( this );
                //
                if ( iWaiter != null )
                {
                    iWaiter.Set();
                    iWaiter.Close();
                    iWaiter = null;
                }
            }
        }
        #endregion

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

        public override int GetHashCode()
        {
            return FileName.GetHashCode();
        }
        #endregion

        #region Data members
        private readonly SIImage iImage;
        private uint iRelocationAddress = 0;
        private bool iDecompressionException = false;
        private AutoResetEvent iWaiter = null;
        private object iWaiterSyncRoot = new object();
        #endregion
    }
}