gssettingsuis/Gs/GSFramework/src/GSPluginLoader.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:52:52 +0200
changeset 0 8c5d936e5675
child 14 9941bcf99348
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2005-2008 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:  GS plugin loader.
*
*/


// INCLUDE FILES
#include    <gspluginloader.h>
#include    <gsplugininterface.h>
#include    <gsprivatepluginproviderids.h>
#include    "GsLogger.h"
#include    "GSBaseDocument.h"
#include    "GSPluginWrapper.h"
#include    "GSPluginAndViewIdCache.h"
#include    <mgswatchdog.h>

#include    <aknViewAppUi.h>
#include    <AknIconArray.h>
#include    <aknlists.h> // CAknSingleLargeStyleListbox
#include    <eikclbd.h> // CColumnListBoxData
#include    <gulicon.h> // For CGulIcon
#include    <utf.h> // CnvUtfConverter
#include  	<basched.h>
#include    <AknInfoPopupNoteController.h>

//Flag for enabling/disablign compare by category feature
#undef RD_GS_COMPARE_BY_CATEGORY

// ================= MEMBER FUNCTIONS =======================

#ifdef _DEBUG
    #pragma message("-----_DEBUG ACTIVATED IN GS!-----")
#endif //_GEBUG

#ifdef _GS_PERFORMANCE_TRACES
    #pragma message("-GS plugin load performance measurements activated-")
#endif // _GS_PERFORMANCE_TRACES

const TInt KGSCaptionSize = 256;

