xcfw/src/xcfwengine.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 12:11:15 +0200
changeset 3 ff572005ac23
parent 0 79c6a41cd166
child 19 edd621764147
permissions -rw-r--r--
Revision: 201001 Kit: 201004

/*
* Copyright (c) 2002-2005 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:  Implementation of XCFW Engine
*
*/



// INCLUDE FILES
#include    "xcfwengine.h"
#include    "gecoobjectfactorybase.h"
#include    "gecodefaultobject.h"
#include    "xcfwtree.h"
#include    "xcfwlocalizer.h"
#include    "xcfwpanic.h"
#include    "xcfwentityconverter.h"

#include    <gmxmlnode.h>
#include    <gmxmlelement.h>
#include    <gmxmlcomposer.h>
#include    <gmxmldocument.h>
#include    <gmxmlcharacterdata.h>
#include    <gmxmltext.h>
#include    <gmxmlcdatasection.h>

// CONSTANTS
// default XML declaration
_LIT( KXMLDeclaration, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
// default Doctype declaration
_LIT( KDocTypeDecl, "<!DOCTYPE xcfwml SYSTEM \"%S\">");
_LIT( KDocTypeDeclNoDTD, "<!DOCTYPE xcfwml>");
_LIT( KMmsDTD, "mms_smil.dtd"); //this is autogenerated by GMXML if no DTD decl.
_LIT( KXCFWAnd, "&" );
_LIT( KXCFWSemiC, ";" );
_LIT( KDTDExt, ".dtd" );
_LIT( KLocFormat, "%0*d\\");
const TInt KDTDExtLen = 4; // ".dtd"
const TInt KLocFormatLen = 7; // "%0*d\\" 

//Entity reference extra character count
const TInt KExtraChars = 2;
const TInt KMaxDTDLength = 160;


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

// -----------------------------------------------------------------------------
// CXCFWEngine::CXCFWEngine
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CXCFWEngine::CXCFWEngine(
    MXCFWEngineObserver* aObserver ):
    CActive( CActive::EPriorityStandard )
    {
    iObserver = aObserver;
    CActiveScheduler::Add( this );
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::ConstructL()
    {
    //Create default object factory
    iDefaultFactory = CGECODefaultObjectFactory::NewL();
    iState = EStateIdle;
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CXCFWEngine* CXCFWEngine::NewL(
    MXCFWEngineObserver* aObserver )
    {
    
    __ASSERT_LEAVE( aObserver!=NULL, KErrArgument );
    
    CXCFWEngine* self = new( ELeave ) CXCFWEngine( aObserver ) ;
    
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );

    return self;
    }

    
// Destructor
EXPORT_C CXCFWEngine::~CXCFWEngine()
    {
    if ( IsActive() )
        {
        Cancel();
        }

    // Reset object factory array (factories are not owned)
    iFactoryList.Reset();
    iFactoryList.Close();    

    // delete default object factory
    delete iDefaultFactory;

    // delete xml parser
    delete iParser;

    // delete XML composer
    delete iComposer;

    // delete XML document object
    delete iXMLDoc;

    // delete XML file name buffer
    delete iFile;

    // delete DTD file name buffer
    delete iDTD;

    // delete localizer instance
    delete iLocalizer;

    // delete node text buffer
    delete iNodeText;

    // Set non-owned pointers to NULL
    iCurrentXMLNode = NULL;
    if ( iTree )
        {
        iTree->SetLocked( EFalse );
        iTree = NULL;
        }
    iCurrentTreeNode = NULL;
    iObserver = NULL;

    //Close file system handle (just in case)
    iFileSystem.Close();
    
   }

// -----------------------------------------------------------------------------
// CXCFWEngine::RunL
// Engine conducts itself according to internal state.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::RunL()
    {

    TRequestStatus *s = &iStatus;
    
    switch ( iState )
        {

        case EStateInitializingLoad:
            {
            //Instantiate parser and request parsing. If ParseFile returns
            //an error code, it is most probably a file that is not found
            //or it can't be currently accessed => leave.            
            //we'll get notification from parser through ParseFileCompleteL
            //when ready.
            iState = EStateLoadingFile;
            delete iParser;
	    iParser = NULL;
            iParser = CMDXMLParser::NewL( this );    
            User::LeaveIfError ( 
                iParser->ParseFile( iFileSystem, iFile->Des() ) );
            break;                
            }    

        case EStateInitializingSave:
            {
            if ( iLocalizer && iLocalizer->LastError() != KErrNone )
                {
                iObserver->HandleEngineErrorL ( KErrGeneral );
                Cancel();
                FreeResources();
                }
            else
                {
                iState = EStateConstructingDOM;
                SetActive();
                User::RequestComplete( s, KErrNone );                    
                }
            break;
            }
    

        case EStateParsing: //Constructing XCFWTree from DOM
            {
            if ( iCurrentXMLNode )
                {
                iTree->SetLocked( EFalse );
                DOM2TreeNextCycleL();
                iTree->SetLocked( ETrue );
                iState = EStateParsing;   
                SetActive();
                User::RequestComplete( s, KErrNone );
                }
            else
                {
                iState = EStateIdle;
                //free parsing resources
                iObserver->HandleEngineEventL( 
                    MXCFWEngineObserver::EEvtParsingComplete );
                FreeResources();
                }

            #ifdef __XCFW_MODULE_TEST
            iObserver->HandleEngineEventL( MXCFWEngineObserver::EEvtNull );                
            #endif

            break;
            }

        case EStateConstructingDOM: //Constructing DOM from XCFW Tree
            {
            if ( iCurrentTreeNode )
                {
                iTree->SetLocked( EFalse );
                Tree2DOMNextCycleL();
                iTree->SetLocked( ETrue );
                iState = EStateConstructingDOM;
                SetActive();
                User::RequestComplete( s, KErrNone );
                }
            else
                {
                iTree->SetLocked( EFalse );
                iState = EStateSavingFile;                    
                //delete possible previous instance of composer
                //and create new.
                delete iComposer;
                iConverter = NULL; //Deleted by composer
                iConverter = new ( ELeave ) CXCFWEntityConverter;
                iComposer = CMDXMLComposer::NewL( this );
                iComposer->SetEntityConverter( iConverter );
                
		// Ask composer to compose the file.
                // we'll get notification about the op through 
                // ComposeFileCompleteL
                User::LeaveIfError( 
                    iComposer->ComposeFile( 
                    iFileSystem, iFile->Des(), iXMLDoc, EUtf8 ) );
                }

            #ifdef __XCFW_MODULE_TEST
            iObserver->HandleEngineEventL( MXCFWEngineObserver::EEvtNull );                
            #endif
            
            break;                
            }


        case EStateIdle: //idle state, not doing anything
            {
            break;
            } 

        default:
            {
            break;
            }
        }
    
    
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::RunError
// Notify observer about the error and free resources
// -----------------------------------------------------------------------------
//
TInt CXCFWEngine::RunError(
    TInt aError )
    {
    TInt ret = KErrNone;
    iStateByLastError = iState;

    FreeResources();            
    
    TRAP( ret, iObserver->HandleEngineErrorL( aError ) );    
    return ret;
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::DoCancel
// Notify observer about operation cancellation and free resources.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::DoCancel()
    {
    TInt state = iState;
    FreeResources();
    switch ( state )
        {
        case EStateInitializingLoad: //fallthrough
        case EStateParsing:
            {
            TInt err = 0;
            TRAP(err, iObserver->HandleEngineEventL( 
                MXCFWEngineObserver::EEvtParsingCanceled ) );
            break;   
            }
        case EStateInitializingSave: //fallthrough
        case EStateConstructingDOM:
            {
            TInt err = 0;
            TRAP(err, iObserver->HandleEngineEventL( 
                MXCFWEngineObserver::EEvtSavingCanceled ) );
            break;   
            }
        default:
            break;
        }
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::CancelOperation
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::CancelOperation()
    {
    
    Cancel();
    //in case engine was not active, need to free the resources here.
    FreeResources();
                
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::LoadL
// Wrapper to support loading of XML without giving a DTD file name (i.e. when
// loading content that has no localized strings)
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::LoadL(
    MXCFWTree& aTree, 
    const TDesC& aFile )
    {
    LoadL( aTree, aFile, KNullDesC );
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::LoadL
// XML loading operation is started. Internal members are initialized and
// object is set active. 
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::LoadL(
    MXCFWTree& aTree, 
    const TDesC& aFile, 
    const TDesC& aDTDFile )
    {

    // If we're active at the moment or iState is something else than Idle,
    // leave with KErrInUse.
    if ( IsActive() || iState != EStateIdle )
        {
        User::Leave( KErrInUse );        
        }

    User::LeaveIfError( iFileSystem.Connect() );
    
    //delete previous instances of parser and DTD name buffers
    delete iParser;     
    iParser = NULL;               
    delete iDTD;
    iDTD = NULL;
    iDTD = aDTDFile.AllocL();
    delete iFile;
    iFile = NULL;
    iFile = aFile.AllocL();

    //Set tree to use (not owned)
    iTree = &aTree;
    if ( iTree->Root () )
        {
        //if the tree has already a root, we'll load items under that root
        iCurrentTreeNode = iTree->Root();            
        }
    else
        {
        iCurrentTreeNode = NULL;            
        }

    iStateByLastError = EStateIdle;

    //Reset possible DTD name 
    iTree->SetDTDNameL( aDTDFile );
    iTree->SetLocked( ETrue );

    delete iParser;
    iParser = NULL;
    iParser = CMDXMLParser::NewL( this );    
    User::LeaveIfError ( 
        iParser->ParseFile( iFileSystem, iFile->Des() ) );
    iState = EStateLoadingFile;

    }

// -----------------------------------------------------------------------------
// CXCFWEngine::SaveL
// Wrapper to support saving of XML without giving a DTD file name (i.e. when
// saving content that has no localized strings)
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::SaveL(
    MXCFWTree& aTree, 
    const TDesC& aFile )
    {
    SaveL( aTree, aFile, aTree.DTDName() );
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::SaveL
// Save operation is initialized and started
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::SaveL(
    MXCFWTree& aTree,
    const TDesC& aFile,
    const TDesC& aDTDFile )
    {

    if ( IsActive() || iState != EStateIdle )
        {
        User::Leave( KErrInUse );        
        }

    User::LeaveIfError( iFileSystem.Connect() );

    //create folder if not exist
    TChar bs = '\\';
    if ( aFile.Locate( bs ) != KErrNotFound )
        {
        TPtrC dir = aFile.Left( aFile.LocateReverse( bs ) + 1 );
        TInt ret = iFileSystem.MkDirAll( dir );
        if ( KErrAlreadyExists != ret && KErrNone != ret ) 
            {
            User::Leave( ret );                
            }
        }

    //Set tree pointer ( not owned )
    iTree = &aTree;
    if ( iTree->Root () )
        {
        //init current tree node to root if there's one
        iCurrentTreeNode = iTree->Root();            
        }
    else
        {
        // this tree can't be saved, has no data
        User::Leave( KErrArgument );
        }

    //delete previous instances of parser and filename buffers
    delete iComposer;     
    iComposer = NULL;               

    delete iFile;
    iFile = NULL;
    iFile = aFile.AllocL();

    delete iDTD;
    iDTD = NULL;
    iDTD = aDTDFile.AllocL();

    iStateByLastError = EStateIdle;

    // delete possible previous instance of XML Doc object
    // create new doc and initialize with XML decl + Doctype
    delete iXMLDoc;
    iXMLDoc = NULL;
    iXMLDoc = CMDXMLDocument::NewL();
    iXMLDoc->SetVersionTagL( KXMLDeclaration );

    // set doc type tag according to given DTD file name
    if ( aDTDFile.Compare ( KNullDesC) != 0 )
        {
        TBuf<KMaxDTDLength> buf;
        TInt bsloc = aDTDFile.LocateReverse( bs );
        
        // take just the file name, no preceding path chars
        // (the assumption is that loc files are stored
        // at relative path \locNN\dtdname.dtd related to
        // XML file location)
        if ( bsloc != KErrNotFound)
            {
            TPtrC dtdname = aDTDFile.Mid( bsloc + 1 );
            buf.Format( KDocTypeDecl, &dtdname);
            }
        else
            {
            buf.Format( KDocTypeDecl, &aDTDFile );
            }
        
        iXMLDoc->SetDocTypeTagL( buf );
        }
    else
        {
        iXMLDoc->SetDocTypeTagL( KDocTypeDeclNoDTD );
        }
    // notify observer that we're about to start saving
    iObserver->HandleEngineEventL( 
        MXCFWEngineObserver::EEvtSavingStarted );

    iState = EStateInitializingSave;
    PrepareEntityConverterAndSetActiveL();

    // lock tree to prevent changes during save
    iTree->SetLocked( ETrue );   
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::HasTextData
// returns ETrue if the current xml node has text data under it.
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CXCFWEngine::HasTextData()
    {
    TBool ret = EFalse;
    if ( iCurrentXMLNode && iCurrentXMLNode->FirstChild() )
        {
        CMDXMLNode::TDOMNodeType t = iCurrentXMLNode->FirstChild()->NodeType();
        
        if ( t == CMDXMLNode::ETextNode || t == CMDXMLNode::ECDATASectionNode )
            {
            ret = ETrue;                
            }
        }
    return ret;
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::TextDetailsL
// returns text details for the current XML node (if it has text)
// Node may contain CDATA sections, but mixed content is not supported.
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::TextDetailsL(
    TPtrC& aText,
    TBool& aIsLocalized )
    {
    _LIT(KEntityRef, "*&*;*");
    
    
    TInt err = KErrNotFound;
    CMDXMLNode* ptr = iCurrentXMLNode->FirstChild();

    if ( ptr )
        {
        
        //delete previous text pointer now
        delete iNodeText;
        iNodeText = NULL;
        
        //loop through all text / cdata elements
        while ( ptr && 
            ( ptr->NodeType() == CMDXMLNode::ETextNode || 
            ptr->NodeType() == CMDXMLNode::ECDATASectionNode ) )
            {
            err = KErrNone;
            TPtrC nextdata;
            switch ( ptr->NodeType() )
                {
                case CMDXMLNode::ETextNode:
                    {
                    nextdata.Set( ((CMDXMLCharacterData*)ptr)->Data() );                    
                    break;
                    }
                case CMDXMLNode::ECDATASectionNode:
                    {
                    nextdata.Set( ((CMDXMLCDATASection*)ptr)->Data() );                    
                    break;
                    }
                default:
                    {
                    err = KErrNotFound;
                    break;
                    }
                }
                
            if ( KErrNone == err )
                {
                //create nodetext buffer if we don't have it yet.
                if ( !iNodeText )
                    {
                    iNodeText = HBufC::NewL( nextdata.Length() );
                    iNodeText->Des().Copy( nextdata );    
                    }
                else
                    {
                    //increase nodetext buffer and append new data.
                    iNodeText = iNodeText->ReAllocL( 
                        iNodeText->Length() + nextdata.Length() );
                    iNodeText->Des().Append( nextdata );
                    }
                }
            ptr = ptr->NextSibling();
            }
        
        //If we have some text, do localization
        if( iNodeText )
            {
            err = KErrNone;
            aText.Set( *iNodeText );
            aIsLocalized = EFalse;
            
            if ( aText.Match( KEntityRef ) != KErrNotFound && iLocalizer)
                {
                TPtrC ltext;
                if ( KErrNone == iLocalizer->EntityRefToText( aText, ltext ) )
                    {
                    aText.Set( ltext );                        
                    aIsLocalized = ETrue;
                    }
                }
            }
        }
    User::LeaveIfError( err );
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::NumAttributes
// return number of attributes for current XML node
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CXCFWEngine::NumAttributes()
    {

    TInt count = 0;

    //Return attribute count for normal element only.
    if ( iCurrentXMLNode )
        {
        if ( iCurrentXMLNode->NodeType() == CMDXMLNode::EElementNode )
            {
            count = ((CMDXMLElement*)iCurrentXMLNode)->NumAttributes();
            }
        }

    return count;    
    }
    
// -----------------------------------------------------------------------------
// CXCFWEngine::AttributeDetailsL
// Function reads attributes from current xml node and returns them in TPtrC's
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::AttributeDetailsL(
    const TInt aIndex, 
    TPtrC& aAttributeName, 
    TPtrC& aAttributeValue, 
    TBool& aIsLocalized )
    {
    _LIT(KEntityRef, "*&*;*");
    
    
    //Return attribute details for normal element only.
    if ( iCurrentXMLNode )
        {
        if ( iCurrentXMLNode->NodeType() == CMDXMLNode::EElementNode )
            {
            aIsLocalized = EFalse;
            // Get attribute name + value
            User::LeaveIfError (
                ((CMDXMLElement*)iCurrentXMLNode)->
                AttributeDetails(aIndex, aAttributeName, aAttributeValue) );
            
            // query localizer component for localized text
            if ( aAttributeValue.Match( KEntityRef ) != KErrNotFound 
                 && iLocalizer )
                {
                TPtrC ltext;
                if ( KErrNone == iLocalizer->EntityRefToText( 
                    aAttributeValue, ltext) )
                    {
                    aAttributeValue.Set( ltext );                        
                    aIsLocalized = ETrue;
                    }
                }
            }
        }
    }
    

// -----------------------------------------------------------------------------
// CXCFWEngine::AttributeDetailsL
// Function reads attributes from current xml node and returns them in TPtrC's
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::AttributeDetailsL(
    const TInt aIndex, 
    TPtrC& aAttributeName, 
    TPtrC& aAttributeValue)
    {
    
    //Return attribute details for normal element only.
    if ( iCurrentXMLNode )
        {
        if ( iCurrentXMLNode->NodeType() == CMDXMLNode::EElementNode )
            {
            // Get attribute name + value
            User::LeaveIfError (
                ((CMDXMLElement*)iCurrentXMLNode)->
                AttributeDetails(aIndex, aAttributeName, aAttributeValue) );
            }
        }
    }
    
// -----------------------------------------------------------------------------
// CXCFWEngine::UnRegisterObjectFactory
// Removes given object factory pointer from factory array. Does not delete.
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CXCFWEngine::UnRegisterObjectFactory(
    CGECOObjectFactoryBase* aFactory )
    {
    
    TInt err = KErrNotFound;
    TInt maxindex = iFactoryList.Count() - 1;

    for ( TInt i = maxindex; i >= 0 ; i-- )
        {
        if ( iFactoryList[i] == aFactory )
            {
            iFactoryList.Remove(i);
            err = KErrNone;
            }
        }

    return err;
        
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::RegisterObjectFactory
// Adds given object factory pointer to factory array. Ownership NOT taken.
// Adding same factory many times is not possible.
// -----------------------------------------------------------------------------
//
EXPORT_C void CXCFWEngine::RegisterObjectFactoryL(
    CGECOObjectFactoryBase* aFactory )
    {
    
    __ASSERT_LEAVE ( aFactory != NULL, KErrArgument );

    TInt maxindex = iFactoryList.Count() - 1;
    
    for ( TInt i = maxindex; i>= 0 ; i-- )
        {
        if ( iFactoryList[i] == aFactory )
            {
            User::Leave( KErrAlreadyExists );                
            }
        }
        
    // add to factory array
    User::LeaveIfError( iFactoryList.Append( aFactory ) );

    }
    
// -----------------------------------------------------------------------------
// CXCFWEngine::ParseFileCompleteL()
// Detaches parsed XML document from parser. If DTD file was provided in LoadL
// call, we will next load the DTD for getting entity references ready. If no
// DTD file was given, we go straight to parsing.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::ParseFileCompleteL()
    {

    //see if we have urecoverable errors from GMXML => if error severity is
    //fatal, let's not go any further in processing.
    if ( iParser->ErrorSeverity() == EXMLFatal )
        {
        iStateByLastError = iState;
        iState = EStateIdle;
        iObserver->HandleEngineErrorL( iParser->Error() );
        FreeResources();  
        }
    else
        {
        //delete previous instance of document
        if ( iXMLDoc )
            {
            delete iXMLDoc;
            iXMLDoc = NULL;            
            }
            
        iXMLDoc = iParser->DetachXMLDoc();
        iCurrentXMLNode = iXMLDoc->DocumentElement()->FirstChild();

        //set up DTD if not already done
        PrepareDTDPathL();

        TRAPD( err, iObserver->HandleEngineEventL( 
                MXCFWEngineObserver::EEvtParsingStarted ) );
        if ( KErrNone != err )
            {
            iObserver->HandleEngineErrorL( err );
            Cancel();
            FreeResources();                
            }

        //Set active        
        iState = EStateParsing;
        PrepareEntityConverterAndSetActiveL();
        }
    }
    
// -----------------------------------------------------------------------------
// CXCFWEngine::AddCurrentXMLNodeToTreeL
// New content object is generated, initialized and added to tree. Object
// initialization is done with a registered object factory if there's such.
// Otherwise default object factory is used.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::AddCurrentXMLNodeToTreeL()
    {
    
    __ASSERT_LEAVE( iTree && iCurrentXMLNode, KErrGeneral );

    CGECOObjectBase* obj = NULL;
    CGECOObjectFactoryBase* factory = NULL;
    TInt count = iFactoryList.Count();

    //XCFW will only handle element nodes.
    if ( iCurrentXMLNode->NodeType() == CMDXMLNode::EElementNode )
        {
        if ( count > 0 )
            {
            //loop through factories starting from the most recently added
            //until a factory returns an object for the given tag or we run
            //out of factories.
            for ( TInt i = count-1 ; i>= 0 && !obj ; i--)
                {
                //Query factory for object
                factory = iFactoryList[i];
                obj = factory->
                    GetContentObjectAndSetContextL( 
                        iCurrentXMLNode->NodeName() );
                }
            }
        
        // if none of the user factories recognized this tag, 
        // use default factory.
        if ( !obj ) 
            {
            factory = iDefaultFactory;
            obj = factory->GetContentObjectAndSetContextL( 
                iCurrentXMLNode->NodeName() );	
            }
        }

    //if we have an object, let's add it to tree. 
    //otherwise the whole branch starting from this node will
    //be discarded from XCFWTree.
    if ( obj )
        {
        CleanupStack::PushL( obj );
        
        factory->InitializeObjectL( *this );
        
        if ( !iCurrentTreeNode )
            {
            //Adding root.
            iCurrentTreeNode = iTree->AddNodeL( obj );
            }
        else
            {
            //add under certain parent.
            iCurrentTreeNode = iTree->AddNodeL( obj, iCurrentTreeNode );            
            }
            
        CleanupStack::Pop( obj );
        }
    else
        {
        //Notify observer about unknown data if current node is an element node        
        if ( iCurrentXMLNode->NodeType() == CMDXMLNode::EElementNode )
            {
            iObserver->HandleEngineErrorL( KErrUnknown );            
            }

        // discard this branch in tree: loop out to next sibling of 
        // this node or its parent
        while ( iCurrentXMLNode && !iCurrentXMLNode->NextSibling() )
            {
            iCurrentXMLNode = iCurrentXMLNode->ParentNode();
            if ( iCurrentXMLNode && iCurrentTreeNode->Parent() )
                {
                iCurrentTreeNode = iCurrentTreeNode->Parent();
                }
            }
            
        // set next node pointer to process
        if( iCurrentXMLNode && iCurrentXMLNode->NextSibling() )
			{
			iCurrentXMLNode = iCurrentXMLNode->NextSibling();
			}
        }
            
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::DOM2TreeNextCycleL
// XML DOM is traversed node by node, and elements are added to content tree. 
// Each call leaves will set iCurrentXMLNode to point to the next DOM node to 
// be processed until there's no more nodes.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::DOM2TreeNextCycleL()
    {

    CMDXMLNode* reference = NULL;

    if ( iCurrentXMLNode )
        {

        reference = iCurrentXMLNode;
        
        //add this XML node data to content tree
        AddCurrentXMLNodeToTreeL();    
        // if node was discareded for some reason, let's keep calling
        // until a node is accepted.
        while ( iCurrentXMLNode && iCurrentXMLNode != reference )
            {
            reference = iCurrentXMLNode;
            AddCurrentXMLNodeToTreeL();                
            }
        
        if ( !iCurrentXMLNode )
            {
            return;                
            }
        
        //if this node has children, go to first child now
        if ( iCurrentXMLNode->FirstChild() )
            {
            iCurrentXMLNode = iCurrentXMLNode->FirstChild();                        
            }
        else //no children
            {
            
            //update XCFWTree parent node pointer as this xml node had no child
            if ( iCurrentTreeNode && iCurrentTreeNode->Parent() )
                {
                iCurrentTreeNode = iCurrentTreeNode->Parent();    
                }

            //if there's siblings at the same level, go to next sibling
            if ( iCurrentXMLNode->NextSibling() )
                {
                iCurrentXMLNode = iCurrentXMLNode->NextSibling();    
                }
            else //no siblings left
                {
                // get back in the tree to a level that has still siblings left
                while ( iCurrentXMLNode && !iCurrentXMLNode->NextSibling() )
                    {
                    iCurrentXMLNode = iCurrentXMLNode->ParentNode();
                    // update XCFWTree parent pointer if necessary
                    if ( iCurrentXMLNode && 
                        iCurrentTreeNode && iCurrentTreeNode->Parent() )
                        {
                        iCurrentTreeNode = iCurrentTreeNode->Parent();
                        }
                    }
                // now we're either at a level that has siblings, or then
                // we're out of nodes. If there's a sibling, we'll process
                // that next
                if( iCurrentXMLNode && iCurrentXMLNode->NextSibling() )
					{
					iCurrentXMLNode = iCurrentXMLNode->NextSibling();
					}
                }
            }
        }
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::CurrentState()
// Returns engine's internal state. Client may want to know this at error
// situations to determine if a retry would be necessary. 
// -----------------------------------------------------------------------------
//
EXPORT_C CXCFWEngine::TXCFWEngineState CXCFWEngine::CurrentState()
    {
    //If the last state change was by an error, return the state that
    //engine was in when error occurred (error routine will set the state to
    //EStateIdle). Otherwise return the current state.
    if ( iStateByLastError != EStateIdle )
        {
        return iStateByLastError;            
        }
    else
        {
        return iState;
        }
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::ComposeFileCompleteL()
// Called by GMXML composer when DOM has been saved to file.
// Possible fatal errors are sent forward to XCFW client. Otherwise the client
// is just informed with saving completed event.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::ComposeFileCompleteL()
    {

    //see if we have urecoverable errors from GMXML => if error severity is
    //fatal, let's not go any further in processing.
    if ( iComposer->ErrorSeverity() == EXMLFatal )
        {
        TInt err = iComposer->Error();
        iStateByLastError = iState;
        iState = EStateIdle;
        FreeResources();  
        iObserver->HandleEngineErrorL( err );
        }
    else
        {
        FreeResources();
        iObserver->HandleEngineEventL( 
            MXCFWEngineObserver::EEvtSavingComplete );
        }
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::Tree2DOMNextCycleLL
// XCFWTree is traversed node by node, and elements are added to XML DOM. 
// Each call leaves will set iCurrentTreeNode to point to the next node to 
// be processed until there's no more nodes left in XCFW Tree.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::Tree2DOMNextCycleL()
    {

    MXCFWNode* reference = NULL;

    if ( iCurrentTreeNode )
        {

        reference = iCurrentTreeNode;
        
        //add this tree node data to DOM
        AddCurrentTreeNodeToDOML();    
        // if node was discareded for some reason, let's keep calling
        // until a node is accepted.
        while ( iCurrentTreeNode && iCurrentTreeNode != reference )
            {
            reference = iCurrentTreeNode;
            AddCurrentTreeNodeToDOML();                
            }
        
        if ( !iCurrentTreeNode )
            {
            return;                
            }
        
        //if this node has children, go to first child now
        if ( iCurrentTreeNode->FirstChild() )
            {
            iCurrentTreeNode = iCurrentTreeNode->FirstChild();                        
            }
        else //no children
            {
            
            //update DOM parent node pointer as this Tree node had no child
            if ( iCurrentXMLNode && iCurrentXMLNode->ParentNode() )
                {
                iCurrentXMLNode = iCurrentXMLNode->ParentNode();    
                }

            //if there's siblings at the same level, go to next sibling
            if ( iCurrentTreeNode->NextSibling() )
                {
                iCurrentTreeNode = iCurrentTreeNode->NextSibling();    
                }
            else //no siblings left
                {
                // get back in the tree to a level that has still siblings left
                while ( iCurrentTreeNode && !iCurrentTreeNode->NextSibling() )
                    {
                    iCurrentTreeNode = iCurrentTreeNode->Parent();
                    // update DOM parent pointer if necessary
                    if ( iCurrentTreeNode && 
                        iCurrentXMLNode && iCurrentXMLNode->ParentNode() )
                        {
                        iCurrentXMLNode = iCurrentXMLNode->ParentNode();
                        }
                    }
                // now we're either at a level that has siblings, or then
                // we're out of nodes. If there's a sibling, we'll process
                // that next
                if( iCurrentTreeNode && iCurrentTreeNode->NextSibling() )
					{
					iCurrentTreeNode = iCurrentTreeNode->NextSibling();
					}
                }
            }
        }  
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::AddCurrentTreeNodeToDOML
// New XML DOM element node is generated out of the XCFW Tree node data.
// DOM node data is queried from XCFW Tree node using the corresponding
// object factory. If registered object factory recognizes this node's typeid,
// default factory implementation is used.
// New XML Element node is added to XML DOM.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::AddCurrentTreeNodeToDOML()
    {

    __ASSERT_LEAVE( iTree && iCurrentTreeNode, KErrGeneral );

    CGECOObjectBase* obj = iCurrentTreeNode->Data();
    CGECOObjectFactoryBase* factory = NULL;
    TInt count = iFactoryList.Count();
    TInt err = KErrNotSupported;
    //Find factory for the current tree node
    if ( count > 0 )
        {
        //loop through factories starting from the most recently added
        //until a factory returns KErrNone for SetContext or we run out 
        //of factories
        for ( TInt i = count-1 ; i>= 0 && KErrNone != err ; i--)
            {
            //Query factory for object
            factory = iFactoryList[i];
            err = factory->SetContext( obj );
            }
        }

    // if none of the user factories recognized this object, 
    // use default factory.
    if ( KErrNone != err ) 
        {
        factory = iDefaultFactory;
        err = factory->SetContext( obj );	
        }


    //if we have an object, let's add it to tree. 
    //otherwise the whole branch starting from this node will
    //be discarded from XCFWTree.
    if ( err == KErrNone )
        {
        CMDXMLElement* node = CMDXMLElement::NewLC( 
            ETrue, iXMLDoc, obj->TypeIdentifier() );
        
        TInt counter = factory->NumAttributes() - 1;
        while ( counter >= 0 )
            {
            TPtrC attrname;
            TPtrC attrvalue;
            HBufC* ebuf = NULL;
            factory->AttributeDetailsL( counter, attrname, attrvalue );
            
            node->SetAttributeL( attrname, attrvalue, ETrue );

            if ( ebuf )
                {
                CleanupStack::PopAndDestroy( ebuf );
                }

            counter--;                
            }

        //if object has text data, let's put it to a child node...
        if ( factory->HasTextData() )
            {
            CMDXMLText* textnode = CMDXMLText::NewLC( iXMLDoc );
            TPtrC text;
            TBool locstatus;
            factory->TextDetailsL( text, locstatus );
            HBufC* ebuf = NULL;
            //Check localization
            if ( locstatus && iLocalizer )
                {
                TPtrC eref;
                if ( KErrNone == iLocalizer->TextToEntityRef( text, eref ) )
                    {
                    ebuf = HBufC::NewLC( eref.Length() + KExtraChars );
                    ebuf->Des().Copy( KXCFWAnd );
                    ebuf->Des().Append( eref );
                    ebuf->Des().Append( KXCFWSemiC );
                    text.Set( ebuf->Des() );                  
                    }
                }
            textnode->SetDataL( text );
            node->AppendChild( textnode );
            //destroying entity ref buffer is safe now
            if ( ebuf )
                {
                CleanupStack::PopAndDestroy( ebuf );                    
                }
            CleanupStack::Pop( textnode );
            }
            
        if ( !iCurrentXMLNode )
            {
            iXMLDoc->DocumentElement()->AppendChild(node);
            }
        else
            {
            iCurrentXMLNode->AppendChild( node );
            }
        iCurrentXMLNode = node;
        CleanupStack::Pop( node );
        }
    else
        {
        //Notify observer about unknown data        
        iObserver->HandleEngineErrorL( KErrUnknown );            
    
        // discard this branch in tree: loop out to next sibling of 
        // this node or its parent
        while ( iCurrentTreeNode && !iCurrentTreeNode->NextSibling() )
            {
            iCurrentTreeNode = iCurrentTreeNode->Parent();
            if ( iCurrentTreeNode && iCurrentXMLNode->ParentNode() )
                {
                iCurrentXMLNode = iCurrentXMLNode->ParentNode();
                }
            }
            
        // set next node pointer to process
        if( iCurrentTreeNode && iCurrentTreeNode->NextSibling() )
			{
			iCurrentTreeNode = iCurrentTreeNode->NextSibling();
			}
        }
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::FreeResources
// XML parser / composer resources are freed (DOM tree will be deleted from mem)
// File name buffers are freed.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::FreeResources()
    {
    iState = EStateIdle;
    iFileSystem.Close();
    delete iParser;
    iParser = NULL;
    delete iComposer;
    iComposer = NULL;
    delete iFile;
    iFile = NULL;
    delete iDTD;
    iDTD = NULL;
    delete iXMLDoc;
    iXMLDoc = NULL;
    iCurrentXMLNode = NULL;
    iCurrentTreeNode = NULL;
    if ( iTree )
        {
        iTree->SetLocked( EFalse );
        iTree = NULL;    
        }
    }


// -----------------------------------------------------------------------------
// CXCFWEngine::PrepareEntityConverterL
// Localizer is created and DTD load is requested. Localizer will complete
// pending request when done => Engine's RunL will be called.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::PrepareEntityConverterAndSetActiveL()
    {

    TRequestStatus *s = &iStatus;

    delete iLocalizer;
    iLocalizer = NULL;
    iLocalizer = CXCFWLocalizer::NewL();


    //If we have a DTD
    if ( iDTD->Des().Compare( KNullDesC ) != 0 )
        {
        // delete possible previous localizer instance and create new.
        // For performance reasons, it could be wise to first
        // check if we're loading the same DTD as last time. This
        // could be done at localizer side.

        // Ask Localizer to load Entity references. Localizer will
        // complete the request when ready.
        SetActive();
        TRAPD( err, iLocalizer->LoadDTDL( iDTD->Des(), iFileSystem, &iStatus) );
        if ( KErrNone != err )
            {
            User::RequestComplete(s, KErrNone );
            iObserver->HandleEngineErrorL( KErrDTDLoadFailed );
            //Complete here, since localizer will not do it
            delete iLocalizer;
            iLocalizer = NULL;
            }
        }
    else
        {
        SetActive();
        User::RequestComplete( s, KErrNone );             
        }
    }

// -----------------------------------------------------------------------------
// CXCFWEngine::PrepareDTDPathL()
// Function checks the XML DOM for doc type declaration and extracts the 
// possible dtd file name from it. DTD path is then created out of 
// XML file location + localization folder template + dtd name.
// CXCFWLocalizer will then complete the string with current language setting
// and search for the file using language downgrade path if necessary.
// -----------------------------------------------------------------------------
//
void CXCFWEngine::PrepareDTDPathL()
    {
    //set up DTD if not already done
    if ( iDTD && iXMLDoc && iDTD->Des().Compare ( KNullDesC ) == 0 )
        {
        
        //check if we have a dtd defined...
        const TChar KQuote = '"';
        const TChar KBckSlash = '\\';
        TInt extStart = iXMLDoc->DocTypeTag().Find( KDTDExt );
        if ( extStart != KErrNotFound )
            {
            if ( iXMLDoc->DocTypeTag().Find ( KMmsDTD ) != KErrNotFound )
                {
                iXMLDoc->SetDocTypeTagL( KDocTypeDeclNoDTD );                
                }
            else
                {
                TInt delim = iXMLDoc->DocTypeTag().Left( extStart ).
                    LocateReverse( KQuote ) + 1;
                TInt bsdelim = iXMLDoc->DocTypeTag().Left( extStart).
                    LocateReverse ( KBckSlash ) + 1;
                delim = (bsdelim>delim)?bsdelim:delim;
                
                if ( delim != KErrNotFound )
                    {
                    TInt dtdnamelen = extStart - delim + KDTDExtLen;
                    TInt pathlen = iFile->Des().LocateReverse ( KBckSlash );
                    delete iDTD;
                    iDTD = NULL;
                    iDTD = HBufC::NewL( pathlen + dtdnamelen + KLocFormatLen );
                    iDTD->Des().Copy( iFile->Des().Left( pathlen ) );
                    iDTD->Des().Append( KBckSlash );
                    iDTD->Des().Append( KLocFormat );
                    iDTD->Des().Append ( iXMLDoc->DocTypeTag().
                        Mid( delim, dtdnamelen ) );                
                    }
                }                
            }
        }
    
    if ( iDTD )
        {
        //Store DTD name to tree, so it is available at save.
        iTree->SetDTDNameL( iDTD->Des() );
        }
    }

//  End of File