File: | libsynthesis/src/sysync/multifielditem.cpp |
Warning: | line 585, column 8 Called C++ object pointer is null |
1 | /* | |||
2 | * File: MultiFieldItem.cpp | |||
3 | * | |||
4 | * Author: Lukas Zeller (luz@plan44.ch) | |||
5 | * | |||
6 | * TMultiFieldItem | |||
7 | * Item consisting of multiple data fields (TItemField objects) | |||
8 | * | |||
9 | * Copyright (c) 2001-2011 by Synthesis AG + plan44.ch | |||
10 | * | |||
11 | * 2001-08-08 : luz : created | |||
12 | * | |||
13 | */ | |||
14 | ||||
15 | // includes | |||
16 | #include "prefix_file.h" | |||
17 | #include "sysync.h" | |||
18 | #include "multifielditem.h" | |||
19 | #include "multifielditemtype.h" | |||
20 | ||||
21 | ||||
22 | using namespace sysync; | |||
23 | ||||
24 | namespace sysync { | |||
25 | ||||
26 | // Config | |||
27 | // ====== | |||
28 | ||||
29 | ||||
30 | TFieldListConfig::TFieldListConfig(const char* aName, TConfigElement *aParentElement) : | |||
31 | TConfigElement(aName,aParentElement) | |||
32 | { | |||
33 | clear(); | |||
34 | } // TFieldListConfig::TFieldListConfig | |||
35 | ||||
36 | ||||
37 | TFieldListConfig::~TFieldListConfig() | |||
38 | { | |||
39 | clear(); | |||
40 | } // TFieldListConfig::~TFieldListConfig | |||
41 | ||||
42 | ||||
43 | // init defaults | |||
44 | void TFieldListConfig::clear(void) | |||
45 | { | |||
46 | // init defaults | |||
47 | fAgeSortable=false; | |||
48 | fFields.clear(); | |||
49 | #ifdef HARDCODED_TYPE_SUPPORT | |||
50 | fFieldListTemplateP=NULL__null; | |||
51 | #endif | |||
52 | // clear inherited | |||
53 | inherited::clear(); | |||
54 | } // TFieldListConfig::clear | |||
55 | ||||
56 | ||||
57 | #ifdef CONFIGURABLE_TYPE_SUPPORT1 | |||
58 | ||||
59 | // config element parsing | |||
60 | bool TFieldListConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine) | |||
61 | { | |||
62 | // - fieldlist entry | |||
63 | // <field name="REV" type="timestamp" compare="never" age="yes" merge="no"/> | |||
64 | if (strucmp(aElementName,"field")==0) { | |||
65 | // may not contain anything | |||
66 | expectEmpty(); | |||
67 | // check attributes | |||
68 | const char *nam = getAttr(aAttributes,"name"); | |||
69 | const char *type = getAttr(aAttributes,"type"); | |||
70 | const char *rel = getAttr(aAttributes,"compare"); | |||
71 | if (!(nam && *nam && type && rel)) | |||
72 | return fail("'field' must have 'name', 'type' and 'compare' attributes"); | |||
73 | // parse enums | |||
74 | sInt16 ty; | |||
75 | if (!StrToEnum(ItemFieldTypeNames,numFieldTypes,ty,type)) | |||
76 | return fail("Unknown 'type' attribute: '%s'",type); | |||
77 | sInt16 eqrel; | |||
78 | if (!StrToEnum(compareRelevanceNames,numEQmodes,eqrel,rel)) | |||
79 | return fail("Unknown 'compare' attribute: '%s'",rel); | |||
80 | // set defaults | |||
81 | bool agerelevant=false; // not age relevant by default | |||
82 | sInt16 mergemode=mem_none-1; // no merge by default | |||
83 | // get optional attributes | |||
84 | if (!getAttrBool(aAttributes,"age",agerelevant,true)) | |||
85 | return fail("Bad boolean value"); | |||
86 | #ifdef ARRAYFIELD_SUPPORT1 | |||
87 | bool array=false; // not an array | |||
88 | if (!getAttrBool(aAttributes,"array",array,true)) | |||
89 | return fail("Bad boolean value"); | |||
90 | #endif | |||
91 | const char *p = getAttr(aAttributes,"merge"); | |||
92 | if (p) { | |||
93 | // sort out special cases | |||
94 | if (strucmp(p,"no")==0) | |||
95 | mergemode=mem_none-1; | |||
96 | else if (strucmp(p,"fillempty")==0) | |||
97 | mergemode=mem_fillempty-2; | |||
98 | else if (strucmp(p,"addunassigned")==0) | |||
99 | mergemode=mem_addunassigned-3; | |||
100 | else if (strucmp(p,"append")==0) | |||
101 | mergemode=mem_concat0; | |||
102 | else if (strucmp(p,"lines")==0) | |||
103 | mergemode='\n'; | |||
104 | else if (strlen(p)==1) | |||
105 | mergemode=*p; // single char is merge char | |||
106 | else | |||
107 | return fail("Invalid value '%s' for 'merge' attribute",p); | |||
108 | } | |||
109 | // now add new field specification | |||
110 | TFieldDefinition fielddef; | |||
111 | // prepare template element | |||
112 | fielddef.type=(TItemFieldTypes)ty; | |||
113 | #ifdef ARRAYFIELD_SUPPORT1 | |||
114 | fielddef.array = array; | |||
115 | #endif | |||
116 | TCFG_ASSIGN(fielddef.fieldname,nam){ if (nam) fielddef.fieldname=nam; else fielddef.fieldname.erase (); }; | |||
117 | fielddef.eqRelevant=(TEqualityMode)eqrel; | |||
118 | fielddef.ageRelevant=agerelevant; | |||
119 | fAgeSortable=fAgeSortable || fielddef.ageRelevant; // if at least one field is age-relevant, we can sort items | |||
120 | fielddef.mergeMode=mergemode; | |||
121 | // copy into array | |||
122 | fFields.push_back(fielddef); | |||
123 | } | |||
124 | // - none known here | |||
125 | else | |||
126 | return inherited::localStartElement(aElementName,aAttributes,aLine); | |||
127 | // ok | |||
128 | return true; | |||
129 | } // TFieldListConfig::localStartElement | |||
130 | ||||
131 | ||||
132 | // resolve | |||
133 | void TFieldListConfig::localResolve(bool aLastPass) | |||
134 | { | |||
135 | if (aLastPass) { | |||
136 | // check for required settings | |||
137 | if (fFields.size()==0) | |||
138 | SYSYNC_THROW(TSyncException("fieldlist must contain at least one field"))throw TSyncException("fieldlist must contain at least one field" ); | |||
139 | } | |||
140 | // resolve inherited | |||
141 | inherited::localResolve(aLastPass); | |||
142 | } // TFieldListConfig::localResolve | |||
143 | ||||
144 | #endif | |||
145 | ||||
146 | ||||
147 | // get index of a field | |||
148 | sInt16 TFieldListConfig::fieldIndex(const char *aName, size_t aLen) | |||
149 | { | |||
150 | TFieldDefinitionList::iterator pos; | |||
151 | sInt16 n; | |||
152 | for (n=0,pos=fFields.begin(); pos!=fFields.end(); ++n,pos++) { | |||
153 | if (strucmp(aName,pos->TCFG_CSTR(fieldname)fieldname.c_str(),aLen)==0) { | |||
154 | return n; // return field ID | |||
155 | } | |||
156 | } | |||
157 | return VARIDX_UNDEFINED-128; // not found | |||
158 | } // TFieldListConfig::fieldIndex | |||
159 | ||||
160 | ||||
161 | // profile handler | |||
162 | ||||
163 | TProfileHandler::TProfileHandler(TProfileConfig *aProfileCfgP, TMultiFieldItemType *aItemTypeP) | |||
164 | { | |||
165 | // save profile config pointer | |||
166 | fItemTypeP = aItemTypeP; | |||
167 | // no related datastore yet | |||
168 | fRelatedDatastoreP = NULL__null; | |||
169 | } // TProfileHandler::TProfileHandler | |||
170 | ||||
171 | ||||
172 | TProfileHandler::~TProfileHandler() | |||
173 | { | |||
174 | // nop for now | |||
175 | } // TProfileHandler::~TProfileHandler | |||
176 | ||||
177 | ||||
178 | // - get session pointer | |||
179 | TSyncSession *TProfileHandler::getSession(void) | |||
180 | { | |||
181 | return fItemTypeP ? fItemTypeP->getSession() : NULL__null; | |||
182 | } // TProfileHandler::getSession | |||
183 | ||||
184 | // - get session zones pointer | |||
185 | GZones *TProfileHandler::getSessionZones(void) | |||
186 | { | |||
187 | return fItemTypeP ? fItemTypeP->getSessionZones() : NULL__null; | |||
188 | } // TProfileHandler::getSessionZones | |||
189 | ||||
190 | ||||
191 | #ifdef SYDEBUG2 | |||
192 | ||||
193 | TDebugLogger *TProfileHandler::getDbgLogger(void) | |||
194 | { | |||
195 | // commands log to session's logger | |||
196 | return fItemTypeP ? fItemTypeP->getDbgLogger() : NULL__null; | |||
197 | } // TProfileHandler::getDbgLogger | |||
198 | ||||
199 | uInt32 TProfileHandler::getDbgMask(void) | |||
200 | { | |||
201 | if (!fItemTypeP) return 0; // no item type, no debug | |||
202 | return fItemTypeP->getDbgMask(); | |||
203 | } // TProfileHandler::getDbgMask | |||
204 | ||||
205 | #endif | |||
206 | ||||
207 | ||||
208 | // - check availability (depends on item "supported" flags only in SyncML datastore context) | |||
209 | bool TProfileHandler::isFieldAvailable(TMultiFieldItem &aItem, sInt16 aFieldIndex) | |||
210 | { | |||
211 | if (fRelatedDatastoreP) { | |||
212 | // in datastore/SyncML context, only fields supported on both sides are considered "available" | |||
213 | return aItem.isAvailable(aFieldIndex); | |||
214 | } | |||
215 | else { | |||
216 | // in non-datastore context, all fields are considered available, as long as | |||
217 | // the field index is in range | |||
218 | TMultiFieldItemType *mfitP = aItem.getItemType(); | |||
219 | return mfitP && mfitP->isFieldIndexValid(aFieldIndex); | |||
220 | } | |||
221 | } // TProfileHandler::isFieldAvailable | |||
222 | ||||
223 | ||||
224 | ||||
225 | ||||
226 | ||||
227 | ||||
228 | // Profile config root element | |||
229 | ||||
230 | TProfileConfig::TProfileConfig(const char* aName, TConfigElement *aParentElement) : | |||
231 | TConfigElement(aName,aParentElement) | |||
232 | { | |||
233 | clear(); | |||
234 | } // TProfileConfig::TProfileConfig | |||
235 | ||||
236 | ||||
237 | TProfileConfig::~TProfileConfig() | |||
238 | { | |||
239 | clear(); | |||
240 | } // TProfileConfig::~TProfileConfig | |||
241 | ||||
242 | ||||
243 | // init defaults | |||
244 | void TProfileConfig::clear(void) | |||
245 | { | |||
246 | // init defaults | |||
247 | fFieldListP=NULL__null; // no field list linked | |||
248 | // clear inherited | |||
249 | inherited::clear(); | |||
250 | } // TProfileConfig::clear | |||
251 | ||||
252 | ||||
253 | ||||
254 | #ifdef HARDCODED_TYPE_SUPPORT | |||
255 | ||||
256 | // read hardcoded fieldlist config | |||
257 | void TFieldListConfig::readFieldListTemplate(const TFieldDefinitionsTemplate *aTemplateP) | |||
258 | { | |||
259 | if (!aTemplateP) return; | |||
260 | // save the link to the template as well (we'll need it for maxsize etc. later) | |||
261 | fFieldListTemplateP=aTemplateP; | |||
262 | fFields.clear(); | |||
263 | // copy values | |||
264 | fAgeSortable=false; | |||
265 | TFieldDefinition fielddef; | |||
266 | for (sInt16 i=0; i<aTemplateP->numFields; i++) { | |||
267 | const TFieldDefinitionTemplate *afieldP= &(aTemplateP->fieldDefs[i]); | |||
268 | // prepare template element | |||
269 | fielddef.type=afieldP->type; | |||
270 | #ifdef ARRAYFIELD_SUPPORT1 | |||
271 | fielddef.array = afieldP->array; | |||
272 | #endif | |||
273 | TCFG_ASSIGN(fielddef.fieldname,afieldP->fieldname){ if (afieldP->fieldname) fielddef.fieldname=afieldP->fieldname ; else fielddef.fieldname.erase(); }; | |||
274 | fielddef.eqRelevant=afieldP->eqRelevant; | |||
275 | fielddef.ageRelevant=afieldP->ageRelevant; | |||
276 | fAgeSortable=fAgeSortable || fielddef.ageRelevant; // if at least one field is age-relevant, we can sort | |||
277 | fielddef.mergeMode=afieldP->mergeMode; | |||
278 | // copy into array | |||
279 | fFields.push_back(fielddef); | |||
280 | } | |||
281 | } // TFieldListConfig::readFieldListTemplate | |||
282 | ||||
283 | #endif | |||
284 | ||||
285 | ||||
286 | ||||
287 | TMultiFieldDatatypesConfig::TMultiFieldDatatypesConfig(TConfigElement *aParentElement) : | |||
288 | TDatatypesConfig("datatypes",aParentElement) | |||
289 | { | |||
290 | clear(); | |||
291 | } // TMultiFieldDatatypesConfig::TMultiFieldDatatypesConfig | |||
292 | ||||
293 | ||||
294 | TMultiFieldDatatypesConfig::~TMultiFieldDatatypesConfig() | |||
295 | { | |||
296 | // make sure we don't re-build types (createHardcodedTypes() in clear()) | |||
297 | // so we call internalClear() here! | |||
298 | internalClear(); | |||
299 | } // TMultiFieldDatatypesConfig::~TMultiFieldDatatypesConfig | |||
300 | ||||
301 | ||||
302 | // init defaults | |||
303 | void TMultiFieldDatatypesConfig::clear(void) | |||
304 | { | |||
305 | // remove internals | |||
306 | internalClear(); | |||
307 | // Now datatypes registry is really empty | |||
308 | #ifdef HARDCODED_TYPE_SUPPORT | |||
309 | // - add hard-coded default type information (if any) | |||
310 | static_cast<TRootConfig *>(getRootElement())->createHardcodedTypes(this); | |||
311 | #endif | |||
312 | } // TMultiFieldDatatypesConfig::clear | |||
313 | ||||
314 | ||||
315 | // init defaults | |||
316 | void TMultiFieldDatatypesConfig::internalClear(void) | |||
317 | { | |||
318 | // remove fieldlists | |||
319 | TFieldListsList::iterator pos1; | |||
320 | for(pos1=fFieldLists.begin();pos1!=fFieldLists.end();pos1++) | |||
321 | delete *pos1; | |||
322 | fFieldLists.clear(); | |||
323 | // remove profiles | |||
324 | TProfilesList::iterator pos2; | |||
325 | for(pos2=fProfiles.begin();pos2!=fProfiles.end();pos2++) | |||
326 | delete *pos2; | |||
327 | fProfiles.clear(); | |||
328 | // clear inherited | |||
329 | inherited::clear(); | |||
330 | } // TMultiFieldDatatypesConfig::internalClear | |||
331 | ||||
332 | ||||
333 | // get a field list by name | |||
334 | TFieldListConfig *TMultiFieldDatatypesConfig::getFieldList(const char *aName) | |||
335 | { | |||
336 | TFieldListsList::iterator pos; | |||
337 | for(pos=fFieldLists.begin();pos!=fFieldLists.end();pos++) { | |||
338 | if (strucmp((*pos)->getName(),aName)==0) { | |||
339 | // found | |||
340 | return *pos; | |||
341 | } | |||
342 | } | |||
343 | return NULL__null; // not found | |||
344 | } // TMultiFieldDatatypesConfig::getFieldList | |||
345 | ||||
346 | ||||
347 | // get a profile by name | |||
348 | TProfileConfig *TMultiFieldDatatypesConfig::getProfile(const char *aName) | |||
349 | { | |||
350 | TProfilesList::iterator pos; | |||
351 | for(pos=fProfiles.begin();pos!=fProfiles.end();pos++) { | |||
352 | if (strucmp((*pos)->getName(),aName)==0) { | |||
353 | // found | |||
354 | return *pos; | |||
355 | } | |||
356 | } | |||
357 | return NULL__null; // not found | |||
358 | } // TMultiFieldDatatypesConfig::getProfile | |||
359 | ||||
360 | ||||
361 | ||||
362 | #ifdef CONFIGURABLE_TYPE_SUPPORT1 | |||
363 | ||||
364 | // config element parsing | |||
365 | bool TMultiFieldDatatypesConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine) | |||
366 | { | |||
367 | // checking the elements | |||
368 | // - field lists or profiles can appear at this level | |||
369 | bool newFieldList=false; | |||
370 | TProfileConfig *newProfileP=NULL__null; | |||
371 | TFieldListConfig *flP = NULL__null; | |||
372 | // in case it is a field list or a profile - check for name | |||
373 | const char* nam = getAttr(aAttributes,"name"); | |||
374 | // now check if fieldlist or profile | |||
375 | if (strucmp(aElementName,"fieldlist")==0) { | |||
376 | newFieldList=true; | |||
377 | } | |||
378 | // the xml tag itself is used as the profile's typename (historical reasons/compatibility with existing config) | |||
379 | else if ((newProfileP=getSyncAppBase()->getRootConfig()->newProfileConfig(nam,aElementName,this))!=NULL__null) { | |||
380 | const char* flnam = getAttr(aAttributes,"fieldlist"); | |||
381 | if (!flnam) | |||
382 | return fail("%s is missing 'fieldlist' attribute",aElementName); | |||
383 | else { | |||
384 | flP = getFieldList(flnam); | |||
385 | if (!flP) | |||
386 | return fail("fieldlist '%s' unknown in %s",flnam,aElementName); | |||
387 | } | |||
388 | } | |||
389 | // - tag not known here | |||
390 | else | |||
391 | return TDatatypesConfig::localStartElement(aElementName,aAttributes,aLine); | |||
392 | // known tag, check if we need further processing | |||
393 | if (newFieldList || newProfileP) { | |||
394 | if (!nam) | |||
395 | return fail("%s is missing 'name' attribute",aElementName); | |||
396 | // create new named field list or use already created profile | |||
397 | if (newFieldList) { | |||
398 | // new field list | |||
399 | TFieldListConfig *fieldlistcfgP = new TFieldListConfig(nam,this); | |||
400 | fFieldLists.push_back(fieldlistcfgP); // save in list | |||
401 | expectChildParsing(*fieldlistcfgP); // let element handle parsing | |||
402 | } | |||
403 | else { | |||
404 | // new profile | |||
405 | newProfileP->fFieldListP=flP; // set field list for profile | |||
406 | fProfiles.push_back(newProfileP); // save in list | |||
407 | expectChildParsing(*newProfileP); // let element handle parsing | |||
408 | } | |||
409 | } | |||
410 | // ok | |||
411 | return true; | |||
412 | } // TMultiFieldDatatypesConfig::localStartElement | |||
413 | ||||
414 | ||||
415 | // resolve | |||
416 | void TMultiFieldDatatypesConfig::localResolve(bool aLastPass) | |||
417 | { | |||
418 | // resolve profiles | |||
419 | TProfilesList::iterator pos1; | |||
420 | for(pos1=fProfiles.begin();pos1!=fProfiles.end();pos1++) { | |||
421 | (*pos1)->localResolve(aLastPass); | |||
422 | } | |||
423 | // resolve field lists | |||
424 | TFieldListsList::iterator pos2; | |||
425 | for(pos2=fFieldLists.begin();pos2!=fFieldLists.end();pos2++) { | |||
426 | (*pos2)->localResolve(aLastPass); | |||
427 | } | |||
428 | // resolve inherited | |||
429 | inherited::localResolve(aLastPass); | |||
430 | } // TMultiFieldDatatypesConfig::localResolve | |||
431 | ||||
432 | ||||
433 | #endif | |||
434 | ||||
435 | ||||
436 | /* | |||
437 | * Implementation of TMultiFieldItem | |||
438 | */ | |||
439 | ||||
440 | /* public TMultiFieldItem members */ | |||
441 | ||||
442 | ||||
443 | TMultiFieldItem::TMultiFieldItem( | |||
444 | TMultiFieldItemType *aItemTypeP, // owner's (=source) type | |||
445 | TMultiFieldItemType *aTargetItemTypeP // target type (for optimization) | |||
446 | ) : | |||
447 | TSyncItem(aItemTypeP) | |||
448 | { | |||
449 | // save types | |||
450 | fItemTypeP = aItemTypeP; // owner (source) type | |||
451 | fTargetItemTypeP = aTargetItemTypeP; // target (destination) type | |||
452 | // copy field definitions pointer for fast access | |||
453 | fFieldDefinitionsP = fItemTypeP->getFieldDefinitions(); | |||
454 | if (!fFieldDefinitionsP) | |||
455 | SYSYNC_THROW(TSyncException(DEBUGTEXT("MultiFieldItem without FieldDefinitions","mfi3")))throw TSyncException("MultiFieldItem without FieldDefinitions" ); | |||
456 | // test if target has same field defs | |||
457 | if (fTargetItemTypeP->getFieldDefinitions()!=fFieldDefinitionsP) | |||
458 | SYSYNC_THROW(TSyncException(DEBUGTEXT("MultiFieldItem with non-matching target field definitions","mfi1")))throw TSyncException("MultiFieldItem with non-matching target field definitions" ); | |||
459 | // create fields array | |||
460 | fFieldsP = new TItemFieldP[fFieldDefinitionsP->numFields()]; | |||
461 | // - init it with null pointers | |||
462 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) fFieldsP[i]=NULL__null; | |||
463 | } // TMultiFieldItem::TMultiFieldItem | |||
464 | ||||
465 | ||||
466 | TMultiFieldItem::~TMultiFieldItem() | |||
467 | { | |||
468 | // remove fields | |||
469 | cleardata(); | |||
470 | // remove fields list | |||
471 | delete[] fFieldsP; | |||
472 | } // TMultiFieldItem::~TMultiFieldItem | |||
473 | ||||
474 | ||||
475 | ||||
476 | // remove all data from item | |||
477 | void TMultiFieldItem::cleardata(void) | |||
478 | { | |||
479 | if (fFieldDefinitionsP) { | |||
480 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
481 | if (fFieldsP[i]) { | |||
482 | delete fFieldsP[i]; // delete field object | |||
483 | fFieldsP[i]=NULL__null; | |||
484 | } | |||
485 | } | |||
486 | } | |||
487 | } // TMultiFieldItem::cleardata | |||
488 | ||||
489 | ||||
490 | #if defined(CHECKSUM_CHANGELOG1) && !defined(RECORDHASH_FROM_DBAPI) | |||
491 | ||||
492 | // changelog support: calculate CRC over contents | |||
493 | uInt16 TMultiFieldItem::getDataCRC(uInt16 crc, bool aEQRelevantOnly) | |||
494 | { | |||
495 | // iterate over all fields | |||
496 | if (fFieldDefinitionsP) { | |||
497 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
498 | if (!aEQRelevantOnly || fFieldDefinitionsP->fFields[i].eqRelevant!=eqm_none) { | |||
499 | if (fFieldsP[i]) { | |||
500 | crc=fFieldsP[i]->getDataCRC(crc); | |||
501 | } | |||
502 | } | |||
503 | } | |||
504 | } | |||
505 | return crc; | |||
506 | } // TMultiFieldItem::getDataCRC | |||
507 | ||||
508 | #endif | |||
509 | ||||
510 | ||||
511 | ||||
512 | // adjust fid and repeat offset to access array element if | |||
513 | // base fid is an array field or to offset fid accordingly | |||
514 | // if based fid is NOT an array field | |||
515 | // - returns adjusted aFid and aIndex ready to be used with getArrayField() | |||
516 | // - returns true if aFid IS an array field | |||
517 | bool TMultiFieldItem::adjustFidAndIndex(sInt16 &aFid, sInt16 &aIndex) | |||
518 | { | |||
519 | bool arrfield; | |||
520 | ||||
521 | #ifdef ARRAYFIELD_SUPPORT1 | |||
522 | // fid is offset repoffset only if not an array field | |||
523 | arrfield=true; | |||
524 | // check for array field first | |||
525 | TItemField *fldP = getField(aFid); | |||
526 | if (fldP) { | |||
527 | if (!(fldP->isArray())) { | |||
528 | // no array field | |||
529 | arrfield=false; | |||
530 | aFid += aIndex; // use array offset as additional field ID offset | |||
531 | aIndex=0; // no array index | |||
532 | } | |||
533 | } | |||
534 | else { | |||
535 | // Note: if field does not exist, do not apply offset, but don't report array field either! | |||
536 | arrfield = false; | |||
537 | } | |||
538 | #else | |||
539 | // without array support, fid is always offset by rep offset | |||
540 | aFid += aIndex; | |||
541 | aIndex=0; // no array index | |||
542 | arrfield=false; | |||
543 | #endif | |||
544 | // return true if this is really an array field | |||
545 | return arrfield; | |||
546 | } // adjustFidAndIndex | |||
547 | ||||
548 | ||||
549 | ||||
550 | // return specified leaf field of array field or regular field | |||
551 | // depending if aFid addresses an array or not. | |||
552 | // (This is a shortcut method to access fields specified by a base fid and a repeat) | |||
553 | TItemField *TMultiFieldItem::getArrayFieldAdjusted(sInt16 aFid, sInt16 aIndex, bool aExistingOnly) | |||
554 | { | |||
555 | adjustFidAndIndex(aFid,aIndex); | |||
556 | return getArrayField(aFid, aIndex, aExistingOnly); | |||
557 | } // TMultiFieldItem::getArrayFieldAdjusted | |||
558 | ||||
559 | ||||
560 | // return specified leaf field of array field | |||
561 | TItemField *TMultiFieldItem::getArrayField(sInt16 aFid, sInt16 aIndex, bool aExistingOnly) | |||
562 | { | |||
563 | #ifdef ARRAYFIELD_SUPPORT1 | |||
564 | TItemField *fiP = getField(aFid); | |||
565 | if (!fiP) return NULL__null; | |||
566 | return fiP->getArrayField(aIndex,aExistingOnly); | |||
567 | #else | |||
568 | // without array support, we can only access index==0 | |||
569 | if (aIndex>0) return NULL__null; // other indices don't exist | |||
570 | return getField(aFid); | |||
571 | #endif | |||
572 | } // TMultiFieldItem::getArrayField | |||
573 | ||||
574 | ||||
575 | // get field by name (returns NULL if not known, creates if known but not existing yet) | |||
576 | TItemField *TMultiFieldItem::getArrayField(const char *aFieldName, sInt16 aIndex, bool aExistingOnly) | |||
577 | { | |||
578 | return getArrayField(fItemTypeP->getFieldIndex(aFieldName),aIndex,aExistingOnly); | |||
579 | } // TMultiFieldItem::getArrayField | |||
580 | ||||
581 | ||||
582 | // get field by index (returns NULL if not known, creates if known but not existing yet) | |||
583 | TItemField *TMultiFieldItem::getField(sInt16 aFieldIndex) | |||
584 | { | |||
585 | if (!fItemTypeP->isFieldIndexValid(aFieldIndex)) return NULL__null; // invalid index | |||
| ||||
586 | TItemField *fiP = fFieldsP[aFieldIndex]; | |||
587 | if (!fiP) { | |||
588 | // we must create the field first | |||
589 | fiP=newItemField( | |||
590 | fFieldDefinitionsP->fFields[aFieldIndex].type, | |||
591 | getSessionZones() | |||
592 | #ifdef ARRAYFIELD_SUPPORT1 | |||
593 | ,fFieldDefinitionsP->fFields[aFieldIndex].array | |||
594 | #endif | |||
595 | ); | |||
596 | // save in array | |||
597 | fFieldsP[aFieldIndex] = fiP; | |||
598 | } | |||
599 | return fiP; | |||
600 | } // TMultiFieldItem::getField | |||
601 | ||||
602 | ||||
603 | // find index of field (returns FID_NOT_SUPPORTED if field is not a field of this item) | |||
604 | sInt16 TMultiFieldItem::getIndexOfField(const TItemField *aFieldP) | |||
605 | { | |||
606 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
607 | if (fFieldsP[i]==aFieldP) { | |||
608 | // found field, return it's index | |||
609 | return i; | |||
610 | } | |||
611 | } | |||
612 | return FID_NOT_SUPPORTED-128; // not found | |||
613 | } // TMultiFieldItem::getIndexOfField | |||
614 | ||||
615 | ||||
616 | ||||
617 | // get field by name (returns NULL if not known, creates if known but not existing yet) | |||
618 | TItemField *TMultiFieldItem::getField(const char *aFieldName) | |||
619 | { | |||
620 | return getField(fItemTypeP->getFieldIndex(aFieldName)); | |||
621 | } // TMultiFieldItem::getField | |||
622 | ||||
623 | ||||
624 | // get field reference (create if not yet created) | |||
625 | // throws if bad index | |||
626 | TItemField &TMultiFieldItem::getFieldRef(sInt16 aFieldIndex) | |||
627 | { | |||
628 | TItemField *fiP = getField(aFieldIndex); | |||
629 | if (!fiP) | |||
630 | SYSYNC_THROW(TSyncException(DEBUGTEXT("getFieldRef with bad index called","mfi2")))throw TSyncException("getFieldRef with bad index called"); // invalid index | |||
631 | return *fiP; | |||
632 | } // TMultiFieldItem::getFieldRef | |||
633 | ||||
634 | ||||
635 | // check if field is assigned (exists and has a value) | |||
636 | bool const TMultiFieldItem::isAssigned(const char *aFieldName) | |||
637 | { | |||
638 | return isAssigned(fItemTypeP->getFieldIndex(aFieldName)); | |||
639 | } // TMultiFieldItem::isAssigned | |||
640 | ||||
641 | ||||
642 | TMultiFieldItemType *TMultiFieldItem::getLocalItemType(void) | |||
643 | { | |||
644 | return fItemTypeP && fItemTypeP->isRemoteType() ? fTargetItemTypeP : fItemTypeP; | |||
645 | } // TMultiFieldItem::getLocalItemType | |||
646 | ||||
647 | ||||
648 | TMultiFieldItemType *TMultiFieldItem::getRemoteItemType(void) | |||
649 | { | |||
650 | return fItemTypeP && fItemTypeP->isRemoteType() ? fItemTypeP : fTargetItemTypeP; | |||
651 | } // TMultiFieldItem::getRemoteItemType | |||
652 | ||||
653 | ||||
654 | ||||
655 | // check if field is assigned (exists and has a value) | |||
656 | bool const TMultiFieldItem::isAssigned(sInt16 aFieldIndex) | |||
657 | { | |||
658 | // check if field object exists at all | |||
659 | if (!fItemTypeP->isFieldIndexValid(aFieldIndex)) return false; // invalid index | |||
660 | TItemField *fiP = fFieldsP[aFieldIndex]; | |||
661 | if (!fiP) return false; // field object does not exist | |||
662 | // return if field object is assigned | |||
663 | return fiP->isAssigned(); | |||
664 | } // TMultiFieldItem::isAssigned | |||
665 | ||||
666 | ||||
667 | // field availability (combined source & target) | |||
668 | bool TMultiFieldItem::isAvailable(const char *aFieldName) | |||
669 | { | |||
670 | return isAvailable(fItemTypeP->getFieldIndex(aFieldName)); | |||
671 | } // TMultiFieldItem::isAvailable | |||
672 | ||||
673 | ||||
674 | // field availability (combined source & target) | |||
675 | bool TMultiFieldItem::isAvailable(sInt16 aFieldIndex) | |||
676 | { | |||
677 | if (fItemTypeP && fTargetItemTypeP) { | |||
678 | if (!fItemTypeP->isFieldIndexValid(aFieldIndex)) return false; // invalid index | |||
679 | return | |||
680 | fItemTypeP->getFieldOptions(aFieldIndex)->available && | |||
681 | fTargetItemTypeP->getFieldOptions(aFieldIndex)->available; | |||
682 | } | |||
683 | else | |||
684 | return false; // source or target missing, not available | |||
685 | } // TMultiFieldItem::isAvailable | |||
686 | ||||
687 | ||||
688 | bool TMultiFieldItem::knowsRemoteFieldOptions(void) | |||
689 | { | |||
690 | // knows them if either myself or the other side has received devInf | |||
691 | // (depends: received item has it in its own type, to be sent one in the target type) | |||
692 | return | |||
693 | (fItemTypeP && fItemTypeP->hasReceivedFieldOptions()) || | |||
694 | (fTargetItemTypeP && fTargetItemTypeP->hasReceivedFieldOptions()); | |||
695 | } // TMultiFieldItem::knowsRemoteFieldOptions | |||
696 | ||||
697 | ||||
698 | ||||
699 | // make sure that all fields that are available in source and target are | |||
700 | // assigned at least an empty value | |||
701 | void TMultiFieldItem::assignAvailables(void) | |||
702 | { | |||
703 | if (fFieldDefinitionsP) { | |||
704 | for (sInt16 k=0; k<fFieldDefinitionsP->numFields(); k++) { | |||
705 | if (isAvailable(k)) { | |||
706 | TItemField *fldP=getField(k); // force creation | |||
707 | if (fldP) { | |||
708 | // make sure it is assigned a "empty" value | |||
709 | if (fldP->isUnassigned()) | |||
710 | fldP->assignEmpty(); | |||
711 | } | |||
712 | } | |||
713 | } | |||
714 | } | |||
715 | } // TMultiFieldItem::assignAvailables | |||
716 | ||||
717 | ||||
718 | ||||
719 | ||||
720 | // cast pointer to same type, returns NULL if incompatible | |||
721 | TMultiFieldItem *TMultiFieldItem::castToSameTypeP(TSyncItem *aItemP) | |||
722 | { | |||
723 | if (aItemP->isBasedOn(ity_multifield)) { | |||
724 | TMultiFieldItem *multifielditemP=static_cast<TMultiFieldItem *> (aItemP); | |||
725 | // class compatible, now test type compatibility | |||
726 | // - field definition list must be the same instance(!) in both items | |||
727 | if (fFieldDefinitionsP==multifielditemP->fFieldDefinitionsP) | |||
728 | return multifielditemP; | |||
729 | else | |||
730 | return NULL__null; | |||
731 | } | |||
732 | // not even class compatible | |||
733 | return NULL__null; | |||
734 | } // TMultiFieldItem::castToSameTypeP | |||
735 | ||||
736 | ||||
737 | // test if comparable (at least for equality) | |||
738 | bool TMultiFieldItem::comparable(TSyncItem &aItem) | |||
739 | { | |||
740 | // test if comparable: other type must be same type of multifield | |||
741 | return castToSameTypeP(&aItem)!=NULL__null; | |||
742 | } // TMultiFieldItem::comparable | |||
743 | ||||
744 | ||||
745 | // test if sortable (by age, newer are > than older) | |||
746 | bool TMultiFieldItem::sortable(TSyncItem &aItem) | |||
747 | { | |||
748 | if (!fFieldDefinitionsP->fAgeSortable) return false; // not sortable at all | |||
749 | if (comparable(aItem)) { | |||
750 | // item is comparable (has same FieldDefinitions) | |||
751 | // Now check if all ageRelevant fields are assigned on both sides | |||
752 | // Note: we can static-cast here because comparable() has verified aItem's type | |||
753 | TMultiFieldItem *multifielditemP=static_cast<TMultiFieldItem *> (&aItem); | |||
754 | // search for ageRelevant fields | |||
755 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
756 | if (fFieldDefinitionsP->fFields[i].ageRelevant) { | |||
757 | // check if available for both types | |||
758 | if (!( | |||
759 | isAssigned(i) && // my own | |||
760 | multifielditemP->isAssigned(i) // aItem's | |||
761 | )) | |||
762 | return false; // missing needed field on one side | |||
763 | } | |||
764 | } | |||
765 | return true; // all ageRelevant fields are assigned in both sides | |||
766 | } | |||
767 | else return false; // not comparable is not sortable either | |||
768 | } // TMultiFieldItem::sortable | |||
769 | ||||
770 | ||||
771 | #ifdef OBJECT_FILTERING1 | |||
772 | ||||
773 | ||||
774 | // check post-fetch filter | |||
775 | bool TMultiFieldItem::postFetchFiltering(TLocalEngineDS *aDatastoreP) | |||
776 | { | |||
777 | return fItemTypeP->postFetchFiltering(this,aDatastoreP); | |||
778 | } // TMultiFieldItem::postFetchFiltering | |||
779 | ||||
780 | ||||
781 | // test if item passes filter | |||
782 | bool TMultiFieldItem::testFilter(const char *aFilterString) | |||
783 | { | |||
784 | // process filter without modifying | |||
785 | #ifdef SYDEBUG2 | |||
786 | PDEBUGPRINTFX(DBG_DATA+DBG_FILTER+DBG_HOT,({ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Testing filter '%s' against item:", aFilterString ); } | |||
787 | "Testing filter '%s' against item:",{ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Testing filter '%s' against item:", aFilterString ); } | |||
788 | aFilterString{ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Testing filter '%s' against item:", aFilterString ); } | |||
789 | )){ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Testing filter '%s' against item:", aFilterString ); }; | |||
790 | if (*aFilterString && PDEBUGTEST(DBG_DATA+DBG_FILTER+DBG_USERDATA)(((0x00000080 +0x08000000 +0x01000000) & getDbgMask()) == (0x00000080 +0x08000000 +0x01000000))) { | |||
791 | debugShowItem(DBG_DATA0x00000080+DBG_FILTER0x08000000); | |||
792 | } | |||
793 | #endif | |||
794 | bool result=processFilter(false,aFilterString); | |||
795 | PDEBUGPRINTFX(DBG_DATA+DBG_FILTER+DBG_HOT,({ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Filter test result is %s", result ? "TRUE" : "FALSE" ); } | |||
796 | "Filter test result is %s",{ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Filter test result is %s", result ? "TRUE" : "FALSE" ); } | |||
797 | result ? "TRUE" : "FALSE"{ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Filter test result is %s", result ? "TRUE" : "FALSE" ); } | |||
798 | )){ if (((0x00000080 +0x08000000 +0x00000001) & getDbgMask( )) == (0x00000080 +0x08000000 +0x00000001)) getDbgLogger()-> setNextMask(0x00000080 +0x08000000 +0x00000001).DebugPrintfLastMask ( "Filter test result is %s", result ? "TRUE" : "FALSE" ); }; | |||
799 | // false on syntax error | |||
800 | if (*aFilterString) { | |||
801 | PDEBUGPRINTFX(DBG_ERROR,("unexpected chars in filter expression: %s",aFilterString)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("unexpected chars in filter expression: %s" ,aFilterString); }; | |||
802 | return false; | |||
803 | } | |||
804 | return result; | |||
805 | } // TMultiFieldItem::testFilter | |||
806 | ||||
807 | ||||
808 | // make item pass filter | |||
809 | bool TMultiFieldItem::makePassFilter(const char *aFilterString) | |||
810 | { | |||
811 | // process filter with making modifications such that item passes filter condition | |||
812 | bool result = processFilter(true,aFilterString); | |||
813 | // false on syntax error | |||
814 | if (*aFilterString) { | |||
815 | PDEBUGPRINTFX(DBG_ERROR+DBG_FILTER,("unexpected chars in filter expression: %s",aFilterString)){ if (((0x00000002 +0x08000000) & getDbgMask()) == (0x00000002 +0x08000000)) getDbgLogger()->setNextMask(0x00000002 +0x08000000 ).DebugPrintfLastMask ("unexpected chars in filter expression: %s" ,aFilterString); }; | |||
816 | return false; | |||
817 | } | |||
818 | return result; | |||
819 | } // TMultiFieldItem::makePassFilter | |||
820 | ||||
821 | ||||
822 | // process filter expression | |||
823 | bool TMultiFieldItem::processFilter(bool aMakePass, const char *&aPos, const char *aStop, sInt16 aLastOpPrec) | |||
824 | { | |||
825 | char c=0; | |||
826 | const char *st; | |||
827 | string str; | |||
828 | bool result; | |||
829 | sInt16 fid; | |||
830 | sInt16 cmpres=0; | |||
831 | bool neg; | |||
832 | bool assignToMakeTrue; | |||
833 | bool specialValue; | |||
834 | bool caseinsensitive; | |||
835 | TStringField idfield; | |||
836 | TItemField *fldP; | |||
837 | ||||
838 | // determine max length | |||
839 | if (aStop==NULL__null) aStop=aPos+strlen(aPos); | |||
| ||||
840 | // empty expression is true | |||
841 | result=true; | |||
842 | // process simple term (<ident><op><value>) | |||
843 | // Note: do not allow negation to make sure | |||
844 | // that TRUE of a comparison always | |||
845 | // adds to TRUE of the entire expression | |||
846 | neg=false; // not negated | |||
847 | // - get first non-space | |||
848 | while (aPos<=aStop) { | |||
849 | c=*aPos; | |||
850 | if (c!=' ') break; | |||
851 | aPos++; | |||
852 | } | |||
853 | // Term starts here, first char is c, aPos points to it | |||
854 | // - check subexpression paranthesis | |||
855 | if (c=='(') { | |||
856 | // boolean term is grouped subexpression, don't stop at logical operation | |||
857 | aPos++; | |||
858 | result=processFilter(aMakePass,aPos,aStop,0); // dont stop at any logical operator | |||
859 | // check if matching paranthesis | |||
860 | if (*(aPos++)!=')') { | |||
861 | PDEBUGPRINTFX(DBG_ERROR+DBG_FILTER,("Filter expression error (missing \")\") at: %s",--aPos)){ if (((0x00000002 +0x08000000) & getDbgMask()) == (0x00000002 +0x08000000)) getDbgLogger()->setNextMask(0x00000002 +0x08000000 ).DebugPrintfLastMask ("Filter expression error (missing \")\") at: %s" ,--aPos); }; | |||
862 | //%%% no, don't skip rest, as otherwise caller will not know that filter processing failed!%%% aPos=aStop; // skip rest | |||
863 | return false; // always fail | |||
864 | } | |||
865 | if (neg) result=!result; | |||
866 | } | |||
867 | else if (c==0) { | |||
868 | // empty term, counts as true | |||
869 | return result; | |||
870 | } | |||
871 | else { | |||
872 | // must be simple boolean term | |||
873 | // - remember start of ident | |||
874 | st=aPos; | |||
875 | // - search end of ident | |||
876 | while (isFilterIdent(c)(isalnum(c) || c=='_' || c=='.')) c=*(++aPos); | |||
877 | // - c/aPos=char after ident, get ident | |||
878 | str.assign(st,aPos-st); | |||
879 | // - check for subscript index | |||
880 | uInt16 subsIndex=0; // no index (index is 1-based in DS 1.2 filter specs) | |||
881 | if (c=='[') { | |||
882 | // expect numeric index | |||
883 | aPos++; // next | |||
884 | aPos+=StrToUShort(aPos,subsIndex); | |||
885 | if (*aPos!=']') { | |||
886 | PDEBUGPRINTFX(DBG_ERROR+DBG_FILTER,("Filter expression error (missing \"]\") at: %s",--aPos)){ if (((0x00000002 +0x08000000) & getDbgMask()) == (0x00000002 +0x08000000)) getDbgLogger()->setNextMask(0x00000002 +0x08000000 ).DebugPrintfLastMask ("Filter expression error (missing \"]\") at: %s" ,--aPos); }; | |||
887 | return false; // syntax error, does not pass | |||
888 | } | |||
889 | c=*(++aPos); // process next after subscript | |||
890 | } | |||
891 | // - get field ID for that ident (can be -1 if none found) | |||
892 | // - check special idents first | |||
893 | if (str=="LUID") { | |||
894 | // this is SyncML-TAF Standard | |||
895 | // it is also produced by DS 1.2 &LUID; pseudo-identifier | |||
896 | if (IS_CLIENT(!getSyncAppBase()->isServer())) | |||
897 | idfield.setAsString(getLocalID()); | |||
898 | else | |||
899 | idfield.setAsString(getRemoteID()); | |||
900 | fldP=&idfield; | |||
901 | } | |||
902 | else if (str=="LOCALID") { | |||
903 | // this is a Synthesis extension | |||
904 | idfield.setAsString(getLocalID()); | |||
905 | fldP=&idfield; | |||
906 | } | |||
907 | #ifdef SYSYNC_SERVER1 | |||
908 | else if (IS_SERVER(getSyncAppBase()->isServer()) && str=="GUID") { | |||
909 | // this is a Synthesis extension, added for symmetry to LUID | |||
910 | idfield.setAsString(getLocalID()); | |||
911 | fldP=&idfield; | |||
912 | } | |||
913 | else if (IS_SERVER(getSyncAppBase()->isServer()) && str=="REMOTEID") { | |||
914 | // this is a Synthesis extension | |||
915 | idfield.setAsString(getRemoteID()); | |||
916 | fldP=&idfield; | |||
917 | } | |||
918 | #endif // SYSYNC_SERVER | |||
919 | else { | |||
920 | // must be a field | |||
921 | if (fItemTypeP) | |||
922 | fid=fItemTypeP->getFilterIdentifierFieldIndex(str.c_str(),subsIndex); | |||
923 | else | |||
924 | fid=VARIDX_UNDEFINED-128; // none | |||
925 | // now get field pointer (or NULL if field not found) | |||
926 | fldP = getField(fid); | |||
927 | } | |||
928 | // - skip spaces | |||
929 | while (isspace(c)) c=*(++aPos); | |||
930 | // - check for makepass-assignment modifier ":" | |||
931 | assignToMakeTrue=c==':'; | |||
932 | if (assignToMakeTrue) c=*(++aPos); | |||
933 | // - check for special-value modifier "*" | |||
934 | specialValue=c=='*'; | |||
935 | if (specialValue) c=*(++aPos); | |||
936 | // - check for case-insensitive comparison mode | |||
937 | caseinsensitive=c=='^'; | |||
938 | if (caseinsensitive) c=*(++aPos); | |||
939 | // - now find comparison mode | |||
940 | // cmpres = expected strcmp-style result: | |||
941 | // 0 if equal, 1 if ident > value, -1 if ident < value, | |||
942 | aPos++; // consume first char of comparison anyway | |||
943 | if (c=='%') { cmpres=2; } // special flag for CONTAINS | |||
944 | else if (c=='$') { cmpres=2; neg=!neg; } | |||
945 | else if (c=='=') { cmpres=0; } // equal | |||
946 | else if (c=='>') { | |||
947 | if (*aPos=='=') { aPos++; neg=!neg; cmpres=-1; } // >= is not < | |||
948 | else { cmpres=1; } // > | |||
949 | } | |||
950 | else if (c=='<') { | |||
951 | if (*aPos=='>') { aPos++; neg=!neg; cmpres=0; } // <> is not = | |||
952 | else if (*aPos=='=') { aPos++; neg=!neg; cmpres=1; } // <= is not > | |||
953 | else { cmpres=-1; } // < | |||
954 | } | |||
955 | // - now read value | |||
956 | st=aPos; // should start here | |||
957 | // - find end (end of string, closing paranthesis or logical op) | |||
958 | while (aPos<aStop && *aPos!='&' && *aPos!='|' && *aPos!=')') aPos++; | |||
959 | // - assign st string | |||
960 | str.assign(st,aPos-st); | |||
961 | // - check field | |||
962 | if (!fldP) { | |||
963 | // field does not exist -> result of term, negated or not, is always FALSE | |||
964 | result=false; | |||
965 | } | |||
966 | else { | |||
967 | if (cmpres==2) { | |||
968 | // "contains" | |||
969 | // - create a reference field | |||
970 | TItemField *valfldP = newItemField(fldP->getElementType(),getSessionZones()); | |||
971 | // - assign value as string | |||
972 | valfldP->setAsString(str.c_str()); | |||
973 | result = fldP->contains(*valfldP,caseinsensitive); | |||
974 | // assign to make pass if enabled | |||
975 | if (!result && aMakePass && assignToMakeTrue) { | |||
976 | if (fldP->isArray()) | |||
977 | fldP->append(*valfldP); // just append another element to make it contained | |||
978 | else | |||
979 | *fldP = *valfldP; // just overwrite value with to-be-contained value | |||
980 | result=true; // now passes | |||
981 | } | |||
982 | delete valfldP; // no longer needed | |||
983 | } | |||
984 | else { | |||
985 | if (specialValue) { | |||
986 | if (cmpres!=0) | |||
987 | result=false; // can only compare for equal | |||
988 | else { | |||
989 | if (str=="E") { | |||
990 | // empty | |||
991 | result = fldP->isEmpty(); | |||
992 | } | |||
993 | else if (str=="N") { | |||
994 | // NULL, unassigned | |||
995 | result = fldP->isAssigned(); | |||
996 | } | |||
997 | if (neg) result=!result; | |||
998 | // make empty or unassigned to pass filter (make non-empty is not possible) | |||
999 | if (!result && aMakePass && assignToMakeTrue && !neg) { | |||
1000 | if (str=="E") { | |||
1001 | fldP->assignEmpty(); | |||
1002 | } | |||
1003 | else if (str=="N") { | |||
1004 | fldP->unAssign(); | |||
1005 | } | |||
1006 | result=true; // now passes | |||
1007 | } | |||
1008 | } | |||
1009 | } | |||
1010 | else { | |||
1011 | // create a reference field | |||
1012 | TItemField *valfldP = newItemField(fldP->getElementType(),getSessionZones()); | |||
1013 | // assign value as string | |||
1014 | valfldP->setAsString(str.c_str()); | |||
1015 | // compare fields, then compare result with what was expected | |||
1016 | result = (fldP->compareWith(*valfldP,caseinsensitive) == cmpres); | |||
1017 | // negate result if needed | |||
1018 | if (neg) result=!result; | |||
1019 | // if field not assigned, comparison is always false | |||
1020 | if (fldP->isUnassigned()) result=false; | |||
1021 | // assign to make pass if enabled | |||
1022 | if (!result && aMakePass && assignToMakeTrue) { | |||
1023 | (*fldP) = (*valfldP); | |||
1024 | result=true; // now passes | |||
1025 | } | |||
1026 | // now clear again | |||
1027 | delete valfldP; | |||
1028 | } | |||
1029 | } | |||
1030 | } | |||
1031 | } | |||
1032 | // term is now evaluated, show what follows | |||
1033 | // - check for boolean op chain, aPos points now to possible logical operator | |||
1034 | do { | |||
1035 | // - skip spaces | |||
1036 | c=*aPos; | |||
1037 | while (c==' ') c=*(++aPos); | |||
1038 | // - check char at aPos | |||
1039 | if (c=='&') { | |||
1040 | // AND | |||
1041 | if (2<=aLastOpPrec) return result; // evaluation continues in caller (always as long as we don't have higher prec than AND) | |||
1042 | // skip op | |||
1043 | aPos++; | |||
1044 | // next term must be true as well | |||
1045 | // - return when encountering AND, OR and end of expression | |||
1046 | // - next term must also be modified to make pass | |||
1047 | bool termres = processFilter(aMakePass, aPos, aStop, 2); | |||
1048 | result = result && termres; | |||
1049 | } | |||
1050 | else if (c=='|') { | |||
1051 | // OR | |||
1052 | if (1<=aLastOpPrec) return result; // evaluation continues in caller | |||
1053 | // skip op | |||
1054 | aPos++; | |||
1055 | // next term must be true only if this one is not true | |||
1056 | // - return only when encountering AND or | |||
1057 | // - if first term is already true, next must never be modifed to pass | |||
1058 | bool termres = processFilter(result ? false : aMakePass, aPos, aStop, 1); | |||
1059 | result = result || termres; | |||
1060 | } | |||
1061 | else { | |||
1062 | // End of Expression | |||
1063 | // would be: if (0<=aLastOpPrec) | |||
1064 | return result; | |||
1065 | } | |||
1066 | } while(true); | |||
1067 | } | |||
1068 | ||||
1069 | #endif | |||
1070 | ||||
1071 | #ifdef SYSYNC_SERVER1 | |||
1072 | ||||
1073 | // compare function, returns 0 if equal, 1 if this > aItem, -1 if this < aItem | |||
1074 | sInt16 TMultiFieldItem::compareWith( | |||
1075 | TSyncItem &aItem, | |||
1076 | TEqualityMode aEqMode, | |||
1077 | TLocalEngineDS *aDatastoreP | |||
1078 | #ifdef SYDEBUG2 | |||
1079 | ,bool aDebugShow | |||
1080 | #endif | |||
1081 | ) | |||
1082 | { | |||
1083 | #ifndef SYDEBUG2 | |||
1084 | const aDebugShow = false; | |||
1085 | #endif | |||
1086 | sInt16 cmpres; | |||
1087 | TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem); | |||
1088 | if (!multifielditemP) { | |||
1089 | cmpres = SYSYNC_NOT_COMPARABLE-999; | |||
1090 | goto exit; | |||
1091 | } | |||
1092 | // do the compare | |||
1093 | if (fItemTypeP) | |||
1094 | cmpres=fItemTypeP->compareItems(*this,*multifielditemP,aEqMode,aDebugShow,aDatastoreP); | |||
1095 | else | |||
1096 | cmpres=standardCompareWith(*multifielditemP,aEqMode,aDebugShow); | |||
1097 | exit: | |||
1098 | #ifdef SYDEBUG2 | |||
1099 | if (aDebugShow) { | |||
1100 | OBJDEBUGPRINTFX(getItemType()->getSession(),DBG_DATA,({ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1101 | "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd",{ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1102 | getLocalID(),{ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1103 | getRemoteID(),{ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1104 | aItem.getLocalID(),{ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1105 | aItem.getRemoteID(),{ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1106 | (sInt16) aEqMode,{ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1107 | cmpres{ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); } | |||
1108 | )){ if ((getItemType()->getSession()) && (((0x00000080 ) & (getItemType()->getSession())->getDbgMask()) == (0x00000080))) (getItemType()->getSession())->getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd" , getLocalID(), getRemoteID(), aItem.getLocalID(), aItem.getRemoteID (), (sInt16) aEqMode, cmpres ); }; | |||
1109 | } | |||
1110 | #endif | |||
1111 | return cmpres; | |||
1112 | } // TMultiFieldItem::compareWith | |||
1113 | ||||
1114 | ||||
1115 | // compare function, returns 0 if equal, 1 if this > aItem, -1 if this < aItem | |||
1116 | sInt16 TMultiFieldItem::standardCompareWith( | |||
1117 | TMultiFieldItem &aItem, | |||
1118 | TEqualityMode aEqMode, | |||
1119 | bool aDebugShow | |||
1120 | ) | |||
1121 | { | |||
1122 | sInt16 commonfound=0; | |||
1123 | sInt16 result=0; // default to equal | |||
1124 | // we should test for comparable() before! | |||
1125 | if (!comparable(aItem)) { | |||
1126 | result=SYSYNC_NOT_COMPARABLE-999; | |||
1127 | goto exit; | |||
1128 | } | |||
1129 | // now compare field-by-field | |||
1130 | // - equal means equality of all eqRelevant fields (both non-existing is | |||
1131 | // equality, too) | |||
1132 | // (but possibly differences in ageRelevant fields) | |||
1133 | // - larger/smaller means not equal in eqRelevant fields but | |||
1134 | // older/newer by ageRelevant fields | |||
1135 | // - SYSYNC_NOT_COMPARABLE means not equal and not ageSortable either | |||
1136 | if (aEqMode!=eqm_nocompare) { | |||
1137 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
1138 | // both fields must be available in their respective ItemType | |||
1139 | if (!getItemType()->getFieldOptions(i)->available || | |||
1140 | !aItem.getItemType()->getFieldOptions(i)->available) | |||
1141 | continue; // not available in both items, do not compare | |||
1142 | // then test for equality (if relevant in given context) | |||
1143 | if (fFieldDefinitionsP->fFields[i].eqRelevant>=aEqMode) { | |||
1144 | // at least one is available and relevant in both items | |||
1145 | commonfound++; | |||
1146 | // this is an EQ-relevant field | |||
1147 | // - get fields | |||
1148 | TItemField &f1=getFieldRef(i); | |||
1149 | TItemField &f2=aItem.getFieldRef(i); | |||
1150 | // - For slowsync and firstsync matching, non-ASSIGNED fields will not | |||
1151 | // be compared (to allow matching a less-equipped clinet record with its | |||
1152 | // better equipped server record and vice versa. Example is the S55 | |||
1153 | // which discards private addresses, old method rendered lots of | |||
1154 | // duplicates on slow sync | |||
1155 | if (aEqMode>=eqm_slowsync) { | |||
1156 | if (f1.isUnassigned() || f2.isUnassigned()) | |||
1157 | continue; // omit comparing fields where one side is unassigned | |||
1158 | } | |||
1159 | // - get assigned status of both fields | |||
1160 | // BCPPB revealed bad error: isAssigned was not called (forgot ())!!! | |||
1161 | // %%% Note: I think that isAssigned() is the wrong function here, we will use | |||
1162 | // !isEmpty(), which returns true for unassigned fields as well as for empty ones | |||
1163 | //bool a1 = f1.isAssigned(); // assignment status of field in this item | |||
1164 | //bool a2 = f2.isAssigned(); // assignment status of same field in other item | |||
1165 | bool a1 = !f1.isEmpty(); // non-empty status of field in this item | |||
1166 | bool a2 = !f2.isEmpty(); // non-empty status of same field in other item | |||
1167 | // - if both are unassigned -> equal | |||
1168 | if (!a1 && !a2) continue; // we are staying equal, test next field | |||
1169 | // - if one of them is unassigned -> not equal | |||
1170 | // - if both are assigned, fields must be equal | |||
1171 | if (!a1 || !a2) { | |||
1172 | // one not assigned | |||
1173 | result=SYSYNC_NOT_COMPARABLE-999; | |||
1174 | #ifdef SYDEBUG2 | |||
1175 | if (aDebugShow) { | |||
1176 | // not assigned | |||
1177 | if (!a1) { | |||
1178 | PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("- not equal because fid=%hd not assigned/empty in this item",i)){ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ("- not equal because fid=%hd not assigned/empty in this item" ,i); }; | |||
1179 | } else if (!a2) { | |||
1180 | PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("- not equal because fid=%hd not assigned/empty in other item",i)){ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ("- not equal because fid=%hd not assigned/empty in other item" ,i); }; | |||
1181 | } | |||
1182 | } | |||
1183 | #endif | |||
1184 | break; | |||
1185 | } | |||
1186 | else if (f1 != f2) { | |||
1187 | // content not equal | |||
1188 | #ifdef SYDEBUG2 | |||
1189 | string ds; | |||
1190 | if (aDebugShow) { | |||
1191 | // assigned but not equal | |||
1192 | PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("- not equal because fid=%hd not same in both items:",i)){ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ("- not equal because fid=%hd not same in both items:" ,i); }; | |||
1193 | getField(i)->getAsString(ds); | |||
1194 | PDEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_USERDATA,({ if (((0x00000080 +0x10000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x10000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x10000000 +0x01000000).DebugPrintfLastMask ( "- this item : '%-.1000s'",ds.c_str() ); } | |||
1195 | "- this item : '%-.1000s'",ds.c_str(){ if (((0x00000080 +0x10000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x10000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x10000000 +0x01000000).DebugPrintfLastMask ( "- this item : '%-.1000s'",ds.c_str() ); } | |||
1196 | )){ if (((0x00000080 +0x10000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x10000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x10000000 +0x01000000).DebugPrintfLastMask ( "- this item : '%-.1000s'",ds.c_str() ); }; | |||
1197 | aItem.getField(i)->getAsString(ds); | |||
1198 | PDEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_USERDATA,({ if (((0x00000080 +0x10000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x10000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x10000000 +0x01000000).DebugPrintfLastMask ( "- other item : '%-.1000s'",ds.c_str() ); } | |||
1199 | "- other item : '%-.1000s'",ds.c_str(){ if (((0x00000080 +0x10000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x10000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x10000000 +0x01000000).DebugPrintfLastMask ( "- other item : '%-.1000s'",ds.c_str() ); } | |||
1200 | )){ if (((0x00000080 +0x10000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x10000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x10000000 +0x01000000).DebugPrintfLastMask ( "- other item : '%-.1000s'",ds.c_str() ); }; | |||
1201 | PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,({ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- thisItem.CompareWith(otherItem) = %hd" , getFieldRef(i).compareWith(aItem.getFieldRef(i)) ); } | |||
1202 | "- thisItem.CompareWith(otherItem) = %hd",{ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- thisItem.CompareWith(otherItem) = %hd" , getFieldRef(i).compareWith(aItem.getFieldRef(i)) ); } | |||
1203 | getFieldRef(i).compareWith(aItem.getFieldRef(i)){ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- thisItem.CompareWith(otherItem) = %hd" , getFieldRef(i).compareWith(aItem.getFieldRef(i)) ); } | |||
1204 | )){ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- thisItem.CompareWith(otherItem) = %hd" , getFieldRef(i).compareWith(aItem.getFieldRef(i)) ); }; | |||
1205 | } | |||
1206 | #endif | |||
1207 | // now check for cut-off situation | |||
1208 | sInt32 s1=getItemType()->getFieldOptions(i)->maxsize; | |||
1209 | sInt32 s2=aItem.getItemType()->getFieldOptions(i)->maxsize; | |||
1210 | // Note: (2002-12-01) do not actually use size, as it will probably not be accurate enough, | |||
1211 | // but always pass FIELD_OPT_MAXSIZE_UNKNOWN. | |||
1212 | if (s1!=FIELD_OPT_MAXSIZE_NONE0) s1=FIELD_OPT_MAXSIZE_UNKNOWN-1; | |||
1213 | if (s2!=FIELD_OPT_MAXSIZE_NONE0) s2=FIELD_OPT_MAXSIZE_UNKNOWN-1; | |||
1214 | // Now check short versions | |||
1215 | if (f1.isShortVers(f2,s2) || f2.isShortVers(f1,s1)) { | |||
1216 | // cutoff detected, counts as equal | |||
1217 | #ifdef SYDEBUG2 | |||
1218 | if (aDebugShow) { | |||
1219 | PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,({ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- Cutoff detected, field considered equal, maxsize(thisitem)=%ld, maxsize(otheritem)=%ld" , (long)s1,(long)s2 ); } | |||
1220 | "- Cutoff detected, field considered equal, maxsize(thisitem)=%ld, maxsize(otheritem)=%ld",{ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- Cutoff detected, field considered equal, maxsize(thisitem)=%ld, maxsize(otheritem)=%ld" , (long)s1,(long)s2 ); } | |||
1221 | (long)s1,(long)s2{ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- Cutoff detected, field considered equal, maxsize(thisitem)=%ld, maxsize(otheritem)=%ld" , (long)s1,(long)s2 ); } | |||
1222 | )){ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ( "- Cutoff detected, field considered equal, maxsize(thisitem)=%ld, maxsize(otheritem)=%ld" , (long)s1,(long)s2 ); }; | |||
1223 | } | |||
1224 | #endif | |||
1225 | } | |||
1226 | else { | |||
1227 | // no cutoff, not equal | |||
1228 | result=SYSYNC_NOT_COMPARABLE-999; | |||
1229 | break; | |||
1230 | } | |||
1231 | } // else if not equal | |||
1232 | } // if eq-relevant | |||
1233 | } // for all fields | |||
1234 | } // if EQ-compare at all | |||
1235 | if (!commonfound) result=SYSYNC_NOT_COMPARABLE-999; | |||
1236 | // if not equal, try to compare age (if age-sortable item at all) | |||
1237 | if (result!=0 && fFieldDefinitionsP->fAgeSortable) { | |||
1238 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
1239 | // then test for age (if relevant) | |||
1240 | if (fFieldDefinitionsP->fFields[i].ageRelevant) { | |||
1241 | // this is an age relevant field | |||
1242 | // - get assigned status of both fields | |||
1243 | bool a1 = isAssigned(i); // assignment status of field in this item | |||
1244 | bool a2 = aItem.isAssigned(i); // assignment status of same field in other item | |||
1245 | // - if both are unassigned -> cannot decide, continue | |||
1246 | if (!a1 && !a2) continue; // test next field | |||
1247 | // - if one of them is unassigned -> not age-comparable | |||
1248 | if (!a1 || !a2) { | |||
1249 | result=SYSYNC_NOT_COMPARABLE-999; | |||
1250 | goto exit; | |||
1251 | } | |||
1252 | // - if both are assigned, return field comparison value | |||
1253 | result=getFieldRef(i).compareWith(aItem.getFieldRef(i)); | |||
1254 | if (result!=0) { | |||
1255 | // not equal, newer item determined | |||
1256 | goto exit; | |||
1257 | } | |||
1258 | // continue to resolve age with next fields | |||
1259 | } | |||
1260 | } | |||
1261 | // no age relevant fields or all age relevant fields equal (possibly all unassigned) | |||
1262 | result=SYSYNC_NOT_COMPARABLE-999; | |||
1263 | } | |||
1264 | // done | |||
1265 | exit: | |||
1266 | return result; | |||
1267 | } // TMultiFieldItem::standardCompareWith | |||
1268 | ||||
1269 | #endif // server only | |||
1270 | ||||
1271 | ||||
1272 | ||||
1273 | // update dependencies of fields (such as BLOB proxies) on localID | |||
1274 | void TMultiFieldItem::updateLocalIDDependencies(void) | |||
1275 | { | |||
1276 | const char *localid = getLocalID(); | |||
1277 | // go through all fields | |||
1278 | for (sInt16 k=0; k<fFieldDefinitionsP->numFields(); k++) { | |||
1279 | TItemField *fldP = getField(k); | |||
1280 | for (sInt16 i=0; i<fldP->arraySize(); i++) { | |||
1281 | TItemField *leaffldP = fldP->getArrayField(i); | |||
1282 | if (leaffldP) leaffldP->setParentLocalID(localid); | |||
1283 | } | |||
1284 | } | |||
1285 | } // TMultiFieldItem::updateLocalIDDependencies | |||
1286 | ||||
1287 | ||||
1288 | ||||
1289 | ||||
1290 | /// @brief replace data contents from specified item | |||
1291 | /// @param aAvailableOnly: only replace contents actually available in aItem, leave rest untouched | |||
1292 | /// NOTE: this was changed slightly between 1.x.8.5 and 1.x.8.6: | |||
1293 | /// If the source type has not received devinf saying which fields are available, | |||
1294 | /// only fields that are actually ASSIGNED are written. If aAssignedOnly is | |||
1295 | /// additionally set, only assigned fields will be written anyway. | |||
1296 | /// @param aDetectCutOffs: use field's maxsize specs to detect contents cut off by limited field | |||
1297 | /// lengths and do not replace data if target is equal with source up to field length | |||
1298 | /// @param aAssignedOnly: just copy assigned fields (no check for availability) | |||
1299 | /// @param aTransferUnassigned: transfer unassigned status from source item (i.e. unassign those | |||
1300 | /// in target that are unassigned in source, no check for availability) | |||
1301 | bool TMultiFieldItem::replaceDataFrom(TSyncItem &aItem, bool aAvailableOnly, bool aDetectCutoffs, bool aAssignedOnly, bool aTransferUnassigned) | |||
1302 | { | |||
1303 | TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem); | |||
1304 | if (!multifielditemP) return false; | |||
1305 | // ok, same type, copy data | |||
1306 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
1307 | if ( | |||
1308 | !aAvailableOnly || ( | |||
1309 | !aAssignedOnly && // availability is relevant only if not aAssignedOnly | |||
1310 | multifielditemP->fItemTypeP->hasReceivedFieldOptions() && | |||
1311 | multifielditemP->fItemTypeP->getFieldOptions(i)->available | |||
1312 | ) | |||
1313 | || multifielditemP->isAssigned(i) | |||
1314 | ) { | |||
1315 | // copy field | |||
1316 | sInt32 siz=multifielditemP->fItemTypeP->getFieldOptions(i)->maxsize; | |||
1317 | if (aDetectCutoffs && siz!=FIELD_OPT_MAXSIZE_NONE0) { | |||
1318 | // check if source fields's content is fully contained at beginning of | |||
1319 | // target string, if yes, don't do anything | |||
1320 | // Note: (2002-12-01) do not actually use size, as it will probably not be accurate enough, | |||
1321 | // but always pass FIELD_OPT_MAXSIZE_UNKNOWN. | |||
1322 | if (getFieldRef(i).isShortVers(multifielditemP->getFieldRef(i),FIELD_OPT_MAXSIZE_UNKNOWN-1)) { | |||
1323 | // yes, we think that this is a cut-off, | |||
1324 | // so leave target untouched as it has more complete version of this field | |||
1325 | continue; | |||
1326 | } | |||
1327 | } | |||
1328 | // copy source field into this target field | |||
1329 | getFieldRef(i)=multifielditemP->getFieldRef(i); | |||
1330 | } | |||
1331 | else if (aTransferUnassigned && !multifielditemP->isAssigned(i)) { | |||
1332 | // explicitly transfer unassigned status | |||
1333 | // Note: this is useful in read-modify-write done exclusively for cutoff prevention, | |||
1334 | // as it prevents re-writing fields that were not actually transmitted from the remote | |||
1335 | // (i.e. no get-from-DB-and-write-same-value-back). Might be essential in case of special | |||
1336 | // fields where the datastore MUST know if these were sent with the data, like FN in pocketpc) | |||
1337 | getFieldRef(i).unAssign(); | |||
1338 | } | |||
1339 | } | |||
1340 | return true; | |||
1341 | } // TMultiFieldItem::replaceDataFrom | |||
1342 | ||||
1343 | ||||
1344 | // check item before processing it | |||
1345 | bool TMultiFieldItem::checkItem(TLocalEngineDS *aDatastoreP) | |||
1346 | { | |||
1347 | return fItemTypeP->checkItem(*this,aDatastoreP); | |||
1348 | } // TMultiFieldItem::checkItem | |||
1349 | ||||
1350 | ||||
1351 | #ifdef SYSYNC_SERVER1 | |||
1352 | ||||
1353 | // merge this item with specified item. | |||
1354 | // Notes: | |||
1355 | // - specified item is treated as loosing item, this item is winning item | |||
1356 | // - also updates other item to make sure it is equal to the winning after the merge | |||
1357 | // sets (but does not reset) change status of this and other item. | |||
1358 | // Note that changes of non-relevant fields are not reported here. | |||
1359 | void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP, int mode) | |||
1360 | { | |||
1361 | TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem); | |||
1362 | if (!multifielditemP) return; | |||
1363 | // do the merge | |||
1364 | if (fItemTypeP) | |||
1365 | fItemTypeP->mergeItems(*this,*multifielditemP,aChangedThis,aChangedOther,aDatastoreP, mode); | |||
1366 | else | |||
1367 | standardMergeWith(*multifielditemP,aChangedThis,aChangedOther, mode); | |||
1368 | // show result | |||
1369 | OBJDEBUGPRINTFX(getItemType()->getSession(),DBG_DATA+DBG_CONFLICT,({ if ((getItemType()->getSession()) && (((0x00000080 +0x20000000) & (getItemType()->getSession())->getDbgMask ()) == (0x00000080 +0x20000000))) (getItemType()->getSession ())->getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)" , aChangedThis ? "" : "not ", aChangedOther ? "" : "not " ); } | |||
1370 | "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)",{ if ((getItemType()->getSession()) && (((0x00000080 +0x20000000) & (getItemType()->getSession())->getDbgMask ()) == (0x00000080 +0x20000000))) (getItemType()->getSession ())->getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)" , aChangedThis ? "" : "not ", aChangedOther ? "" : "not " ); } | |||
1371 | aChangedThis ? "" : "not ",{ if ((getItemType()->getSession()) && (((0x00000080 +0x20000000) & (getItemType()->getSession())->getDbgMask ()) == (0x00000080 +0x20000000))) (getItemType()->getSession ())->getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)" , aChangedThis ? "" : "not ", aChangedOther ? "" : "not " ); } | |||
1372 | aChangedOther ? "" : "not "{ if ((getItemType()->getSession()) && (((0x00000080 +0x20000000) & (getItemType()->getSession())->getDbgMask ()) == (0x00000080 +0x20000000))) (getItemType()->getSession ())->getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)" , aChangedThis ? "" : "not ", aChangedOther ? "" : "not " ); } | |||
1373 | )){ if ((getItemType()->getSession()) && (((0x00000080 +0x20000000) & (getItemType()->getSession())->getDbgMask ()) == (0x00000080 +0x20000000))) (getItemType()->getSession ())->getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)" , aChangedThis ? "" : "not ", aChangedOther ? "" : "not " ); }; | |||
1374 | } // TMultiFieldItem::mergeWith | |||
1375 | ||||
1376 | ||||
1377 | // merge this item with specified item. | |||
1378 | // Notes: | |||
1379 | // - specified item is treated as loosing item, this item is winning item | |||
1380 | // - also updates other item to make sure it is equal to the winning after the merge | |||
1381 | // returns update status of this and other item. Note that changes of non-relevant fields are | |||
1382 | // not reported here. | |||
1383 | void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther, | |||
1384 | int mode, | |||
1385 | const std::set<std::string> &aIgnoreFields) | |||
1386 | { | |||
1387 | // same type of multifield, try to merge | |||
1388 | for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) { | |||
1389 | // Ignore fields if told so by optional MERGEFIELDS() parameter. | |||
1390 | if (aIgnoreFields.find(fFieldDefinitionsP->fFields[i].fieldname) != aIgnoreFields.end()) { | |||
1391 | continue; | |||
1392 | } | |||
1393 | // get merge mode | |||
1394 | sInt16 sep=fFieldDefinitionsP->fFields[i].mergeMode; | |||
1395 | // possible merging is only relevant (=to be reported) for fields that are not eqm_none | |||
1396 | bool mergerelevant = fFieldDefinitionsP->fFields[i].eqRelevant!=eqm_none; | |||
1397 | // check if available in both items at all | |||
1398 | if ( | |||
1399 | fItemTypeP->getFieldOptions(i)->available && // winning | |||
1400 | aItem.fItemTypeP->getFieldOptions(i)->available // loosing | |||
1401 | ) { | |||
1402 | // fields available in both items | |||
1403 | // - get both fields | |||
1404 | TItemField &winningField = getFieldRef(i); | |||
1405 | TItemField &loosingField = aItem.getFieldRef(i); | |||
1406 | // - get assigned status of both fields | |||
1407 | bool winning = winningField.isAssigned(); | |||
1408 | bool loosing = loosingField.isAssigned(); | |||
1409 | // - now decide what to do | |||
1410 | if (sep!=mem_none-1 && mode == MERGE_OPTION_FROM_CONFIG) { | |||
1411 | // merge enabled | |||
1412 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,({ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant" , fFieldDefinitionsP->fFields[i].fieldname.c_str(), sep, mergerelevant ? "" : "NOT " ); } | |||
1413 | "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant",{ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant" , fFieldDefinitionsP->fFields[i].fieldname.c_str(), sep, mergerelevant ? "" : "NOT " ); } | |||
1414 | fFieldDefinitionsP->fFields[i].TCFG_CSTR(fieldname),{ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant" , fFieldDefinitionsP->fFields[i].fieldname.c_str(), sep, mergerelevant ? "" : "NOT " ); } | |||
1415 | sep,{ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant" , fFieldDefinitionsP->fFields[i].fieldname.c_str(), sep, mergerelevant ? "" : "NOT " ); } | |||
1416 | mergerelevant ? "" : "NOT "{ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant" , fFieldDefinitionsP->fFields[i].fieldname.c_str(), sep, mergerelevant ? "" : "NOT " ); } | |||
1417 | )){ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant" , fFieldDefinitionsP->fFields[i].fieldname.c_str(), sep, mergerelevant ? "" : "NOT " ); }; | |||
1418 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,({ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "- %sassigned in winning / %sassigned in loosing" , winning ? "" : "not ", loosing ? "" : "not " ); } | |||
1419 | "- %sassigned in winning / %sassigned in loosing",{ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "- %sassigned in winning / %sassigned in loosing" , winning ? "" : "not ", loosing ? "" : "not " ); } | |||
1420 | winning ? "" : "not ",{ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "- %sassigned in winning / %sassigned in loosing" , winning ? "" : "not ", loosing ? "" : "not " ); } | |||
1421 | loosing ? "" : "not "{ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "- %sassigned in winning / %sassigned in loosing" , winning ? "" : "not ", loosing ? "" : "not " ); } | |||
1422 | )){ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ( "- %sassigned in winning / %sassigned in loosing" , winning ? "" : "not ", loosing ? "" : "not " ); }; | |||
1423 | // - if both are unassigned -> nop | |||
1424 | if (!winning && !loosing) continue; // test next field | |||
1425 | // - if this item has field unassigned and other has it assigned: copy contents | |||
1426 | else if (!winning && loosing && (sep==mem_fillempty-2 || sep==mem_addunassigned-3)) { | |||
1427 | // assign loosing item's content to non-assigned winning item | |||
1428 | getFieldRef(i)=aItem.getFieldRef(i); | |||
1429 | #ifdef SYDEBUG2 | |||
1430 | string ds; | |||
1431 | getFieldRef(i).getAsString(ds); | |||
1432 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,({ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- assigned value '%" ".40" "s' to winning (which had nothing assigned here)" , ds.c_str() ); } | |||
1433 | "- assigned value '%" FMT_LENGTH(".40") "s' to winning (which had nothing assigned here)",{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- assigned value '%" ".40" "s' to winning (which had nothing assigned here)" , ds.c_str() ); } | |||
1434 | FMT_LENGTH_LIMITED(40,ds.c_str()){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- assigned value '%" ".40" "s' to winning (which had nothing assigned here)" , ds.c_str() ); } | |||
1435 | )){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- assigned value '%" ".40" "s' to winning (which had nothing assigned here)" , ds.c_str() ); }; | |||
1436 | #endif | |||
1437 | // count only if relevant and not assigned empty value | |||
1438 | // (so empty and unassigned are treated equally) | |||
1439 | if (mergerelevant && !aItem.getFieldRef(i).isEmpty()) | |||
1440 | aChangedThis=true; // merged something into this item | |||
1441 | } | |||
1442 | else if (winning && loosing) { | |||
1443 | // merge loosing field into winning field | |||
1444 | if (sep==mem_fillempty-2) { | |||
1445 | // only fill up empty winning fields | |||
1446 | if (winningField.isEmpty() && !loosingField.isEmpty()) { | |||
1447 | // only copy value from loosing if winning is empty | |||
1448 | winningField=loosingField; | |||
1449 | #ifdef SYDEBUG2 | |||
1450 | string ds; | |||
1451 | winningField.getAsString(ds); | |||
1452 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,({ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- copied value '%" ".40" "s' from loosing to empty winning" , ds.c_str() ); } | |||
1453 | "- copied value '%" FMT_LENGTH(".40") "s' from loosing to empty winning",{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- copied value '%" ".40" "s' from loosing to empty winning" , ds.c_str() ); } | |||
1454 | FMT_LENGTH_LIMITED(40,ds.c_str()){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- copied value '%" ".40" "s' from loosing to empty winning" , ds.c_str() ); } | |||
1455 | )){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- copied value '%" ".40" "s' from loosing to empty winning" , ds.c_str() ); }; | |||
1456 | #endif | |||
1457 | if (mergerelevant) aChangedThis=true; | |||
1458 | } | |||
1459 | } | |||
1460 | else { | |||
1461 | // try real merge (sep might be 0 (mem_concat) or a separator char) | |||
1462 | #ifdef SYDEBUG2 | |||
1463 | string ds1,ds2; | |||
1464 | winningField.getAsString(ds1); | |||
1465 | loosingField.getAsString(ds2); | |||
1466 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,({ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- try merging winning value '%" ".40" "s' with loosing value '%" ".40" "s'", ds1.c_str(), ds2.c_str() ); } | |||
1467 | "- try merging winning value '%" FMT_LENGTH(".40") "s' with loosing value '%" FMT_LENGTH(".40") "s'",{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- try merging winning value '%" ".40" "s' with loosing value '%" ".40" "s'", ds1.c_str(), ds2.c_str() ); } | |||
1468 | FMT_LENGTH_LIMITED(40,ds1.c_str()),{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- try merging winning value '%" ".40" "s' with loosing value '%" ".40" "s'", ds1.c_str(), ds2.c_str() ); } | |||
1469 | FMT_LENGTH_LIMITED(40,ds2.c_str()){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- try merging winning value '%" ".40" "s' with loosing value '%" ".40" "s'", ds1.c_str(), ds2.c_str() ); } | |||
1470 | )){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- try merging winning value '%" ".40" "s' with loosing value '%" ".40" "s'", ds1.c_str(), ds2.c_str() ); }; | |||
1471 | #endif | |||
1472 | if (winningField.merge(loosingField,sep)) | |||
1473 | aChangedThis=true; | |||
1474 | #ifdef SYDEBUG2 | |||
1475 | winningField.getAsString(ds1); | |||
1476 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,({ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( " merged %sthing, winning value is '%" ".40" "s'", aChangedThis ? "some" : "no", ds1.c_str() ); } | |||
1477 | " merged %sthing, winning value is '%" FMT_LENGTH(".40") "s'",{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( " merged %sthing, winning value is '%" ".40" "s'", aChangedThis ? "some" : "no", ds1.c_str() ); } | |||
1478 | aChangedThis ? "some" : "no",{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( " merged %sthing, winning value is '%" ".40" "s'", aChangedThis ? "some" : "no", ds1.c_str() ); } | |||
1479 | FMT_LENGTH_LIMITED(40,ds1.c_str()){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( " merged %sthing, winning value is '%" ".40" "s'", aChangedThis ? "some" : "no", ds1.c_str() ); } | |||
1480 | )){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( " merged %sthing, winning value is '%" ".40" "s'", aChangedThis ? "some" : "no", ds1.c_str() ); }; | |||
1481 | #endif | |||
1482 | } | |||
1483 | } | |||
1484 | } // merge enabled | |||
1485 | // with or without merge, loosing fields must be equal to winning ones | |||
1486 | // - for non merge-relevant (that is, never-compared) fields, | |||
1487 | // just assign winning value to loosing and do not compare (this | |||
1488 | // is important to avoid pulling large blobs and strings here - | |||
1489 | // assignment just passes the proxy) | |||
1490 | if (!mergerelevant) { | |||
1491 | // everything is handled by the field assignment mechanisms | |||
1492 | if (mode == MERGE_OPTION_CHANGE_THIS) { | |||
1493 | winningField = loosingField; | |||
1494 | } else { | |||
1495 | loosingField = winningField; | |||
1496 | } | |||
1497 | } | |||
1498 | else if (winningField!=loosingField) { | |||
1499 | // merge relevant fields will get more sophisticated treatment, such | |||
1500 | // as checking if a change has occurred and cutoff detection | |||
1501 | #ifdef SYDEBUG2 | |||
1502 | string wfv,lfv; | |||
1503 | winningField.getAsString(wfv); | |||
1504 | loosingField.getAsString(lfv); | |||
1505 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,({ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "Winning and loosing Field '%s' not equal: '%" ".30" "s' <> '%" ".30" "s'", fFieldDefinitionsP->fFields[i].fieldname.c_str (), wfv.c_str(),lfv.c_str() ); } | |||
1506 | "Winning and loosing Field '%s' not equal: '%" FMT_LENGTH(".30") "s' <> '%" FMT_LENGTH(".30") "s'",{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "Winning and loosing Field '%s' not equal: '%" ".30" "s' <> '%" ".30" "s'", fFieldDefinitionsP->fFields[i].fieldname.c_str (), wfv.c_str(),lfv.c_str() ); } | |||
1507 | fFieldDefinitionsP->fFields[i].TCFG_CSTR(fieldname),{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "Winning and loosing Field '%s' not equal: '%" ".30" "s' <> '%" ".30" "s'", fFieldDefinitionsP->fFields[i].fieldname.c_str (), wfv.c_str(),lfv.c_str() ); } | |||
1508 | FMT_LENGTH_LIMITED(30,wfv.c_str()),FMT_LENGTH_LIMITED(30,lfv.c_str()){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "Winning and loosing Field '%s' not equal: '%" ".30" "s' <> '%" ".30" "s'", fFieldDefinitionsP->fFields[i].fieldname.c_str (), wfv.c_str(),lfv.c_str() ); } | |||
1509 | )){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "Winning and loosing Field '%s' not equal: '%" ".30" "s' <> '%" ".30" "s'", fFieldDefinitionsP->fFields[i].fieldname.c_str (), wfv.c_str(),lfv.c_str() ); }; | |||
1510 | #endif | |||
1511 | // update loosing item, too, unless the winning field is shorter or explicitly requested | |||
1512 | if (mode == MERGE_OPTION_CHANGE_THIS || | |||
1513 | loosingField.isShortVers(winningField,fItemTypeP->getFieldOptions(i)->maxsize)) { | |||
1514 | // winning field is short version of loosing field -> loosing field is "better", use it | |||
1515 | winningField=loosingField; | |||
1516 | aChangedThis=true; | |||
1517 | } | |||
1518 | else { | |||
1519 | // standard case, loosing field is replaced by winning field | |||
1520 | loosingField=winningField; | |||
1521 | aChangedOther=true; | |||
1522 | } | |||
1523 | // this is some kind of item-level merge as well | |||
1524 | #ifdef SYDEBUG2 | |||
1525 | string ds; | |||
1526 | winningField.getAsString(ds); | |||
1527 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,({ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- updated fields such that both have same value '%" ".40" "s'", ds.c_str() ); } | |||
1528 | "- updated fields such that both have same value '%" FMT_LENGTH(".40") "s'",{ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- updated fields such that both have same value '%" ".40" "s'", ds.c_str() ); } | |||
1529 | FMT_LENGTH_LIMITED(40,ds.c_str()){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- updated fields such that both have same value '%" ".40" "s'", ds.c_str() ); } | |||
1530 | )){ if (((0x00000080 +0x20000000 +0x01000000) & getDbgMask( )) == (0x00000080 +0x20000000 +0x01000000)) getDbgLogger()-> setNextMask(0x00000080 +0x20000000 +0x01000000).DebugPrintfLastMask ( "- updated fields such that both have same value '%" ".40" "s'", ds.c_str() ); }; | |||
1531 | #endif | |||
1532 | } | |||
1533 | } // field available in both items | |||
1534 | } // field loop | |||
1535 | } // TMultiFieldItem::standardMergeWith | |||
1536 | ||||
1537 | #endif // server only | |||
1538 | ||||
1539 | #ifdef SYDEBUG2 | |||
1540 | // show item contents for debug | |||
1541 | void TMultiFieldItem::debugShowItem(uInt32 aDbgMask) | |||
1542 | { | |||
1543 | TFieldListConfig *fielddefsP = getFieldDefinitions(); | |||
1544 | string val,fshow,oloc,orem; | |||
1545 | TFieldOptions *foptP; | |||
1546 | TItemField *fldP; | |||
1547 | ||||
1548 | if (PDEBUGTEST(aDbgMask|DBG_DETAILS)(((aDbgMask|0x40000000) & getDbgMask()) == (aDbgMask|0x40000000 ))) { | |||
1549 | // very detailed | |||
1550 | bool hasdata = | |||
1551 | getSyncOp()!=sop_archive_delete && | |||
1552 | getSyncOp()!=sop_soft_delete && | |||
1553 | getSyncOp()!=sop_delete && | |||
1554 | getSyncOp()!=sop_copy && | |||
1555 | getSyncOp()!=sop_move; | |||
1556 | // - item header info | |||
1557 | PDEBUGPRINTFX(aDbgMask|DBG_DETAILS|DBG_HOT,({ if (((aDbgMask|0x40000000|0x00000001) & getDbgMask()) == (aDbgMask|0x40000000|0x00000001)) getDbgLogger()->setNextMask (aDbgMask|0x40000000|0x00000001).DebugPrintfLastMask ( "Item LocalID='%s', RemoteID='%s', operation=%s%s" , getLocalID(), getRemoteID(), SyncOpNames[getSyncOp()], hasdata && (((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask |0x01000000)) ? ", size: [maxlocal,maxremote,actual]" : "" ); } | |||
1558 | "Item LocalID='%s', RemoteID='%s', operation=%s%s",{ if (((aDbgMask|0x40000000|0x00000001) & getDbgMask()) == (aDbgMask|0x40000000|0x00000001)) getDbgLogger()->setNextMask (aDbgMask|0x40000000|0x00000001).DebugPrintfLastMask ( "Item LocalID='%s', RemoteID='%s', operation=%s%s" , getLocalID(), getRemoteID(), SyncOpNames[getSyncOp()], hasdata && (((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask |0x01000000)) ? ", size: [maxlocal,maxremote,actual]" : "" ); } | |||
1559 | getLocalID(),{ if (((aDbgMask|0x40000000|0x00000001) & getDbgMask()) == (aDbgMask|0x40000000|0x00000001)) getDbgLogger()->setNextMask (aDbgMask|0x40000000|0x00000001).DebugPrintfLastMask ( "Item LocalID='%s', RemoteID='%s', operation=%s%s" , getLocalID(), getRemoteID(), SyncOpNames[getSyncOp()], hasdata && (((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask |0x01000000)) ? ", size: [maxlocal,maxremote,actual]" : "" ); } | |||
1560 | getRemoteID(),{ if (((aDbgMask|0x40000000|0x00000001) & getDbgMask()) == (aDbgMask|0x40000000|0x00000001)) getDbgLogger()->setNextMask (aDbgMask|0x40000000|0x00000001).DebugPrintfLastMask ( "Item LocalID='%s', RemoteID='%s', operation=%s%s" , getLocalID(), getRemoteID(), SyncOpNames[getSyncOp()], hasdata && (((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask |0x01000000)) ? ", size: [maxlocal,maxremote,actual]" : "" ); } | |||
1561 | SyncOpNames[getSyncOp()],{ if (((aDbgMask|0x40000000|0x00000001) & getDbgMask()) == (aDbgMask|0x40000000|0x00000001)) getDbgLogger()->setNextMask (aDbgMask|0x40000000|0x00000001).DebugPrintfLastMask ( "Item LocalID='%s', RemoteID='%s', operation=%s%s" , getLocalID(), getRemoteID(), SyncOpNames[getSyncOp()], hasdata && (((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask |0x01000000)) ? ", size: [maxlocal,maxremote,actual]" : "" ); } | |||
1562 | hasdata && PDEBUGTEST(aDbgMask|DBG_USERDATA) ? ", size: [maxlocal,maxremote,actual]" : ""{ if (((aDbgMask|0x40000000|0x00000001) & getDbgMask()) == (aDbgMask|0x40000000|0x00000001)) getDbgLogger()->setNextMask (aDbgMask|0x40000000|0x00000001).DebugPrintfLastMask ( "Item LocalID='%s', RemoteID='%s', operation=%s%s" , getLocalID(), getRemoteID(), SyncOpNames[getSyncOp()], hasdata && (((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask |0x01000000)) ? ", size: [maxlocal,maxremote,actual]" : "" ); } | |||
1563 | )){ if (((aDbgMask|0x40000000|0x00000001) & getDbgMask()) == (aDbgMask|0x40000000|0x00000001)) getDbgLogger()->setNextMask (aDbgMask|0x40000000|0x00000001).DebugPrintfLastMask ( "Item LocalID='%s', RemoteID='%s', operation=%s%s" , getLocalID(), getRemoteID(), SyncOpNames[getSyncOp()], hasdata && (((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask |0x01000000)) ? ", size: [maxlocal,maxremote,actual]" : "" ); }; | |||
1564 | if (!hasdata) | |||
1565 | return; // do not show data for delete | |||
1566 | if (!PDEBUGTEST(aDbgMask|DBG_USERDATA)(((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask|0x01000000 ))) { | |||
1567 | PDEBUGPRINTFX(aDbgMask|DBG_DETAILS,("*** field data not shown because userdata log is disabled ***")){ if (((aDbgMask|0x40000000) & getDbgMask()) == (aDbgMask |0x40000000)) getDbgLogger()->setNextMask(aDbgMask|0x40000000 ).DebugPrintfLastMask ("*** field data not shown because userdata log is disabled ***" ); }; | |||
1568 | return; // do not show any field data | |||
1569 | } | |||
1570 | // - fields | |||
1571 | fshow.erase(); | |||
1572 | for (sInt16 k=0; k<fielddefsP->numFields(); k++) { | |||
1573 | SYSYNC_TRYtry { | |||
1574 | const TFieldDefinition *fdP = &(fielddefsP->fFields[k]); | |||
1575 | // get options | |||
1576 | oloc="?"; | |||
1577 | if (fItemTypeP) { | |||
1578 | foptP=fItemTypeP->getFieldOptions(k); | |||
1579 | if (!foptP->available) | |||
1580 | oloc="n/a"; | |||
1581 | else | |||
1582 | StringObjPrintf(oloc,"%ld",(long)foptP->maxsize); | |||
1583 | } | |||
1584 | orem="?"; | |||
1585 | if (fTargetItemTypeP) { | |||
1586 | foptP=fTargetItemTypeP->getFieldOptions(k); | |||
1587 | if (!foptP->available) | |||
1588 | orem="n/a"; | |||
1589 | else | |||
1590 | StringObjPrintf(orem,"%ld",(long)foptP->maxsize); | |||
1591 | } | |||
1592 | // get value (but prevent pulling proxies) | |||
1593 | fldP=NULL__null; | |||
1594 | size_t n=0; // empty or unknown size | |||
1595 | if (isAssigned(k)) { | |||
1596 | fldP = getField(k); | |||
1597 | val.erase(); | |||
1598 | n = fldP->StringObjFieldAppend(val,99); // get string representation and size | |||
1599 | } | |||
1600 | else | |||
1601 | val="<unassigned>"; | |||
1602 | // now show main info | |||
1603 | StringObjAppendPrintf(fshow, | |||
1604 | "- %2d : %10s %-15s [%4s,%4s,%6ld] : %s\n", | |||
1605 | k, // fid | |||
1606 | ItemFieldTypeNames[fdP->type], // field type | |||
1607 | TCFG_CSTR(fdP->fieldname)fdP->fieldname.c_str(), // field name | |||
1608 | oloc.c_str(), | |||
1609 | orem.c_str(), | |||
1610 | long(n), | |||
1611 | val.c_str() | |||
1612 | ); | |||
1613 | // show array contents if any | |||
1614 | if (fldP && fldP->isArray()) { | |||
1615 | int arridx; | |||
1616 | for (arridx=0; arridx<fldP->arraySize(); arridx++) { | |||
1617 | // show array elements | |||
1618 | TItemField *elemP = fldP->getArrayField(arridx,true); | |||
1619 | val.erase(); | |||
1620 | elemP->StringObjFieldAppend(val,99); | |||
1621 | StringObjAppendPrintf(fshow, | |||
1622 | " -- element %4d : %s\n", | |||
1623 | arridx, | |||
1624 | val.c_str() | |||
1625 | ); | |||
1626 | } | |||
1627 | } // isArray | |||
1628 | } | |||
1629 | SYSYNC_CATCH(exception &e)catch(exception &e) { | |||
1630 | PDEBUGPRINTFX(DBG_ERROR,("Exception when trying to show field fid=%hd: %s[FLUSH]",k,e.what())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Exception when trying to show field fid=%hd: %s[FLUSH]" ,k,e.what()); }; | |||
1631 | SYSYNC_ENDCATCH} | |||
1632 | SYSYNC_CATCH(...)catch(...) { | |||
1633 | PDEBUGPRINTFX(DBG_ERROR,("Unknown Exception when trying to show field fid=%hd[FLUSH]",k)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Unknown Exception when trying to show field fid=%hd[FLUSH]" ,k); }; | |||
1634 | SYSYNC_ENDCATCH} | |||
1635 | } // for all fields | |||
1636 | PDEBUGPUTSXX(aDbgMask|DBG_DETAILS|DBG_USERDATA,fshow.c_str(),0,true){ if (((aDbgMask|0x40000000|0x01000000) & getDbgMask()) == (aDbgMask|0x40000000|0x01000000)) getDbgLogger()->DebugPuts ( aDbgMask|0x40000000|0x01000000,fshow.c_str(),0,true); }; | |||
1637 | } | |||
1638 | else if (PDEBUGTEST(aDbgMask)(((aDbgMask) & getDbgMask()) == (aDbgMask))) { | |||
1639 | // single line only | |||
1640 | StringObjPrintf(fshow, | |||
1641 | "Item locID='%s', RemID='%s', op=%s", | |||
1642 | getLocalID(), | |||
1643 | getRemoteID(), | |||
1644 | SyncOpNames[getSyncOp()] | |||
1645 | ); | |||
1646 | if (!PDEBUGTEST(aDbgMask|DBG_USERDATA)(((aDbgMask|0x01000000) & getDbgMask()) == (aDbgMask|0x01000000 ))) { | |||
1647 | fshow+=", *** userdata log disabled ***"; | |||
1648 | } | |||
1649 | else if ( | |||
1650 | getSyncOp()==sop_archive_delete || | |||
1651 | getSyncOp()==sop_soft_delete || | |||
1652 | getSyncOp()==sop_delete | |||
1653 | ) { | |||
1654 | // do not show data for delete | |||
1655 | } | |||
1656 | else { | |||
1657 | fshow+=", Fields: "; | |||
1658 | for (sInt16 k=0; k<fielddefsP->numFields(); k++) { | |||
1659 | if (isAssigned(k)) { | |||
1660 | if (!getField(k)->isEmpty() && !getField(k)->hasProxy()) { | |||
1661 | getField(k)->StringObjFieldAppend(fshow,20); | |||
1662 | fshow+=", "; | |||
1663 | } | |||
1664 | } | |||
1665 | } | |||
1666 | } | |||
1667 | PDEBUGPUTSX(aDbgMask,fshow.c_str()){ if (((aDbgMask) & getDbgMask()) == (aDbgMask)) getDbgLogger ()->DebugPuts( aDbgMask,fshow.c_str()); }; | |||
1668 | } | |||
1669 | } // TMultiFieldItem::debugShowItem | |||
1670 | ||||
1671 | #endif | |||
1672 | ||||
1673 | /* end of TMultiFieldItem implementation */ | |||
1674 | ||||
1675 | ||||
1676 | #ifdef DBAPI_TUNNEL_SUPPORT | |||
1677 | ||||
1678 | // TMultiFieldItemKey | |||
1679 | // ================== | |||
1680 | ||||
1681 | ||||
1682 | // set new content item | |||
1683 | void TMultiFieldItemKey::setItem(TMultiFieldItem *aItemP, bool aPassOwner) | |||
1684 | { | |||
1685 | forgetItem(); | |||
1686 | fItemP = aItemP; | |||
1687 | fOwnsItem = aPassOwner; | |||
1688 | fWritten = fItemP; // if we have set an item, this counts as written | |||
1689 | } // TMultiFieldItemKey::setItem | |||
1690 | ||||
1691 | ||||
1692 | // get FID for specified name | |||
1693 | sInt16 TMultiFieldItemKey::getFidFor(cAppCharP aName, stringSize aNameSz) | |||
1694 | { | |||
1695 | if (!fItemP) return VARIDX_UNDEFINED-128; // no item, no field is accessible | |||
1696 | ||||
1697 | TFieldListConfig *flcP = fItemP->getFieldDefinitions(); | |||
1698 | ||||
1699 | // check for iterator commands first | |||
1700 | if (strucmp(aName,VALNAME_FIRST".FIRST")==0) { | |||
1701 | fIteratorFid = 0; | |||
1702 | if (fIteratorFid<flcP->numFields()) | |||
1703 | return fIteratorFid; | |||
1704 | } | |||
1705 | else if (strucmp(aName,VALNAME_NEXT".NEXT")==0) { | |||
1706 | if (fIteratorFid<flcP->numFields()) | |||
1707 | fIteratorFid++; | |||
1708 | if (fIteratorFid<flcP->numFields()) | |||
1709 | return fIteratorFid; | |||
1710 | } | |||
1711 | else { | |||
1712 | return flcP->fieldIndex(aName,aNameSz); | |||
1713 | } | |||
1714 | // none found | |||
1715 | return VARIDX_UNDEFINED-128; | |||
1716 | } // TMultiFieldItemKey::getFidFor | |||
1717 | ||||
1718 | ||||
1719 | ||||
1720 | TItemField *TMultiFieldItemKey::getBaseFieldFromFid(sInt16 aFid) | |||
1721 | { | |||
1722 | if (!fItemP) return NULL__null; // no item, no field is accessible | |||
1723 | return fItemP->getField(aFid); | |||
1724 | } // TMultiFieldItemKey::getBaseFieldFromFid | |||
1725 | ||||
1726 | ||||
1727 | bool TMultiFieldItemKey::getFieldNameFromFid(sInt16 aFid, string &aFieldName) | |||
1728 | { | |||
1729 | if (!fItemP) return false; // no item, no field is accessible | |||
1730 | // name is field name | |||
1731 | TFieldListConfig *flcP = fItemP->getFieldDefinitions(); | |||
1732 | if (aFid>=0 && aFid<flcP->numFields()) { | |||
1733 | aFieldName = flcP->fFields[aFid].fieldname; | |||
1734 | return true; | |||
1735 | } | |||
1736 | // none found | |||
1737 | return false; | |||
1738 | } // TMultiFieldItemKey::getFieldNameFromFid | |||
1739 | ||||
1740 | ||||
1741 | #endif // DBAPI_TUNNEL_SUPPORT | |||
1742 | ||||
1743 | ||||
1744 | } // namespace sysync | |||
1745 | ||||
1746 | // eof |