// ----------------------------------------------------------------------------
// CGSPluginLoader::NewL
//
// EPOC two-phased constructor
// ----------------------------------------------------------------------------
//
EXPORT_C CGSPluginLoader* CGSPluginLoader::NewL( CAknViewAppUi* aAppUi )
    {
    CGSPluginLoader* self = new( ELeave ) CGSPluginLoader;
    CleanupStack::PushL( self );
    self->ConstructL( aAppUi );
    CleanupStack::Pop( self );
    return self;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::CGSPluginLoader
//
// C++ default constructor can NOT contain any code, that
// might leave.
//
//  Uses low priority for Active Object so that more bandwith is available for 
//  the most important pluginloaders. 
// ----------------------------------------------------------------------------
//
CGSPluginLoader::CGSPluginLoader() 
    : CActive( EPriorityLow ),
      iRequestedPriority( EPriorityLow )
    {
    __GSLOGSTRING( "[GSPlgLoader] CGSPluginLoader()" );
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::ConstructL
//
// EPOC default constructor can leave.
// ----------------------------------------------------------------------------
//
EXPORT_C void CGSPluginLoader::ConstructL( CAknViewAppUi* aAppUi )
    {
    __GSLOGSTRING( "[GSPlgLoader] ConstructL()" );
    iAppUi = aAppUi;
    iDocument = static_cast<CGSBaseDocument*>( iAppUi->Document() );
    iImplInfoArray = iDocument->GetImplInfo();

    CActiveScheduler* scheluder = CActiveScheduler::Current();
    __GSLOGSTRING1( "[GSPlgLoader] Current CActiveScheduler:0x%X", scheluder );
    __GSLOGSTRING1( "[GSPlgLoader] CActiveScheduler stackdepth: %d",
        scheluder->StackDepth() );

    CActiveScheduler::Add( this );

    iWatchDog = iDocument->WatchDog();
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::~CGSPluginLoader
//
// Destructor
// ----------------------------------------------------------------------------
//
EXPORT_C CGSPluginLoader::~CGSPluginLoader()
    {
    __GSLOGSTRING( "[GSPlgLoader] ~CGSPluginLoader()" );
    AbortAsyncLoad();
    Cancel();

    if( iErrorPopup )
        {
        iErrorPopup->HideInfoPopupNote();
        delete iErrorPopup;
        iErrorPopup = NULL;
        }
    //iPluginArray is not owned and therefore not deleted.
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::LoadAsync
//
//
// ----------------------------------------------------------------------------
//
EXPORT_C void CGSPluginLoader::LoadAsyncL(
    TUid aInterfaceUid,
    TUid aParentUid,
    CArrayPtrFlat<CGSPluginInterface>* aPluginArray )
    {
    iPluginArray = aPluginArray;
    iParentUid = aParentUid;

    __GSLOGSTRING3(
        "[GSPlgLoader] LoadAsync(). aInterfaceUid:0x%X aParentUid:0x%X, aPluginArray:0x%X",
        aInterfaceUid, aParentUid, aPluginArray );
    // Reset iterator:
    iImplInfoArrayIterator = 0;

    __GSLOGSTRING1( "[GSPlgLoader] Implementation info count: %d",
        iImplInfoArray.Count() );

    NotifyProgress();

    //Begin CActive asynchronous loop.
    CompleteOwnRequest();
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::LoadSyncL
//
//
// ----------------------------------------------------------------------------
//
CGSPluginInterface& CGSPluginLoader::LoadSyncL( TUid aInterfaceUid,
                                                TUid aImplementationUid )
    {
    Cancel();
    CGSPluginInterface* ret = NULL;

    // Get a list of all implementations, even though we only want one specific.
    // There appears to be no way to otherwise extract a specific implementation
    // info object :(
    // Search for the implementation that matches aImplementationUid
    const TInt impCount = iImplInfoArray.Count();
    for( TInt i=0; i<impCount; i++ )
        {
        const CImplementationInformation* info = iImplInfoArray[ i ];
        if  ( info->ImplementationUid() == aImplementationUid )
            {
            ret = &CreatePluginInstanceL( *info );
            break;
            }
        }

    if  ( !ret )
        {
        User::Leave( KErrNotFound );
        }
    return *ret;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::AbortAsyncLoad
//
//
// ----------------------------------------------------------------------------
//
EXPORT_C void CGSPluginLoader::AbortAsyncLoad()
    {
    __GSLOGSTRING( "[GSPlgLoader] AbortAsyncLoad()" );
    Cancel();
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::RunL
//
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::RunL()
    {
    iRunLDebugCount++;
    
    // This must be done only in RunL or otherwise request might be 
    // outstanding.
    if( iRequestedPriority != Priority() )
        {
        __GSLOGSTRING3(
            "[CGSPluginLoader::RunL] 0x%X original priority:%d requested priority:%d", 
            iParentUid.iUid, 
            Priority(),
            iRequestedPriority );
        
        SetPriority( iRequestedPriority );
        }
    
    LoadNextPluginL();

    // Check if there are still more plugins to be loaded:
    if ( iImplInfoArrayIterator < iImplInfoArray.Count() )
        {
        NotifyProgress();
        // Continue CActive asynchronous loop.
        CompleteOwnRequest();
        }
    else
        {
        // All plugins loaded:
        __GSLOGSTRING( "[GSPlgLoader] Loading plugins finished." );
        NotifyFinished();
        }
    }


// ---------------------------------------------------------------------------
// CScGenreItemConstructionConductor::CompleteOwnRequest
//
// Issue request complete notification.
// ---------------------------------------------------------------------------
void CGSPluginLoader::CompleteOwnRequest()
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, KErrNone );
    SetActive();
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::RunError
//
//
// ----------------------------------------------------------------------------
//
TInt CGSPluginLoader::RunError( TInt aError )
    {
    // This method is called when a plugin loading fails.
    // Always "fake" the return value so that ActiveSchedule
    // keeps running and later plugins are continued to be loaded.
    // Check if still plugins to be loaded:
    if( iImplInfoArrayIterator < iImplInfoArray.Count() )
        {
        NotifyProgress();

        //Continue CActive asynchronous loop.
        CompleteOwnRequest();
        }
    else // All plugins loaded:
        {
        NotifyFinished();
        }

    if ( aError == KLeaveExit )
        {
        return KLeaveExit;
        }

    return KErrNone;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::DoCancel
//
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::DoCancel()
    {

    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::NotifyProgress
//
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::NotifyProgress()
    {
    if( iObserver )
        {
        iObserver->HandlePluginLoaded( MGSPluginLoadObserver::EGSSuccess);
        }
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::NotifyFinished
//
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::NotifyFinished()
    {

    #ifdef _GS_PLUGINLOADER_FINAL_SORTING_TRACES
        TRAP_IGNORE( PrintOrderTracesL( iPluginArray ) );
    #endif // _GS_PLUGINLOADER_FINAL_SORTING_TRACES

    // I have finished loading: No need to keep me in the expensive scheduler
    // queue.
    Deque();

    if( iObserver )
        {
        iObserver->HandlePluginLoaded( MGSPluginLoadObserver::EGSFinished );
        }
    
    __GSLOGSTRING1( "[CGSPluginLoader::NotifyFinished] 0x%X", 
                    iParentUid.iUid );
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::SetObserver
//
//
// ----------------------------------------------------------------------------
//
EXPORT_C void CGSPluginLoader::SetObserver(MGSPluginLoadObserver* aObserver)
    {
    __GSLOGSTRING1("[GSPlgLoader] Observer set:0x%X", aObserver);
    iObserver = aObserver;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::ParseToUid
// Parses a UID from descriptor of form '0xNNNNNNNN' where N is hexadecimal.
//
// ----------------------------------------------------------------------------
//
TInt CGSPluginLoader::ParseToUid( const TDesC8& aSource, TUid& aTarget )
    {
    // Remove "0x" from the descriptor if it exists
    _LIT8(KHexPrefix, "0x");

    TPtrC8 pSource( aSource );
    const TInt prefixPosition = pSource.Find( KHexPrefix );
    if  ( prefixPosition != KErrNotFound )
        {
        pSource.Set( aSource.Mid( prefixPosition + KHexPrefix().Length() ) );
        }

    // Parse to integer
    TLex8 lex( pSource );
    TUint integer = 0;

    // Parse using TRadix::EHex as radix:
    const TInt err = lex.Val( integer, EHex );
    aTarget.iUid = integer;

    if( err != KErrNone )
        {
        // If parsing parent UID failed, do not load plugin:
        __GSLOGSTRING1(
            "[GSPlgLoader] Parsing parent UID failed. Error code:%d",
            err );
        }
    return err;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::ParseOrderNumber
//
//
// ----------------------------------------------------------------------------
//
TInt CGSPluginLoader::ParseOrderNumber( const TDesC8& aSource, TInt& aOrderNumber )
    {
    // Parse plugin's order number from opaque_data:
    TLex8 lex( aSource );
    const TInt orderErr = lex.Val( aOrderNumber );
    return orderErr;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::PrintInfoDebug
// Print CImplementationInformation to log. Used for debugging.
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::PrintInfoDebugL( const CImplementationInformation& aInfo,
                                      TInt aIterator,
                                      TInt aPluginCount )
    {


    #pragma message("-CGSPluginLoader verbose traces activated-")
    __GSLOGSTRING2( "[GSPlgLoader::LoadNextPluginL] %d/%d",
                    aIterator,
                    aPluginCount);

    __GSLOGSTRING1( "[GSPlgLoader] --Plugin 0x%X info--", &aInfo);
    __GSLOGSTRING1( "[GSPlgLoader] DisplayName:%S", &aInfo.DisplayName() );
    __GSLOGSTRING1( "[GSPlgLoader] ImplementationUid:0x%X",
                    aInfo.ImplementationUid() );

    const TInt KMaxEComDataLength = 256;

    HBufC* dataType = HBufC::New( KMaxEComDataLength );
    HBufC* opaqueData = HBufC::New( KMaxEComDataLength );

    dataType->Des().Copy( aInfo.DataType() );
    opaqueData->Des().Copy( aInfo.OpaqueData() );
    __GSLOGSTRING1( "[GSPlgLoader] DataType:%S", dataType );
    __GSLOGSTRING1( "[GSPlgLoader] OpaqueData:%S", opaqueData );

    delete opaqueData;
    delete dataType;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::LoadNextPluginL
// Iterate through iImplInfoArray. Load the plugin if it is eligible for
// loading. Loaded plugin is added to iPluginArray. Each time a plugin is
// loaded, iObserver is notified.
//
// ----------------------------------------------------------------------------
//
EXPORT_C void CGSPluginLoader::LoadNextPluginL()
    {
    // Iterate through iImplInfoArray. This loop continues between function
    // calls. Therefore member variable iImplInfoArrayIterator is used as a
    // counter. Loop will break when match is found and continues on next RunL.
    for( ; iImplInfoArrayIterator < iImplInfoArray.Count();  )
        {
        const CImplementationInformation* info =
            iImplInfoArray[ iImplInfoArrayIterator ];

        iImplInfoArrayIterator++;

        #ifdef _GS_PLUGINLOADER_VERBOSE_TRACES
            PrintInfoDebugL( *info, iImplInfoArrayIterator, iImplInfoArray.Count() );
        #endif //_GS_PLUGINLOADER_VERBOSE_TRACES

        // Parse parent UID from default_data:
        TUid parentUid;
        const TInt uidErr = ParseToUid( info->DataType(), parentUid );

        if( uidErr == KErrNone  && iParentUid == parentUid )
            {
            // If this plugin is OK -> load it:
            __GSLOGSTRING2( "[GSPlgLoader] %S eligible for parent 0x%X",
                    &info->DisplayName(), iParentUid.iUid );
            CGSPluginInterface* plugin = NULL;
            TInt error = KErrCancel;
            if( !iWatchDog->IsInBlackList( info->ImplementationUid() ) )
                {
                // Only panics move quarantined plugins to blacklist. Leaving is
                // normal programmatic functionality and therefore does not move
                // plugin to blacklist.
                iWatchDog->QuarantineL( info->ImplementationUid() );

                #ifdef _GS_PERFORMANCE_TRACES
                    TTime timeStart;
                    TTime timeEnd;
                    timeStart.HomeTime();
                #endif //_GS_PERFORMANCE_TRACES

                // Create plugin. Trap leave for debugging purposes.
                TRAP( error, plugin = &CreatePluginInstanceL( *info ); );

                #ifdef _GS_PERFORMANCE_TRACES
                    timeEnd.HomeTime();
                    TTimeIntervalMicroSeconds funcDuration = timeEnd.MicroSecondsFrom( timeStart );
                    __GSLOGSTRING2( "[GSPlgLoader::LoadNextPluginL/perf] %Ld    (%S)", funcDuration, &info->DisplayName() );
                #endif //_GS_PERFORMANCE_TRACES

                TRAP_IGNORE( iWatchDog->RemoveFromQuarantineL( info->ImplementationUid() ); );
                }
            if( error == KErrNone )
                {
                // Plugin ownership is transfered to iPluginArray
                InsertPluginInOrderL( plugin, iPluginArray );
                }
            else if( error == KLeaveExit )
                {
                __GSLOGSTRING( "[GSPlgLoader::LoadNextPluginL] LEAVE: KLeaveExit!!!" );
                // Must pass KLeaveExit through or otherwise Exit-command will
                // not be handled.
                User::Leave( KLeaveExit );
                }
            else
                {
                // Error note is displayed even if plugin is not loaded
                // -> plugin is in blacklist -> blacklist note displayed.
                #ifdef _DEBUG
                    DisplayErrorPopupL( error, info );
                #endif //_DEBUG
                }
            // Wait for next round
            break;
            }
        }
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::DisplayErrorPopupL
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::DisplayErrorPopupL(
        TInt aError,
        const CImplementationInformation* aInfo )
    {

    // Log error:
    __GSLOGSTRING3( "[GSPlgLoader] %S (0x%X) loading failed with error code %d",
                    &aInfo->DisplayName(),
                    aInfo->ImplementationUid().iUid,
                    aError );

    // Runtime info message used only in _DEBUG builds.
    // Buffer needs space for about 50 chars for debug text
    // + 8 chars for hex UID
    // + DisplayName().Length()
    // + error code 10 chars...
    HBufC* buf = HBufC::NewLC( 100 + aInfo->DisplayName().Length() );
    TPtr ptr = buf->Des();

    if( aError == KErrCancel )
        {
        _LIT( KDbgMsgBlacklisted, "Plugin in blacklist:\n%S (0x%X)" );
        ptr.Format( KDbgMsgBlacklisted,
                    &aInfo->DisplayName(),
                    aInfo->ImplementationUid().iUid );
        }
    else
        {
        _LIT( KDbgMsg, "Error:\n%S (0x%X)\nloading failed with error code %d" );
        ptr.Format( KDbgMsg,
                    &aInfo->DisplayName(),
                    aInfo->ImplementationUid().iUid,
                    aError );
        }
    if( iErrorPopup )
        {
        delete iErrorPopup;
        iErrorPopup = NULL;
        }
    iErrorPopup = CAknInfoPopupNoteController::NewL();
    iErrorPopup->SetTextL( ptr );
    CleanupStack::PopAndDestroy( buf );
    iErrorPopup->ShowInfoPopupNote();
    }

// ----------------------------------------------------------------------------
// CGSPluginLoader::CreatePluginInstanceL
//
//
// ----------------------------------------------------------------------------
//
CGSPluginInterface& CGSPluginLoader::CreatePluginInstanceL(
    const CImplementationInformation& aImpInfo )
    {
    // Create a wrapper for the plugin - this will take
    // care of cleaning up the plugin dll. This must be on
    // the cleanup stack above the plugin object itself - since
    // the cleanup stack will first delete the plugin, then the
    // plugin wrapper will be called to unload the ECOM plugin
    // dll itself.
    CGSPluginWrapper* wrapper = iDocument->NewPluginUnloadWrapperLC();

    // Now we can load the plugin
    const TUid implUid = aImpInfo.ImplementationUid();

    CGSPluginInterface* plugin = CGSPluginInterface::NewL( implUid,
                                                           iAppUi );// Remove iAppUi
    CleanupStack::PushL ( plugin );

    // And now its okay to update the wrapper with the plugin's
    // ECOM destructor info.
    wrapper->SetDetails( plugin->iDtor_ID_Key );

    // If plugin's resource definition had a valid order number,
    // set it to plugin.
    //
    // If parent is GSAppsPlugin, this is where the ordering
    // could be changed to follow the to-be-implemented resource file
    // defining the plugin order.

    // Parse plugin's order number from opaque_data:
    TInt orderNumber = 0;
    const TInt orderErr = ParseOrderNumber( aImpInfo.OpaqueData(), orderNumber );
    if  ( orderErr == KErrNone && orderNumber >= 0 )
        {
        plugin->iOrder = orderNumber;
        }

    // When a specific view has been activated externally to GS (e.g
    // whilst GS is not running) we have to load the view's plugin on-demand.
    // This means that the plugin is essentially free-floating and is not bound
    // to a parent's CGSParentPlugin::iPluginArray.
    //
    // In this situation, even though we have activated a specific view,
    // the plugin loader(s) continue to operate in the background.
    //
    // Eventually, as the loaders progress, a second attempt will occur to
    // load the same view (that was activated externally).
    //
    // In this situation, we discard the recently loaded instance of the plugin
    // and instead preserve the view that the user is (presumably) already using.
    //
    // However, we must ensure that we synchronous the parent's iPluginArray
    // with the pre-existing view instance.
    const TUid viewId = plugin->Id();
    CGSPluginAndViewIdCache& pluginViewIdCache = iDocument->PluginViewIdCache();
    const TBool isAlreadyLoaded = pluginViewIdCache.IsPluginLoaded( viewId );

    if  ( !isAlreadyLoaded )
        {
        // Cache the view uid & ECOM plugin dll uid to enable us
        // to more quickly handle external view activation requests
        // when GS is not already running.
        pluginViewIdCache.RegisterViewAndImplementationAssociationL( viewId, implUid );

        // Prepare to register the plugin with the cache.  Pushes a cleanup stack
        // item in case adding the view should leave.
        pluginViewIdCache.PrepareToRegisterPluginInstanceLC( viewId );

        // Add loaded view to appUi. At this point, the GS framework
        // is no longer responsible for the lifetime of the 'plugin'
        // object.
        //
        // However, to ensure sucessful cleanup of deleted
        // views, we have separated the ECOM plugin dll (controlled by
        // plugin->iDtor_ID_Key) to a separate object. This object is
        // owned by the loader.
        iAppUi->AddViewL( plugin );

        // Pop the cleanup item - all is well now.
        CleanupStack::Pop(); // cleanup item from PrepareToRegisterPluginInstanceLC

        CleanupStack::Pop( plugin ); // view framework is now responsible for this memory.

        // Document, when it is destroyed, will handle unloading of ECOM dll.
        CleanupStack::Pop( wrapper );

        // Also register that we have loaded an instance of the specified view
        // from a plugin.
        pluginViewIdCache.RegisterPluginInstanceL( viewId, *plugin );
        }
    else
        {
        // Plugin is already loaded, presumably due to external view activation
        // request.
        //
        // Discard "just loaded" instance and use the pre-loaded one instead.
        CleanupStack::PopAndDestroy( 2, wrapper );
        plugin = pluginViewIdCache.PluginInstance( viewId );
        }

    return *plugin;
    }

// ----------------------------------------------------------------------------
// CGSPluginLoader::SortPluginsL
//
// ----------------------------------------------------------------------------
//
EXPORT_C void CGSPluginLoader::SortPluginsL(
        CArrayPtrFlat<CGSPluginInterface>* aPlugins )
    {
    RPointerArray<CGSPluginInterface> plugins;
    TLinearOrder<CGSPluginInterface> order( CGSPluginLoader::Compare );

    // Insertion will also order
    for( TInt i = 0; i < aPlugins->Count(); i++ )
        {
        plugins.InsertInOrderL( (*aPlugins)[i], order );
        }

    // Replace original array content with sorted items
    aPlugins->Reset();
    for( TInt i = 0; i < plugins.Count(); i++ )
        {
        aPlugins->AppendL( plugins[i] );
        }
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::Compare
//
// Compare two plugins.
// Precedence:
// [1. plugin provider category]
// 2. plugin order number
// 3. plugin caption
// Plugin provider gategory is currently disabled (not supported yet).
// ----------------------------------------------------------------------------
//
TInt CGSPluginLoader::Compare( const CGSPluginInterface& aFirst,
                               const CGSPluginInterface& aSecond )
    {
    TInt comparison = CompareCategory( aFirst, aSecond );
    if( comparison == KGSComparisonEqual )
        {
        comparison = CompareIndex( aFirst, aSecond );
        if( comparison == KGSComparisonEqual )
            {
            comparison = CompareCaption( aFirst, aSecond );
            }
        }
    return comparison;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::InsertPluginInOrderL
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::InsertPluginInOrderL(
    CGSPluginInterface* aPlugin,
    CArrayPtrFlat<CGSPluginInterface>* aPlugins )
    {
    CGSPluginInterface* comparedPlugin;
    TInt comparison = 0;
    TBool inserted = EFalse;

    for( TInt i = 0; i < aPlugins->Count(); i++ )
        {
        comparedPlugin = (*aPlugins)[i];
        // Optimization: do not call time consuming Compare() multiple times!
        comparison = Compare( *aPlugin, *comparedPlugin );
        if( comparison < 0 )
            {
            aPlugins->InsertL( i, aPlugin );
            inserted = ETrue;
            break;
            }
        else if( comparison == 0 )
            {
            aPlugins->InsertL( i+1, aPlugin );
            inserted = ETrue;
            break;
            }
        }
    // Plugin was not before any other plugin - make sure it's appended
    if( !inserted )
        {
        aPlugins->AppendL( aPlugin );
        }

    #ifdef _GS_PLUGINLOADER_ITERATION_SORTING_TRACES
        PrintOrderTracesL( aPlugins );
    #endif // _GS_PLUGINLOADER_ITERATION_SORTING_TRACES

    }

// ----------------------------------------------------------------------------
// CGSPluginLoader::CompareCategory
//
// ----------------------------------------------------------------------------
//
void CGSPluginLoader::PrintOrderTracesL(
        CArrayPtrFlat<CGSPluginInterface>* aPlugins )
    {
    __GSLOGSTRING1( "---[CGSPluginLoader] Sorted list for 0x%X---", iParentUid.iUid );
    HBufC* name = HBufC::New( KGSCaptionSize );
    CleanupStack::PushL( name );

    TPtr ptr = name->Des();
    CGSPluginInterface* plg;

    for( TInt i = 0; i < aPlugins->Count(); i++ )
        {
        plg = (*aPlugins)[i];
        plg->GetCaptionL( ptr );
        __GSLOGSTRING4( "[CGSPluginLoader] Sorted list[%d] Category:%d (0x%X) %S",
                plg->iOrder,
                plg->PluginProviderCategory(),
                plg->Id().iUid,
                &ptr );
        }
    CleanupStack::PopAndDestroy( name );

    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::CompareCategory
//
//
// ----------------------------------------------------------------------------
//
TInt CGSPluginLoader::CompareCategory( const CGSPluginInterface& aFirst,
                                       const CGSPluginInterface& aSecond )
    {
#ifdef RD_GS_COMPARE_BY_CATEGORY
    TInt comparison = KGSComparisonBefore;//KGSComparisonEqual;

    // Compare if a is before b:
    TInt a = aFirst.PluginProviderCategory();
    TInt b = aSecond.PluginProviderCategory();

    // Cannot use less/greater comparison because int values used in here
    // (KGSPluginProviderInternal) must not be revealed to 3rd parties.
    if( a != b )
        {
        switch ( a )
            {
/*            case KGSPluginProviderInternal:
                if( b != KGSPluginProviderInternal )
                    {
                    comparison = KGSComparisonAfter;
                    }
                break;*/
            case CGSPluginInterface::EGSPluginProviderOEM:
                if( b == KGSPluginProviderInternal )
                    {
                    comparison = KGSComparisonAfter;
                    }
                break;
            case CGSPluginInterface::EGSPluginProviderOperator:
                if( b == KGSPluginProviderInternal ||
                    b == CGSPluginInterface::EGSPluginProviderOEM )
                    {
                    comparison = KGSComparisonAfter;
                    }
                break;
            case CGSPluginInterface::EGSPluginProvider3rdParty:
                if( b == KGSPluginProviderInternal ||
                    b == CGSPluginInterface::EGSPluginProviderOEM ||
                    b == CGSPluginInterface::EGSPluginProviderOperator )
                    {
                    comparison = KGSComparisonAfter;
                    }
                break;
            default:
                comparison = KGSComparisonBefore;
                break;
            }
        }
    else
        {
        comparison = KGSComparisonEqual;
        }

    return comparison;

#else //RD_GS_COMPARE_BY_CATEGORY
    #pragma message("Comparing by category DISABLED")
    // Force comparison to equal so category comparison will not matter. If
    // comparison by gategory is needed, simply remove the line below:
    return KGSComparisonEqual;
#endif //RD_GS_COMPARE_BY_CATEGORY
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::CompareCaption
//
//
// ----------------------------------------------------------------------------
//
TInt CGSPluginLoader::CompareCaption( const CGSPluginInterface& aFirst,
                                      const CGSPluginInterface& aSecond )
    {
    HBufC* firstCaptionBuf =  HBufC::New( KGSCaptionSize );
    HBufC* secondCaptionBuf = HBufC::New( KGSCaptionSize );
    TPtr firstCaption  = firstCaptionBuf->Des();
    TPtr secondCaption = secondCaptionBuf->Des();
    TInt comparison = KGSComparisonEqual;

    TRAPD( err,  aFirst.GetCaptionL( firstCaption ); );
    TRAPD( err2, aSecond.GetCaptionL( secondCaption ); );

    if( err == KErrNone && err2 == KErrNone )
        {
        // CompareC return value must be converted to KGSComparisonXXX value.
        TInt result = secondCaption.CompareC( firstCaption );
        if( result < 0 )
            {
            comparison = KGSComparisonAfter;
            }
        else if( result > 0 )
            {
            comparison = KGSComparisonBefore;
            }
        }
    delete firstCaptionBuf;
    delete secondCaptionBuf;
    return comparison;
    }


// ----------------------------------------------------------------------------
// CGSPluginLoader::CompareIndex
//
//
// ----------------------------------------------------------------------------
//
TInt CGSPluginLoader::CompareIndex( const CGSPluginInterface& aFirst,
                                    const CGSPluginInterface& aSecond )
    {
    TInt comparison = KGSComparisonEqual;

    // The plugin having index is before the one not having one

    if( aFirst.iOrder  == KGSPluginNotIndexed &&
        aSecond.iOrder == KGSPluginNotIndexed )
        {
        // Neither have index -> equal
        comparison = KGSComparisonEqual;
        }
    else if( aFirst.iOrder == KGSPluginNotIndexed )
        {
        // The plugin having index is before the one not having one
        comparison = KGSComparisonAfter;
        }
    else if( aSecond.iOrder == KGSPluginNotIndexed )
        {
        // The plugin having index is before the one not having one
        comparison = KGSComparisonBefore;
        }
    else if( aFirst.iOrder < aSecond.iOrder )
        {
        // Compare actual index values
        comparison = KGSComparisonBefore;
        }
    else if( aFirst.iOrder > aSecond.iOrder )
        {
        // Compare actual index values
        comparison = KGSComparisonAfter;
        }

    return comparison;
    }



EXPORT_C void CGSPluginLoader::RequestPriority( CActive::TPriority aPriority )
    {
    iRequestedPriority = aPriority; 
    }


//  End of File