meos-2024/code/oDataContainer.cpp
2021-12-13 19:28:59 +01:00

1452 lines
40 KiB
C++

/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2021 Melin Software HB
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
#include "stdafx.h"
#include <set>
#include "oDataContainer.h"
#include "oEvent.h"
#include "gdioutput.h"
#include "xmlparser.h"
#include "Table.h"
#include "meos_util.h"
#include "Localizer.h"
#include "meosException.h"
oDataContainer::oDataContainer(int maxsize) {
dataPointer = 0;
dataMaxSize = maxsize;
stringIndexPointer = 0;
stringArrayIndexPointer = 2;
}
oDataContainer::~oDataContainer(void) {
}
CellType oDataDefiner::getCellType() const {
return CellType::cellEdit;
}
oDataInfo::oDataInfo() {
memset(Name, 0, sizeof(Name));
Index = 0;
Size = 0;
Type = 0;
SubType = 0;
tableIndex = 0;
decimalSize = 0;
decimalScale = 1;
zeroSortPadding = 0;
memset(Description, 0, sizeof(Description));
}
oDataInfo::~oDataInfo() {
}
oDataInfo &oDataContainer::addVariableInt(const char *name,
oIntSize isize, const char *description,
const shared_ptr<oDataDefiner> &dataDef) {
oDataInfo odi;
odi.dataDefiner = dataDef;
odi.Index=dataPointer;
strcpy_s(odi.Name, name);
strcpy_s(odi.Description, description);
if (isize == oIS64)
odi.Size=sizeof(__int64);
else
odi.Size=sizeof(int);
odi.Type=oDTInt;
odi.SubType=isize;
if (dataPointer+odi.Size<=dataMaxSize){
dataPointer+=odi.Size;
return addVariable(odi);
}
else
throw std::exception("oDataContainer: Out of bounds.");
}
oDataInfo &oDataContainer::addVariableDecimal(const char *name, const char *descr, int fixedDeci) {
oDataInfo &odi = addVariableInt(name, oISDecimal, descr);
odi.decimalSize = fixedDeci;
int &s = odi.decimalScale;
s = 1;
for (int k = 0; k < fixedDeci; k++)
s*=10;
return odi;
}
oDataInfo &oDataContainer::addVariableString(const char *name, const char *descr, const shared_ptr<oDataDefiner> &dataDef) {
return addVariableString(name, -1, descr, dataDef);
}
oDataInfo &oDataContainer::addVariableString(const char *name, int maxChar,
const char *descr, const shared_ptr<oDataDefiner> &dataDef)
{
oDataInfo odi;
odi.dataDefiner = dataDef;
strcpy_s(odi.Name,name);
strcpy_s(odi.Description,descr);
if (maxChar > 0) {
odi.Index = dataPointer;
odi.Size = sizeof(wchar_t)*(maxChar+1);
odi.Type = oDTString;
odi.SubType = oSSString;
if (dataPointer+odi.Size<=dataMaxSize){
dataPointer+=odi.Size;
return addVariable(odi);
}
else
throw std::exception("oDataContainer: Out of bounds.");
}
else {
odi.Index = stringIndexPointer++;
odi.Size = 0;
odi.Type = oDTStringDynamic;
odi.SubType = oSSString;
return addVariable(odi);
}
}
oDataInfo &oDataContainer::addVariableEnum(const char *name, int maxChar, const char *descr,
const vector< pair<wstring, wstring> > enumValues) {
oDataInfo &odi = addVariableString(name, maxChar, descr);
odi.SubType = oSSEnum;
for (size_t k = 0; k<enumValues.size(); k++)
odi.enumDescription.push_back(enumValues[k]);
return odi;
}
oDataInfo &oDataContainer::addVariable(oDataInfo &odi) {
if (findVariable(odi.Name))
throw std::exception("oDataContainer: Variable already exist.");
//index[odi.Name]=odi;
index.insert(hash(odi.Name), ordered.size());
ordered.push_back(odi);
return ordered.back();
}
int oDataContainer::hash(const char *name) {
int res = 0;
while(*name != 0) {
res = 31 * res + *name;
name++;
}
return res;
}
oDataInfo *oDataContainer::findVariable(const char *name) {
/* map<string, oDataInfo>::iterator it=index.find(Name);
if (it == index.end())
return 0;
else return &(it->second);
return 0;*/
int res;
if (index.lookup(hash(name), res)) {
return &ordered[res];
}
return 0;
}
const oDataInfo *oDataContainer::findVariable(const char *name) const
{
if (name == 0)
return 0;
/*map<string, oDataInfo>::const_iterator it=index.find(Name);
if (it == index.end())
return 0;
else return &(it->second);
*/
int res;
if (index.lookup(hash(name), res)) {
return &ordered[res];
}
return 0;
}
void oDataContainer::initData(oBase *ob, int datasize) {
if (datasize<dataPointer)
throw std::exception("oDataContainer: Buffer too small.");
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
memset(data, 0, dataPointer);
memset(oldData, 0, dataPointer);
if (stringIndexPointer > 0 || stringArrayIndexPointer>2) {
vector<vector<wstring>> &str = *strptr;
str.clear();
str.resize(stringArrayIndexPointer);
str[0].resize(stringIndexPointer);
str[1].resize(stringIndexPointer);
}
}
bool oDataContainer::setInt(void *data, const char *Name, int V)
{
oDataInfo *odi=findVariable(Name);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
if (odi->Type!=oDTInt)
throw std::exception("oDataContainer: Variable of wrong type.");
if (odi->SubType == oIS64)
throw std::exception("oDataContainer: Variable to large.");
LPBYTE vd=LPBYTE(data)+odi->Index;
if (*((int *)vd)!=V){
*((int *)vd)=V;
return true;
}
else return false;//Not modified
}
bool oDataContainer::setInt64(void *data, const char *Name, __int64 V)
{
oDataInfo *odi=findVariable(Name);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
if (odi->Type!=oDTInt)
throw std::exception("oDataContainer: Variable of wrong type.");
if (odi->SubType != oIS64)
throw std::exception("oDataContainer: Variable to large.");
LPBYTE vd=LPBYTE(data)+odi->Index;
if (*((__int64 *)vd)!=V){
*((__int64 *)vd)=V;
return true;
}
else return false;//Not modified
}
int oDataContainer::getInt(const void *data, const char *Name) const
{
const oDataInfo *odi=findVariable(Name);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
if (odi->Type!=oDTInt)
throw std::exception("oDataContainer: Variable of wrong type.");
if (odi->SubType == oIS64)
throw std::exception("oDataContainer: Variable to large.");
LPBYTE vd=LPBYTE(data)+odi->Index;
return *((int *)vd);
}
__int64 oDataContainer::getInt64(const void *data, const char *Name) const
{
const oDataInfo *odi=findVariable(Name);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
if (odi->Type!=oDTInt)
throw std::exception("oDataContainer: Variable of wrong type.");
LPBYTE vd=LPBYTE(data)+odi->Index;
if (odi->SubType == oIS64)
return *((__int64 *)vd);
else {
int tmp = *((int *)vd);
return tmp;
}
}
bool oDataContainer::setString(oBase *ob, const char *name, const wstring &v) {
oDataInfo *odi=findVariable(name);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
if (odi->Type == oDTString) {
LPBYTE vd=LPBYTE(data)+odi->Index;
if (wcscmp((wchar_t *)vd, v.c_str())!=0){
wcsncpy_s((wchar_t *)vd, odi->Size/sizeof(wchar_t), v.c_str(), (odi->Size-1)/sizeof(wchar_t));
return true;
}
else return false;//Not modified
}
else if (odi->Type == oDTStringDynamic) {
wstring &str = (*strptr)[0][odi->Index];
if (str == v)
return false; // Same string
str = v;
return true;
}
else
throw std::exception("oDataContainer: Variable of wrong type.");
}
const wstring &oDataContainer::formatString(const oBase *ob, const char *Name) const {
const oDataInfo *odi = findVariable(Name);
if (odi->dataDefiner) {
return odi->dataDefiner->formatData(ob);
}
else if (odi->Type == oDTString) {
return getString(ob, Name);
}
else if (odi->Type == oDTInt) {
return itow(getInt(ob, Name));
}
throw std::exception("oDataContainer: Formatting failed.");
}
const wstring &oDataContainer::getString(const oBase *ob, const char *Name) const {
const oDataInfo *odi=findVariable(Name);
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
if (odi->Type == oDTString) {
LPBYTE vd=LPBYTE(data)+odi->Index;
wstring &res = StringCache::getInstance().wget();
res = (wchar_t *) vd;
return res;
}
else if (odi->Type == oDTStringDynamic) {
wstring &str = (*strptr)[0][odi->Index];
return str;
}
else
throw std::exception("oDataContainer: Variable of wrong type.");
}
bool oDataContainer::setDate(void *data, const char *Name, const wstring &V)
{
oDataInfo *odi=findVariable(Name);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
if (odi->Type!=oDTInt)
throw std::exception("oDataContainer: Variable of wrong type.");
int year=0,month=0,day=0;
swscanf_s(V.c_str(), L"%d-%d-%d", &year, &month, &day);
int C=year*10000+month*100+day;
LPBYTE vd=LPBYTE(data)+odi->Index;
if (*((int *)vd)!=C){
*((int *)vd)=C;
return true;
}
else return false;//Not modified
}
const wstring &oDataContainer::getDate(const void *data,
const char *Name) const
{
const oDataInfo *odi=findVariable(Name);
if (!odi)
throw std::exception("oDataContainer: Variable not found.");
if (odi->Type!=oDTInt)
throw std::exception("oDataContainer: Variable of wrong type.");
LPBYTE vd=LPBYTE(data)+odi->Index;
int C=*((int *)vd);
wchar_t bf[24];
if (C%10000!=0 || C==0)
swprintf_s(bf, L"%04d-%02d-%02d", C/10000, (C/100)%100, C%100);
else
swprintf_s(bf, L"%04d", C/10000);
wstring &res = StringCache::getInstance().wget();
res = bf;
return res;
}
bool oDataContainer::write(const oBase *ob, xmlparser &xml) const {
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
xml.startTag("oData");
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
if (di.Type==oDTInt){
LPBYTE vd=LPBYTE(data)+di.Index;
if (di.SubType != oIS64) {
int nr;
memcpy(&nr, vd, sizeof(int));
xml.write(di.Name, nr);
}
else {
__int64 nr;
memcpy(&nr, vd, sizeof(__int64));
xml.write64(di.Name, nr);
}
}
else if (di.Type == oDTString){
LPBYTE vd=LPBYTE(data)+di.Index;
wstring out = (wchar_t*)vd;
xml.write(di.Name, out);
}
else if (di.Type == oDTStringDynamic) {
const wstring &str = (*strptr)[0][di.Index];
xml.write(di.Name, str);
}
}
xml.endTag();
return true;
}
void oDataContainer::set(oBase *ob, const xmlobject &xo) {
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
xmlList xl;
xo.getObjects(xl);
xmlList::const_iterator it;
for(it=xl.begin(); it != xl.end(); ++it){
oDataInfo *odi=findVariable(it->getName());
if (odi) {
if (odi->Type == oDTInt){
LPBYTE vd=LPBYTE(data)+odi->Index;
if (odi->SubType != oIS64)
*((int *)vd) = it->getInt();
else
*((__int64 *)vd) = it->getInt64();
}
else if (odi->Type == oDTString) {
LPBYTE vd=LPBYTE(data)+odi->Index;
wcsncpy_s((wchar_t *)vd, odi->Size/sizeof(wchar_t), it->getw(), (odi->Size-1)/sizeof(wchar_t));
}
else if (odi->Type == oDTStringDynamic) {
wstring &str = (*strptr)[0][odi->Index];
str = it->getw();
}
}
}
allDataStored(ob);
}
void oDataContainer::buildDataFields(gdioutput &gdi, int maxFieldSize) const
{
vector<string> fields;
for (size_t k = 0; k < ordered.size(); k++)
fields.push_back(ordered[k].Name);
buildDataFields(gdi, fields, maxFieldSize);
}
void oDataContainer::buildDataFields(gdioutput &gdi, const vector<string> &fields, int maxFieldSize) const
{
for (size_t k=0;k<fields.size();k++) {
//map<string, oDataInfo>::const_iterator it=index.find(fields[k]);
const oDataInfo *odi = findVariable(fields[k].c_str());
//if (it==index.end())
if (odi == 0)
throw std::exception( ("Bad key: " + fields[k]).c_str());
const oDataInfo &di=*odi;
string Id=di.Name+string("_odc");
if (di.Type==oDTInt){
if (di.SubType == oISDate || di.SubType == oISTime)
gdi.addInput(Id, L"", 10, 0, gdi.widen(di.Description) + L":");
else
gdi.addInput(Id, L"", 6, 0, gdi.widen(di.Description) + L":");
}
else if (di.Type==oDTString){
gdi.addInput(Id, L"", min(di.Size+2, maxFieldSize), 0, gdi.widen(di.Description) + L":");
}
else if (di.Type==oDTStringDynamic){
gdi.addInput(Id, L"", maxFieldSize, 0, gdi.widen(di.Description) + L":");
}
}
}
int oDataContainer::getDataAmountMeasure(const void *data) const
{
int amount = 0;
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
if (di.Type==oDTInt) {
LPBYTE vd=LPBYTE(data)+di.Index;
int nr;
memcpy(&nr, vd, sizeof(int));
if (nr != 0)
amount++;
}
else if (di.Type==oDTString) {
LPBYTE vd=LPBYTE(data)+di.Index;
amount += strlen((char *)vd);
}
}
return amount;
}
void oDataContainer::fillDataFields(const oBase *ob, gdioutput &gdi) const
{
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
string Id=di.Name+string("_odc");
if (di.Type==oDTInt){
LPBYTE vd=LPBYTE(data)+di.Index;
if (di.SubType != oIS64) {
int nr;
memcpy(&nr, vd, sizeof(int));
if (di.SubType == oISCurrency) {
if (strcmp(di.Name, "CardFee") == 0 && nr == -1)
nr = 0; //XXX CardFee hack. CardFee = 0 is coded as -1
gdi.setText(Id.c_str(), ob->getEvent()->formatCurrency(nr));
}
else {
wchar_t bf[64];
formatNumber(nr, di, bf);
gdi.setText(Id.c_str(), bf);
}
}
else {
__int64 nr;
memcpy(&nr, vd, sizeof(__int64));
wchar_t bf[16];
oBase::converExtIdentifierString(nr, bf);
gdi.setText(Id.c_str(), bf);
}
}
else if (di.Type==oDTString){
LPBYTE vd=LPBYTE(data)+di.Index;
gdi.setText(Id.c_str(), (wchar_t *)vd);
}
else if (di.Type==oDTStringDynamic){
const wstring &str = (*strptr)[0][di.Index];
gdi.setText(Id.c_str(), str);
}
}
}
bool oDataContainer::saveDataFields(oBase *ob, gdioutput &gdi) {
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
string Id=di.Name+string("_odc");
if (!gdi.hasWidget(Id)) {
continue;
}
if (di.Type==oDTInt){
int no = 0;
if (di.SubType == oISCurrency) {
no = ob->getEvent()->interpretCurrency(gdi.getText(Id.c_str()));
}
else if (di.SubType == oISDate) {
no = convertDateYMS(gdi.getText(Id.c_str()), true);
}
else if (di.SubType == oISTime) {
no = convertAbsoluteTimeHMS(gdi.getText(Id.c_str()), -1);
}
else if (di.SubType == oISDecimal) {
wstring str = gdi.getText(Id.c_str());
for (size_t k = 0; k < str.length(); k++) {
if (str[k] == ',') {
str[k] = '.';
break;
}
}
double val = _wtof(str.c_str());
no = int(di.decimalScale * val);
}
else {
no = gdi.getTextNo(Id.c_str());
}
LPBYTE vd=LPBYTE(data)+di.Index;
int oldNo = *((int *)vd);
if (oldNo != no) {
*((int *)vd)=no;
ob->updateChanged();
}
}
else if (di.Type == oDTString) {
LPBYTE vd=LPBYTE(data)+di.Index;
wstring oldS = (wchar_t *)vd;
wstring newS = gdi.getText(Id.c_str());
if (oldS != newS) {
wcsncpy_s((wchar_t *)vd, di.Size/sizeof(wchar_t), newS.c_str(), (di.Size-1)/sizeof(wchar_t));
ob->updateChanged();
}
}
else if (di.Type == oDTStringDynamic) {
wstring &oldS = (*strptr)[0][di.Index];
wstring newS = gdi.getText(Id.c_str());
if (oldS != newS) {
oldS = newS;
ob->updateChanged();
}
}
}
return true;
}
string oDataContainer::C_INT64(const string &name)
{
return " `"+name+"` BIGINT NOT NULL DEFAULT 0, ";
}
string oDataContainer::C_INT(const string &name)
{
return " `"+name+"` INT NOT NULL DEFAULT 0, ";
}
string oDataContainer::C_SMALLINT(const string &name)
{
return " `"+name+"` SMALLINT NOT NULL DEFAULT 0, ";
}
string oDataContainer::C_TINYINT(const string &name)
{
return " `"+name+"` TINYINT NOT NULL DEFAULT 0, ";
}
string oDataContainer::C_SMALLINTU(const string &name)
{
return " `"+name+"` SMALLINT UNSIGNED NOT NULL DEFAULT 0, ";
}
string oDataContainer::C_TINYINTU(const string &name)
{
return " `"+name+"` TINYINT UNSIGNED NOT NULL DEFAULT 0, ";
}
string oDataContainer::C_STRING(const string &name, int len)
{
if (len>0) {
char bf[16];
sprintf_s(bf, "%d", len);
return " `"+name+"` VARCHAR("+ bf +") NOT NULL DEFAULT '', ";
}
else {
return " `"+name+"` MEDIUMTEXT NOT NULL, ";
}
}
string oDataContainer::SQL_quote(const wchar_t *in)
{
int len = wcslen(in);
size_t alloc = len*4+4;
if (alloc < 500) {
char output[512];
int len8 = WideCharToMultiByte(CP_UTF8, 0, in, len+1, output, alloc, 0, 0);
output[len8] = 0;
const char *inutf = output;
char out[1024];
int o=0;
while(*inutf && o<1023){
if (*inutf=='\'')
out[o++]='\'';
if (*inutf=='\\')
out[o++]='\\';
out[o++]=*inutf;
inutf++;
}
out[o]=0;
return out;
}
else {
vector<char> output;
output.resize(alloc);
int len8 = WideCharToMultiByte(CP_UTF8, 0, in, len+1, &output.front(), alloc, 0, 0);
output[len8] = 0;
const char *inutf = &output.front();
vector<char> out;
out.reserve(alloc);
while(*inutf){
if (*inutf=='\'')
out.push_back('\'');
if (*inutf=='\\')
out.push_back('\\');
out.push_back(*inutf);
inutf++;
}
out.push_back(0);
string outs(&out[0]);
return outs;
}
}
string oDataContainer::generateSQLDefinition(const std::set<string> &exclude) const {
string sql;
bool addSyntx = !exclude.empty();
for (size_t k = 0; k < ordered.size(); k++) {
if (exclude.count(ordered[k].Name) == 0) {
if (addSyntx)
sql += "ADD COLUMN ";
const oDataInfo &di = ordered[k];
string name = di.Name;
if (di.Type==oDTInt){
if (di.SubType==oIS32 || di.SubType==oISDate || di.SubType==oISCurrency ||
di.SubType==oISTime || di.SubType==oISDecimal)
sql+=C_INT(name);
else if (di.SubType==oIS16)
sql+=C_SMALLINT(name);
else if (di.SubType==oIS8)
sql+=C_TINYINT(name);
else if (di.SubType==oIS64)
sql+=C_INT64(name);
else if (di.SubType==oIS16U)
sql+=C_SMALLINTU(name);
else if (di.SubType==oIS8U)
sql+=C_TINYINTU(name);
}
else if (di.Type==oDTString){
sql+=C_STRING(name, di.Size-1);
}
else if (di.Type==oDTStringDynamic || di.Type==oDTStringArray){
sql+=C_STRING(name, -1);
}
}
}
if (addSyntx && !sql.empty())
return sql.substr(0, sql.length() - 2); //Remove trailing comma-space
else
return sql;
}
bool oDataContainer::isModified(const oDataInfo &di,
const void *data,
const void *oldData,
vector< vector<wstring> > *strptr) const {
if (di.Type == oDTInt) {
LPBYTE vd=LPBYTE(data)+di.Index;
LPBYTE vdOld=LPBYTE(oldData)+di.Index;
if (di.SubType != oIS64) {
return memcmp(vd, vdOld, 4) != 0;
}
else {
return memcmp(vd, vdOld, 8) != 0;
}
}
else if (di.Type == oDTString) {
LPBYTE vdB = LPBYTE(data) + di.Index;
LPBYTE vdOldB = LPBYTE(oldData) + di.Index;
wchar_t * vd=(wchar_t *)vdB;
wchar_t * vdOld=(wchar_t *)vdOldB;
return wcscmp(vd, vdOld) != 0;
}
else if (di.Type == oDTStringDynamic){
const wstring &newS = (*strptr)[0][di.Index];
const wstring &oldS = (*strptr)[1][di.Index];
return newS != oldS;
}
else if (di.Type == oDTStringArray){
const vector<wstring> &newS = (*strptr)[di.Index];
const vector<wstring> &oldS = (*strptr)[di.Index+1];
return newS != oldS;
}
else
return true;
}
void oDataContainer::allDataStored(const oBase *ob) {
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
memcpy(oldData, data, ob->getDISize());
if (stringIndexPointer > 0 || stringArrayIndexPointer > 2) {
for (size_t k = 0; k < stringArrayIndexPointer; k+=2) {
(*strptr)[k+1] = (*strptr)[k];
}
}
}
namespace {
char *ensureCapacity(int size, vector<char> &bfData, int &alloc) {
assert(alloc > 100);
if (size >= alloc) {
alloc += size;
bfData.resize(alloc);
}
return &bfData[0];
}
}
string oDataContainer::generateSQLSet(const oBase *ob, bool forceSetAll) const {
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
string sql;
int alloc = 256;
vector<char> bfData(alloc);
char *bf = &bfData[0];
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
if (!forceSetAll && !isModified(di, data, oldData, strptr)) {
continue;
}
if (di.Type==oDTInt) {
LPBYTE vd=LPBYTE(data)+di.Index;
if (di.SubType == oIS8U) {
sprintf_s(bf, alloc, ", `%s`=%u", di.Name, (*((int *)vd))&0xFF);
sql+=bf;
}
else if (di.SubType == oIS16U) {
sprintf_s(bf, alloc, ", `%s`=%u", di.Name, (*((int *)vd))&0xFFFF);
sql+=bf;
}
else if (di.SubType == oIS8) {
char r = (*((int *)vd))&0xFF;
sprintf_s(bf, alloc, ", `%s`=%d", di.Name, (int)r);
sql+=bf;
}
else if (di.SubType == oIS16) {
short r = (*((int *)vd))&0xFFFF;
sprintf_s(bf, alloc, ", `%s`=%d", di.Name, (int)r);
sql+=bf;
}
else if (di.SubType != oIS64) {
sprintf_s(bf, alloc, ", `%s`=%d", di.Name, *((int *)vd));
sql+=bf;
}
else {
char tmp[32];
_i64toa_s(*((__int64 *)vd), tmp, 32, 10);
sprintf_s(bf, alloc, ", `%s`=%s", di.Name, tmp);
sql+=bf;
}
}
else if (di.Type==oDTString) {
LPBYTE vd=LPBYTE(data)+di.Index;
sprintf_s(bf, alloc, ", `%s`='%s'", di.Name, SQL_quote((wchar_t *)vd).c_str());
sql+=bf;
}
else if (di.Type==oDTStringDynamic) {
const wstring &str = (*strptr)[0][di.Index];
bf = ensureCapacity(2 * str.length() + 30, bfData, alloc);
sprintf_s(bf, alloc, ", `%s`='%s'", di.Name, SQL_quote(str.c_str()).c_str());
sql+=bf;
}
else if (di.Type==oDTStringArray) {
const wstring str = encodeArray((*strptr)[di.Index]);
bf = ensureCapacity(2 * str.length() + 30, bfData, alloc);
sprintf_s(bf, alloc, ", `%s`='%s'", di.Name, SQL_quote(str.c_str()).c_str());
sql+=bf;
}
}
return sql;
}
bool oDataContainer::merge(oBase &destination, const oBase &source, const oBase *base) const {
bool modified = false;
void *destdata, *oldDataDmy;
vector< vector<wstring> > *deststrptr;
destination.getDataBuffers(destdata, oldDataDmy, deststrptr);
void *srcdata;
vector< vector<wstring> > *srcstrptr;
source.getDataBuffers(srcdata, oldDataDmy, srcstrptr);
void *basedata = nullptr;
vector< vector<wstring> > *basestrptr = nullptr;
if (base)
base->getDataBuffers(basedata, oldDataDmy, basestrptr);
auto setData = [](void *d, void *s, void *b, int off, int size) {
LPBYTE vd = LPBYTE(d) + off;
LPBYTE vs = LPBYTE(s) + off;
if (memcmp(vd, vs, size) != 0) {
if (b == nullptr || memcmp(LPBYTE(b) + off, vs, size) != 0) {
memcpy(vd, vs, size);
return true;
}
}
return false;
};
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di = ordered[kk];
if (di.Type == oDTInt) {
if (di.SubType != oIS64) {
if (setData(destdata, srcdata, basedata, di.Index, sizeof(int)))
modified = true;
}
else {
if (setData(destdata, srcdata, basedata, di.Index, sizeof(int64_t)))
modified = true;
}
}
else if (di.Type == oDTString) {
if (setData(destdata, srcdata, basedata, di.Index, di.Size))
modified = true;
}
else if (di.Type == oDTStringDynamic) {
const wstring &s = (*srcstrptr)[0][di.Index];
wstring &d = (*deststrptr)[0][di.Index];
if (s != d) {
if (basestrptr == nullptr || (*basestrptr)[0][di.Index] != s) {
d = s;
modified = true;
}
}
}
else if (di.Type == oDTStringArray) {
const auto &s = (*srcstrptr)[di.Index];
auto &d = (*deststrptr)[di.Index];
if (s != d) {
if (basestrptr == nullptr || (*basestrptr)[di.Index] != s) {
d = s;
modified = true;
}
}
}
}
return modified;
}
void oDataContainer::getVariableInt(const void *data,
list<oVariableInt> &var) const {
var.clear();
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
if (di.Type == oDTInt) {
LPBYTE vd=LPBYTE(data)+di.Index;
oVariableInt vi;
memcpy(vi.name, di.Name, sizeof(vi.name));
if (di.SubType != oIS64)
vi.data32 = (int *)vd;
else
vi.data64 = (__int64 *)vd;
var.push_back(vi);
}
}
}
void oDataContainer::getVariableString(const oBase *ob,
list<oVariableString> &var) const
{
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
var.clear();
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
if (di.Type == oDTString){
LPBYTE vd=(LPBYTE)(data)+di.Index;
oVariableString vs((wchar_t *)vd, di.Size);
memcpy(vs.name, di.Name, sizeof(vs.name));
var.push_back(vs);
}
else if (di.Type == oDTStringDynamic) {
oVariableString vs((*strptr)[0], di.Index);
memcpy(vs.name, di.Name, sizeof(vs.name));
var.push_back(vs);
}
else if (di.Type == oDTStringArray) {
oVariableString vs((*strptr)[di.Index]);
memcpy(vs.name, di.Name, sizeof(vs.name));
var.push_back(vs);
}
}
}
oDataInterface oDataContainer::getInterface(void *data, int datasize, oBase *ob)
{
if (datasize<dataPointer) throw std::exception("Out Of Bounds.");
return oDataInterface(this, data, ob);
}
oDataConstInterface oDataContainer::getConstInterface(const void *data, int datasize,
const oBase *ob) const
{
if (datasize<dataPointer) throw std::exception("Out Of Bounds.");
return oDataConstInterface(this, data, ob);
}
oDataInterface::oDataInterface(oDataContainer *odc, void *data, oBase *ob)
{
oB=ob;
oDC=odc;
Data=data;
}
oDataConstInterface::~oDataConstInterface(void)
{
}
oDataConstInterface::oDataConstInterface(const oDataContainer *odc, const void *data, const oBase *ob)
{
oB=ob;
oDC=odc;
Data=data;
}
oDataInterface::~oDataInterface(void)
{
}
void oDataContainer::buildTableCol(Table *table)
{
for (size_t kk = 0; kk < ordered.size(); kk++) {
oDataInfo &di=ordered[kk];
if (di.dataDefiner) {
table->addDataDefiner(di.Name, di.dataDefiner.get());
int w = strlen(di.Description)*6;
di.tableIndex = di.dataDefiner->addTableColumn(table, di.Description, w);
}
else if (di.Type == oDTInt) {
bool right = di.SubType == oISCurrency;
bool numeric = di.SubType != oISDate && di.SubType != oISTime;
int w;
if (di.SubType == oISDecimal)
w = max( (di.decimalSize+4)*10, 70);
else
w = 70;
w = max(int(strlen(di.Description))*6, w);
di.tableIndex = table->addColumn(di.Description, w, numeric, right);
}
else if (di.Type == oDTString) {
int w = max(max(di.Size+1, int(strlen(di.Description)))*6, 70);
for (size_t k = 0; k < di.enumDescription.size(); k++)
w = max<int>(w, lang.tl(di.enumDescription[k].second).length() * 6);
if (di.zeroSortPadding)
di.tableIndex = table->addColumnPaddedSort(di.Description, w, di.zeroSortPadding, true);
else
di.tableIndex = table->addColumn(di.Description, w, false);
}
else if (di.Type == oDTStringDynamic) {
int w = 64*6;
di.tableIndex = table->addColumn(di.Description, w, false);
}
}
}
bool oDataContainer::formatNumber(int nr, const oDataInfo &di, wchar_t bf[64]) const {
if (di.SubType == oISDate) {
if (nr>0) {
swprintf_s(bf, 64, L"%d-%02d-%02d", nr/(100*100), (nr/100)%100, nr%100);
}
else {
bf[0] = '-';
bf[1] = 0;
}
return true;
}
else if (di.SubType == oISTime) {
if (nr>0 && nr<(30*24*3600)) {
if (nr < 24*3600)
swprintf_s(bf, 64, L"%02d:%02d:%02d", nr/3600, (nr/60)%60, nr%60);
else {
int days = nr / (24*3600);
nr = nr % (24*3600);
swprintf_s(bf, 64, L"%d+%02d:%02d:%02d", days, nr/3600, (nr/60)%60, nr%60);
}
}
else {
bf[0] = '-';
bf[1] = 0;
}
return true;
}
else if (di.SubType == oISDecimal) {
if (nr) {
int whole = nr / di.decimalScale;
int part = nr - whole * di.decimalScale;
wstring deci = L",";
wstring ptrn = L"%d" + deci + L"%0" + itow(di.decimalSize) + L"d";
swprintf_s(bf, 64, ptrn.c_str(), whole, abs(part));
}
else
bf[0] = 0;
return true;
}
else {
if (nr)
swprintf_s(bf, 64, L"%d", nr);
else
bf[0] = 0;
return true;
}
}
int oDataContainer::fillTableCol(const oBase &owner, Table &table, bool canEdit) const {
void *data, *oldData;
vector< vector<wstring> > *strptr;
owner.getDataBuffers(data, oldData, strptr);
int nextIndex = 0;
wchar_t bf[64];
oBase &ob = *(oBase *)&owner;
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
if (di.dataDefiner != nullptr) {
if (di.tableIndex >= 0) {
table.set(di.tableIndex, ob, 1000 + di.tableIndex,
di.dataDefiner->formatData(&ob), canEdit && di.dataDefiner->canEdit(),
di.dataDefiner->getCellType());
}
}
else if (di.Type==oDTInt) {
LPBYTE vd=LPBYTE(data)+di.Index;
if (di.SubType != oIS64) {
int nr;
memcpy(&nr, vd, sizeof(int));
if (di.SubType == oISCurrency) {
table.set(di.tableIndex, ob, 1000+di.tableIndex, ob.getEvent()->formatCurrency(nr), canEdit);
}
else {
formatNumber(nr, di, bf);
table.set(di.tableIndex, ob, 1000+di.tableIndex, bf, canEdit);
}
}
else {
__int64 nr;
memcpy(&nr, vd, sizeof(__int64));
wchar_t bf[16];
oBase::converExtIdentifierString(nr, bf);
table.set(di.tableIndex, ob, 1000+di.tableIndex, bf, canEdit);
}
}
else if (di.Type==oDTString) {
LPBYTE vd=LPBYTE(data)+di.Index;
if (di.SubType == oSSString || !canEdit) {
table.set(di.tableIndex, *((oBase*)&owner), 1000+di.tableIndex, (wchar_t *)vd, canEdit, cellEdit);
}
else {
wstring str((wchar_t *)vd);
for (size_t k = 0; k<di.enumDescription.size(); k++) {
if ( str == di.enumDescription[k].first ) {
str = lang.tl(di.enumDescription[k].second);
break;
}
}
table.set(di.tableIndex, *((oBase*)&owner), 1000+di.tableIndex, str, true, cellSelection);
}
}
else if (di.Type == oDTStringDynamic) {
const wstring &str = (*strptr)[0][di.Index];
table.set(di.tableIndex, *((oBase*)&owner), 1000+di.tableIndex, str, canEdit, cellEdit);
}
if (di.tableIndex >= 0)
nextIndex = di.tableIndex + 1;
}
return nextIndex;
}
pair<int, bool> oDataContainer::inputData(oBase *ob, int id,
const wstring &input, int inputId,
wstring &output, bool noUpdate)
{
void *data, *oldData;
vector< vector<wstring> > *strptr;
ob->getDataBuffers(data, oldData, strptr);
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di = ordered[kk];
if (di.tableIndex + 1000 == id) {
if (di.dataDefiner) {
const wstring &src = di.dataDefiner->formatData(ob);
auto ret = di.dataDefiner->setData(ob, input, output, inputId);
bool ch = output != src;
if (ch && noUpdate == false)
ob->synchronize(true);
return ret;
}
else if (di.Type == oDTInt) {
LPBYTE vd = LPBYTE(data) + di.Index;
int no = 0;
if (di.SubType == oISCurrency) {
no = ob->getEvent()->interpretCurrency(input);
}
else if (di.SubType == oISDate) {
no = convertDateYMS(input, true);
}
else if (di.SubType == oISTime) {
no = convertAbsoluteTimeHMS(input, -1);
}
else if (di.SubType == oISDecimal) {
wstring str = input;
for (size_t k = 0; k < str.length(); k++) {
if (str[k] == ',') {
str[k] = '.';
break;
}
}
double val = _wtof(str.c_str());
no = int(di.decimalScale * val);
}
else if (di.SubType == oIS64) {
//__int64 no64 = _atoi64(input.c_str());
__int64 k64;
memcpy(&k64, vd, sizeof(__int64));
__int64 no64 = oBase::converExtIdentifierString(input);
memcpy(vd, &no64, sizeof(__int64));
__int64 out64 = no64;
if (k64 != no64) {
ob->updateChanged();
if (noUpdate == false)
ob->synchronize(true);
memcpy(&out64, vd, sizeof(__int64));
}
//output = itos(out64);
wchar_t outbf[16];
oBase::converExtIdentifierString(out64, outbf);
output = outbf;
return make_pair(0, false);
}
else
no = _wtoi(input.c_str());
int k;
memcpy(&k, vd, sizeof(int));
memcpy(vd, &no, sizeof(int));
wchar_t bf[128];
int outN = no;
if (k != no) {
ob->updateChanged();
if (noUpdate == false)
ob->synchronize(true);
memcpy(&outN, vd, sizeof(int));
}
formatNumber(outN, di, bf);
output = bf;
}
else if (di.Type == oDTString) {
LPBYTE vd = LPBYTE(data) + di.Index;
const wchar_t *str = input.c_str();
if (di.SubType == oSSEnum) {
size_t ix = inputId - 1;
if (ix < di.enumDescription.size()) {
str = di.enumDescription[ix].first.c_str();
}
}
if (wcscmp((wchar_t *)vd, str) != 0) {
wcsncpy_s((wchar_t *)vd, di.Size / sizeof(wchar_t), str, (di.Size - 1) / sizeof(wchar_t));
ob->updateChanged();
if (noUpdate == false)
ob->synchronize(true);
if (di.SubType == oSSEnum) {
size_t ix = inputId - 1;
if (ix < di.enumDescription.size()) {
output = lang.tl(di.enumDescription[ix].second);
// This might be incorrect if data was changed on server,
// but this issue is minor, I think: conflicts have no resolution
// Anyway, the row will typically be reloaded
}
else
output = (wchar_t *)vd;
}
else
output = (wchar_t *)vd;
return make_pair(0, false);
}
else
output = input;
}
else if (di.Type == oDTStringDynamic) {
wstring &vd = (*strptr)[0][di.Index];
if (vd != input) {
vd = input;
ob->updateChanged();
if (noUpdate == false)
ob->synchronize(true);
output = (*strptr)[0][di.Index];
}
else
output = input;
}
}
}
return make_pair(0, false);
}
void oDataContainer::fillInput(const oBase *obj, int id, const char *name,
vector< pair<wstring, size_t> > &out, size_t &selected) const {
void *data, *olddata;
pvectorstr strData;
obj->getDataBuffers(data, olddata, strData);
const oDataInfo * info = findVariable(name);
if (!info) {
for (size_t kk = 0; kk < ordered.size(); kk++) {
const oDataInfo &di=ordered[kk];
if (di.tableIndex+1000 == id) {
info = &di;
break;
}
}
}
if (info && info->Type == oDTString && info->SubType == oSSEnum) {
wchar_t *vd = (wchar_t *)(LPBYTE(data)+info->Index);
selected = -1;
for (size_t k = 0; k < info->enumDescription.size(); ++k) {
out.push_back(make_pair(lang.tl(info->enumDescription[k].second), k+1));
if (info->enumDescription[k].first == wstring(vd))
selected = k+1;
}
}
else if (info && info->dataDefiner) {
info->dataDefiner->fillInput(obj, out, selected);
}
else
throw meosException("Invalid enum");
}
bool oDataContainer::setEnum(oBase *ob, const char *name, int selectedIndex) {
const oDataInfo * info = findVariable(name);
if (info && info->Type == oDTString && info->SubType == oSSEnum) {
if (size_t(selectedIndex - 1) < info->enumDescription.size()) {
return setString(ob, name, info->enumDescription[selectedIndex-1].first);
}
}
throw meosException("Invalid enum");
}
bool oVariableString::store(const wchar_t *in) {
if (data) {
if (wcscmp(in, data) != 0) {
wcsncpy_s(data, maxSize/sizeof(wchar_t), in, (maxSize-1)/sizeof(wchar_t));
return true;
}
return false;
}
else {
vector<wstring> &str = *strData;
if (strIndex>=0) {
if (str[strIndex] != in) {
str[strIndex] = in;
return true;
}
else
return false;
}
}
return false;
}
wstring oDataContainer::encodeArray(const vector<wstring> &input) {
return L"";//XXX
}
void oDataContainer::decodeArray(const string &input, vector<wstring> &output) {
}