|
1 // Copyright (c) 2006-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". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 /** |
|
17 @file |
|
18 @internalComponent |
|
19 */ |
|
20 |
|
21 #include "input.h" |
|
22 #include "filedump.h" |
|
23 |
|
24 namespace |
|
25 { |
|
26 TUint32 DoHash(const HBufC& aText) |
|
27 { |
|
28 const TDesC& ref = aText; |
|
29 return DefaultHash::Des16(ref); |
|
30 } |
|
31 |
|
32 TBool AreTheKeysIdentical(const HBufC& aKey1, const HBufC& aKey2) |
|
33 { |
|
34 return (aKey1 == aKey2); |
|
35 } |
|
36 } //anonymous namespace |
|
37 |
|
38 |
|
39 |
|
40 CInputFileParser* CInputFileParser::FactoryLC(TBool aIsXML, TDesC& aFile, CFileDump* aLog) |
|
41 { |
|
42 CInputFileParser* p = NULL; |
|
43 |
|
44 if (aIsXML) |
|
45 { |
|
46 p = new(ELeave) CXMLFileParser(aFile, aLog); |
|
47 } |
|
48 else |
|
49 { |
|
50 p = new(ELeave) CCfgFileParser(aFile, aLog); |
|
51 } |
|
52 |
|
53 CleanupStack::PushL(p); |
|
54 return p; |
|
55 } |
|
56 |
|
57 |
|
58 CInputFileParser::CInputFileParser(TDesC& aFile, CFileDump* aLog) : |
|
59 iFile(aFile), |
|
60 iLog(aLog), |
|
61 iTable(NULL) |
|
62 { |
|
63 } |
|
64 |
|
65 /** |
|
66 */ |
|
67 TBool CInputFileParser::IsMeshCompatibleL() |
|
68 { |
|
69 iLog->Msg(_L("MESHINFO: Checking if input file <%S> is mesh compatible"), &iFile); |
|
70 |
|
71 InitialiseL(); |
|
72 |
|
73 const TText** pTable = iTable; |
|
74 |
|
75 TBuf<MAX_BUFFER_LEN> table = *pTable; |
|
76 TBool isMeshCompatible = EFalse; |
|
77 while (table.Compare(TPtrC(NO_MORE_RECORDS)) != 0) |
|
78 { |
|
79 iLog->Msg(_L(" ")); |
|
80 iLog->Msg(_L("Parsing %S Table"), &table); |
|
81 |
|
82 if (CheckMeshTemplateL(table) || CheckMeshInsertsL(table)) |
|
83 { |
|
84 iLog->Msg(_L("MESHINFO: Found mesh table <%S>, the configuration file is assumed to be mesh-compatible"), &table); |
|
85 isMeshCompatible = ETrue; |
|
86 break; |
|
87 } |
|
88 table = *(++pTable); |
|
89 } |
|
90 return isMeshCompatible; |
|
91 } |
|
92 |
|
93 |
|
94 void CInputFileParser::Exit(TInt aErr) |
|
95 { |
|
96 iLog->Msg(_L("MESHERR: Configuration file [%S] could not be opened"), &iFile); |
|
97 iLog->Msg(_L("===================")); |
|
98 iLog->Msg(_L("ERROR")); |
|
99 User::Leave(aErr); |
|
100 } |
|
101 |
|
102 |
|
103 CCfgFileParser::CCfgFileParser(TDesC& aFile, CFileDump* aLog) : |
|
104 CInputFileParser(aFile, aLog) |
|
105 { |
|
106 } |
|
107 |
|
108 |
|
109 CCfgFileParser::~CCfgFileParser() |
|
110 { |
|
111 iCfg.CloseConfigFile(); |
|
112 } |
|
113 |
|
114 |
|
115 void CCfgFileParser::InitialiseL() |
|
116 { |
|
117 iTable = const_cast<const TText**>(tableMeshMarkerArray); |
|
118 if (!iCfg.OpenConfigFile(iFile)) |
|
119 { |
|
120 Exit(KErrNotFound); |
|
121 } |
|
122 } |
|
123 |
|
124 |
|
125 TBool CCfgFileParser::CheckMeshTemplateL(TDesC& aTable) |
|
126 { |
|
127 return iCfg.OpenTemplateBlock(aTable); |
|
128 } |
|
129 |
|
130 |
|
131 TBool CCfgFileParser::CheckMeshInsertsL(TDesC& aTable) |
|
132 { |
|
133 return iCfg.OpenAddBlock(aTable); |
|
134 } |
|
135 |
|
136 |
|
137 CXMLFileParser::CXMLFileParser(TDesC& aFile, CFileDump* aLog) : |
|
138 CInputFileParser(aFile, aLog), |
|
139 iPtr(NULL, 0) |
|
140 { |
|
141 } |
|
142 |
|
143 |
|
144 CXMLFileParser::~CXMLFileParser() |
|
145 { |
|
146 delete iPtr.Ptr(); |
|
147 } |
|
148 |
|
149 |
|
150 void CXMLFileParser::InitialiseL() |
|
151 { |
|
152 iTable = const_cast<const TText**>(tableXMLMeshMarkerArray); |
|
153 TRAPD(ret, OpenXMLFileL()); |
|
154 if (ret != KErrNone) |
|
155 { |
|
156 Exit(ret); |
|
157 } |
|
158 } |
|
159 |
|
160 |
|
161 void CXMLFileParser::OpenXMLFileL() |
|
162 { |
|
163 // Shameless copy of 'CIniData::ConstructL()' |
|
164 |
|
165 // Connect to file server |
|
166 // |
|
167 TAutoClose<RFs> fs; |
|
168 User::LeaveIfError(fs.iObj.Connect()); |
|
169 fs.PushL(); |
|
170 |
|
171 |
|
172 // Open file |
|
173 // |
|
174 TAutoClose<RFile> file; |
|
175 TInt size; |
|
176 User::LeaveIfError(file.iObj.Open(fs.iObj, iFile, EFileStreamText | EFileRead)); |
|
177 file.PushL(); |
|
178 |
|
179 |
|
180 // Get file size and read in |
|
181 // |
|
182 User::LeaveIfError(file.iObj.Size(size)); |
|
183 TText* data = (TText*)User::AllocL(size); |
|
184 iPtr.Set(data, TUint(size)/sizeof(TText), TUint(size)/sizeof(TText)); |
|
185 TPtr8 dest((TUint8*)data, 0, size); |
|
186 User::LeaveIfError(file.iObj.Read(dest)); |
|
187 TUint8* ptr = (TUint8*)data; |
|
188 |
|
189 |
|
190 // This is orderred as FEFF assuming the processor is Little Endian |
|
191 // The data in the file is FFFE. PRR 28/9/98 |
|
192 // |
|
193 if (size >= (TInt)sizeof(TText) && iPtr[0]==0xFEFF) |
|
194 { |
|
195 // TODO: strip out UNICODE spaces. |
|
196 |
|
197 // UNICODE Text file so lose the FFFE |
|
198 // |
|
199 Mem::Copy(ptr, ptr+sizeof(TText), size-sizeof(TText)); |
|
200 iPtr.Set(data, TUint(size)/sizeof(TText)-1, TUint(size)/sizeof(TText)-1); |
|
201 } |
|
202 else if(size) |
|
203 { |
|
204 // NON-UNICODE so convert to UNICODE |
|
205 // |
|
206 TText* newdata = (TText*)User::AllocL(size * sizeof(TText)); |
|
207 iPtr.Set(newdata, size, size); |
|
208 TInt i; |
|
209 TInt actualLength = 0; |
|
210 for (i = 0; i < size ; ++i) |
|
211 { |
|
212 // Lose the spaces (at the cost of having allocated a buffer slightly to big...). |
|
213 // This makes the subsequent string searches so much easier and more efficient. |
|
214 // |
|
215 if (!TChar(ptr[i]).IsSpace()) |
|
216 { |
|
217 iPtr[actualLength++] = ptr[i]; |
|
218 } |
|
219 } |
|
220 iPtr.Set(newdata, actualLength, size); |
|
221 delete data; |
|
222 } |
|
223 |
|
224 file.Pop(); |
|
225 fs.Pop(); |
|
226 } |
|
227 |
|
228 |
|
229 // Assemble all the XML validation related strings together. |
|
230 // |
|
231 // In essence, a file is considered mesh compatible (has already been updated with mesh tables) if the record |
|
232 // names in 'dbdef.h::tableXMLMeshMarkerArray' exist in stringsof the form "operation="add">" or |
|
233 // "operation="template">" |
|
234 // |
|
235 _LIT(KTemplate, "template"); |
|
236 _LIT(KAdd, "add"); |
|
237 _LIT(KRecordInsertFormat, "<%Soperation=\"%S\">"); |
|
238 |
|
239 |
|
240 TBool CXMLFileParser::CheckMeshTemplateL(TDesC& aTable) |
|
241 { |
|
242 return (SearchXMLFile(KTemplate, aTable)); |
|
243 } |
|
244 |
|
245 |
|
246 TBool CXMLFileParser::CheckMeshInsertsL(TDesC& aTable) |
|
247 { |
|
248 return SearchXMLFile(KAdd, aTable); |
|
249 } |
|
250 |
|
251 |
|
252 TBool CXMLFileParser::SearchXMLFile(const TDesC& aOpType, const TDesC& aTable) |
|
253 { |
|
254 TBuf<MAX_BUFFER_LEN> xmlTableName; |
|
255 xmlTableName.Format(KRecordInsertFormat, &aTable, &aOpType); |
|
256 |
|
257 // No folding or collation. |
|
258 // |
|
259 return (iPtr.Find(xmlTableName) >= 0); |
|
260 } |
|
261 |
|
262 //------------------------ LinkByTagResolver --------------------------- |
|
263 |
|
264 |
|
265 LinkByTagResolver* LinkByTagResolver::NewL(CfgFile* aIniData, CFileDump* aLogger) |
|
266 { |
|
267 LinkByTagResolver* self = LinkByTagResolver::NewLC(aIniData, aLogger); |
|
268 CleanupStack::Pop(self); |
|
269 return self; |
|
270 } |
|
271 |
|
272 LinkByTagResolver* LinkByTagResolver::NewLC(CfgFile* aIniData, CFileDump* aLogger) |
|
273 { |
|
274 LinkByTagResolver* self = new(ELeave) LinkByTagResolver(aIniData, aLogger); |
|
275 CleanupStack::PushL(self); |
|
276 self->ConstructL(); |
|
277 return self; |
|
278 } |
|
279 |
|
280 //the ctor... |
|
281 LinkByTagResolver::LinkByTagResolver(CfgFile* aIniData, CFileDump* aLogger) |
|
282 : iLinkByTagRecIdPairs(0) |
|
283 , iConfigFile(aIniData) |
|
284 , iLogger(aLogger) |
|
285 { |
|
286 } |
|
287 |
|
288 //the dtor |
|
289 LinkByTagResolver::~LinkByTagResolver() |
|
290 { |
|
291 |
|
292 TPtrHashMapIter<HBufC, HBufC > iter(*iLinkByTagRecIdPairs); |
|
293 |
|
294 for (TInt i = 0; i < iLinkByTagRecIdPairs->Count(); ++i) |
|
295 { |
|
296 iter.NextValue(); |
|
297 |
|
298 const HBufC* cKey = iter.CurrentKey(); |
|
299 const HBufC* cValue = iter.CurrentValue(); |
|
300 |
|
301 delete cKey; |
|
302 delete cValue; |
|
303 |
|
304 cKey = NULL; |
|
305 cValue = NULL; |
|
306 } |
|
307 |
|
308 iLinkByTagRecIdPairs->Close(); |
|
309 delete iLinkByTagRecIdPairs; |
|
310 iLinkByTagRecIdPairs = NULL; |
|
311 |
|
312 } |
|
313 |
|
314 void LinkByTagResolver::ConstructL() |
|
315 { |
|
316 //the hash function for hashing the table |
|
317 const THashFunction32<HBufC > theHashFunc(&DoHash); |
|
318 |
|
319 //the key identity relation to campare the keys |
|
320 const TIdentityRelation<HBufC > theIdentityRelFunc(&AreTheKeysIdentical); |
|
321 |
|
322 //create the hashmap on the heap |
|
323 iLinkByTagRecIdPairs = new(ELeave) RPtrHashMap<HBufC, HBufC >(theHashFunc, theIdentityRelFunc); |
|
324 |
|
325 FillUpHashMapL(); |
|
326 } |
|
327 |
|
328 void LinkByTagResolver::FillUpHashMapL() |
|
329 { |
|
330 TInt i = 0; |
|
331 TPtrC actTable = TPtrC(TablesWithLinkRecords[i]); |
|
332 |
|
333 |
|
334 while (actTable != TPtrC(NO_MORE_RECORDS)) |
|
335 { |
|
336 //first search for template of the given table |
|
337 if (iConfigFile->OpenTemplateBlock(actTable)) |
|
338 //got it, search for the possible link fields in the record |
|
339 { |
|
340 ExemineFieldsL(i); |
|
341 } |
|
342 |
|
343 //and now for ADD sections... |
|
344 if (iConfigFile->OpenAddBlock(actTable)) |
|
345 //got it, search for the possible link fields in the record |
|
346 { |
|
347 ExemineFieldsL(i); |
|
348 } |
|
349 |
|
350 actTable.Set(TPtrC(TablesWithLinkRecords[++i])); |
|
351 } |
|
352 //if everything went OK, we insert two empt string at the end of the RPtrHashMap |
|
353 //to be able to return an empty string instead of leave in the case if the given |
|
354 //search string not found... |
|
355 /*TBuf<0> temp; |
|
356 HBufC* key = temp.Alloc(); |
|
357 HBufC* value = temp.Alloc();*/ |
|
358 |
|
359 HBufC* key = HBufC::New(0); |
|
360 HBufC* value = HBufC::New(0); |
|
361 iLinkByTagRecIdPairs->Insert(key, value); |
|
362 } |
|
363 |
|
364 void LinkByTagResolver::ExemineFieldsL(const TInt aTableIndex) |
|
365 { |
|
366 TInt i = 0; |
|
367 TBuf<MAX_BUFFER_LEN> resolvedLink; |
|
368 HBufC* tempForHashSearch; |
|
369 //TBuf<MAX_BUFFER_LEN> tempForHashSearch; |
|
370 |
|
371 //the fileds form the config file from the section |
|
372 TPtrC actField = TPtrC(LinkRecordsArray[aTableIndex][i]); |
|
373 //the link setting from the file |
|
374 TPtrC setting; |
|
375 //the resolved link to Table.RecId format |
|
376 TPtrC link(resolvedLink); |
|
377 |
|
378 TBool firstRec = ETrue; |
|
379 while (firstRec || iConfigFile->StepToNextBlock()) |
|
380 { |
|
381 i = 0; |
|
382 actField.Set(TPtrC(LinkRecordsArray[aTableIndex][i])); |
|
383 |
|
384 while (actField != TPtrC(NO_MORE_RECORDS)) |
|
385 { |
|
386 if (KErrNone == iConfigFile->GetSetting(actField, setting)) |
|
387 //ok, got a field from the section |
|
388 { |
|
389 if (SearchForLink(setting)) |
|
390 { |
|
391 //just for optimization - if the linking is already in the hashmap |
|
392 //don't resolve it once again... tempForHashSearch should be empty!!! |
|
393 tempForHashSearch = setting.Alloc(); |
|
394 CleanupStack::PushL(tempForHashSearch); |
|
395 |
|
396 if (NULL == iLinkByTagRecIdPairs->Find(*tempForHashSearch)) |
|
397 { |
|
398 //save the state of the CIniFile object... |
|
399 /** |
|
400 This is needed here because in the following sections the pointers in the |
|
401 CIniFile object will shift and the next call of StepToNextBlock could bring |
|
402 some unexpected results... |
|
403 */ |
|
404 TInt aBlockState = iConfigFile->file->BlockState; |
|
405 TInt aBlockStart = iConfigFile->file->blockStart; |
|
406 TInt aBlockEnd = iConfigFile->file->blockEnd; |
|
407 TInt aScanStart = iConfigFile->file->scanStart; |
|
408 TPtr aSection = iConfigFile->file->section; |
|
409 TPtr aBlock = iConfigFile->file->block; |
|
410 |
|
411 resolvedLink.Zero(); |
|
412 TRAPD(err,ResolveLinkToRecIdL(setting, resolvedLink)); |
|
413 if (err != KErrNone) |
|
414 { |
|
415 iLogger->Error(_L("ERROR: [%d] Failed to resolve LinkByTag: [%S] - database will be incomplete"), err, &setting); |
|
416 User::Leave(err); |
|
417 } |
|
418 |
|
419 //restore the state... |
|
420 iConfigFile->file->BlockState = aBlockState; |
|
421 iConfigFile->file->blockStart = aBlockStart; |
|
422 iConfigFile->file->blockEnd = aBlockEnd; |
|
423 iConfigFile->file->scanStart = aScanStart; |
|
424 iConfigFile->file->section.Set(aSection); |
|
425 iConfigFile->file->block.Set(aBlock); |
|
426 |
|
427 //these pointers will be deleted in the dtor!!!!! |
|
428 HBufC* key = setting.Alloc(); |
|
429 HBufC* value = resolvedLink.Alloc(); |
|
430 |
|
431 |
|
432 |
|
433 /*TBuf<MAX_BUFFER_LEN>* key = new(ELeave) TBuf<MAX_BUFFER_LEN>(setting); |
|
434 TBuf<MAX_BUFFER_LEN>* value = new(ELeave) TBuf<MAX_BUFFER_LEN>(linkToResolve);*/ |
|
435 |
|
436 iLogger->Msg(_L("LinkByTag resolved: [%S] to [%S]"), &setting, &resolvedLink); |
|
437 |
|
438 //the key and value are on the heap. What here happens is that |
|
439 //the 2 pointers pointing to the heap cells are copied into the |
|
440 //RPtrHasMap. |
|
441 iLinkByTagRecIdPairs->Insert(key, value); |
|
442 } |
|
443 |
|
444 CleanupStack::PopAndDestroy(tempForHashSearch); |
|
445 //tempForHashSearch.Zero(); |
|
446 } |
|
447 } |
|
448 actField.Set(TPtrC(LinkRecordsArray[aTableIndex][++i])); |
|
449 } |
|
450 |
|
451 firstRec = EFalse; |
|
452 } |
|
453 } |
|
454 |
|
455 /** |
|
456 if the param is 'Link.TableName.someId' format returns the position of the first |
|
457 '.' which is after the 'Link' word. Else returns 0 |
|
458 */ |
|
459 TInt LinkByTagResolver::SearchForLink(const TPtrC& aSetting) |
|
460 { |
|
461 _LIT(linkTag, "Link"); |
|
462 |
|
463 //almost the same code as in DBAccess::SetLinkedRecord |
|
464 const TUint KTableColumnSeperator = '.'; |
|
465 TInt pos = aSetting.Locate(TChar(KTableColumnSeperator)); |
|
466 |
|
467 if (pos != KErrNotFound) |
|
468 { |
|
469 TBuf<KCommsDbSvrMaxColumnNameLength> buffer = aSetting.Left(pos); |
|
470 |
|
471 if(buffer == TPtrC(linkTag)) |
|
472 { |
|
473 return pos; |
|
474 } |
|
475 } |
|
476 |
|
477 return 0; |
|
478 } |
|
479 |
|
480 |
|
481 void LinkByTagResolver::ResolveLinkToRecIdL(const TPtrC& aSetting, TBuf<MAX_BUFFER_LEN>& aResolvedLink) |
|
482 { |
|
483 TInt i = SearchForLink(aSetting); |
|
484 TPtrC tableName = aSetting.Mid(i+1); //now the tableName is TableName.someId |
|
485 |
|
486 //the Id setting from the linked record |
|
487 TPtrC idTagSetting; |
|
488 TPtrC recordId; |
|
489 |
|
490 _LIT(idTag, "Id"); |
|
491 _LIT(commdID, "COMMDB_ID "); |
|
492 |
|
493 //now search for the next '.' |
|
494 const TUint KTableColumnSeperator = '.'; |
|
495 TInt pos = tableName.Locate(TChar(KTableColumnSeperator)); |
|
496 if (pos != KErrNotFound) |
|
497 { |
|
498 TPtrC idTagFromLink = tableName.Right(tableName.Length()-pos-1); |
|
499 tableName.Set(tableName.Left(pos)); |
|
500 |
|
501 //ok, got the table name and the idTag. Now the tableName should be searched for. |
|
502 //As this cannot be a template record we use just the 'OpenAddBlock' method. |
|
503 |
|
504 TBool idTagFound = EFalse; |
|
505 if (iConfigFile->OpenAddBlock(tableName)) |
|
506 //got it, search for the 'Id' fields in the record |
|
507 { |
|
508 TBool firstRec = ETrue; |
|
509 |
|
510 while ( firstRec || (!idTagFound && iConfigFile->StepToNextBlock()) ) |
|
511 { |
|
512 if (KErrNone == iConfigFile->GetSetting(idTag, idTagSetting)) |
|
513 { |
|
514 if (idTagFromLink == idTagSetting) |
|
515 //found the record with the given Id field. Read the record id of it |
|
516 { |
|
517 if (KErrNone == iConfigFile->GetSetting(commdID, recordId)) |
|
518 { |
|
519 idTagFound = ETrue; |
|
520 |
|
521 aResolvedLink.Append(tableName); |
|
522 aResolvedLink.Append('.'); |
|
523 aResolvedLink.Append(recordId); |
|
524 } |
|
525 } |
|
526 } |
|
527 firstRec = EFalse; |
|
528 } |
|
529 } |
|
530 |
|
531 if (!idTagFound) |
|
532 { |
|
533 //The linking should be valid |
|
534 User::Leave(KErrNotFound); |
|
535 } |
|
536 } |
|
537 else |
|
538 { |
|
539 //The linking should be valid |
|
540 User::Leave(KErrNotFound); |
|
541 } |
|
542 } |
|
543 |
|
544 const HBufC* LinkByTagResolver::ResolvedIdForTagLink(const TPtrC& aLinkingString/*, TDesC& aTheResolvedPair*/) |
|
545 { |
|
546 HBufC* tempForHashSearch = aLinkingString.Alloc(); |
|
547 CleanupStack::PushL(tempForHashSearch); |
|
548 |
|
549 //this will point to an element in the RPtrHashMap, so not needed to push |
|
550 //it on the CleanupStack |
|
551 HBufC* resolvedLink; |
|
552 |
|
553 if ( NULL == (resolvedLink = iLinkByTagRecIdPairs->Find(*tempForHashSearch)) ) |
|
554 /** |
|
555 hm... Normaly we should leave here but this would cause the rewrite of the |
|
556 DBAccess::SetLinkedRecord method which is a non leaving method. On the |
|
557 other side TRAPPing would be very expensive as this method is called from |
|
558 a loop... |
|
559 So we return the last element of the RPtrHashMap which will be an empty |
|
560 descriptior. |
|
561 */ |
|
562 { |
|
563 TPtrHashMapIter<HBufC, HBufC > iter(*iLinkByTagRecIdPairs); |
|
564 |
|
565 //iterating at the end of the hashMap... |
|
566 for (TInt i = 0; i < iLinkByTagRecIdPairs->Count(); ++i) |
|
567 { |
|
568 iter.NextValue(); |
|
569 } |
|
570 |
|
571 //return the empty string... |
|
572 //return const_cast<HBufC*>(iter.CurrentKey())->Des(); |
|
573 return const_cast<HBufC*>(iter.CurrentKey()); |
|
574 |
|
575 //aTheResolvedPair(const_cast<HBufC*>(iter.CurrentKey())->Des()); |
|
576 } |
|
577 |
|
578 CleanupStack::PopAndDestroy(tempForHashSearch); |
|
579 |
|
580 //return resolvedLink->Des(); |
|
581 return resolvedLink; |
|
582 //aTheResolvedPair(resolvedLink->Des()); |
|
583 } |
|
584 |
|
585 //EOF |