diff -r 000000000000 -r 667063e416a2 landmarks/locationlandmarks/server/src/EPos_CPosLmNameIndex.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/landmarks/locationlandmarks/server/src/EPos_CPosLmNameIndex.cpp Tue Feb 02 01:06:48 2010 +0200 @@ -0,0 +1,906 @@ +/* +* Copyright (c) 2007 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 CPosLmNameIndex class +* +* +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EPos_PosLmDatabaseManager.h" +#include "epos_lmdebug.h" +#include "EPos_CPosLmNameIndex.h" + +// ============================ LOCAL FUNCTIONS =============================== + +// ============================ MEMBER FUNCTIONS =============================== + +//-------------------------------------------------------------------- +// CLmNameIndex::CIndexItem +//-------------------------------------------------------------------- +// +CPosLmNameIndex::CIndexItem::CIndexItem() +: iId ( KPosLmNullItemId ) + { + } + +CPosLmNameIndex::CIndexItem::CIndexItem( TPosLmItemId aId ) +: iId ( aId ) + { + } + +CPosLmNameIndex::CIndexItem::CIndexItem( TPosLmItemId aId, HBufC* aName ) +: iId ( aId ), iName( aName ) + { + } + +CPosLmNameIndex::CIndexItem::~CIndexItem() + { + delete iName; + } + +CPosLmNameIndex::CIndexItem* CPosLmNameIndex::CIndexItem::NewLC( TPosLmItemId aId, const TDesC& aName ) + { + CIndexItem* self = new (ELeave) CIndexItem( aId ); + CleanupStack::PushL( self ); + self->iName = aName.AllocL(); + return self; + } + +CPosLmNameIndex::CIndexItem* CPosLmNameIndex::CIndexItem::NewLC( RReadStream& aIn ) + { + TPosLmItemId id = aIn.ReadUint32L(); + TInt len = aIn.ReadUint32L(); + if ( id < 1 || len < 0 ) User::Leave( KErrCorrupt ); + + HBufC* name = HBufC::NewLC( len ); + TPtr ptr = name->Des(); + aIn.ReadL( ptr, len ); + + CIndexItem* self = new (ELeave) CIndexItem( id, name ); + CleanupStack::Pop( name ); + + CleanupStack::PushL( self ); + return self; + } + +TPtrC CPosLmNameIndex::CIndexItem::Name() const + { + if ( iName ) + return *iName; + else + return KNullDesC(); + } + +void CPosLmNameIndex::CIndexItem::ExternalizeL( RWriteStream& aOut ) const + { + aOut.WriteUint32L( iId ); + aOut.WriteUint32L( Name().Length() ); + aOut.WriteL( Name() ); + // state is not stored, it is used only for index verification + } + +TInt CPosLmNameIndex::CIndexItem::Size() const + { + return + sizeof( TUint32 ) // id size + + sizeof( TUint32 ) // string length size + + Name().Size(); // string size + } + +TInt CPosLmNameIndex::CIndexItem::CompareById( const CIndexItem& aLeft, const CIndexItem& aRight ) + { + return aLeft.Id() - aRight.Id(); + } + +//-------------------------------------------------------------------- +// CPosLmNameIndex::TLmIndexNameKey +//-------------------------------------------------------------------- +// +CPosLmNameIndex::TLmIndexNameKey::TLmIndexNameKey() : + TKeyArrayFix( 0, ECmpCollated ) + { + // the second parameter - comparison type - is used + // by TKeyArrayFix::Compare() + // the first parameter - offset of the key - is not used: + // overridden At() returns pointer to key not using offsets + } + +TAny* CPosLmNameIndex::TLmIndexNameKey::At( TInt anIndex ) const + { + TAny* any = TKeyArrayFix::At( anIndex ); + CIndexItem** ptr = static_cast( any ); + return (*ptr)->NamePtr(); + } + +//-------------------------------------------------------------------- +// CPosLmNameIndex::TLmIndexIdKey +//-------------------------------------------------------------------- +// +CPosLmNameIndex::TLmIndexIdKey::TLmIndexIdKey() : + TKeyArrayFix( 0, ECmpTUint32 ) + { + } + +TAny* CPosLmNameIndex::TLmIndexIdKey::At( TInt anIndex ) const + { + TAny* any = TKeyArrayFix::At( anIndex ); + CIndexItem** ptr = static_cast( any ); + return (*ptr)->IdPtr(); + } + +//-------------------------------------------------------------------- +// CPosLmNameIndex +//-------------------------------------------------------------------- +// + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CPosLmNameIndex::CPosLmNameIndex( CPosLmLocalDbAccess& aDbAccess ) +: iDbAccess( aDbAccess ), iStatus( KErrNotReady ) + { + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CPosLmNameIndex::~CPosLmNameIndex() + { + Reset(); + delete iArray; + delete iKeyItem; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::Reset() + { + StopEvaluation(); + iArray->ResetAndDestroy(); + iDataSize = 0; + iStatus = KErrNotReady; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CPosLmNameIndex* CPosLmNameIndex::NewL( CPosLmLocalDbAccess& aDbAccess ) + { + CPosLmNameIndex* self = NewLC( aDbAccess ); + CleanupStack::Pop( self ); + return self; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CPosLmNameIndex* CPosLmNameIndex::NewLC( CPosLmLocalDbAccess& aDbAccess ) + { + CPosLmNameIndex* self = new (ELeave) CPosLmNameIndex( aDbAccess ); + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::ConstructL() + { + const TInt KGranularity = 100; + iArray = new (ELeave) CArrayPtrSeg( KGranularity ); + iKeyItem = new (ELeave) CIndexItem; + + iDbAccess.GetDatabase( iDatabase ); + + iTimeStamp.UniversalTime(); + TRAPD( err, LoadL() ); + if ( err ) + { + // need to re-generate index + iStatus = KErrNotReady; + } + else + { + //if no need to re-generate then set all the landmarks to valid state + for(int i =0; i < iArray->Count(); i++) + { + CIndexItem *item = iArray->At(i); + item->SetValid(); + } + iStatus = KErrNone; + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +TReal32 CPosLmNameIndex::Evaluate() + { + TReal32 progress = 1.0; + if ( iStatus ) + { + TRAPD( err, DoEvaluateL( progress ) ); + if ( err ) + { + StopEvaluation(); + iStatus = err; + } + else if ( progress == 1.0 ) // evaluation completed + { + StopEvaluation(); + iStatus = KErrNone; + } + } + return progress; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::StopEvaluation() + { + if ( iIsTableOpen ) + { + iTable.Close(); + iIsTableOpen = EFalse; + } + iIdSortedArray.Reset(); + RemoveInvalidItems(); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::DoEvaluateL( TReal32& aProgress ) + { + const TInt KRowsPerStep( 100 ); + + if ( !iIsTableOpen ) + { + TInt err = iTable.Open( iDatabase, KPosLmLandmarkTable, RDbRowSet::EReadOnly ); + User::LeaveIfError( err ); + iIsTableOpen = ETrue; + + if ( iTable.FirstL() ) + { + // Reset(); + // Don't reset what's loaded - try to check the index with + // contents of the database and update accordingly. + // Reason: if changes are small (most probable case) this job + // is faster than rebuilding new index from scratch + CreateByIdSortingL(); + iTablePosition = 0; + iStatus = KErrNotReady; + } + else + { + // no records in the table + iTable.Close(); + iIsTableOpen = EFalse; + aProgress = 1.0; + return; + } + } + + TInt numRows = KRowsPerStep; + while( !iTable.AtEnd() && numRows-- > 0 ) + { + iTable.GetL(); + + TPosLmItemId id = iTable.ColUint32( EPosLmLcLandmarkIdCol ); + + HBufC* name = PosLmServerUtility::ReadFromLongTextColumnLC( + iTable, EPosLmLcNameCol ); + + // If the item already exists in the index, mark it as verified + // otherwise - add to index + CIndexItem* item = ItemById( id ); + // when comparing contents of the index with database + // it is ok to use binary comparison + if ( !item || item->Name().Compare( *name ) != 0 ) + { + // the ID has been removed or changed in database + if ( item ) + { + // the original item has been changed + item->SetInvalid(); + } + // add new item for this ID + // here comparison must be done using collation + CIndexItem* newItem = DoInsertL( id, name ); // takes onwership of name + newItem->SetValid(); + + TLinearOrder order( CIndexItem::CompareById ); + iIdSortedArray.InsertInOrder( newItem, order ); + CleanupStack::Pop( name ); + } + else + { + // this item already exists in the index + item->SetValid(); + CleanupStack::PopAndDestroy( name ); + } + + iTable.NextL(); + iTablePosition++; + } + + // return progress; + TInt count = iTable.CountL(); + if ( count > 0 ) + { + aProgress = TReal32( iTablePosition ) / TReal32( count ); + } + else + { + aProgress = 1.0; + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CArrayFixSeg* CPosLmNameIndex::GetSortedIdsLC() const + { + return GetSortedIdsLC( 0, Count() ); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CArrayFixSeg* CPosLmNameIndex::GetSortedIdsLC( TInt aFirst, TInt aCount ) const + { + if ( iStatus == KErrNone ) + { + const TInt KGranularity( 512 ); + CArrayFixSeg* array = new (ELeave) CArrayFixSeg( KGranularity ); + CleanupStack::PushL( array ); + TInt first = Max( 0, aFirst ); + TInt last = Min( first + aCount, iArray->Count() ); + for ( TInt i = first; i < last; i++ ) + { + CIndexItem* item = iArray->At(i); + array->AppendL( item->Id() ); + } + return array; + } + else + { + User::Leave( iStatus ); + return NULL; + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +TInt CPosLmNameIndex::GetSortedIds( + TInt aFirst, TInt aCount, + TPosLmItemId* aTarget, TInt& aReminder ) const + { + if ( aFirst < 0 || aCount < 0 || aTarget == NULL ) + return KErrArgument; + + if ( iStatus == KErrNone ) + { + TInt first = Min( Max( aFirst, 0 ), iArray->Count() ); + TInt last = Max( 0, Min( aFirst + aCount, iArray->Count() ) ); + for ( TInt i = first; i < last; i++ ) + { + CIndexItem* item = iArray->At( i ); + *(aTarget + i-first ) = item->Id(); + } + aReminder = Count() - last; + return last - first; + } + else + { + return iStatus; + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::InsertL( const CPosLandmark& aLandmark ) + { + TPtrC name; + aLandmark.GetLandmarkName( name ); + InsertL( aLandmark.LandmarkId(), name ); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::InsertL( TPosLmItemId aLmid, const TDesC& aName ) + { + HBufC* name = aName.AllocLC(); + InsertL( aLmid, name ); + CleanupStack::Pop( name ); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::InsertL( TPosLmItemId aLmid, HBufC* aName ) + { + DoInsertL( aLmid, aName ); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CPosLmNameIndex::CIndexItem* CPosLmNameIndex::DoInsertL( TPosLmItemId aLmid, HBufC* aName ) + { + if ( Find( aLmid ) >= 0 ) // finds valid item + { + User::Leave( KErrAlreadyExists ); // duplicate ID found + } + + CIndexItem* landmark = new (ELeave) CIndexItem( aLmid, aName ); // takes ownership of aName + CleanupStack::PushL( landmark ); + + TLmIndexNameKey key; + iArray->InsertIsqAllowDuplicatesL( landmark, key ); + + CleanupStack::Pop( landmark ); + iDataSize += landmark->Size(); + + if ( !iInTransaction ) + { + landmark->SetValid(); + } + // otherwise item remains in Temp state + + return landmark; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::AppendL( TPosLmItemId aLmid, HBufC* aName ) + { + CIndexItem* landmark = new (ELeave) CIndexItem( aLmid, aName ); + CleanupStack::PushL( landmark ); + AppendL( landmark ); + CleanupStack::Pop( landmark ); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::AppendL( CIndexItem* aLandmark ) + { + ASSERT( aLandmark ); + iArray->AppendL( aLandmark ); + iDataSize += aLandmark->Size(); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::Remove( TPosLmItemId aLmid ) + { + TInt index = Find( aLmid ); + if ( index >= 0 ) + { + DoRemove( index ); + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::DoRemove( TInt aIndex ) + { + ASSERT( aIndex >= 0 && aIndex < iArray->Count() ); + + CIndexItem* entry = iArray->At( aIndex ); + if ( !iInTransaction ) + { + iDataSize -= entry->Size(); + ASSERT( iDataSize >= 0 ); + delete entry; + iArray->Delete( aIndex ); + } + else + { + entry->SetInvalid(); + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::UpdateL( TPosLmItemId aId, const TDesC& aName ) + { + HBufC* name = aName.AllocLC(); + UpdateL( aId, name ); + CleanupStack::Pop( name ); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::UpdateL( TPosLmItemId aLmid, HBufC* aName ) + { + TInt index = Find( aLmid ); + if ( index >= 0 ) + { + CIndexItem* entry = iArray->At( index ); + // if name is exactly same, no need to change anything + if ( entry->Name().Compare( *aName ) != 0 ) + { + DoRemove( index ); + InsertL( aLmid, aName ); + } + else + { + delete aName; + } + } + else + { + delete aName; + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +TInt CPosLmNameIndex::Find( TPosLmItemId aLmid ) const + { + for ( TInt i = 0; i < iArray->Count(); i++ ) + { + CIndexItem* entry = iArray->At(i); + if ( entry->Id() == aLmid && entry->IsValid() ) + { + return i; + } + } + return KErrNotFound; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +TInt CPosLmNameIndex::Count() const + { + return iArray->Count(); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +TInt CPosLmNameIndex::DataSize() const + { + return + sizeof( TUint32 ) // total amount of items + + iDataSize; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +const CPosLmNameIndex::CItem& CPosLmNameIndex::Item( TInt aIndex ) const + { + return *( iArray->At( aIndex ) ); + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::ExternalizeL( RWriteStream& aOut ) const + { + aOut.WriteInt32L( iArray->Count() ); + for ( TInt i = 0; i < iArray->Count(); i++ ) + { + CIndexItem* item = iArray->At( i ); + item->ExternalizeL( aOut ); + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::InternalizeL( RReadStream& aIn ) + { + Reset(); + TInt32 count = aIn.ReadInt32L(); + if ( count < 0 ) User::Leave( KErrCorrupt ); + for ( TInt i = 0; i < count; i++ ) + { + CIndexItem* item = CIndexItem::NewLC( aIn ); + AppendL( item ); // array takes ownership + CleanupStack::Pop( item ); + } + iStatus = KErrNone; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::LoadL() + { + // Skip index loading for empty databases + TInt numLandmarks = PosLmServerUtility::TotalLandmarkCountL( iDbAccess ); + if ( numLandmarks == 0 ) + { + iTimeStamp.UniversalTime(); + iStatus = KErrNone; + return; + } + + RDbTable table; + TInt err = table.Open( iDatabase, KPosLmIndexTable, RDbRowSet::EReadOnly ); + if ( err ) + { + LOG("NameIndex::LoadL: index table not found"); + User::Leave( err ); + } + CleanupClosePushL( table ); + + table.FirstL(); + if ( table.AtEnd() ) + { + LOG("NameIndex::LoadL: index not found"); + User::Leave( KErrNotFound ); + } + + table.GetL(); + + // verify that index is valid for current language + if ( !table.IsColNull( EPosLmIncLanguageCol ) ) + { + TLanguage lang = (TLanguage) table.ColInt32( EPosLmIncLanguageCol ); + if ( User::Language() != lang ) + { + LOG2("NameIndex::LoadL: index not valid, lang %d, current lang %d", + lang, User::Language() ); + User::Leave( KErrCorrupt ); + } + } + else + { + LOG("NameIndex::LoadL: index lang not found"); + User::Leave( KErrCorrupt ); + } + + // read the index + if ( !table.IsColNull( EPosLmIncIndexDataCol ) ) + { + RDbColReadStream readStream; + readStream.OpenL( table, EPosLmIncIndexDataCol ); + CleanupClosePushL( readStream ); + InternalizeL( readStream ); + CleanupStack::PopAndDestroy( &readStream ); + + // basic check for the index + if ( Count() != numLandmarks ) + { + LOG2("NameIndex::LoadL: index not valid, count %d, landmarks in db %d", + Count(), numLandmarks ); + User::Leave( KErrCorrupt ); + } + } + else + { + LOG("NameIndex::LoadL: index data not found"); + User::Leave( KErrCorrupt ); + } + + // read the time stamp + iTimeStamp.UniversalTime(); + if ( !table.IsColNull( EPosLmIncTimestampCol ) ) + { + iTimeStamp = table.ColTime( EPosLmIncTimestampCol ); + } + else + { + LOG("NameIndex::LoadL: index timestamp not found"); + User::Leave( KErrCorrupt ); + } + + CleanupStack::PopAndDestroy ( &table ); + + // index is valid + iStatus = KErrNone; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::SaveL( TChar aDrive ) + { + RDbTable table; + TInt err = table.Open( iDatabase, KPosLmIndexTable, RDbRowSet::EUpdatable ); + if ( err == KErrNotFound ) + { + PosLmDatabaseManager::CreateIndexTableL( iDatabase ); + err = table.Open( iDatabase, KPosLmIndexTable, RDbRowSet::EUpdatable ); + } + User::LeaveIfError( err ); + CleanupClosePushL( table ); + + TInt currentSize = 0; + table.FirstL(); + if ( table.AtEnd() ) + { + table.InsertL(); + } + else + { + table.GetL(); + currentSize = table.ColSize( EPosLmIncIndexDataCol ); + table.UpdateL(); + } + + if ( currentSize < DataSize() ) + { + // check disk size + CPosLmDiskUtilities* diskUtilities = CPosLmDiskUtilities::NewL(); + CleanupStack::PushL( diskUtilities ); + + TInt bytesToWrite = DataSize() - currentSize; + diskUtilities->DiskSpaceBelowCriticalLevelL( bytesToWrite, aDrive ); + + CleanupStack::PopAndDestroy( diskUtilities ); + } + + // current language + table.SetColL( EPosLmIncLanguageCol, User::Language() ); + + // index data + RDbColWriteStream writeStream; + writeStream.OpenL( table, EPosLmIncIndexDataCol ); + CleanupClosePushL( writeStream ); + ExternalizeL( writeStream ); + CleanupStack::PopAndDestroy( &writeStream ); + + // index timestamp + TTime now; + now.UniversalTime(); + table.SetColL( EPosLmIncTimestampCol, now ); + +#ifdef _DEBUG + TBuf<64> mtime; + now.FormatL( mtime, KPosLmTimeFormat ); + LOG1( "NameIndex: Saving index timestamp %S", &mtime); +#endif + + table.PutL(); + CleanupStack::PopAndDestroy ( &table ); + + iTimeStamp = now; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +CPosLmNameIndex::CIndexItem* CPosLmNameIndex::ItemById( TPosLmItemId aId ) const + { + iKeyItem->SetId( aId ); + + TLinearOrder order( CIndexItem::CompareById ); + TInt index = iIdSortedArray.FindInOrder( iKeyItem, order ); + if ( index >=0 ) + return iIdSortedArray[index]; + else + return NULL; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::CreateByIdSortingL() + { + iIdSortedArray.Reset(); + iIdSortedArray.ReserveL( iArray->Count() ); + + for ( TInt i = 0; i < iArray->Count(); i++ ) + { + CIndexItem* item = iArray->At( i ); + TLinearOrder order( CIndexItem::CompareById ); + iIdSortedArray.InsertInOrder( item, order ); + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::RemoveInvalidItems() + { + for ( TInt i = iArray->Count() - 1; i >= 0 ; i-- ) + { + CIndexItem* item = iArray->At( i ); + if ( !item->IsValid() ) + { + delete item; + iArray->Delete( i ); + } + } + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::StartTransaction() + { + ASSERT( !iInTransaction ); + ASSERT( iStatus == KErrNone ); + iInTransaction = ETrue; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::CommitTransaction() + { + ASSERT( iInTransaction ); + ASSERT( iStatus == KErrNone ); + // delete items marked as deleted + // and validate items marked as temporary + for ( TInt i = iArray->Count() - 1; i >= 0 ; i-- ) + { + CIndexItem* item = iArray->At( i ); + if ( item->IsTemp() ) + { + item->SetValid(); + } + else if ( !item->IsValid() ) + { + delete item; + iArray->Delete( i ); + } + } + iInTransaction = EFalse; + } + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- +// +void CPosLmNameIndex::RollbackTransaction() + { + ASSERT( iInTransaction ); + ASSERT( iStatus == KErrNone ); + // delete temp items + // and validate items marked as invalid + for ( TInt i = iArray->Count() - 1; i >= 0 ; i-- ) + { + CIndexItem* item = iArray->At( i ); + if ( item->IsTemp() ) + { + delete item; + iArray->Delete( i ); + } + else if ( !item->IsValid() ) + { + item->SetValid(); + } + } + iInTransaction = EFalse; + }