/*
* Copyright (c) 2002 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:
* MPbkFindPrimitives implementation for european Phonebook.
*
*/
// INCLUDE FILES
#include "CPbkFindPrimitives.h"
#include <MPbkContactNameFormat.h>
#include <MPbkFieldData.h>
#include <PbkViewContactFieldDataAdapter.h>
#include <PbkEngUtils.h>
//#define PBK_ENABLE_DEBUG_PRINT
#include <PbkDebug.h>
namespace {
const TInt KMinTextBufSize = 32;
// ================= LOCAL FUNCTIONS =======================
/**
* Normalizes text by converting all sequences of space characters to a
* single space.
*
* @param aBuffer buffer where the normalized text will be stored. The buffer
* will be reallocated if necessary.
* @param aText the text to be normalized.
* @return normalized text.
*/
const TDesC& NormalizeL(HBufC*& aBuffer, const TDesC& aText)
{
const TInt textLength = aText.Length();
aBuffer = PbkEngUtils::AllocL(aBuffer, Max(textLength,KMinTextBufSize));
TPtr bufPtr(aBuffer->Des());
bufPtr.Zero();
TBool uncommittedSpace = EFalse;
for (TInt i=0; i < textLength; ++i)
{
TChar ch(aText[i]);
if (ch.IsSpace())
{
uncommittedSpace = ETrue;
}
else
{
if (uncommittedSpace)
{
bufPtr.Append(' ');
uncommittedSpace = EFalse;
}
bufPtr.Append(ch);
}
}
// copy final trailing space
if (uncommittedSpace)
{
bufPtr.Append(' ');
}
return (*aBuffer);
}
}
// ================= MEMBER FUNCTIONS =======================
inline CPbkFindPrimitives::CPbkFindPrimitives(MPbkContactNameFormat& aNameFormatter) :
iNameFormatter(aNameFormatter)
{
}
CPbkFindPrimitives* CPbkFindPrimitives::NewL(MPbkContactNameFormat& aNameFormatter)
{
CPbkFindPrimitives* self = new(ELeave) CPbkFindPrimitives(aNameFormatter);
return self;
}
CPbkFindPrimitives::~CPbkFindPrimitives()
{
delete iFindText;
delete iFieldText;
delete iInitialFindText;
iViewSortOrder.Close();
}
inline TBool CPbkFindPrimitives::IsWordSeparator(TChar aCh)
{
return aCh.IsSpace();
}
void CPbkFindPrimitives::SetContactViewSortOrderL
(const RContactViewSortOrder& aSortOrder)
{
iViewSortOrder.CopyL(aSortOrder);
}
/**
* Stores the initial find text.
*
* @see InitialFindText
* @see MatchesInitialFindText
*/
inline void CPbkFindPrimitives::StoreInitialFindTextL
(const TDesC& aInitialFindText)
{
iInitialFindText = PbkEngUtils::CopyL
(iInitialFindText, aInitialFindText, aInitialFindText.Length()+1);
TPtr ptr(iInitialFindText->Des());
ptr.Append('*');
}
/**
* Returns the initial find text passed to StoreInitialFindTextL.
*
* @see StoreInitialFindTextL
* @see MatchesInitialFindText
*/
inline TPtrC CPbkFindPrimitives::InitialFindText() const
{
TPtrC result;
if (iInitialFindText)
{
// Strip trailing '*' appended for matching in StoreInitialFindTextL
return iInitialFindText->Left(iInitialFindText->Length()-1);
}
return result;
}
/**
* Matches (Using TDesC::MatchC) the initial find text against aText. Returns
* ETrue if aText matches the initial find text.
*
* @see StoreInitialFindTextL
* @see InitialFindText
*/
inline TBool CPbkFindPrimitives::DoMatchesInitialFindText
(const TDesC& aText) const
{
if (iInitialFindText)
{
return (aText.MatchC(*iInitialFindText) != KErrNotFound);
}
return EFalse;
}
/**
* Does a prefix matching of aFindText to aContactData. Returns ETrue if
* the find text matches completely to contact data.
*
* @param aContactData contact data to match against. Matching is done in
* field order.
* @param aFindText normalized (see NormalizeL) find text.
*/
inline TBool CPbkFindPrimitives::IsMatchL
(const MPbkFieldDataArray& aContactData, const TDesC& aFindText)
{
/* Consuming of findtext is done in the sort order */
TPtrC findText(aFindText);
const TInt fieldCount = aContactData.PbkFieldCount();
for (TInt i=0; i < fieldCount; ++i)
{
const TDesC& fieldText = NormalizeL(iFieldText, aContactData.PbkFieldAt(i).PbkFieldText());
const TInt fieldTextLength = fieldText.Length();
// find word starts from fieldText
TInt cursor = 0;
TInt wordStart = 0;
while (cursor < fieldTextLength)
{
TBool lastCharWasSpace = EFalse;
TChar cursorChar(fieldText[cursor]);
if (IsWordSeparator(cursorChar))
{
TPtrC word(&fieldText[wordStart], fieldTextLength-wordStart);
TInt matchLen = Min(word.Length(), findText.Length());
if (word.Left(matchLen).MatchC(findText.Left(matchLen)) != KErrNotFound)
{
// Require space in find text between fields
if (matchLen < findText.Length())
{
if (TChar(findText[matchLen]).IsSpace() &&
i < fieldCount-1)
{
// Skip space
++matchLen;
}
else
{
return EFalse;
}
}
// Consume findText
findText.Set(findText.Mid(matchLen));
}
lastCharWasSpace = ETrue;
}
++cursor;
if (lastCharWasSpace)
{
wordStart = cursor;
}
}
if (cursor == fieldTextLength && wordStart != cursor)
{
TPtrC word(&fieldText[wordStart], fieldTextLength-wordStart);
TInt matchLen = Min(word.Length(), findText.Length());
if (word.Left(matchLen).MatchC(findText.Left(matchLen)) != KErrNotFound)
{
// Require space in find text between fields
if (matchLen < findText.Length())
{
if (TChar(findText[matchLen]).IsSpace() &&
i < fieldCount-1)
{
// Skip space
++matchLen;
}
else
{
return EFalse;
}
}
// Consume findText
findText.Set(findText.Mid(matchLen));
}
}
}
// Find text must be fully consumed for a match
return (findText.Length() == 0);
}
/**
* Converts aViewContact to MPbkFieldDataArray and calls
* IsMatchL(const MPbkFieldDataArray& aContactData, const TDesC& aFindText).
*/
TBool CPbkFindPrimitives::IsMatchL
(const CViewContact& aViewContact, const TDesC& aFindText)
{
// Convert aViewContact to MPbkFieldDataArray
const TPbkViewContactFieldDataArray contactData
(aViewContact, iViewSortOrder);
// Filter contactData to contain only fields used in find
// (same fields than in used in title)
const MPbkFieldDataArray& titleData =
iNameFormatter.FilterContactTitleFields(contactData);
// Do matching
return IsMatchL(titleData, aFindText);
}
TBool CPbkFindPrimitives::IsFindMatchL
(const CViewContact& aViewContact, const TDesC& aFindText)
{
// Normalize aFindText by removing all extra spaces
const TDesC& findText = NormalizeL(iFindText,aFindText);
// Do matching
return IsMatchL(aViewContact, findText);
}
void CPbkFindPrimitives::GetInitialMatchesL
(CContactViewBase& aView,
const TDesC& aFindText,
RPointerArray<CViewContact>& aMatchedContacts)
{
// Make first match with CContactViewBase::ContactsMatchingPrefixL
MDesCArray* findWords = PbkEngUtils::BreakInWordsLC
(aFindText, IsWordSeparator);
aView.ContactsMatchingPrefixL(*findWords, aMatchedContacts);
CleanupStack::PopAndDestroy(); // findWords
PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkFindPrimitives::GetInitialMatchesL(%S): contact model returned %d matches"),
&aFindText, aMatchedContacts.Count());
StoreInitialFindTextL(aFindText);
// Filter away contacts that do not fullfill this implementation's criteria
// like matching company name entries.
const TDesC& normFindText = NormalizeL(iFindText, aFindText);
for (TInt i=aMatchedContacts.Count()-1; i >= 0; --i)
{
CViewContact* contact = aMatchedContacts[i];
if (!IsMatchL(*contact, normFindText))
{
aMatchedContacts.Remove(i);
delete contact;
}
}
}
TBool CPbkFindPrimitives::MatchesInitialFindTextL(const TDesC& aText)
{
// NOTE: This must work the same way as cntmodel's matching! Otherwise
// we get problems in hungarian or czech matching with double-characters.
// The main problem case is in CFindState::SetFindTextL(). If you
// make this work differently from cntmodel's match, make sure
// it still works with hungarian "gy" or czech "ch" and "agy" etc...
return DoMatchesInitialFindText(aText);
}
TBool CPbkFindPrimitives::MatchesInitialFindTextL(const CViewContact& aViewContact)
{
TPtrC findText(InitialFindText());
if (findText.Length() > 0)
{
return IsMatchL(aViewContact,findText);
}
return EFalse;
}
// End of File