/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <QListIterator>
#include <qcontactdetailfilter.h>
#include <qcontactphonenumber.h>
#include <qcontactname.h>
#include <qcontactmanager.h>
#include <qcontactavatar.h>
#include <QVector>
#include "logscntfinder.h"
#include "logspredictivetranslator.h"
#include "logslogger.h"
const int MaxPredSearchPatternLen = 15;
const QChar ZeroSepar('0');
// -----------------------------------------------------------------------------
// LogsCntEntry::richText()
// -----------------------------------------------------------------------------
//
QString LogsCntText::richText( QString startTag,
QString endTag ) const
{
QString str = text();
if ( str.length() > 0 && highlights() > 0 ) {
str.insert( highlights() , endTag );
str.insert( 0, startTag );
}
return str;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::LogsCntEntry()
// -----------------------------------------------------------------------------
//
LogsCntEntry::LogsCntEntry( LogsCntEntryHandle& handle,
quint32 cid )
: mType( EntryTypeHistory ), mCid( cid ),
mCached( true ),mHandle(&handle)
{
LogsCntText empty;
mFirstName.append( empty );
mLastName.append( empty );
mAvatarPath = "";
}
// -----------------------------------------------------------------------------
// LogsCntEntry::LogsCntEntry()
// -----------------------------------------------------------------------------
//
LogsCntEntry::LogsCntEntry( quint32 cid )
: mType( EntryTypeContact ), mCid( cid ),
mCached( false ),mHandle(0)
{
LogsCntText empty;
mFirstName.append( empty );
mLastName.append( empty );
mAvatarPath = "";
}
// -----------------------------------------------------------------------------
// copy LogsCntEntry::LogsCntEntry()
// -----------------------------------------------------------------------------
//
LogsCntEntry::LogsCntEntry( const LogsCntEntry& entry )
: mType(entry.mType),
mCid(entry.mCid),
mFirstName(entry.mFirstName),
mLastName(entry.mLastName),
mCached(entry.mCached),
mHandle(entry.mHandle),
mPhoneNumber(entry.mPhoneNumber),
mAvatarPath(entry.mAvatarPath)
{
}
// -----------------------------------------------------------------------------
// LogsCntEntry::~LogsCntEntry()
// -----------------------------------------------------------------------------
//
LogsCntEntry::~LogsCntEntry()
{
}
// -----------------------------------------------------------------------------
// LogsCntEntry::firstName()
// -----------------------------------------------------------------------------
//
const LogsCntTextList& LogsCntEntry::firstName() const
{
return mFirstName;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::lastName()
// -----------------------------------------------------------------------------
//
const LogsCntTextList& LogsCntEntry::lastName() const
{
return mLastName;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::avatarPath()
// -----------------------------------------------------------------------------
//
const QString& LogsCntEntry::avatarPath() const
{
return mAvatarPath;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::phoneNumber()
// -----------------------------------------------------------------------------
//
const LogsCntText& LogsCntEntry::phoneNumber() const
{
return mPhoneNumber;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::speedDial()
// -----------------------------------------------------------------------------
//
const QString& LogsCntEntry::speedDial() const
{
return mSpeedDial;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::contactId()
// -----------------------------------------------------------------------------
//
quint32 LogsCntEntry::contactId() const
{
return mCid;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::handle()
// -----------------------------------------------------------------------------
//
LogsCntEntryHandle* LogsCntEntry::handle() const
{
return mHandle;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::setFirstName()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::setFirstName( const QString& name )
{
mCached=true;
mFirstName.clear();
doSetText( name, mFirstName );
}
// -----------------------------------------------------------------------------
// LogsCntEntry::setLastName()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::setLastName( const QString& name )
{
mCached=true;
mLastName.clear();
doSetText( name, mLastName );
}
// -----------------------------------------------------------------------------
// LogsCntEntry::setAvatarPath()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::setAvatarPath( const QString& avatarpath )
{
mCached=true;
mAvatarPath.clear();
mAvatarPath = avatarpath;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::tokens()
// -----------------------------------------------------------------------------
//
QStringList LogsCntEntry::tokens( const QString& source,
const QChar& separ ) const
{
QStringList target = source.split( separ, QString::SkipEmptyParts );
if ( target.length() > 1 && separ == ZeroSepar ) {
QString& first = target[0];
QString& last = target[target.length()-1];
padWithZeros( first, source, 0 );
padWithZeros( last, source, last.length() );
}
return target;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::padWithLeadingZeros()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::padWithZeros( QString& token,
const QString& source, int padIndex ) const
{
const QChar* content = source.data();
int index = !padIndex ? 0 : source.length()-1;
while( index >= 0 && index < source.length() ) {
if ( content[ index ] == ZeroSepar ) {
token.insert( padIndex, ZeroSepar );
index = !padIndex ? index+1 : index-1;
} else {
index = -1;
}
}
}
// -----------------------------------------------------------------------------
// LogsCntEntry::doSetText()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::doSetText( const QString& text, LogsCntTextList& textlist )
{
QListIterator<QString> iter( tokens( text ) );
while( iter.hasNext() ) {
LogsCntText txt;
txt.mText = iter.next();
txt.mTranslatedText =
LogsPredictiveTranslator::instance()->translate( txt.mText );
textlist.append( txt );
}
if ( textlist.count() == 0 ) {
textlist.append( LogsCntText() );
}
}
// -----------------------------------------------------------------------------
// LogsCntEntry::setPhoneNumber()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::setPhoneNumber( const QString& number )
{
mCached=true;
mPhoneNumber.mText = number;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::resetHighlights()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::resetHighlights( LogsCntTextList& nameArray )
{
QMutableListIterator<LogsCntText> names( nameArray );
while( names.hasNext() ) {
names.next().mHighlights = 0;
}
}
// -----------------------------------------------------------------------------
// LogsCntEntry::setHighlights()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::setHighlights( const QString& pattern )
{
resetHighlights( mFirstName );
resetHighlights( mLastName );
mPhoneNumber.mHighlights =
mPhoneNumber.text().startsWith( pattern ) &&
mPhoneNumber.text().length() >= pattern.length() ?
pattern.length(): 0;
doSetHighlights( pattern, mFirstName );
doSetHighlights( pattern, mLastName );
}
// -----------------------------------------------------------------------------
// LogsCntEntry::doSetHighlights()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::doSetHighlights( const QString& pattern,
LogsCntTextList& nameArray )
{
LogsPredictiveTranslator* translator =
LogsPredictiveTranslator::instance();
QMutableListIterator<LogsCntText> names( nameArray );
bool hasZeros = pattern.contains( ZeroSepar );
//simple
while( names.hasNext() ) {
LogsCntText& nameItem = names.next();
nameItem.mHighlights =
translator->startsWith( nameItem.mText, pattern, false );
}
//complex
QStringList tokenArray = tokens( pattern, ZeroSepar );
QListIterator<QString> patternArray( tokenArray );
while( hasZeros && patternArray.hasNext() ) {
QString patternItem = patternArray.next();
names.toFront();
while( names.hasNext() ) {
LogsCntText& nameItem = names.next();
int matchSize = translator->startsWith( nameItem.mText,
patternItem, !hasZeros );
nameItem.mHighlights = matchSize > nameItem.mHighlights ?
matchSize : nameItem.mHighlights;
}
}
}
// -----------------------------------------------------------------------------
// LogsCntEntry::setSpeedDial()
// -----------------------------------------------------------------------------
//
void LogsCntEntry::setSpeedDial( const QString& number )
{
mSpeedDial = number;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::match()
// -----------------------------------------------------------------------------
//
bool LogsCntEntry::match( const QString& pattern ) const
{
bool match = false;
//direct match with phone number is enough
match = mPhoneNumber.text().startsWith( pattern ) ||
doSimpleMatch( pattern );
if ( !match && pattern.contains( ZeroSepar ) ) {
QStringList patternArray = tokens( pattern, ZeroSepar );
match = doComplexMatch( patternArray );
}
return match;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::doSimpleMatch()
// -----------------------------------------------------------------------------
//
bool LogsCntEntry::doSimpleMatch( const QString& pattern ) const
{
LogsCntTextList nameArray = mFirstName + mLastName; //with empties
QListIterator<LogsCntText> names( nameArray );
int matchCount = 0;
while( names.hasNext() && !matchCount ) {
matchCount = (int)names.next().mTranslatedText.startsWith( pattern );
}
return matchCount > 0;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::doComplexMatch()
// -----------------------------------------------------------------------------
//
bool LogsCntEntry::doComplexMatch( QStringList patternArray ) const
{
const bool zero = false;
LogsCntTextList nameArray = mFirstName + mLastName; //with empties
int targetMatchCount = patternArray.count();
int namesCount = nameArray.count();
//if pattern has more tokens than name(s), it is a missmatch
if ( namesCount < targetMatchCount ) {
return false;
}
QListIterator<LogsCntText> names( nameArray );
QListIterator<QString> patterns( patternArray );
QVector<bool> matchVector(targetMatchCount, zero );
int currentPattern = 0;
int matchCount = 0;
bool match = false;
while( names.hasNext() && matchCount < targetMatchCount ) {
LogsCntText name = names.next();
currentPattern = 0;
patterns.toFront();
match = false;
while ( !name.mText.isEmpty() &&
patterns.hasNext() && !match ) {
QString pattern = patterns.next();
//unique match check
if ( !matchVector.at( currentPattern ) ) {
match = matchVector[ currentPattern ]
= name.mTranslatedText.startsWith( pattern );
matchCount = match ? matchCount+1 : matchCount;
}
currentPattern++;
}
}
return matchCount >= targetMatchCount;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::isCached()
// -----------------------------------------------------------------------------
//
bool LogsCntEntry::isCached() const
{
return mCached;
}
// -----------------------------------------------------------------------------
// LogsCntEntry::type()
// -----------------------------------------------------------------------------
//
LogsCntEntry::EntryType LogsCntEntry::type() const
{
return mType;
}
// -----------------------------------------------------------------------------
// LogsCntFinder::LogsCntFinder()
// -----------------------------------------------------------------------------
//
LogsCntFinder::LogsCntFinder()
: mCachedCounter(0)
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::LogsCntFinder()" )
// Create manager ourselves, object takes care of deletion when registering
// as parent.
QMap<QString, QString> dummyParams;
mContactManager = new QContactManager("symbian", dummyParams, this);
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::LogsCntFinder()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::LogsCntFinder()
// -----------------------------------------------------------------------------
//
LogsCntFinder::LogsCntFinder(QContactManager& contactManager)
: mCachedCounter(0)
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::LogsCntFinder(), cntmgr from client" )
mContactManager = &contactManager;
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::LogsCntFinder()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::~LogsCntFinder()
// -----------------------------------------------------------------------------
//
LogsCntFinder::~LogsCntFinder()
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::~LogsCntFinder()" )
qDeleteAll( mResults );
qDeleteAll( mHistoryEvents );
LogsPredictiveTranslator::deleteInstance();
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::~LogsCntFinder()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::isProgressivePattern
// -----------------------------------------------------------------------------
//
bool LogsCntFinder::isProgressivePattern( const QString& pattern ) const
{
//"" -> XXX not progressive
//XX -> YY not progressive
//XXX -> YY not progressive
int prevPatternLen = mCurrentPredictivePattern.length();
return prevPatternLen > 0 &&
pattern.length() - prevPatternLen > 0;
}
// -----------------------------------------------------------------------------
// LogsCntFinder::predictiveSearchQuery
// -----------------------------------------------------------------------------
//
void LogsCntFinder::predictiveSearchQuery( const QString& pattern )
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::predictiveSearchQuery()" )
LOGS_QDEBUG_2( "logs [FINDER] pattern= ", pattern )
if ( pattern.length() > MaxPredSearchPatternLen ) {
LOGS_QDEBUG( "logs [FINDER] too long pattern. Exit quietly.")
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::predictiveSearchQuery()" )
return;
}
//in this point mCurrentPredictivePattern is previous
bool patternIsProgressive = isProgressivePattern( pattern );
bool resultsAreAllCached = resultsCount() > 0 &&
mCachedCounter == resultsCount();
bool nothingToDo = resultsCount() == 0 &&
patternIsProgressive;
LOGS_QDEBUG_2( "logs [FINDER] patternIsProgressive = ", patternIsProgressive )
LOGS_QDEBUG_2( "logs [FINDER] resultsAreAllCached = ", resultsAreAllCached )
LOGS_QDEBUG_2( "logs [FINDER] nothingToDo = ", nothingToDo )
LOGS_QDEBUG_2( "logs [FINDER] cachedCounter = ", mCachedCounter )
mCurrentPredictivePattern = pattern;
if ( mCurrentPredictivePattern.isEmpty() ) {
qDeleteAll( mResults );
mResults.clear();
mCachedCounter = 0;
} else if ( ( patternIsProgressive &&
resultsAreAllCached ) ||
nothingToDo ) {
doPredictiveCacheQuery();
} else {
mCachedCounter = 0;
LogsCntEntryList recentResults = mResults;
mResults.clear();
doPredictiveHistoryQuery();
doPredictiveContactQuery( recentResults );
qDeleteAll( recentResults );
}
emit queryReady();
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::predictiveSearchQuery()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::doPredictiveHistoryQuery
// -----------------------------------------------------------------------------
//
void LogsCntFinder::doPredictiveHistoryQuery()
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::doPredictiveHistoryQuery()" )
QListIterator<LogsCntEntry*> iter(mHistoryEvents);
while( iter.hasNext() ) {
LogsCntEntry* e = iter.next();
if ( e->match( mCurrentPredictivePattern ) ) {
LogsCntEntry* entry = new LogsCntEntry( *e );
addResult( entry );
}
}
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::doPredictiveHistoryQuery()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::doPredictiveContactQuery
// -----------------------------------------------------------------------------
//
void LogsCntFinder::doPredictiveContactQuery( LogsCntEntryList& recentResults )
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::doPredictiveContactQuery()" )
QContactDetailFilter df;
df.setDetailDefinitionName( QContactName::DefinitionName );
df.setMatchFlags( QContactFilter::MatchKeypadCollation );
df.setValue( mCurrentPredictivePattern );
QList<QContactLocalId> cntIds;
LOGS_QDEBUG( "logs [FINDER] about to call contacts manager" )
cntIds = mContactManager->contactIds( df );
LOGS_QDEBUG_2( "logs [FINDER] number of matched contacts =", cntIds.count() )
int index = 0;
while( index < cntIds.count() ) {
addResult( cntIds.at( index++ ), recentResults );
}
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::doPredictiveContactQuery()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::doPredictiveCacheQuery()
// -----------------------------------------------------------------------------
//
void LogsCntFinder::doPredictiveCacheQuery()
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::doPredictiveCacheQuery()" )
QMutableListIterator<LogsCntEntry*> iter(mResults);
while( iter.hasNext() ) {
LogsCntEntry* entry = iter.next();
if ( !entry->match( mCurrentPredictivePattern ) ) {
mCachedCounter =
entry->isCached() ? mCachedCounter-1 : mCachedCounter;
iter.remove();
delete entry;
} else {
entry->setHighlights( mCurrentPredictivePattern );
}
}
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::doPredictiveCacheQuery()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::addResult()
// -----------------------------------------------------------------------------
//
void LogsCntFinder::addResult( quint32 cntId, LogsCntEntryList& recentResults )
{
QMutableListIterator<LogsCntEntry*> iter(recentResults);
bool reused = false;
while( iter.hasNext() && !reused ) {
LogsCntEntry* entry = iter.next();
if ( entry->contactId() == cntId ) {
LOGS_QDEBUG_4( "logs [FINDER] LogsCntFinder::addResult() - \
re-using entry. contact id ", cntId, "cached=", entry->isCached() );
iter.remove();
addResult( entry );
reused = true;
}
}
if ( !reused ) {
LogsCntEntry* entry = new LogsCntEntry( cntId );
addResult( entry );
}
}
// -----------------------------------------------------------------------------
// LogsCntFinder::addResult()
// -----------------------------------------------------------------------------
//
void LogsCntFinder::addResult( LogsCntEntry* entry )
{
updateResult( entry );
mResults.append( entry );
}
// -----------------------------------------------------------------------------
// LogsCntFinder::updateResult()
// -----------------------------------------------------------------------------
//
void LogsCntFinder::updateResult( LogsCntEntry* entry )
{
if ( entry->isCached() ) {
entry->setHighlights( mCurrentPredictivePattern );
mCachedCounter++;
}
}
// -----------------------------------------------------------------------------
// LogsCntFinder::resultsCount
// -----------------------------------------------------------------------------
//
int LogsCntFinder::resultsCount() const
{
return mResults.count();
}
// -----------------------------------------------------------------------------
// LogsCntFinder::resultAt
// -----------------------------------------------------------------------------
//
const LogsCntEntry& LogsCntFinder::resultAt( int index )
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::resultAt()" )
LOGS_QDEBUG_2( "logs [FINDER] index=", index )
LogsCntEntry* entry = mResults.at( index );
if ( !entry->isCached() ) {
LOGS_QDEBUG_2( "logs [FINDER] caching from DB cid=", entry->contactId() )
QContact contact = mContactManager->contact( entry->contactId() );
QContactName contactName = contact.detail( QContactName::DefinitionName );
entry->setFirstName( contactName.value( QContactName::FieldFirst ) );
entry->setLastName( contactName.value( QContactName::FieldLast ) );
QContactPhoneNumber contactPhoneNumber =
contact.detail( QContactPhoneNumber::DefinitionName );
entry->setPhoneNumber(
contactPhoneNumber.value( QContactPhoneNumber::FieldNumber ) );
QContactAvatar contactAvatar = contact.detail<QContactAvatar>();
if (contactAvatar.subType().compare(
QLatin1String(QContactAvatar::SubTypeImage)) == 0 &&
!contactAvatar.avatar().isEmpty()) {
entry->setAvatarPath(contactAvatar.avatar());
}
updateResult( entry );
}
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::resultAt()" )
return *entry;
}
// -----------------------------------------------------------------------------
// LogsCntFinder::insertEntry
// -----------------------------------------------------------------------------
//
void LogsCntFinder::insertEntry( int index, LogsCntEntry* entry )
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::insertEntry()" )
LOGS_QDEBUG_4( "logs [FINDER] handle=", entry->handle()," to index ", index )
mHistoryEvents.insert( index, entry );
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::insertEntry()" )
}
// -----------------------------------------------------------------------------
// LogsCntFinder::getEntry
// -----------------------------------------------------------------------------
//
LogsCntEntry* LogsCntFinder::getEntry( const LogsCntEntryHandle& handle ) const
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::getEntry()" )
return doGetEntry( mHistoryEvents, handle );
}
// -----------------------------------------------------------------------------
// LogsCntFinder::doGetEntry
// -----------------------------------------------------------------------------
//
LogsCntEntry* LogsCntFinder::doGetEntry( const LogsCntEntryList& list,
const LogsCntEntryHandle& handle ) const
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::doGetEntry()" )
LOGS_QDEBUG_2( "logs [FINDER] handle= ", &handle )
LogsCntEntry* entry = 0;
QListIterator<LogsCntEntry*> iter(list);
while( iter.hasNext() && !entry ) {
LogsCntEntry* e = iter.next();
entry = e->handle() == &handle ? e : 0;
}
LOGS_QDEBUG_2( "logs [FINDER] found=", (entry!=0) )
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::doGetEntry()" )
return entry;
}
// -----------------------------------------------------------------------------
// LogsCntFinder::deleteEntry
// -----------------------------------------------------------------------------
//
void LogsCntFinder::deleteEntry( const LogsCntEntryHandle& handle )
{
LOGS_QDEBUG( "logs [FINDER] -> LogsCntFinder::deleteEntry()" )
LOGS_QDEBUG_2( "logs [FINDER] handle= ", &handle )
LogsCntEntry* toRemoveHistoryEv = doGetEntry( mHistoryEvents, handle );
mHistoryEvents.removeOne( toRemoveHistoryEv );
delete toRemoveHistoryEv;
LOGS_QDEBUG( "logs [FINDER] <- LogsCntFinder::deleteEntry()" )
}