Bug Summary

File:libsynthesis/src/sysync/itemfield.cpp
Warning:line 1568, column 9
Value stored to 'p' is never read

Annotated Source Code

1/*
2 * File: itemfield.cpp
3 *
4 * Author: Lukas Zeller (luz@plan44.ch)
5 *
6 * TItemField
7 * Abstract class, holds a single field value
8 * TStringField, TIntegerField, TTimeStampField etc.
9 * Implementations of field types
10 *
11 * Copyright (c) 2001-2011 by Synthesis AG + plan44.ch
12 *
13 * 2001-08-08 : luz : created
14 *
15 */
16
17// includes
18#include "prefix_file.h"
19#include "sysync.h"
20#include "itemfield.h"
21#include "multifielditemtype.h"
22
23#if defined(CHECKSUM_CHANGELOG1) && !defined(RECORDHASH_FROM_DBAPI)
24#include "sysync_crc16.h"
25#endif
26
27using namespace sysync;
28
29
30namespace sysync {
31
32// type names
33cAppCharP const ItemFieldTypeNames[numFieldTypes] = {
34 "string",
35 "telephone",
36 "integer",
37 "timestamp",
38 "date",
39 "url",
40 "multiline",
41 "blob",
42 "none"
43};
44
45// corresponding SyncML devInf <DataType>
46const TPropDataTypes devInfPropTypes[numFieldTypes] = {
47 proptype_chr, // string -> Character
48 proptype_phonenum, // telephone -> Phone number
49 proptype_int, // integer -> Integer
50 proptype_datetime, // Timestamp -> Date and time of day
51 proptype_datetime, // Date -> Date and time of day
52 proptype_chr, // URL -> Character
53 proptype_text, // multiline -> plain text
54 proptype_bin, // BLOB -> Binary
55 proptype_unknown, // none -> unknown
56};
57
58
59
60/*
61 * Implementation of TItemField
62 */
63
64
65TItemField::TItemField() :
66 fAssigned(false) // unassigned at creation
67{
68} // TItemField::TItemField
69
70
71TItemField::~TItemField()
72{
73} // TItemField::~TItemField
74
75
76#ifdef SYDEBUG2
77
78// show field contents as string for debug output
79size_t TItemField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
80{
81 if (isUnassigned())
82 s+="<unassigned>";
83 else if (isEmpty())
84 s+="<empty>";
85 else
86 appendToString(s, aMaxStrLen);
87 return 0; // non-strings do not have a real size
88} // TItemField::StringObjFieldAppend
89
90#endif
91
92
93
94// append (default to appending string value of other field)
95void TItemField::append(TItemField &aItemField)
96{
97 string s;
98 aItemField.getAsString(s);
99 appendString(s);
100} // TItemField::append
101
102
103// generic cross-type assignment via string format
104TItemField& TItemField::operator=(TItemField &aItemField)
105{
106 string s;
107
108 if (aItemField.isUnassigned()) unAssign(); // copy unassigned status
109 else if (aItemField.isEmpty()) assignEmpty(); // copy empty status
110 else {
111 aItemField.getAsString(s);
112 setAsString(s);
113 }
114 return *this;
115} // TItemField::operator=
116
117
118// get any field as integer
119fieldinteger_t TItemField::getAsInteger(void)
120{
121 // convert to integer number
122 string s;
123 fieldinteger_t i;
124 getAsString(s);
125 if (!
126 #ifndef NO64BITINT
127 StrToLongLong(s.c_str(),i)
128 #else
129 StrToLong(s.c_str(),i)
130 #endif
131 )
132 i=0; // defined value
133 return i;
134} // TItemField::getAsInteger
135
136
137// get integer as numeric string
138void TItemField::setAsInteger(fieldinteger_t aInteger)
139{
140 string s;
141 #ifndef NO64BITINT
142 LONGLONGTOSTR(s,PRINTF_LLD_ARG(aInteger))StringObjPrintf(s,"%lld",static_cast<long long>(aInteger
))
;
143 #else
144 StringObjPrintf(s,"%ld",(sInt32)aInteger);
145 #endif
146 // set as string
147 setAsString(s);
148} // TItemField::setAsInteger
149
150
151// set as string, max number of chars = aLen
152void TItemField::setAsString(cAppCharP aString, size_t aLen)
153{
154 if (!aString)
155 assignEmpty();
156 else {
157 string t(aString,aLen);
158 // now call basic setter
159 setAsString(t.c_str());
160 }
161} // TItemField::setAsString
162
163
164size_t TItemField::getStringSize(void)
165{
166 if (getType()==fty_none) return 0; // empty/unassigned field has no size (avoid unneeded getAsString)
167 string s;
168 getAsString(s);
169 return s.size();
170} // TItemField::getStringSize
171
172
173bool TItemField::contains(TItemField &aItemField, bool aCaseInsensitive)
174{
175 string s;
176 aItemField.getAsString(s);
177 return findInString(s.c_str(), aCaseInsensitive)>=0;
178}
179
180
181#ifdef STREAMFIELD_SUPPORT1
182
183// reset stream (start reading and/or writing at specified position)
184void TItemField::resetStream(size_t aPos)
185{
186 if (aPos==0)
187 fStreamPos=0; // optimized short-cut
188 else {
189 size_t sz=getStreamSize();
190 if (aPos>sz)
191 fStreamPos=sz;
192 else
193 fStreamPos=aPos;
194 }
195} // TItemField::resetStream
196
197
198// read from stream
199size_t TItemField::readStream(void *aBuffer, size_t aMaxBytes)
200{
201 string s;
202 getAsString(s);
203 size_t sz=s.size();
204 if (fStreamPos>sz) fStreamPos=sz;
205 if (fStreamPos+aMaxBytes > sz) aMaxBytes=sz-fStreamPos;
206 if (aMaxBytes>0) {
207 // copy memory
208 memcpy(aBuffer,s.c_str()+fStreamPos,aMaxBytes);
209 }
210 // return number of chars actually read
211 fStreamPos+=aMaxBytes;
212 return aMaxBytes;
213} // TItemField::readStream
214
215
216// write to stream
217size_t TItemField::writeStream(void *aBuffer, size_t aNumBytes)
218{
219 if (aNumBytes==0) return 0;
220 if (fStreamPos!=0) {
221 // not replacing entire contents, need read-modify-write of string
222 string s;
223 getAsString(s);
224 if (fStreamPos>s.size()) fStreamPos=s.size();
225 s.resize(fStreamPos);
226 s.append((cAppCharP)aBuffer,aNumBytes);
227 setAsString(s);
228 }
229 else {
230 setAsString((cAppCharP)aBuffer,aNumBytes);
231 }
232 fStreamPos+=aNumBytes;
233 return aNumBytes;
234} // TItemField::writeStream
235
236#endif
237
238
239/* end of TItemField implementation */
240
241
242#ifdef ARRAYFIELD_SUPPORT1
243/*
244 * Implementation of TArrayField
245 */
246
247
248// constructor
249TArrayField::TArrayField(TItemFieldTypes aLeafFieldType, GZones *aGZonesP)
250{
251 fLeafFieldType=aLeafFieldType;
252 fGZonesP = aGZonesP;
253 // field for index==0 always exists
254 fFirstField = newItemField(fLeafFieldType,fGZonesP,false);
255} // TArrayField::TArrayField
256
257
258// destructor
259TArrayField::~TArrayField()
260{
261 // make sure leaf fields (except idx==0) are gone
262 unAssign();
263 // and kill firstfield
264 delete fFirstField;
265} // TArrayField::~TArrayField
266
267
268
269bool TArrayField::elementsBasedOn(TItemFieldTypes aFieldType) const
270{
271 return fFirstField->isBasedOn(aFieldType);
272} // TArrayField::elementsBasedOn
273
274
275
276#ifdef SYDEBUG2
277
278// show field contents as string for debug output
279size_t TArrayField::StringObjFieldAppend(string &s, uInt16)
280{
281 if (!isAssigned())
282 return inherited::StringObjFieldAppend(s,0); // let ancestor show
283 StringObjAppendPrintf(s,"<array with %ld elements>",(long int)(arraySize()));
284 return 0;
285} // TArrayField::StringObjFieldAppend
286
287#endif
288
289
290// get field from array (creates new if index is larger than current array size)
291TItemField *TArrayField::getArrayField(sInt16 aArrIdx, bool aExistingOnly)
292{
293 TItemField *fldP = NULL__null;
294 // check index, negative index means array field itself
295 if (aArrIdx<0) return this;
296 // check if we have that field already
297 if (aArrIdx<arraySize()) {
298 // pointer array is large enough
299 fldP = fArray[aArrIdx];
300 if (fldP==NULL__null) {
301 // but element does not exist yet, create field for it
302 if (aArrIdx==0)
303 fldP = fFirstField;
304 else
305 fldP = newItemField(fLeafFieldType,fGZonesP,false);
306 fArray[aArrIdx]=fldP;
307 }
308 }
309 else if (aExistingOnly) {
310 // element does not exist and we want not to create new elements:
311 return NULL__null;
312 }
313 else {
314 // element does not exist yet, create new ones up to requested index
315 fArray.reserve(aArrIdx+1);
316 for (sInt16 idx=arraySize(); idx<=aArrIdx; idx++) {
317 fArray.push_back(NULL__null);
318 }
319 // actually create last field only (and use firstField if that happens to be idx==0)
320 if (aArrIdx==0)
321 fldP = fFirstField;
322 else
323 fldP = newItemField(fLeafFieldType,fGZonesP,false);
324 fArray[aArrIdx]=fldP;
325 }
326 // return field
327 return fldP;
328} // TArrayField::getArrayField
329
330
331#if defined(CHECKSUM_CHANGELOG1) && !defined(RECORDHASH_FROM_DBAPI)
332// calc CRC over all fields
333uInt16 TArrayField::getDataCRC(uInt16 crc)
334{
335 for (sInt16 idx=0; idx<arraySize(); idx++) {
336 crc=getArrayField(idx)->getDataCRC(crc);
337 }
338 return crc;
339} // TArrayField::getDataCRC
340#endif
341
342
343// clear all leaf fields
344void TArrayField::unAssign(void)
345{
346 for (sInt16 idx=0; idx<arraySize(); idx++) {
347 if (fArray[idx]) {
348 if (idx==0)
349 fArray[idx]->unAssign(); // first is always kept, it is the fFirstField, so only unassign to remove content
350 else
351 delete fArray[idx];
352 fArray[idx]=NULL__null;
353 }
354 }
355 // clear list now
356 fArray.clear();
357 // clear flag as well that could be set in case of an explicitly assigned empty array
358 fAssigned = false;
359} // TArrayField::unAssign
360
361
362// assign
363TItemField& TArrayField::operator=(TItemField &aItemField)
364{
365 // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
366 if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
367 // handle array-to-array assignments
368 if (aItemField.isArray()) {
369 // delete my current contents
370 unAssign();
371 // copy leaf fields from other field
372 for (sInt16 idx=0; idx<aItemField.arraySize(); idx++) {
373 // assign array members
374 *(getArrayField(idx)) = *(((TItemField *)(&aItemField))->getArrayField(idx));
375 }
376 }
377 else {
378 // non-array (non-empty) assigned to array: just assign value to first element
379 unAssign();
380 *(getArrayField(0)) = aItemField;
381 }
382 return *this;
383} // TArrayField::operator=
384
385
386// append values (only assigned ones)
387// - if other field is an array, too, all elements of other array
388// will be appended at end of this array.
389// - if other field is a scalar, it's value will be appended to
390// the array.
391void TArrayField::append(TItemField &aItemField)
392{
393 if (aItemField.isArray()) {
394 // append elements of another array to this array
395 for (sInt16 idx=0; idx<aItemField.arraySize(); idx++) {
396 // append all array members to this array
397 // Note: calling append recursively!
398 append(*(aItemField.getArrayField(idx)));
399 }
400 }
401 else {
402 // append value of another field to this array
403 // - do not append unassigned fields
404 if (aItemField.isAssigned()) {
405 *(getArrayField(arraySize())) = aItemField;
406 }
407 }
408} // TArrayField::append
409
410
411
412// append string to array = append string as last element of array
413void TArrayField::appendString(cAppCharP aString, size_t aMaxChars)
414{
415 getArrayField(arraySize())->setAsString(aString,aMaxChars);
416} // TArrayField::appendString
417
418
419// contains for arrays means "contains in any of the elements"
420// (if aItemField is an array as well, this means that every element must be
421// contained somewhere in my own array)
422bool TArrayField::contains(TItemField &aItemField, bool aCaseInsensitive)
423{
424 bool contained = false;
425 if (aItemField.isArray()) {
426 contained=true;
427 // array: all array elements must be contained in at least one of my elements
428 for (sInt16 idx=0; idx<aItemField.arraySize(); idx++) {
429 if (!contains(*(aItemField.getArrayField(idx)),aCaseInsensitive)) {
430 // one of the elements of aItemField is not contained in myself -> not contained
431 contained = false;
432 break;
433 }
434 }
435 }
436 else {
437 // leaf element: must be contained in at least one of my elements
438 contained = false;
439 for (sInt16 idx=0; idx<arraySize(); idx++) {
440 if (getArrayField(idx)->contains(aItemField,aCaseInsensitive)) {
441 // the value of aItemField is contained in one of my elements -> contained
442 contained = true;
443 break;
444 }
445 }
446 }
447 return contained;
448} // TItemField::contains
449
450
451
452// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
453// SYSYNC_NOT_COMPARABLE if not comparable at all or not equal and no ordering known
454// Note: array fields are only comparable with other array fields
455sInt16 TArrayField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
456{
457 if (!aItemField.isArray()) return SYSYNC_NOT_COMPARABLE-999;
458 // get sizes of arrays
459 sInt16 mysz = arraySize();
460 sInt16 othersz = aItemField.arraySize();
461 sInt16 commonsz = mysz>othersz ? othersz : mysz;
462 // compare common array elements
463 for (sInt16 idx=0; idx<commonsz; idx++) {
464 sInt16 res = getArrayField(idx)->compareWith(*(aItemField.getArrayField(idx)), aCaseInsensitive);
465 if (res!=0) return res; // all non-equal return
466 }
467 // all compared fields are equal
468 if (mysz==othersz) return 0; // same size : equal
469 // Sizes differ, but that only matters if the extra entries are
470 // actually assigned. Without that special case, we end up
471 // with the situation where parsing, encoding and parsing
472 // again leads to different fields:
473 // - EMAIL;TYPE=OTHER:foo -> EMAIL_FLAGS 1 entry "unassigned"
474 // - unassigned -> EMAIL:foo
475 // - EMAIL:foo -> EMAIL_FLAGS 0 entry
476 // - EMAIL_FLAGS 1 entry "unassigned" > EMAIL_FLAGS 0 entry
477 TItemField &largerField = mysz < othersz ? aItemField : *this;
478 sInt16 minsz, maxsz;
479 sInt16 res; // larger array is greater
480 if (mysz < othersz) {
481 minsz = mysz;
482 maxsz = othersz;
483 res = -1;
484 } else {
485 minsz = othersz;
486 maxsz = mysz;
487 res = 1;
488 }
489 for (sInt16 idx=minsz; idx<maxsz; idx++) {
490 if (largerField.getArrayField(idx)->isAssigned())
491 // found real difference
492 return res;
493 }
494 // larger array contains only extra unassigned entries, ignore them
495 return 0;
496} // TArrayField::compareWith
497
498/* end of TArrayField implementation */
499#endif
500
501
502
503/*
504 * Implementation of TStringField
505 */
506
507
508TStringField::TStringField()
509{
510 #ifdef STREAMFIELD_SUPPORT1
511 fBlobProxyP=NULL__null;
512 #endif
513} // TStringField::TStringField
514
515
516TStringField::~TStringField()
517{
518 #ifdef STREAMFIELD_SUPPORT1
519 // remove proxy if any
520 TBlobProxy::unlink(fBlobProxyP);
521 #endif
522} // TStringField::~TStringField
523
524
525#if defined(CHECKSUM_CHANGELOG1) && !defined(RECORDHASH_FROM_DBAPI)
526
527// changelog support: calculate CRC over contents
528uInt16 TStringField::getDataCRC(uInt16 crc)
529{
530 // CRC over characters in the string
531 return sysync_crc16_block(getCStr(),getStringSize(),crc);
532} // TStringField::getDataCRC
533
534#endif
535
536
537// assignment
538TItemField& TStringField::operator=(TItemField &aItemField)
539{
540 DELETEPROXYsetBlobProxy(__null); // forget old value and old proxy as well
541 // handle myself only if other is string field as well
542 if (aItemField.isBasedOn(fty_string)) {
543 // copy fields 1:1
544 const TStringField *sfP = static_cast<const TStringField *>(&aItemField);
545 fString=sfP->fString;
546 fAssigned=sfP->fAssigned;
547 #ifdef STREAMFIELD_SUPPORT1
548 fBlobProxyP=sfP->fBlobProxyP; // copy proxy as well
549 if (fBlobProxyP) fBlobProxyP->link(); // link again, now both fields use the proxy
550 #endif
551 }
552 else
553 TItemField::operator=(aItemField); // generic cross-type assignment (via string)
554 return *this;
555} // TStringField::operator=
556
557
558// get size of string
559size_t TStringField::getStringSize(void)
560{
561 #ifdef STREAMFIELD_SUPPORT1
562 return getStreamSize();
563 #else
564 return fString.size();
565 #endif
566} // TStringField::getStringSize
567
568
569// normalized string is contents without leading or trailing control chars or spaces
570void TStringField::getAsNormalizedString(string &aString)
571{
572 size_t nsiz = getStringSize();
573 // we need the actual string to do this
574 PULLFROMPROXYpullFromProxy();
575 // find first non-control or non-WSP char
576 size_t start = 0;
577 while (start<nsiz && ((uInt8)fString[start])<=' ') start++;
578 // find last non-control or non-WSP char
579 while (nsiz>0 && ((uInt8)fString[nsiz-1])<=' ') nsiz--;
580 // take string without any leading or trailing white space or other control chars
581 aString.assign(fString,start,nsiz-start);
582} // TStringField::getAsNormalizedString
583
584
585
586
587#ifdef SYDEBUG2
588
589// show field contents as string for debug output
590size_t TStringField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
591{
592 size_t n = 0;
593 // empty or unassigned is handled by base class
594 if (isEmpty()) { return inherited::StringObjFieldAppend(s,aMaxStrLen); }
595 // with proxy installed, do not pull the string
596 if (PROXYINSTALLED(fBlobProxyP!=__null)) {
597 s+="<proxy installed>";
598 n=0; // unknown size at this time
599 }
600 else {
601 // no proxy installed
602 s+='"';
603 // - check if display length is sufficient
604 size_t i;
605 n = getStringSize();
606 if (aMaxStrLen==0 || n<=10 || n<=size_t(aMaxStrLen-2)) {
607 // strings below 11 chars are always shown in full
608 s.append(fString);
609 }
610 else {
611 i = (aMaxStrLen-5)/2; // half of the size that can be displayed
612 s.append(fString,0,i);
613 s.append("...");
614 s.append(fString,n-i,i);
615 }
616 s+='"';
617 }
618 // return actual string size (not shortened one!)
619 return n;
620} // TStringField::StringObjFieldAppend
621
622#endif
623
624
625
626
627#ifdef STREAMFIELD_SUPPORT1
628
629// set blob loader proxy (ownership is passed to field)
630void TStringField::setBlobProxy(TBlobProxy *aBlobProxyP)
631{
632 if (fBlobProxyP==aBlobProxyP) return; // same proxy, just ignore
633 // unlink previous proxy, if any
634 TBlobProxy::unlink(fBlobProxyP);
635 // assign new proxy
636 fBlobProxyP=aBlobProxyP;
637 // assigning a proxy means that the field is now assigned
638 fAssigned=true;
639} // TStringField::setBlobProxy
640
641
642// pull entire string from proxy and forget proxy
643void TStringField::pullFromProxy(void)
644{
645 if (fBlobProxyP) {
646 const size_t bufsiz=4096;
647 cAppCharP bufP = new char[bufsiz];
648 resetStream();
649 size_t by;
650 SYSYNC_TRYtry {
651 do {
652 by=fBlobProxyP->readBlobStream(this, fStreamPos, (void *)bufP, bufsiz);
653 fString.append(bufP,by);
654 } while (by==bufsiz);
655 }
656 SYSYNC_CATCH(exception &e)catch(exception &e) {
657 // avoid crashing session if proxy pull fails
658 fString="Server error while getting data from proxy object: ";
659 fString+=e.what();
660 SYSYNC_ENDCATCH}
661 delete [] bufP;
662 // proxy is no longer needed
663 TBlobProxy::unlink(fBlobProxyP);
664 }
665} // TStringField::pullFromProxy
666
667
668// return size of stream
669size_t TStringField::getStreamSize(void)
670{
671 if (fBlobProxyP)
672 SYSYNC_TRYtry {
673 return fBlobProxyP->getBlobSize(this); // return size of entire blob
674 }
675 SYSYNC_CATCH(exception &e)catch(exception &e) {
676 return 0; // cannot return actual size
677 SYSYNC_ENDCATCH}
678 else
679 return fString.size(); // just return size of already stored string
680} // TStringField::getStreamSize
681
682
683// read as stream
684size_t TStringField::readStream(void *aBuffer, size_t aMaxBytes)
685{
686 if (fBlobProxyP) {
687 SYSYNC_TRYtry {
688 // let proxy handle this
689 return fBlobProxyP->readBlobStream(this, fStreamPos, aBuffer, aMaxBytes);
690 }
691 SYSYNC_CATCH(...)catch(...) {
692 // do not return anything
693 return 0; // do not return anything
694 SYSYNC_ENDCATCH}
695 }
696 else {
697 // read from string itself
698 size_t sz=fString.size();
699 if (fStreamPos>sz) fStreamPos=sz;
700 if (fStreamPos+aMaxBytes > sz) aMaxBytes=sz-fStreamPos;
701 if (aMaxBytes>0) {
702 // copy memory
703 memcpy(aBuffer,fString.c_str()+fStreamPos,aMaxBytes);
704 }
705 // return number of chars actually read
706 fStreamPos+=aMaxBytes;
707 return aMaxBytes;
708 }
709} // TStringField::readStream
710
711
712// write as stream
713size_t TStringField::writeStream(void *aBuffer, size_t aNumBytes)
714{
715 if (aNumBytes==0) return 0;
716 if (fStreamPos>fString.size()) fStreamPos=fString.size();
717 fString.resize(fStreamPos);
718 fString.append((cAppCharP)aBuffer,aNumBytes);
719 fStreamPos+=aNumBytes;
720 return aNumBytes;
721} // TStringField::writeStream
722
723#endif
724
725
726
727// set as string, max number of chars to assign = aLen
728void TStringField::setAsString(cAppCharP aString, size_t aLen)
729{
730 DELETEPROXYsetBlobProxy(__null); // forget old value and old proxy as well
731 if (!aString)
732 fString.erase();
733 else
734 fString.assign(aString,aLen);
735 stringWasAssigned();
736} // TStringField::setAsString
737
738
739// append to string
740void TStringField::appendString(cAppCharP aString, size_t aMaxChars)
741{
742 PULLFROMPROXYpullFromProxy(); // make sure we have all chars
743 if (aString) {
744 fString.append(aString,aMaxChars);
745 }
746 stringWasAssigned();
747} // TStringField::appendString
748
749
750// merge field contents into this field
751bool TStringField::merge(TItemField &aItemField, const char aSep)
752{
753 bool mergedsomething=false;
754
755 if (aItemField.isBasedOn(fty_string)) {
756 TStringField *sfP = static_cast<TStringField *>(&aItemField);
757 #ifdef STREAMFIELD_SUPPORT1
758 pullFromProxy(); // make sure we have all chars
759 sfP->pullFromProxy(); // make sure we have all chars
760 #endif
761 if (aSep) {
762 // take each aSep separated part of source (aItemField), and if it is not
763 // yet part of target (this), then add it
764 string::size_type j,i=0;
765 string part;
766 do {
767 // extract part from source
768 j=sfP->fString.find(aSep,i);
769 if (j==string::npos)
770 part.assign(sfP->fString,i,sfP->fString.size()-i);
771 else
772 part.assign(sfP->fString,i,j-i);
773 // see if it is contained in target already
774 if (!part.empty() && fString.find(part)==string::npos) {
775 // not contained, add
776 if (!fString.empty()) fString+=aSep;
777 inherited::appendString(part);
778 mergedsomething=true;
779 }
780 // check next part
781 i=j+1; // char after last separator
782 } while (j!=string::npos);
783 }
784 else {
785 // no intelligent separator based merge, just append if not equal
786 if (sfP->fString != fString) {
787 inherited::appendString(sfP->fString);
788 mergedsomething=true;
789 }
790 }
791 }
792 return mergedsomething;
793} // TStringField::merge
794
795
796// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
797// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
798// types do not match.
799// Note: ordering may NOT be age relevant; it just means that an ordering
800// for this field type exists.
801// Note: Both fields must be assigned. NO TEST IS DONE HERE!
802sInt16 TStringField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
803{
804 int result;
805 PULLFROMPROXYpullFromProxy();
806 if (aItemField.isBasedOn(fty_string)) {
807 TStringField *sfP = static_cast<TStringField *>(&aItemField);
808 #ifdef STREAMFIELD_SUPPORT1
809 sfP->pullFromProxy(); // make sure we have all chars
810 #endif
811 // direct compare possible, return strcmp
812 if (aCaseInsensitive)
813 result=strucmp(fString.c_str(),sfP->fString.c_str());
814 else
815 result=fString.compare(sfP->fString);
816 }
817 else {
818 // convert other field to string
819 string s;
820 aItemField.getAsString(s);
821 if (aCaseInsensitive)
822 result=strucmp(fString.c_str(),s.c_str());
823 else
824 result=fString.compare(s);
825 }
826 return result >0 ? 1 : (result<0 ? -1 : 0);
827} // TStringField::compareWith
828
829
830// check if specified field is shortened version of this one
831bool TStringField::isShortVers(TItemField &aItemField, sInt32 aOthersMax)
832{
833 if (!aItemField.isBasedOn(fty_string)) return false; // different types
834 TStringField *sfP = static_cast<TStringField *>(&aItemField);
835 #ifdef STREAMFIELD_SUPPORT1
836 pullFromProxy(); // make sure we have all chars
837 sfP->pullFromProxy(); // make sure we have all chars
838 #endif
839 // same type
840 // - if other field is empty, it does not count as a shortened version in any case
841 if (sfP->isEmpty()) return false;
842 // - show if other field is shortened version of this one
843 return (
844 aOthersMax!=FIELD_OPT_MAXSIZE_NONE0 && // other field is limited (if not, cannot be short version)
845 (aOthersMax==FIELD_OPT_MAXSIZE_UNKNOWN-1 || uInt32(aOthersMax)==sfP->getStringSize()) && // size is unknown or other value is max size
846 findInString(sfP->fString.c_str())==0 // other value is contained at beginning of this value
847 );
848} // TStringField::isShortVers
849
850
851// - check if String is contained in value and returns position
852sInt16 TStringField::findInString(cAppCharP aString, bool aCaseInsensitive)
853{
854 PULLFROMPROXYpullFromProxy();
855 if (aString==NULL__null || *aString==0)
856 return fString.empty() ? 0 : -1; // if I am empty myself,treat empty string as contained at beginning
857 // no empty reference string
858 if (!aCaseInsensitive) {
859 return fString.find(aString);
860 }
861 else {
862 return strupos(fString.c_str(),aString,fString.size());
863 }
864};
865
866
867/* end of TStringField implementation */
868
869
870/*
871 * Implementation of TBlobField
872 */
873
874
875TBlobField::TBlobField()
876{
877 // nothing known about contents yet
878 fHasEncoding = enc_none;
879 fWantsEncoding = enc_none;
880 fCharset = chs_utf8;
881} // TBlobField::TBlobField
882
883
884TBlobField::~TBlobField()
885{
886} // TBlobField::~TBlobField
887
888
889// assignment
890TItemField& TBlobField::operator=(TItemField &aItemField)
891{
892 DELETEPROXYsetBlobProxy(__null); // forget old value and old proxy as well
893 // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
894 if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
895 // handle non-empty myself if other is based on string (blob is just a enhanced string)
896 if (aItemField.isBasedOn(fty_string)) {
897 // assign string portions
898 TStringField::operator=(aItemField);
899 if(aItemField.isBasedOn(fty_blob)) {
900 // other is a blob, copy blob specific options
901 const TBlobField *bfP = static_cast<const TBlobField *>(&aItemField);
902 fHasEncoding=bfP->fHasEncoding;
903 fWantsEncoding=bfP->fWantsEncoding;
904 fCharset=bfP->fCharset;
905 }
906 else {
907 // reset blob options to default for string content
908 fHasEncoding = enc_none;
909 fWantsEncoding = enc_none;
910 fCharset = chs_utf8;
911 }
912 }
913 else
914 TItemField::operator=(aItemField); // generic cross-type assignment (via string)
915 return *this;
916} // TBlobField::operator=
917
918
919
920#ifdef SYDEBUG2
921
922// debug support
923size_t TBlobField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
924{
925 // empty or unassigned is handled by base class
926 if (isEmpty()) { return inherited::StringObjFieldAppend(s, aMaxStrLen); }
927 // avoid pulling a proxy for debug
928 if (!PROXYINSTALLED(fBlobProxyP!=__null)) {
929 appendToString(s); // show standard string which is pseudo-content
930 return getStringSize(); // return actual size of BLOB
931 }
932 else
933 return inherited::StringObjFieldAppend(s, aMaxStrLen); // with proxy installed, base class will show that
934} // TBlobField::StringObjFieldAppend
935
936#endif // SYDEBUG
937
938
939/* end of TBlobField implementation */
940
941
942
943/*
944 * Implementation of TTelephoneField
945 */
946
947
948TTelephoneField::TTelephoneField()
949{
950} // TTelephoneField::TTelephoneField
951
952
953TTelephoneField::~TTelephoneField()
954{
955} // TTelephoneField::~TTelephoneField
956
957
958void TTelephoneField::getAsNormalizedString(string &aString)
959{
960 cAppCharP p = getCStr();
961 char c;
962 aString.erase();
963 // only returns alphanum and +,*,#, rest is filtered out
964 while ((c=*p++)!=0) {
965 if (isalnum(c) || c=='+' || c=='#' || c=='*') aString+=c;
966 }
967} // TTelephoneField::getAsNormalizedString
968
969
970// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
971// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
972// types do not match.
973// Note: ordering may NOT be age relevant; it just means that an ordering
974// for this field type exists.
975// Note: Both fields must be assigned. NO TEST IS DONE HERE!
976sInt16 TTelephoneField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
977{
978 if (!aItemField.isBasedOn(fty_telephone)) {
979 // if other field is not telephone, try normal string comparison
980 return TStringField::compareWith(aItemField, aCaseInsensitive);
981 }
982 // compare possible, return strcmp of normalized numbers
983 TTelephoneField *tfP = static_cast<TTelephoneField *>(&aItemField);
984 string s1,s2;
985 getAsNormalizedString(s1);
986 tfP->getAsNormalizedString(s2);
987 if (s1==s2) return 0; // equal
988 else return SYSYNC_NOT_COMPARABLE-999; // not equal, ordering makes no sense for tel numbers
989} // TTelephoneField::compareWith
990
991
992/* end of TTelephoneField implementation */
993
994
995/*
996 * Implementation of TTelephoneField
997 */
998
999TMultilineField::TMultilineField()
1000{
1001} // TMultilineField::TMultilineField
1002
1003
1004TMultilineField::~TMultilineField()
1005{
1006} // TMultilineField::~TMultilineField
1007
1008
1009// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
1010// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
1011// types do not match.
1012// Note: ordering may NOT be age relevant; it just means that an ordering
1013// for this field type exists.
1014// Note: Both fields must be assigned. NO TEST IS DONE HERE!
1015sInt16 TMultilineField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
1016{
1017 if (!aItemField.isBasedOn(fty_multiline)) {
1018 // if other field is not multiline, try normal string comparison
1019 return TStringField::compareWith(aItemField);
1020 }
1021 // compare possible, return strcmp of normalized texts
1022 TMultilineField *mfP = static_cast<TMultilineField *>(&aItemField);
1023 string s1,s2;
1024 getAsNormalizedString(s1);
1025 mfP->getAsNormalizedString(s2);
1026 // compare
1027 sInt8 result;
1028 if (aCaseInsensitive)
1029 result=strucmp(s1.c_str(),s2.c_str());
1030 else
1031 result=strcmp(s1.c_str(),s2.c_str());
1032 return result >0 ? 1 : (result<0 ? -1 : 0);
1033} // TMultilineField::compareWith
1034
1035
1036/* end of TMultilineField implementation */
1037
1038/*
1039 * Implementation of TURLField
1040 */
1041
1042TURLField::TURLField()
1043{
1044} // TURLField::TURLField
1045
1046
1047TURLField::~TURLField()
1048{
1049} // TURLField::~TURLField
1050
1051
1052
1053void TURLField::stringWasAssigned(void)
1054{
1055 // post-process string that was just assigned
1056 string proto;
1057 if (!fString.empty()) {
1058 // make sure we have a URL with protocol
1059 splitURL(fString.c_str() ,&proto, NULL__null, NULL__null, NULL__null, NULL__null, NULL__null, NULL__null);
1060 if (proto.empty()) {
1061 // no protocol set, but string not empty --> assume http
1062 fString.insert(0, "http://");
1063 }
1064 }
1065 inherited::stringWasAssigned();
1066} // TURLField::stringWasAssigned
1067
1068
1069/* end of TURLField implementation */
1070
1071
1072
1073/*
1074 * Implementation of TTimestampField
1075 */
1076
1077
1078TTimestampField::TTimestampField(GZones *aGZonesP)
1079{
1080 fGZonesP = aGZonesP;
1081 fTimestamp=0;
1082 fTimecontext=TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
1083} // TTimestampField::TTimestampField
1084
1085
1086TTimestampField::~TTimestampField()
1087{
1088} // TTimestampField::~TTimestampField
1089
1090
1091#if defined(CHECKSUM_CHANGELOG1) && !defined(RECORDHASH_FROM_DBAPI)
1092
1093// changelog support: calculate CRC over contents
1094uInt16 TTimestampField::getDataCRC(uInt16 crc)
1095{
1096 // calculate CRC such that only a effective change in the time zone will cause a
1097 // CRC change: TCTX_SYSTEM must be converted to UTC as changing the system time zone
1098 // should not re-sync the entire calendar
1099 lineartime_t crcts = fTimestamp;
1100 timecontext_t crcctx = fTimecontext;
1101 if (TCTX_IS_SYSTEM(crcctx)) {
1102 if (TzConvertTimestamp(crcts,crcctx,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),fGZonesP))
1103 crcctx=TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)); // use UTC representation for CRC (= same as in 3.0)
1104 }
1105 // try to avoid re-sync when upgrading from 3.0 to 3.1
1106 if (TCTX_IS_UTC(crcctx)) crcctx=0; // this is what we had in 3.0 for non-floating timestamps
1107 // CRC over timestamp itself and zone offset
1108 crc=sysync_crc16_block(&crcts,sizeof(crcts),crc);
1109 return sysync_crc16_block(&crcctx,sizeof(crcctx),crc);
1110} // TTimestampField::getDataCRC
1111
1112#endif
1113
1114
1115// assignment
1116TItemField& TTimestampField::operator=(TItemField &aItemField)
1117{
1118 // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
1119 if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
1120 // handle non-empty myself
1121 if (aItemField.isBasedOn(fty_timestamp)) {
1122 const TTimestampField *sfP = static_cast<const TTimestampField *>(&aItemField);
1123 fTimestamp=sfP->fTimestamp;
1124 fTimecontext=sfP->fTimecontext;
1125 fAssigned=sfP->fAssigned;
1126 }
1127 else if (aItemField.getCalcType()==fty_integer) {
1128 setAsInteger(aItemField.getAsInteger());
1129 }
1130 else
1131 TItemField::operator=(aItemField); // generic cross-type assignment (via string)
1132 return *this;
1133} // TTimestampField::operator=
1134
1135
1136// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
1137// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
1138// types do not match.
1139// Note: ordering may NOT be age relevant; it just means that an ordering
1140// for this field type exists.
1141// Note: Both fields must be assigned. NO TEST IS DONE HERE!
1142sInt16 TTimestampField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
1143{
1144 sInt16 res;
1145 lineartime_t cmpval;
1146 timecontext_t cmpcontext;
1147
1148 // determine value to compare with
1149 if (aItemField.isBasedOn(fty_timestamp)) {
1150 cmpval=static_cast<TTimestampField *>(&aItemField)->fTimestamp;
1151 cmpcontext=static_cast<TTimestampField *>(&aItemField)->fTimecontext;
1152 }
1153 else {
1154 // not same type
1155 if (aItemField.getCalcType()==fty_integer) {
1156 // use second argument as lineartime units number
1157 cmpval=aItemField.getAsInteger();
1158 cmpcontext=fTimecontext; // treat number as timestamp value in my own context
1159 }
1160 else {
1161 // use second argument as ISO8601 string
1162 string s;
1163 aItemField.getAsString(s);
1164 ISO8601StrToTimestamp(s.c_str(), cmpval, cmpcontext);
1165 if (TCTX_IS_UNKNOWN(cmpcontext)) cmpcontext=fTimecontext; // treat unqualified ISO timestamp in my own context
1166 }
1167 }
1168 // convert compare value into my own context (if not already so)
1169 if (!TzConvertTimestamp(cmpval,cmpcontext,fTimecontext,fGZonesP))
1170 return SYSYNC_NOT_COMPARABLE-999; // contexts are not compatible to compare
1171 // compare possible, return strcmp-type result
1172 res=fTimestamp==cmpval ? 0 : (fTimestamp>cmpval ? 1 : -1);
1173 if (res!=0) {
1174 // greater/less-type result is valid only if both sides are not empty
1175 if (fTimestamp==0) return SYSYNC_NOT_COMPARABLE-999; // empty value, cannot compare
1176 if (cmpval==0) return SYSYNC_NOT_COMPARABLE-999; // empty value, cannot compare
1177 }
1178 return res;
1179} // TTimestampField::compareWith
1180
1181
1182#ifdef SYDEBUG2
1183
1184// debug support
1185size_t TTimestampField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
1186{
1187 // empty or unassigned is handled by base class
1188 if (isEmpty()) { return inherited::StringObjFieldAppend(s, aMaxStrLen); }
1189
1190 string str;
1191 timecontext_t tctx;
1192 TimestampToISO8601Str(str,fTimestamp,fTimecontext,true,true);
1193 s.append(str);
1194 tctx=fTimecontext;
1195 if (TCTX_IS_TZ(tctx)) {
1196 // symbolic time zone not represented in ISO8601, show extra
1197 s.append(" (");
1198 if (TCTX_IS_UNKNOWN(tctx)) {
1199 s.append("floating");
1200 }
1201 else {
1202 if (TCTX_IS_SYSTEM(tctx)) {
1203 // is the system zone
1204 s.append("System ");
1205 // resolve from meta to symbolic zone
1206 TzResolveMetaContext(tctx,fGZonesP);
1207 }
1208 if (!TCTX_IS_BUILTIN(tctx)) {
1209 s.append("imported ");
1210 }
1211 s.append("TZ: ");
1212 // show time zone name
1213 TimeZoneContextToName(tctx,str,fGZonesP);
1214 s.append(str);
1215 }
1216 s.append(")");
1217 }
1218 // size not known
1219 return 0;
1220} // TTimestampField::StringObjFieldAppend
1221
1222#endif // SYDEBUG
1223
1224
1225// set timestamp from ISO8601 string
1226void TTimestampField::setAsString(cAppCharP aString)
1227{
1228 // - set context if contained in ISO string, otherwise leave it as TCTX_UNKNOWN
1229 setAsISO8601(aString, TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)), false);
1230} // TTimestampField::setAsString
1231
1232
1233// get timestamp as ISO8601 string (default representation)
1234void TTimestampField::getAsString(string &aString)
1235{
1236 // use default rendering
1237 // - show with current context, show UTC with Z but other zones without zone specifier
1238 getAsISO8601(aString,TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)),true,false,false,false);
1239} // getAsString
1240
1241
1242
1243
1244/// @brief add a delta time to the timestamp
1245/// @param aDeltaTime[in] : delta time value in lineartime_t units
1246void TTimestampField::addTime(lineartime_t aDeltaTime)
1247{
1248 fTimestamp=fTimestamp+aDeltaTime;
1249 fAssigned=true;
1250} // TTimestampField::addTime
1251
1252
1253/// @brief get time context
1254/// @return minute offset east of UTC, returns 0 for floating timestamps (and UTC, of course)
1255sInt16 TTimestampField::getMinuteOffset(void)
1256{
1257 sInt16 moffs;
1258
1259 if (TzResolveToOffset(fTimecontext, moffs, fTimestamp, false, fGZonesP))
1260 return moffs; // found offset
1261 else
1262 return 0; // no offset (e.g. floating timestamp)
1263} // TTimestampField::getMinuteOffset
1264
1265
1266/// @brief test for floating time (=time not in a specified zone context)
1267/// @return true if context is TCTX_UNKNOWN
1268bool TTimestampField::isFloating(void)
1269{
1270 return TCTX_IS_UNKNOWN(fTimecontext);
1271} // TTimestampField::isFloating
1272
1273
1274/// @brief make timestamp floating (i.e. remove time zone info from context)
1275void TTimestampField::makeFloating(void)
1276{
1277 // rendering flags from original context, new zone is UNKNOWN (=floating)
1278 fTimecontext = TCTX_JOIN_RFLAGS_TZ(fTimecontext, TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)));
1279} // TTimestampField::makeFloating
1280
1281
1282/// @brief test for duration
1283/// @return true if context has TCTX_DURATION rendering flag set
1284bool TTimestampField::isDuration(void)
1285{
1286 return TCTX_IS_DURATION(fTimecontext);
1287} // TTimestampField::isDuration
1288
1289
1290/// @brief make timestamp a duration (also implies making it floating)
1291void TTimestampField::makeDuration(void)
1292{
1293 // rendering flags from original context, new zone is UNKNOWN (=floating)
1294 fTimecontext |= TCTX_DURATION;
1295 makeFloating();
1296} // TTimestampField::makeDuration
1297
1298
1299/// @brief get timestamp converted to a specified time context
1300/// @param aTargetContext[in] : requests output context for timestamp.
1301/// Use TCTX_UNKNOWN to get timestamp as-is.
1302/// If timestamp is floating, it will always be returned as-is
1303/// @param aActualContext[out] : if not NULL, the actual context of the returned value
1304/// will be returned here. This might be another
1305// context than specified with aTargetContext depending on floating/notime status.
1306/// @return timestamp in lineartime
1307lineartime_t TTimestampField::getTimestampAs(timecontext_t aTargetContext, timecontext_t *aActualContext)
1308{
1309 // default context is that of the field (will be overwritten if result is moved to another context)
1310 if (aActualContext) *aActualContext = fTimecontext; // current field's context
1311 // if no time set or TCTX_UNKNOWN specified, just return the timestamp as-is
1312 if (fTimestamp==noLinearTime || TCTX_IS_UNKNOWN(aTargetContext)) {
1313 return
1314 TCTX_IS_DATEONLY(aTargetContext) ?
1315 lineartime2dateonlyTime(fTimestamp) : // truncated to date-only
1316 fTimestamp; // as-is
1317 }
1318 // convert to the requested context
1319 lineartime_t ts = fTimestamp;
1320 if (TCTX_IS_DATEONLY(fTimecontext)) {
1321 // is date only, connot convert to another zone, just return date part (and original context)
1322 return lineartime2dateonlyTime(ts);
1323 }
1324 else {
1325 // datetime or time
1326 if (!TzConvertTimestamp(ts,fTimecontext,aTargetContext,fGZonesP)) {
1327 // cannot convert, but we return unconverted timestamp if we can also return the actual context
1328 if (aActualContext)
1329 return fTimestamp; // return as-is
1330 else
1331 return noLinearTime; // invalid conversion, can't return a value
1332 }
1333 }
1334 // return target context as actual context
1335 if (aActualContext) *aActualContext = aTargetContext;
1336 // return timestamp, possibly truncated to date-only or time-only if requested
1337 if (TCTX_IS_DATEONLY(aTargetContext))
1338 return lineartime2dateonlyTime(ts);
1339 else if (TCTX_IS_TIMEONLY(aTargetContext))
1340 return lineartime2timeonly(ts);
1341 else
1342 return ts; // date and time
1343} // TTimestampField::getTimestampAs
1344
1345
1346
1347/// @brief get timestamp as ISO8601 string.
1348/// @param aISOString[out] : timestamp in ISO8601 format
1349/// @param aTargetContext[in] : requests output context for timestamp. Use TCTX_UNKNOWN to show timestamp (or dateonly or timeonly) as-is.
1350/// @param aShowWithZ[in] : if set and timezone is UTC, value will be shown with "Z" appended
1351/// @param aShowWithZone[in] : if set and timestamp is not floating, zone offset will be appended in +-xx:xx form
1352/// @param aExtFormat[in] : if set, ISO8601 extended format is used
1353/// @param aWithFracSecs[in] : if set, fractions of seconds will be shown (millisecond resolution)
1354void TTimestampField::getAsISO8601(string &aISOString, timecontext_t aTargetContext, bool aShowWithZ, bool aShowWithZone, bool aExtFormat, bool aWithFracSecs)
1355{
1356 // get time in requested context
1357 lineartime_t ts = getTimestampAs(aTargetContext);
1358 // if target context was set to TCTX_UNKNOWN, this means that we want to use the stored context
1359 if (aTargetContext==TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))) // really explicitly TCTX_UNKNOWN without any rendering flags!
1360 aTargetContext = fTimecontext;
1361 // return empty string if no timestamp or conversion impossible (but show empty duration as such!)
1362 if (ts==noLinearTime && !TCTX_IS_DURATION(fTimecontext)) {
1363 aISOString.erase();
1364 return;
1365 }
1366 // check if time zone should be included or not
1367 if (!TCTX_IS_UNKNOWN(aTargetContext)) {
1368 if (
1369 TCTX_IS_UTC(aTargetContext) ?
1370 !aShowWithZ :
1371 !aShowWithZone
1372 ) {
1373 // prevent showing with time zone
1374 aTargetContext = TCTX_JOIN_RFLAGS_TZ(aTargetContext,TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)));
1375 }
1376 }
1377 // now convert
1378 TimestampToISO8601Str(aISOString, ts, aTargetContext, aExtFormat, aWithFracSecs);
1379} // TTimestampField::getAsISO8601
1380
1381
1382
1383/// @brief move timestamp to specified context (i.e. convert the timestamp value from current to
1384/// specified context). Floating timestamps cannot and will not be moved.
1385/// @param aNewcontext[in] : context to move timestamp to.
1386/// timestamp will be converted to represent the same point in time in the new context
1387/// @param aSetUnmovables : if set, non-movable timestamps will be just assigned the new context,
1388// that is floating timestamps will be bound to specified context or
1389// non-floating timestamps will be made floating if new context is TCTX_UNKNOWN
1390bool TTimestampField::moveToContext(timecontext_t aNewcontext, bool aSetUnmovables)
1391{
1392 bool ok=true;
1393 if (isFloating() || fTimestamp==noLinearTime) {
1394 // floating and empty timestamp cannot be moved
1395 if (aSetUnmovables) {
1396 // bind floating timestamp to specified zone
1397 fTimecontext = aNewcontext;
1398 }
1399 else
1400 ok=false; // floating/empty timestamps can only be fixed, not moved
1401 }
1402 else {
1403 // timestamp is not floating or empty
1404 if (TCTX_IS_UNKNOWN(aNewcontext)) {
1405 if (aSetUnmovables)
1406 fTimecontext = aNewcontext; // make floating
1407 else
1408 ok = false;
1409 }
1410 else {
1411 // new context is not floating and old context isn't, either -> convert
1412 ok = TzConvertTimestamp(fTimestamp, fTimecontext, aNewcontext, fGZonesP);
1413 }
1414 }
1415 if (ok)
1416 fTimecontext = aNewcontext; // we are now in the new context
1417 return ok;
1418} // TTimestampField::moveToContext
1419
1420
1421
1422/// @brief set timestamp from ISO8601 string.
1423/// @return true if successful
1424/// @param aISOString[in] : timestamp in ISO8601 basic or extended format, optionally including Z or +xx:xx zone specifier
1425/// @param aDefaultContext[in] : timezone context to use when ISO8601 does not specify a zone context or when aIgnoreZone is true
1426/// @param aIgnoreZone[in] : if set, timezone specification contained in ISO8601 is ignored. Resulting time context will be aDefaultContext (or TCTX_UNKNOWN for date-only)
1427bool TTimestampField::setAsISO8601(cAppCharP aISOString, timecontext_t aDefaultContext, bool aIgnoreZone)
1428{
1429 bool ok = ISO8601StrToTimestamp(aISOString, fTimestamp, fTimecontext);
1430 if (ok) {
1431 fAssigned=true;
1432 // if timestamp has unknown zone because it is a date-only or a duration, do NOT assign the default zone!
1433 if (aIgnoreZone || (TCTX_IS_UNKNOWN(fTimecontext) && !TCTX_IS_DATEONLY(fTimecontext) && !TCTX_IS_DURATION(fTimecontext))) {
1434 // set default context
1435 fTimecontext = aDefaultContext;
1436 }
1437 }
1438 else
1439 assignEmpty();
1440 // in all cases, this is now assigned (but probably empty if string was not ok)
1441 return ok;
1442} // TTimestampField::setAsISO8601
1443
1444
1445
1446#ifdef EMAIL_FORMAT_SUPPORT1
1447
1448static cAppCharP const rfc822_weekdays[7] = {
1449 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1450};
1451
1452const size_t rfc822maxMonthLen = 3;
1453static cAppCharP const rfc822_months[12] = {
1454 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1455 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1456};
1457
1458
1459/// @brief get timestamp as RFC(2)822 style date
1460/// @param aRFC822String[out] : timestamp in RFC(2)822 format
1461/// @param aTargetContext[in] : requests output context for timestamp. Use TCTX_UNKNOWN to show timestamp as-is.
1462/// @param aShowWithZone[in] : if set and timestamp is not floating, zone offset will be shown
1463void TTimestampField::getAsRFC822date(string &aRFC822String, timecontext_t aTargetContext, bool aShowWithZone)
1464{
1465 // get time in requested context
1466 lineartime_t ts = getTimestampAs(aTargetContext);
1467 // if target context was set to TCTX_UNKNOWN, this means that we want to use the stored context
1468 if (TCTX_IS_UNKNOWN(aTargetContext))
1469 aTargetContext = fTimecontext;
1470 // get elements
1471 sInt16 y,mo,d,h,mi,s,ms,w, moffs;
1472 lineartime2date(ts,&y,&mo,&d);
1473 lineartime2time(ts,&h,&mi,&s,&ms);
1474 w = lineartime2weekday(ts);
1475 // now print
1476 StringObjPrintf(
1477 aRFC822String,
1478 "%s, %02hd %s %04hd %02hd:%02hd:%02hd",
1479 rfc822_weekdays[w],
1480 d,rfc822_months[mo-1],y,
1481 h,mi,s
1482 );
1483 if (aShowWithZone && !TCTX_IS_UNKNOWN(aTargetContext)) {
1484 // get offset
1485 TzResolveToOffset(aTargetContext, moffs, ts, false, fGZonesP);
1486 StringObjAppendPrintf(
1487 aRFC822String,
1488 " %c%02hd%02hd",
1489 moffs>=0 ? '+' : '-',
1490 (uInt16)(abs((int)moffs) / MinsPerHour),
1491 (uInt16)(abs((int)moffs) % MinsPerHour)
1492 );
1493 }
1494} // TTimestampField::getAsRFC822date
1495
1496
1497
1498/// @brief set timestamp as RFC(2)822 style date
1499/// @return true if successful
1500/// @param aRFC822String[in] : timestamp in RFC(2)822 format
1501/// @param aDefaultContext[in] : timezone context to use when RFC822 date does not specify a time zone
1502/// @param aIgnoreZone[in] : if set, timezone specification contained in input string is ignored. Resulting time context will be aDefaultContext
1503bool TTimestampField::setAsRFC822date(cAppCharP aRFC822String, timecontext_t aDefaultContext, bool aIgnoreZone)
1504{
1505 cAppCharP p;
1506 size_t inpSiz = strlen(aRFC822String);
1507 cAppCharP eot=aRFC822String+inpSiz;
1508 sInt16 minoffs=0;
1509 // check for weekday
1510 p = (cAppCharP) memchr(aRFC822String,',',inpSiz);
1511 if (p)
1512 p++; // skip comma
1513 else
1514 p=aRFC822String; // start at beginning
1515 // scan elements
1516 char month[rfc822maxMonthLen+1];
1517 sInt16 y,mo,d,h,m,s;
1518 s=0; // optional second
1519 // scan day
1520 while (p<eot && *p==' ') p++;
1521 if (p+2>eot) return false; // string too short
1522 p+=StrToShort(p,d,2);
1523 // scan month
1524 while (p<eot && *p==' ') p++;
1525 uInt8 mi=0;
1526 while (p<eot && *p!=' ' && mi<rfc822maxMonthLen) month[mi++]=*p++;
1527 month[mi]=0;
1528 // scan year
1529 while (p<eot && *p==' ') p++;
1530 if (p+4>eot) return false; // string too short
1531 p+=StrToShort(p,y,4);
1532 // scan time
1533 while (p<eot && *p==' ') p++;
1534 if (p+2>eot) return false; // string too short
1535 p+=StrToShort(p,h,2);
1536 if (p+3>eot || *p++ !=':') return false; // one for colon, two for minutes
1537 p+=StrToShort(p,m,2);
1538 if (p+3<=eot && *p==':') { // one for colon, two for minutes
1539 // optional second
1540 p++;
1541 p+=StrToShort(p,s,2);
1542 }
1543 // convert month
1544 for (mo=0; mo<12; mo++) if (strucmp(month,rfc822_months[mo])==0) break;
1545 if (mo==12) return false; // bad format
1546 mo++; // make month number
1547 // make timestamp
1548 fTimestamp = date2lineartime(y,mo,d) + time2lineartime(h,m,s,0);
1549 // check time zone
1550 bool neg=false;
1551 while (p<eot && *p==' ') p++;
1552 if (!aIgnoreZone) {
1553 aIgnoreZone=true; // assume invalid zone spec
1554 // check for numeric time zone
1555 if (*p=='-' || *p=='+') {
1556 // there is a timezone and we don't want to ignore it
1557 if (*p=='-') neg=true;
1558 if (p>=eot) return false; // string too short
1559 p++;
1560 sInt16 tzh,tzm;
1561 // scan timezone
1562 if (
1563 p+4<=eot &&
1564 StrToShort(p,tzh,2)==2 &&
1565 StrToShort(p+2,tzm,2)==2
1566 )
1567 {
1568 p+=4;
Value stored to 'p' is never read
1569 // set time zone
1570 minoffs=((tzh*MinsPerHour)+tzm);
1571 if (neg) minoffs = -minoffs;
1572 // make zone
1573 fTimecontext = TCTX_OFFSCONTEXT(minoffs)((timecontext_t)((sInt16)(minoffs) & TCTX_OFFSETMASK ));
1574 aIgnoreZone=false; // zone spec valid
1575 }
1576 }
1577 else if (isalpha(*p)) {
1578 // could be time zone name, internal or olson name (if not, ignore zone spec)
1579 aIgnoreZone = !TimeZoneNameToContext(p,fTimecontext,fGZonesP,true);
1580 }
1581 }
1582 // if no valid zone, use default
1583 if (aIgnoreZone) {
1584 // assume default time
1585 fTimecontext = aDefaultContext;
1586 }
1587 return true;
1588} // RFC822dateToTimeStamp
1589
1590
1591#endif // EMAIL_FORMAT_SUPPORT
1592
1593
1594
1595/* end of TTimestampField implementation */
1596
1597
1598/*
1599 * Implementation of TDateField
1600 */
1601
1602
1603TDateField::TDateField(GZones *aGZonesP) :
1604 TTimestampField(aGZonesP)
1605{
1606 fTimecontext |= TCTX_DATEONLY;
1607} // TDateField::TDateField
1608
1609
1610TDateField::~TDateField()
1611{
1612} // TDateField::~TDateField
1613
1614
1615// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
1616// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
1617// types do not match.
1618// Note: ordering may NOT be age relevant; it just means that an ordering
1619// for this field type exists.
1620// Note: Both fields must be assigned. NO TEST IS DONE HERE!
1621sInt16 TDateField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
1622{
1623 sInt16 res;
1624
1625 // check comparison with non-date
1626 if (!aItemField.isBasedOn(fty_timestamp))
1627 return TTimestampField::compareWith(aItemField); // handles all comparisons with other types
1628 // date comparison is context independent
1629 TTimestampField *sfP = static_cast<TTimestampField *>(&aItemField);
1630 // compare possible, return strcmp-type result
1631 // - convert to date only
1632 lineardate_t a = getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))+TCTX_DATEONLY) / linearDateToTimeFactor;
1633 lineardate_t b = sfP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))+TCTX_DATEONLY) / linearDateToTimeFactor;
1634 // - compare dates
1635 res=a==b ? 0 : (a>b ? 1 : -1);
1636 if (res!=0) {
1637 // greater/less-type result is valid only if both sides are not empty
1638 if ( isEmpty()) return SYSYNC_NOT_COMPARABLE-999; // empty value, cannot compare
1639 if (sfP->isEmpty()) return SYSYNC_NOT_COMPARABLE-999; // empty value, cannot compare
1640 }
1641 return res;
1642} // TDateField::compareWith
1643
1644
1645// get date as ISO8601 string (default representation)
1646void TDateField::getAsString(string &aString)
1647{
1648 // use default rendering
1649 // - show with current context, but always date-only, no Z or zone info
1650 inherited::getAsISO8601(aString,TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))+TCTX_DATEONLY,false,false,false,false);
1651} // TDateField::getAsString
1652
1653
1654// set date from ISO8601 string (default interpretation)
1655void TDateField::setAsString(cAppCharP aString)
1656{
1657 // default interpretation is floating date
1658 // - parse timestamp, ignore timezone
1659 if (setAsISO8601(aString, TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))+TCTX_DATEONLY, true)) {
1660 // truncate to date-only
1661 fTimestamp = lineartime2dateonlyTime(fTimestamp);
1662 }
1663 // in all cases, this is now assigned (but probably empty)
1664 fAssigned=true;
1665} // TDateField::setAsString
1666
1667
1668
1669/* end of TDateField implementation */
1670
1671
1672/*
1673 * Implementation of TIntegerField
1674 */
1675
1676
1677TIntegerField::TIntegerField()
1678{
1679 fInteger=0;
1680 fEmpty=true;
1681} // TIntegerField::TIntegerField
1682
1683
1684TIntegerField::~TIntegerField()
1685{
1686} // TIntegerField::~TIntegerField
1687
1688
1689#if defined(CHECKSUM_CHANGELOG1) && !defined(RECORDHASH_FROM_DBAPI)
1690
1691// changelog support: calculate CRC over contents
1692uInt16 TIntegerField::getDataCRC(uInt16 crc)
1693{
1694 // CRC over integer number
1695 return sysync_crc16_block(&fInteger,sizeof(fInteger),crc);
1696} // TIntegerField::getDataCRC
1697
1698#endif
1699
1700
1701// assignment
1702TItemField& TIntegerField::operator=(TItemField &aItemField)
1703{
1704 // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
1705 if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
1706 // handle non-empty myself
1707 if (aItemField.isBasedOn(fty_integer)) {
1708 const TIntegerField *sfP = static_cast<const TIntegerField *>(&aItemField);
1709 fInteger=sfP->fInteger;
1710 fEmpty=sfP->fEmpty;
1711 fAssigned=sfP->fAssigned;
1712 }
1713 else if (aItemField.getCalcType()==fty_integer) {
1714 fInteger=aItemField.getAsInteger();
1715 fEmpty=false;
1716 fAssigned=aItemField.isAssigned();
1717 }
1718 else
1719 TItemField::operator=(aItemField); // generic cross-type assignment (via string)
1720 return *this;
1721} // TIntegerField::operator=
1722
1723
1724// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
1725// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
1726// types do not match.
1727// Note: ordering may NOT be age relevant; it just means that an ordering
1728// for this field type exists.
1729// Notes: - Both fields must be assigned. NO TEST IS DONE HERE!
1730// - emptyness is not taken into account (empty counts as 0 here)
1731sInt16 TIntegerField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
1732{
1733 if (aItemField.getCalcType()!=fty_integer) return TItemField::compareWith(aItemField); // handles generic comparisons
1734 fieldinteger_t otherInt = aItemField.getAsInteger();
1735 // compare possible, return strcmp-type result
1736 return fInteger==otherInt ? 0 : (fInteger>otherInt ? 1 : -1);
1737} // TIntegerField::compareWith
1738
1739
1740// get integer as integer
1741fieldinteger_t TIntegerField::getAsInteger(void)
1742{
1743 return fInteger;
1744} // TIntegerField::getAsInteger
1745
1746
1747// set integer
1748void TIntegerField::setAsInteger(fieldinteger_t aInteger)
1749{
1750 fInteger=aInteger;
1751 fAssigned=true;
1752 fEmpty=false;
1753} // TIntegerField::setAsInteger
1754
1755
1756
1757// set integer from numeric string
1758void TIntegerField::setAsString(cAppCharP aString)
1759{
1760 // check for hex
1761 bool ok;
1762 if (strucmp(aString,"0x",2)==0) {
1763 // hex number
1764 #ifndef NO64BITINT
1765 ok = HexStrToULongLong(aString+2,*((uInt64 *)&fInteger));
1766 #else
1767 ok = HexStrToULong(aString+2,*((uInt32 *)&fInteger));
1768 #endif
1769 }
1770 else {
1771 // decimal integer number
1772 #ifndef NO64BITINT
1773 ok = StrToLongLong(aString,fInteger);
1774 #else
1775 ok = StrToLong(aString,fInteger);
1776 #endif
1777 }
1778 if (!ok)
1779 fInteger=0; // defined value
1780 // setting with a non-integer value makes the integer empty (but having integer value 0)
1781 fEmpty = !ok;
1782 // in all cases, this is now assigned (but probably with the default=0)
1783 fAssigned=true;
1784} // TIntegerField::setAsString
1785
1786
1787// get integer as numeric string
1788void TIntegerField::getAsString(string &aString)
1789{
1790 if (fEmpty)
1791 aString.erase();
1792 else {
1793 #ifndef NO64BITINT
1794 LONGLONGTOSTR(aString,PRINTF_LLD_ARG(fInteger))StringObjPrintf(aString,"%lld",static_cast<long long>(fInteger
))
;
1795 #else
1796 StringObjPrintf(aString,"%ld",(sInt32)fInteger);
1797 #endif
1798 }
1799} // TIntegerField::getAsString
1800
1801
1802/* end of TIntegerField implementation */
1803
1804
1805// factory function
1806TItemField *newItemField(const TItemFieldTypes aType, GZones *aGZonesP, bool aAsArray)
1807{
1808 #ifdef ARRAYFIELD_SUPPORT1
1809 if (aAsArray) {
1810 return new TArrayField(aType,aGZonesP);
1811 }
1812 else
1813 #endif
1814 {
1815 switch (aType) {
1816 case fty_string: return new TStringField;
1817 case fty_telephone: return new TTelephoneField;
1818 case fty_integer: return new TIntegerField;
1819 case fty_timestamp: return new TTimestampField(aGZonesP);
1820 case fty_date: return new TDateField(aGZonesP);
1821 case fty_url: return new TURLField;
1822 case fty_multiline: return new TMultilineField;
1823 case fty_blob: return new TBlobField;
1824 case fty_none: return new TItemField; // base class, can represent EMPTY and UNASSIGNED
1825 default: return NULL__null;
1826 }
1827 }
1828} // newItemField
1829
1830
1831#ifdef ENGINEINTERFACE_SUPPORT1
1832
1833// TItemFieldKey
1834// =============
1835
1836// get value's ID (e.g. internal index)
1837sInt32 TItemFieldKey::GetValueID(cAppCharP aName)
1838{
1839 // check special suffixes
1840 size_t namsz = strlen(aName);
1841 sInt32 fldID = 0; // basic
1842 if (namsz >= strlen(VALSUFF_TZNAME".TZNAME") &&
1843 strucmp(aName+namsz-strlen(VALSUFF_TZNAME".TZNAME"),VALSUFF_TZNAME".TZNAME")==0) {
1844 // time zone name as string requested
1845 namsz-=7;
1846 fldID += VALID_FLAG_TZNAME0x010000;
1847 }
1848 else if (namsz >= strlen(VALSUFF_TZOFFS".TZOFFS") &&
1849 strucmp(aName+namsz-strlen(VALSUFF_TZOFFS".TZOFFS"),VALSUFF_TZOFFS".TZOFFS")==0) {
1850 // time zone offset in minutes
1851 namsz-=7;
1852 fldID += VALID_FLAG_TZOFFS0x020000;
1853 }
1854 else if (namsz >= strlen(VALSUFF_NORM".NORM") &&
1855 strucmp(aName+namsz-strlen(VALSUFF_NORM".NORM"),VALSUFF_NORM".NORM")==0) {
1856 // normalized value
1857 namsz-=5;
1858 fldID += VALID_FLAG_NORM0x200000;
1859 }
1860 else if (namsz >= strlen(VALSUFF_ARRSZ".ARRAYSIZE") &&
1861 strucmp(aName+namsz-strlen(VALSUFF_ARRSZ".ARRAYSIZE"),VALSUFF_ARRSZ".ARRAYSIZE")==0) {
1862 // array size
1863 namsz-=10;
1864 fldID += VALID_FLAG_ARRSIZ0x040000;
1865 }
1866 else if (namsz >= strlen(VALSUFF_NAME".VALNAME") &&
1867 strucmp(aName+namsz-strlen(VALSUFF_NAME".VALNAME"),VALSUFF_NAME".VALNAME")==0) {
1868 // value name
1869 namsz-=8;
1870 fldID += VALID_FLAG_VALNAME0x080000;
1871 }
1872 else if (namsz >= strlen(VALSUFF_TYPE".VALTYPE") &&
1873 strucmp(aName+namsz-strlen(VALSUFF_TYPE".VALTYPE"),VALSUFF_TYPE".VALTYPE")==0) {
1874 // value type
1875 namsz-=8;
1876 fldID += VALID_FLAG_VALTYPE0x100000;
1877 }
1878 // check if this is only a query for the flags alone
1879 if (strucmp(aName,VALNAME_FLAG".FLAG",namsz)==0)
1880 return fldID; // return flags alone
1881 // get fid for given name
1882 sInt16 fid = getFidFor(aName,namsz);
1883 if (fid==VARIDX_UNDEFINED-128)
1884 return KEYVAL_ID_UNKNOWN; // unknown field
1885 else
1886 return fldID + (sInt32)((uInt16)fid); // return FID in lo word, flags in highword
1887} // TItemFieldKey::GetValueID
1888
1889
1890
1891// get value's native type
1892uInt16 TItemFieldKey::GetValueType(sInt32 aID)
1893{
1894 // fid is lower 16 bits of aID (and gets negative if bit15 of aID is set)
1895 sInt16 fid;
1896 *((uInt16 *)(&fid)) = aID & VALID_MASK_FID0x00FFFF; // assign without sign extension
1897 // now get base field
1898 TItemField *baseFieldP = getBaseFieldFromFid(fid);
1899 if (baseFieldP) {
1900 // field exists
1901 // - check special queries
1902 if (aID & VALID_FLAG_ARRSIZ0x040000)
1903 return VALTYPE_INT16; // size is a 16-bit integer, regardless of field type
1904 else if (aID & VALID_FLAG_VALNAME0x080000)
1905 return VALTYPE_TEXT; // name of the value is text
1906 else if (aID & VALID_FLAG_VALTYPE0x100000)
1907 return VALTYPE_INT16; // VALTYPE_XXX are 16-bit integers
1908 else if (aID & VALID_FLAG_NORM0x200000)
1909 return VALTYPE_TEXT; // normalized value is text
1910 // - otherwise, valtype depends on field type
1911 TItemFieldTypes fty = baseFieldP->getElementType();
1912 // return native type
1913 switch (fty) {
1914 case fty_none:
1915 return VALTYPE_UNKNOWN;
1916 case fty_blob:
1917 return VALTYPE_BUF;
1918 case fty_string:
1919 case fty_multiline:
1920 case fty_telephone:
1921 case fty_url:
1922 return VALTYPE_TEXT;
1923 case fty_integer:
1924 return VALTYPE_INT64;
1925 case fty_timestamp:
1926 case fty_date:
1927 if (aID & VALID_FLAG_TZNAME0x010000)
1928 return VALTYPE_TEXT; // time zone name is a text
1929 else if (aID & VALID_FLAG_TZOFFS0x020000)
1930 return VALTYPE_INT16; // minute offset is 16-bit integer
1931 else
1932 return VALTYPE_TIME64; // time values
1933 case numFieldTypes:
1934 // invalid
1935 break;
1936 } // switch
1937 }
1938 // no field, no type
1939 return VALTYPE_UNKNOWN;
1940} // TItemFieldKey::GetValueType
1941
1942
1943// helper for getting a leaf field pointer (interpretation of aRepOffset depends on array field or not)
1944TItemField *TItemFieldKey::getFieldFromFid(sInt16 aFid, sInt16 aRepOffset, bool aExistingOnly)
1945{
1946 TItemField *fieldP=NULL__null;
1947 // get field (or base field)
1948 #ifdef ARRAYFIELD_SUPPORT1
1949 fieldP = getBaseFieldFromFid(aFid);
1950 if (!fieldP) return NULL__null; // no field
1951 if (fieldP->isArray()) {
1952 // use aRepOffset as array index
1953 fieldP = fieldP->getArrayField(aRepOffset,aExistingOnly);
1954 }
1955 else
1956 #endif
1957 {
1958 // use aRepOffset as fid offset
1959 #ifdef SCRIPT_SUPPORT1
1960 if (aFid<0) aRepOffset=0; // locals are never offset
1961 #endif
1962 fieldP = getBaseFieldFromFid(aFid+aRepOffset);
1963 }
1964 return fieldP;
1965} // TItemFieldKey::getFieldFromFid
1966
1967
1968
1969
1970// get value
1971TSyError TItemFieldKey::GetValueInternal(
1972 sInt32 aID, sInt32 aArrayIndex,
1973 appPointer aBuffer, memSize aBufSize, memSize &aValSize
1974)
1975{
1976 string sval;
1977 TItemField *fieldP;
1978 // fid is lower 16 bits of aID (and gets negative if bit15 of aID is set)
1979 sInt16 fid;
1980 *((uInt16 *)(&fid)) = aID & VALID_MASK_FID0x00FFFF; // assign without sign extension
1981
1982 // check for special queries not actually accessing a leaf field
1983 if (aID & VALID_FLAG_ARRSIZ0x040000) {
1984 // return array size (returns DB_NotFound for non-arrays)
1985 fieldP = getBaseFieldFromFid(fid);
1986 // if not an array, return DB_NotFound
1987 if (!fieldP || !fieldP->isArray())
1988 return DB_NotFound; // there is no array size for non-arrays
1989 // return size
1990 aValSize=sizeof(sInt16);
1991 // value if enough room
1992 if (aBufSize>=aValSize)
1993 *((sInt16 *)aBuffer)=fieldP->arraySize();
1994 // ok
1995 return LOCERR_OK;
1996 }
1997 else if (aID & VALID_FLAG_VALNAME0x080000) {
1998 // only return name of field
1999 if (!getFieldNameFromFid(fid,sval)) return DB_NotFound;
2000 if (aBufSize>=sval.size())
2001 memcpy(aBuffer,sval.c_str(),sval.size()); // copy name data
2002 aValSize=sval.size();
2003 return LOCERR_OK;
2004 }
2005 else if (aID & VALID_FLAG_VALTYPE0x100000) {
2006 // only return VALTYPE of field
2007 uInt16 valtype = GetValueType(aID & ~VALID_FLAG_VALTYPE0x100000);
2008 aValSize = sizeof(valtype);
2009 if (aBufSize>=aValSize)
2010 memcpy(aBuffer,&valtype,aValSize); // copy valtype uInt16
2011 return LOCERR_OK;
2012 }
2013 // Get actual data - we need the leaf field (unless we pass <0 for aArrayIndex, which accesses the array base field)
2014 fieldP = getFieldFromFid(fid, aArrayIndex, true); // existing array elements only
2015 if (!fieldP) {
2016 // array instance does not exist
2017 return LOCERR_OUTOFRANGE;
2018 }
2019 else {
2020 // leaf field (or explicitly requested array base field) exists
2021 if (!fieldP->isAssigned()) return DB_NotFound; // no content found because none assigned
2022 if (fieldP->isEmpty()) return DB_NoContent; // empty
2023 if (fieldP->isArray()) return LOCERR_OUTOFRANGE; // no real access for array base field possible - if we get here it means non-empty
2024 // assigned and not empty, return actual value
2025 TItemFieldTypes fty = fieldP->getType();
2026 appPointer valPtr = NULL__null;
2027 size_t sz;
2028 fieldinteger_t intVal;
2029 lineartime_t ts;
2030 timecontext_t tctx;
2031 sInt16 minOffs;
2032 TTimestampField *tsFldP;
2033 if (aID & VALID_FLAG_NORM0x200000) {
2034 // for all field types: get normalized string value
2035 fieldP->getAsNormalizedString(sval);
2036 aValSize = sval.size();
2037 valPtr = (appPointer)sval.c_str();
2038 }
2039 else {
2040 switch (fty) {
2041 case fty_timestamp:
2042 case fty_date:
2043 tsFldP = static_cast<TTimestampField *>(fieldP);
2044 if (aID & VALID_FLAG_TZNAME0x010000) {
2045 // time zone name is a text
2046 sval.erase(); // no zone
2047 tctx = tsFldP->getTimeContext();
2048 if (!TCTX_IS_UNKNOWN(tctx) || TCTX_IS_DURATION(tctx) || TCTX_IS_DATEONLY(tctx)) {
2049 // has a zone (or is duration/dateonly), get name
2050 TimeZoneContextToName(tctx, sval, tsFldP->getGZones());
2051 }
2052 aValSize = sval.size();
2053 valPtr = (appPointer)sval.c_str();
2054 }
2055 else if (aID & VALID_FLAG_TZOFFS0x020000) {
2056 // minute offset as 16-bit integer
2057 aValSize = sizeof(sInt16);
2058 if (
2059 tsFldP->isFloating() ||
2060 !TzResolveToOffset(tsFldP->getTimeContext(), minOffs, tsFldP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))), false, tsFldP->getGZones())
2061 )
2062 return DB_NotFound; // cannot get minute offset or floating timestamp -> no result
2063 // return minute offset
2064 valPtr = &minOffs;
2065 }
2066 else {
2067 // return timestamp itself (either as-is or in UTC, if TMODE_FLAG_FLOATING not set)
2068 aValSize = sizeof(lineartime_t);
2069 if (fTimeMode & TMODE_FLAG_FLOATING)
2070 ts = tsFldP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))); // as-is
2071 else
2072 ts = tsFldP->getTimestampAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),&tctx); // always as UTC, will be converted to SYSTEM possibly by caller
2073 // return timestamp
2074 valPtr = &ts;
2075 }
2076 break;
2077 case fty_integer:
2078 aValSize = sizeof(fieldinteger_t);
2079 intVal = fieldP->getAsInteger();
2080 valPtr = &intVal;
2081 break;
2082 case fty_none:
2083 aValSize = 0;
2084 break;
2085 case fty_blob:
2086 static_cast<TBlobField *>(fieldP)->getBlobDataPtrSz(valPtr,sz);
2087 aValSize = sz;
2088 break;
2089 case fty_string:
2090 case fty_multiline:
2091 case fty_telephone:
2092 case fty_url:
2093 aValSize = fieldP->getStringSize();
2094 valPtr = (appPointer)static_cast<TStringField *>(fieldP)->getCStr();
2095 break;
2096 case numFieldTypes:
2097 // invalid
2098 break;
2099 } // switch
2100 }
2101 // now we have valid aValSize and valPtr
2102 if (aBuffer && aBufSize>=aValSize) {
2103 // copy value data
2104 memcpy(aBuffer,valPtr,aValSize);
2105 }
2106 }
2107 // ok
2108 return LOCERR_OK;
2109} // TItemFieldKey::GetValueInternal
2110
2111
2112
2113// set value
2114TSyError TItemFieldKey::SetValueInternal(
2115 sInt32 aID, sInt32 aArrayIndex,
2116 cAppPointer aBuffer, memSize aValSize
2117)
2118{
2119 TItemField *fieldP;
2120 // fid is lower 16 bits of aID (and gets negative if bit15 of aID is set)
2121 sInt16 fid;
2122 *((uInt16 *)(&fid)) = aID & VALID_MASK_FID0x00FFFF; // assign without sign extension
2123
2124 // check for special array size query
2125 if (aID & VALID_FLAG_ARRSIZ0x040000) {
2126 // array size is not writable
2127 return DB_NotAllowed;
2128 }
2129 // now get leaf field
2130 fieldP = getFieldFromFid(fid, aArrayIndex, false); // create element if needed
2131 if (!fieldP) {
2132 // should not happen
2133 return DB_Error;
2134 }
2135 else {
2136 // leaf field exists, set value
2137 fWritten = true;
2138 TItemFieldTypes fty = fieldP->getType();
2139 fieldinteger_t intVal;
2140 lineartime_t ts;
2141 timecontext_t tctx;
2142 sInt16 minOffs;
2143 string sval;
2144 TTimestampField *tsFldP;
2145 // treat setting normalized value like setting as string
2146 if (aID & VALID_FLAG_NORM0x200000)
2147 fty = fty_string; // treat like string
2148 // handle NULL (empty) case
2149 if (aBuffer==0) {
2150 // buffer==NULL means NULL value
2151 fieldP->assignEmpty();
2152 return LOCERR_OK;
2153 }
2154 // now handle according to type
2155 switch (fty) {
2156 case fty_timestamp:
2157 case fty_date:
2158 tsFldP = static_cast<TTimestampField *>(fieldP);
2159 if (aID & VALID_FLAG_TZNAME0x010000) {
2160 // set time zone by name
2161 sval.assign((cAppCharP)aBuffer,aValSize);
2162 tctx = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
2163 if (!sval.empty()) {
2164 // convert (internal or olson names allowed)
2165 if (!TimeZoneNameToContext(sval.c_str(), tctx, tsFldP->getGZones(), true))
2166 return LOCERR_BADPARAM; // bad timezone name
2167 }
2168 // set context
2169 tsFldP->setTimeContext(tctx);
2170 }
2171 else if (aID & VALID_FLAG_TZOFFS0x020000) {
2172 // minute offset as 16-bit integer
2173 minOffs = *((sInt16 *)aBuffer);
2174 // set context
2175 tsFldP->setTimeContext(TCTX_MINOFFSET(minOffs));
2176 }
2177 else {
2178 // set timestamp itself (either as-is or in UTC, if TMODE_FLAG_FLOATING not set)
2179 ts = *((lineartime_t *)aBuffer);
2180 if ((fTimeMode & TMODE_FLAG_FLOATING)==0)
2181 tsFldP->setTimestampAndContext(ts,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ))); // incoming timestamp is UTC, set it as such
2182 else
2183 tsFldP->setTimestamp(ts); // just set timestamp as-is and don't touch rest
2184 }
2185 break;
2186 case fty_integer:
2187 intVal = *((fieldinteger_t *)aBuffer);
2188 fieldP->setAsInteger(intVal);
2189 break;
2190 case fty_blob:
2191 static_cast<TBlobField *>(fieldP)->setBlobDataPtrSz((void *)aBuffer,aValSize);
2192 break;
2193 case fty_string:
2194 case fty_multiline:
2195 case fty_telephone:
2196 case fty_url:
2197 fieldP->setAsString((cAppCharP)aBuffer,aValSize);
2198 break;
2199 case fty_none:
2200 case numFieldTypes:
2201 // invalid
2202 break;
2203 } // switch
2204 }
2205 // ok
2206 return LOCERR_OK;
2207} // TItemFieldKey::SetValueInternal
2208
2209
2210#endif // ENGINEINTERFACE_SUPPORT
2211
2212
2213
2214} // namespace sysync
2215
2216// eof