First public contribution.
1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
18 #include <logcntdef.h>
19 #include "logservpanic.h"
20 #include "LogServDatabaseTransactionInterface.h"
21 #include <logfilterandeventconstants.hrh>
22 #include "LogServCacheStrings.h"
23 #include "LogServCacheTypes.h"
24 #include "LogServSqlStrings.h"
27 # define __LOGFILTQ_INVARIANT() Invariant()
29 # define __LOGFILTQ_INVARIANT() void(0)
33 KFilterFields array contains all fields that can participate in a filter.
34 The term "field" refers a constant, which value is power of two.
35 Every "field" uniquely identifies one of the event properties.
36 The field values are used in the implementation of the TLogFilterExprBuilder class for initializing the
37 iField data member, used to identify the currently processed event property value.
40 const TUint16 KFilterFields[] =
44 ELogDurationTypeField,
51 #ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
52 ELogSimIdField, //The new ELogSimIdField must be before the flags: see
53 //DoNextProcessFieldByFieldFilterByFilterL() implementation and the assert.
59 The size of the KFilterFields array.
62 const TInt KFilterFieldsSize = sizeof(KFilterFields) / sizeof(KFilterFields[0]);
65 const TInt KLogBlockSize = 128;
66 const TInt KLogPredicateListGranuality = 10;
67 const TInt KLogMaxPredicateLength = 128;
68 const TInt KLogNumberCharsToMatch = 9;
71 Sets the iDatabase data member.
72 The rest of the data member is left uninitialized. They will be initialized later by the BuildExprLC() call.
73 @param aDatabase A reference to MLogServDatabaseTransactionInterface interface.
75 TLogFilterExprBuilder::TLogFilterExprBuilder(MLogServDatabaseTransactionInterface& aDatabase) :
81 Builds the WHERE expresssion of an SQL statement used later to filter events from the Event table.
82 How the builder works? - BuildExprLC() gets as a parameter list of filters: CLogFilterList.
83 Each filter is an object of CLogFilter type. The CLogFilter class contains the same set of properties as the CLogEvent
84 class: phone number, SIM id, call type, etc. Some of the filter property values are set, defining the desired set
85 of events that can pass the filter.
87 For example, the filter list has two filters:
88 - Filter 1: number is "1011", SimId is 200;
89 - Filter 2: duration type is 2;
91 BuildExprLC() takes the filter list and depending on passed TLogFilterConstructionType type:
92 - ELogFilterConstructFilterByFilterFieldByField
93 Organises two loops: the outer loop iterates over all field types, the inner loop iterates over the filters;
94 - ELogFilterConstructFieldByFieldFilterByFilter
95 Organises two loops: the outer loop iterates over the filters, the inner loop iterates over all field types;
97 On each inner loop iteration the current field value of the filter will be checked and if it is not null then a predicate
98 will be created of form "<column name> = <field value>" and added to the list of predicates.
99 If the field value is null but it is allowed to include that field in the expression (the related bit of
100 CLogFilter::iNullFields is set. This is tested by using CLogFilter::NullFields()), then the predicate format will be
101 "<column name> IS NULL".
102 So using the example filters above, the predicates lists will be:
103 - "Number = '1011'", "SimId - 200";
106 At the end of each outer loop iteration the predicates will be "linked" using either "AND" if the field type is "flags"
107 or "OR" for the rest of the fields types.
108 So, after the first outer loop iteration, the constructed part of the WHERE expression will be:
109 "Number = '1011' OR SimId = 200".
110 After the second outer loop iteration the expression will be: "DType = 2".
111 Also, depending on the TLogFilterConstructionType type these expressions will be "linked" either by "AND" or "OR":
112 "(Number = '1011' OR SimId = 200) AND (DType = 2)".
114 On the first outer loop iteration, the predicates expression will be prefixed by the string refered by the aInitial
115 parameter (that could be "WHERE " or " AND " or " OR ").
117 When the processing of the filters finishes, the ready for use expression will be in the aExpr out parameter.
119 @param aExpr Out parameter. If BuildExprLC() completes successfully, aExpr contrains the generated expression.
120 The caller is responsible for the destruction of aExpr. BuildExprLC() will create aExpr and put it on
122 aFilterList Filters list;
123 aInitial String that has to be added in front of the generated expresssion;
124 aType Defines the order of the processing of the filters and the fields;
125 @leave KErrNoMemory Out of memory condition has occured.
127 void TLogFilterExprBuilder::BuildExprLC(RLogDynBuf& aExpr, const CLogFilterList& aFilterList, const TDesC& aInitial,
128 TLogFilterConstructionType aType)
130 aExpr.CreateLC(KLogBlockSize);
131 iPredicateList = new (ELeave) CDesCArrayFlat(KLogPredicateListGranuality);
132 CleanupStack::PushL(iPredicateList);
134 iFilterList = &aFilterList;
135 iInitial = &aInitial;
136 iConstructionType = aType;
138 iField = KFilterFields[0];
141 __LOGFILTQ_INVARIANT();
143 switch(iConstructionType)
145 case ELogFilterConstructFilterByFilterFieldByField:
146 DoNextProcessFilterByFilterFieldByFieldL(aExpr);
148 case ELogFilterConstructFieldByFieldFilterByFilter:
149 DoNextProcessFieldByFieldFilterByFilterL(aExpr);
152 __ASSERT_DEBUG(EFalse, Panic(ELogInvalidConstructionType));
163 CleanupStack::PopAndDestroy(iPredicateList);
167 Processes the filters and the fields.
169 - the outer loop is on the fields;
170 - the inner loop is on the filters;
172 @param aExpr Out parameter. The place where the expression is generated.
173 @leave KErrNoMemory Out of memory condition has occured.
177 void TLogFilterExprBuilder::DoNextProcessFilterByFilterFieldByFieldL(RLogDynBuf& aExpr)
179 __LOGFILTQ_INVARIANT();
180 const TInt KCount = iFilterList->Count();
181 for(TInt i=0;i<KFilterFieldsSize;++i)
183 iField = KFilterFields[i];
184 for(iFilterIndex=0; iFilterIndex<KCount; ++iFilterIndex)
186 iField != ELogFlagsField ? MakePredicatesL() : MakeFlagPredicatesL();
188 MakeConditionL(aExpr);
189 iPredicateList->Reset();
194 Processes the filters and the fields.
196 - the outer loop is on the filters;
197 - the inner loop is on the fields;
199 @param aExpr Out parameter. The place where the expression is generated.
200 @leave KErrNoMemory Out of memory condition has occured.
204 void TLogFilterExprBuilder::DoNextProcessFieldByFieldFilterByFilterL(RLogDynBuf& aExpr)
206 __LOGFILTQ_INVARIANT();
207 const TInt KCount = iFilterList->Count();
208 for(iFilterIndex=0; iFilterIndex<KCount; ++iFilterIndex)
210 //Process all filter fields except flags.
211 for(TInt i=0;i<(KFilterFieldsSize-1);++i)
213 iField = KFilterFields[i];
216 //Process the flag fields last
217 iField = KFilterFields[KFilterFieldsSize - 1];
218 __ASSERT_DEBUG(iField == ELogFlagsField, User::Invariant());
219 MakeFlagPredicatesL();
220 MakeConditionL(aExpr);
221 iPredicateList->Reset();
226 Called on each inner iteration from DoNextProcessFieldByFieldFilterByFilterL() and
227 DoNextProcessFilterByFilterFieldByFieldL().
228 Generates a predicate in one of the following formats:
229 - "<column name> = <field value>"
230 - "<column name> IS NULL"
231 The generated predicate will be added to the predicates list (iPredicateList data member).
233 @leave KErrNoMemory Out of memory condition has occured.
235 @see DoNextProcessFilterByFilterFieldByFieldL()
236 @see DoNextProcessFieldByFieldFilterByFilterL()
238 void TLogFilterExprBuilder::MakePredicatesL()
240 __LOGFILTQ_INVARIANT();
242 const CLogFilter* filter = iFilterList->At(iFilterIndex);
243 __ASSERT_DEBUG(filter, Panic(ELogNullFilterInList1));
245 // Check for null field in filter and if the field has already been used
246 TBuf<KLogMaxPredicateLength> predicate;
247 if (!IsFieldEmpty(*filter) && !IsDuplicateField(*filter))
249 MakePredicateL(predicate, *filter);
250 if (predicate.Length() > 0)
251 iPredicateList->AppendL(predicate);
254 // Add Null predicate if required and not already specified
255 if (filter->NullFields() & iField && !IsDuplicateNullField())
258 MakeNullPredicate(predicate);
259 if (predicate.Length() > 0)
260 iPredicateList->AppendL(predicate);
263 __LOGFILTQ_INVARIANT();
267 Called on each outer loop iteration.
268 At this time, all generated predicates are in the predicates list (iPredicateList data member).
269 The predicates will be "linked" into the expression using "AND" for the "flags" and "OR" for the rest of the fields.
270 Depending on the TLogFilterConstructionType type (iConstructionType data member) either "AND" or "OR" will be used
271 to "link" pedicates from two different predicates lists.
273 @param aExpr Out parameter. The place where the expression is generated.
274 @leave KErrNoMemory Out of memory condition has occured.
276 @see DoNextProcessFilterByFilterFieldByFieldL()
277 @see DoNextProcessFieldByFieldFilterByFilterL()
278 @see MakePredicatesL()
280 void TLogFilterExprBuilder::MakeConditionL(RLogDynBuf& aExpr)
282 __ASSERT_DEBUG(iFilterList != NULL, User::Invariant());
283 __ASSERT_DEBUG(iInitial != NULL, User::Invariant());
284 __ASSERT_DEBUG(iConstructionType == ELogFilterConstructFilterByFilterFieldByField || iConstructionType == ELogFilterConstructFieldByFieldFilterByFilter, User::Invariant());
285 __ASSERT_DEBUG(iPredicateList != NULL, User::Invariant());
287 // Are there any predicates to add?
288 TInt total = iPredicateList->MdcaCount();
292 // Add separator between conditions
293 if(aExpr.Length() == 0)
295 aExpr.AppendL(*iInitial);
299 switch(iConstructionType)
301 case ELogFilterConstructFilterByFilterFieldByField:
302 aExpr.AppendL(KLogAnd);
304 case ELogFilterConstructFieldByFieldFilterByFilter:
305 aExpr.AppendL(KLogOr);
308 __ASSERT_DEBUG(EFalse, Panic(ELogInvalidConstructionType));
313 aExpr.AppendL(KLogOpenBracket);
319 // Add separator between predicates
322 if (iField != ELogFlagsField)
323 aExpr.AppendL(KLogOr);
325 aExpr.AppendL(KLogAnd);
328 aExpr.AppendL((*iPredicateList)[count]);
332 // Close the brackets
333 aExpr.AppendL(KLogCloseBracket);
337 Called on each inner loop iteration.
338 Processes the "flags" filter fields and generates predicates.
340 @leave KErrNoMemory Out of memory condition has occured.
342 @see DoNextProcessFilterByFilterFieldByFieldL()
343 @see DoNextProcessFieldByFieldFilterByFilterL()
345 void TLogFilterExprBuilder::MakeFlagPredicatesL()
347 __LOGFILTQ_INVARIANT();
349 const CLogFilter* filter = iFilterList->At(iFilterIndex);
350 __ASSERT_DEBUG(filter, Panic(ELogNullFilterInList2));
352 // Return if no flags are set
353 if (filter->Flags() == KLogNullFlags)
356 TBuf<KLogMaxPredicateLength> predicate;
358 // Go through each bit in turn
359 iFlagIndex = KLogFlagsCount;
362 // See if the current flag is set in filter and bit wasn't set in any previous filters
363 if (filter->Flags() & 0x1 << iFlagIndex && !IsDuplicateField(*filter))
365 // Generate predicate - if null field flag set we don't want the flag to be set
366 predicate.Format(KLogFlagPredicate, &KLogFlagString, iFlagIndex + 1, (filter->NullFields() & iField) ? 0 : 1);
367 iPredicateList->AppendL(predicate);
372 __LOGFILTQ_INVARIANT();
376 Depending on the currently processed field (iField data member) the function returns the column name that
377 has to be used in the predicate being generated.
379 @return A const reference to the column name.
381 @see MakeNullPredicate()
382 @see MakePredicateL()
384 const TDesC& TLogFilterExprBuilder::ColumnName() const
386 __LOGFILTQ_INVARIANT();
389 case ELogContactField:
390 return KLogFieldEventContactString();
391 case ELogDirectionField:
392 return KLogFieldEventDirectionString();
393 case ELogDurationTypeField:
394 return KLogFieldEventDTypeString();
395 case ELogEventTypeField:
396 return KLogFieldEventTypeString();
397 case ELogNumberField:
398 return KLogFieldEventNumberString();
399 case ELogRemotePartyField:
400 return KLogFieldEventRemoteString();
401 case ELogStatusField:
402 return KLogFieldEventStatusString();
403 case ELogSubjectField:
404 return KLogFieldEventSubjectString();
406 return KLogFieldEventLinkString();
408 return KLogFieldEventDataString();
409 case ELogStartTimeField:
410 case ELogEndTimeField:
411 return KLogFieldEventTimeString();
412 #ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
414 return KLogFieldEventSimId();
417 __ASSERT_DEBUG(EFalse, Panic(ELogNoSuchState3));
420 return KLogUnknownString();
424 Generates a predicate of form "<column name> IS NULL" in the passed descriptor.
426 @param aDes Out parameter. The place where the predicate will be generated.
428 @see MakePredicatesL()
430 void TLogFilterExprBuilder::MakeNullPredicate(TDes& aDes)
432 __LOGFILTQ_INVARIANT();
433 aDes.Format(KLogNullPredicate, &ColumnName());
434 __LOGFILTQ_INVARIANT();
438 Generates a predicate of form "<column name> = <field value>" in the passed descriptor.
440 @param aDes Out parameter. The place where the predicate will be generated.
441 @param aFilter The filter where the field values will be picked from.
442 The current field is identified by the value of the iField data member.
444 @leave The leaving codes from TTime::FormatL().
446 @see MakePredicatesL()
448 void TLogFilterExprBuilder::MakePredicateL(TDes& aDes, const CLogFilter& aFilter)
450 __LOGFILTQ_INVARIANT();
451 const TDesC& columnName = ColumnName();
454 case ELogContactField:
455 aDes.Format(KLogNumberPredicate, &columnName, aFilter.Contact());
458 case ELogDirectionField:
460 TLogStringId id = iDatabase.DTICacheStrings().FindId(aFilter.Direction());
461 aDes.Format(KLogNumberPredicate, &columnName, id);
465 case ELogDurationTypeField:
466 aDes.Format(KLogNumberPredicate, &columnName, aFilter.DurationType());
469 case ELogEventTypeField:
471 const TLogServCacheTypeEntry& entry = iDatabase.DTICacheTypes().FindByUid(aFilter.EventType());
472 aDes.Format(KLogNumberPredicate, &columnName, entry.iEventTypeId);
476 // If the phone number in the filter is at least KLogNumberCharsToMatch long
477 // then it does a wild card search for numbers that match the last KLogNumberCharsToMatch chars
478 case ELogNumberField:
480 if (aFilter.Number().Length() < KLogNumberCharsToMatch)
481 aDes.Format(KLogStringPredicate, &columnName, &aFilter.Number());
484 TPtrC number(aFilter.Number().Right(KLogNumberCharsToMatch));
485 aDes.Format(KLogLikePredicate, &columnName, &number);
490 case ELogRemotePartyField:
491 //We need to check RemoteParty string for any single quotes and
492 //add a second single quote to properly handle strings such as
495 //If single quotes are found we need to modify the string
496 TInt quoteIndex = aFilter.RemoteParty().Locate('\'');
499 //Allocate a buffer twice the length of the string to cater for
500 //the worst case when every character is a single quote
501 TPtrC temp = aFilter.RemoteParty();
502 TBuf<KLogMaxRemotePartyLength * 2> buf;
504 //loop through and replace all single quotes
505 //with two quote characters
506 while(quoteIndex >= 0)
508 _LIT(KQuoteStr, "''");
509 //Append the characters before the single quote followed
510 //by two quote characters
511 buf.Append(temp.Left(quoteIndex));
512 buf.Append(KQuoteStr);
514 //Set the substring to the remaining characters after the
515 //single quote and look for the next single quote character
516 temp.Set(temp.Mid(quoteIndex + 1));
517 quoteIndex = temp.Locate('\'');
520 //No quotes left so append the remaining data
522 aDes.Format(KLogStringPredicate, &columnName, &buf);
526 aDes.Format(KLogStringPredicate, &columnName, &aFilter.RemoteParty());
531 case ELogStatusField:
533 TLogStringId id = iDatabase.DTICacheStrings().FindId(aFilter.Status());
534 aDes.Format(KLogNumberPredicate, &columnName, id);
538 case ELogStartTimeField:
540 TTime time = aFilter.StartTime();
541 TBuf<KLogMaxDateLength> buf;
542 time.FormatL(buf, LogUtils::DateFormatForLocale());
543 aDes.Format(KLogDateAfterPredicate, &columnName, &buf);
547 case ELogEndTimeField:
549 TTime time = aFilter.EndTime();
550 TBuf<KLogMaxDateLength> buf;
551 time.FormatL(buf, LogUtils::DateFormatForLocale());
552 aDes.Format(KLogDateBeforePredicate, &columnName, &buf);
556 #ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
558 aDes.Format(KLogUNumberPredicate, &columnName, aFilter.SimId());
563 __ASSERT_DEBUG(EFalse, Panic(ELogNoSuchState4));
566 __LOGFILTQ_INVARIANT();
570 Determines whether the current field identified by the value of the iField data member, is a duplicate, used/defined
571 by the previously processed filters. For example, if there are two filters and both define the "number" field value, then
572 only the "number" value from the first filter will be used.
574 @param aFilter The current filter being processed;
575 @return True the current field value has been already used in some of the previously processed filters, false otherwise.
577 @see IsDuplicateNullField()
578 @see MakePredicatesL()
580 TBool TLogFilterExprBuilder::IsDuplicateField(const CLogFilter& aFilter) const
582 __LOGFILTQ_INVARIANT();
583 TInt count = iFilterIndex;
586 const CLogFilter* testFilter = iFilterList->At(count);
587 __ASSERT_DEBUG(testFilter, Panic(ELogNullFilterInList3));
588 if (IsDuplicate(aFilter, *testFilter))
595 Determines whether the current "null" field identified by the value of the iField data member, is a duplicate, used/defined
596 by the previously processed filters.
598 @param aFilter The current filter being processed;
599 @return True the current "null" field value has been already used in some of the previously processed filters, false otherwise.
601 @see IsDuplicateField()
602 @see MakePredicatesL()
604 TBool TLogFilterExprBuilder::IsDuplicateNullField() const
606 __LOGFILTQ_INVARIANT();
607 TInt count = iFilterIndex;
610 const CLogFilter* testFilter = iFilterList->At(count);
611 __ASSERT_DEBUG(testFilter, Panic(ELogNullFilterInList4));
612 if (testFilter->NullFields() & iField)
619 Determines whether the current field value, identified by the value of the iField data member, is a null value.
621 @param aFilter The current filter being processed;
622 @return True The current field value is null, false otherwise.
624 @see MakePredicatesL()
626 TBool TLogFilterExprBuilder::IsFieldEmpty(const CLogFilter& aFilter) const
628 __LOGFILTQ_INVARIANT();
631 case ELogContactField:
632 return aFilter.Contact() == KLogNullContactId;
634 case ELogDirectionField:
635 return aFilter.Direction().Length() == 0;
637 case ELogDurationTypeField:
638 return aFilter.DurationType() == KLogNullDurationType;
640 case ELogEventTypeField:
641 return aFilter.EventType() == KNullUid;
643 case ELogNumberField:
644 return aFilter.Number().Length() == 0;
646 case ELogRemotePartyField:
647 return aFilter.RemoteParty().Length() == 0;
649 case ELogStatusField:
650 return aFilter.Status().Length() == 0;
652 case ELogStartTimeField:
653 return (aFilter.StartTime() == TTime(0));
655 case ELogEndTimeField:
656 return (aFilter.EndTime() == TTime(0));
658 // These fields are not part of the filter
659 case ELogSubjectField:
664 #ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
666 return (aFilter.SimId() == KLogNullSimId);
670 __ASSERT_DEBUG(EFalse, Panic(ELogNoSuchState5));
677 The function checks whether the field identified by the value of the iField data member has the same value in the
678 passed two filter objects.
680 @param aFilter1 Filter 1
681 @param aFilter1 Filter 2
682 @return True The values of the current field are identical in both filters, false otherwise.
684 @see IsDuplicateField()
686 TBool TLogFilterExprBuilder::IsDuplicate(const CLogFilter& aFilter1, const CLogFilter& aFilter2) const
688 __LOGFILTQ_INVARIANT();
691 case ELogContactField:
692 return aFilter1.Contact() == aFilter2.Contact();
694 case ELogDirectionField:
695 return aFilter1.Direction().CompareF(aFilter2.Direction()) == 0;
697 case ELogDurationTypeField:
698 return aFilter1.DurationType() == aFilter2.DurationType();
700 case ELogEventTypeField:
701 return aFilter1.EventType() == aFilter2.EventType();
703 // The number fields in filters are considered to be duplicates if the last KLogNumberCharsToMatch chars match
704 case ELogNumberField:
706 TPtrC number1(aFilter1.Number().Right(KLogNumberCharsToMatch));
707 TPtrC number2(aFilter2.Number().Right(KLogNumberCharsToMatch));
708 return number1.CompareF(number2) == 0;
711 case ELogRemotePartyField:
712 return aFilter1.RemoteParty().CompareF(aFilter2.RemoteParty()) == 0;
714 case ELogStatusField:
715 return aFilter1.Status().CompareF(aFilter2.Status()) == 0;
717 case ELogStartTimeField:
718 return aFilter1.StartTime() == aFilter2.StartTime();
720 case ELogEndTimeField:
721 return aFilter1.EndTime() == aFilter2.EndTime();
724 return aFilter2.Flags() & iFlagIndex;
726 #ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
728 return aFilter1.SimId() == aFilter2.SimId();
732 __ASSERT_DEBUG(EFalse, Panic(ELogNoSuchState6));
739 void TLogFilterExprBuilder::Invariant() const
741 __ASSERT_DEBUG(KFilterFields[KFilterFieldsSize - 1] == ELogFlagsField, User::Invariant());
742 __ASSERT_DEBUG(iFilterList != NULL, User::Invariant());
743 __ASSERT_DEBUG(iInitial != NULL, User::Invariant());
744 __ASSERT_DEBUG(iConstructionType == ELogFilterConstructFilterByFilterFieldByField || iConstructionType == ELogFilterConstructFieldByFieldFilterByFilter, User::Invariant());
745 __ASSERT_DEBUG((TUint)iFilterIndex < iFilterList->Count(), User::Invariant());
747 for(idx=0;idx<KFilterFieldsSize && iField!=KFilterFields[idx];++idx)
750 __ASSERT_DEBUG(idx < KFilterFieldsSize, User::Invariant());
751 __ASSERT_DEBUG((iField & (iField - 1)) == 0, User::Invariant());
752 __ASSERT_DEBUG((TUint)iFlagIndex < KLogFlagsCount, User::Invariant());
753 __ASSERT_DEBUG(iPredicateList != NULL, User::Invariant());