fixed memory leak in table unpack code; added Valgrind suppression file
[public/netxms.git] / src / libnetxms / table.cpp
CommitLineData
a3d3f9d5 1/*
5039dede 2** NetXMS - Network Management System
d6bbfa4e 3** Copyright (C) 2003-2014 Victor Kirhenshtein
5039dede
AK
4**
5** This program is free software; you can redistribute it and/or modify
68f384ea
VK
6** it under the terms of the GNU Lesser General Public License as published
7** by the Free Software Foundation; either version 3 of the License, or
5039dede
AK
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
68f384ea 15** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** File: table.cpp
20**
21**/
22
23#include "libnetxms.h"
46750a00 24#include <expat.h>
f1b8f11c
VK
25#include <zlib.h>
26
27#define DEFAULT_OBJECT_ID (0)
28#define DEFAULT_STATUS (-1)
5039dede 29
5891329f 30/**
d6bbfa4e
VK
31 * Create empty table row
32 */
33TableRow::TableRow(int columnCount)
34{
35 m_cells = new ObjectArray<TableCell>(columnCount, 8, true);
36 for(int i = 0; i < columnCount; i++)
f1b8f11c 37 m_cells->add(new TableCell());
d6bbfa4e
VK
38 m_objectId = 0;
39}
40
41/**
42 * Table row copy constructor
43 */
44TableRow::TableRow(TableRow *src)
45{
46 m_cells = new ObjectArray<TableCell>(src->m_cells->size(), 8, true);
47 for(int i = 0; i < src->m_cells->size(); i++)
48 m_cells->add(new TableCell(src->m_cells->get(i)));
49 m_objectId = src->m_objectId;
50}
51
52/**
5891329f
VK
53 * Create empty table
54 */
a0ddfb29 55Table::Table() : RefCountObject()
5039dede 56{
d6bbfa4e 57 m_data = new ObjectArray<TableRow>(32, 32, true);
e31c8e60 58 m_title = NULL;
5891329f 59 m_source = DS_INTERNAL;
967893bb 60 m_columns = new ObjectArray<TableColumnDefinition>(8, 8, true);
d6bbfa4e 61 m_extendedFormat = false;
5039dede
AK
62}
63
0f506caa
VK
64/**
65 * Create table from NXCP message
66 */
b368969c 67Table::Table(NXCPMessage *msg) : RefCountObject()
0f506caa 68{
967893bb 69 m_columns = new ObjectArray<TableColumnDefinition>(8, 8, true);
0f506caa
VK
70 createFromMessage(msg);
71}
5039dede 72
0f506caa 73/**
a0ddfb29
VK
74 * Copy constructor
75 */
76Table::Table(Table *src) : RefCountObject()
77{
d6bbfa4e
VK
78 m_extendedFormat = src->m_extendedFormat;
79 m_data = new ObjectArray<TableRow>(src->m_data->size(), 32, true);
2ef99110 80 int i;
d6bbfa4e
VK
81 for(i = 0; i < src->m_data->size(); i++)
82 m_data->add(new TableRow(src->m_data->get(i)));
a0ddfb29
VK
83 m_title = (src->m_title != NULL) ? _tcsdup(src->m_title) : NULL;
84 m_source = src->m_source;
d6bbfa4e
VK
85 m_columns = new ObjectArray<TableColumnDefinition>(src->m_columns->size(), 8, true);
86 for(i = 0; i < src->m_columns->size(); i++)
a0ddfb29
VK
87 m_columns->add(new TableColumnDefinition(src->m_columns->get(i)));
88}
89
a3d3f9d5 90/**
f1b8f11c
VK
91 * Table destructor
92 */
93Table::~Table()
94{
95 destroy();
96 delete m_columns;
97 delete m_data;
98}
99
100/**
101 * Destroy table data
102 */
103void Table::destroy()
104{
105 m_columns->clear();
106 m_data->clear();
107 safe_free(m_title);
108}
109
110/**
a3d3f9d5 111 * XML parser state for creating LogParser object from XML
112 */
113#define XML_STATE_INIT -1
114#define XML_STATE_END -2
115#define XML_STATE_ERROR -255
63ff3c9d
VK
116#define XML_STATE_TABLE 0
117#define XML_STATE_COLUMNS 1
118#define XML_STATE_COLUMN 2
119#define XML_STATE_DATA 3
120#define XML_STATE_ROW 4
121#define XML_STATE_CELL 5
a3d3f9d5 122
123/**
124 * State information for XML parser
125 */
126typedef struct
127{
128 Table *table;
129 int state;
f58eac42 130 String *buffer;
63ff3c9d 131 int column;
a3d3f9d5 132} XML_PARSER_STATE;
133
134/**
135 * Element start handler for XML parser
136 */
137static void StartElement(void *userData, const char *name, const char **attrs)
138{
139 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
140
141 if (!strcmp(name, "table"))
142 {
63ff3c9d
VK
143 if (ps->state == XML_STATE_INIT)
144 {
145 ps->table->setExtendedFormat(XMLGetAttrBoolean(attrs, "extendedFormat", false));
146 ps->table->setSource(XMLGetAttrInt(attrs, "source", 0));
147 const char *title = XMLGetAttr(attrs, "name");
148 if (title != NULL)
149 {
a3d3f9d5 150#ifdef UNICODE
63ff3c9d
VK
151 WCHAR *wtitle = WideStringFromUTF8String(title);
152 ps->table->setTitle(wtitle);
153 free(wtitle);
a3d3f9d5 154#else
63ff3c9d 155 ps->table->setTitle(title);
a3d3f9d5 156#endif
63ff3c9d
VK
157 }
158 ps->state = XML_STATE_TABLE;
159 }
160 else
161 {
162 ps->state = XML_STATE_ERROR;
163 }
a3d3f9d5 164 }
165 else if (!strcmp(name, "columns"))
166 {
c637ecf7 167 ps->state = (ps->state == XML_STATE_TABLE) ? XML_STATE_COLUMNS : XML_STATE_ERROR;
a3d3f9d5 168 }
169 else if (!strcmp(name, "column"))
170 {
9bd6341f 171 if (ps->state == XML_STATE_COLUMNS)
63ff3c9d 172 {
a3d3f9d5 173#ifdef UNICODE
63ff3c9d
VK
174 wchar_t *name = WideStringFromUTF8String(CHECK_NULL_A(XMLGetAttr(attrs, "name")));
175 const char *tmp = XMLGetAttr(attrs, "displayName");
176 wchar_t *displayName = (tmp != NULL) ? WideStringFromUTF8String(tmp) : NULL;
a3d3f9d5 177#else
63ff3c9d
VK
178 const char *name = CHECK_NULL_A(XMLGetAttr(attrs, "name"));
179 const char *displayName = XMLGetAttr(attrs, "displayName");
a3d3f9d5 180#endif
63ff3c9d
VK
181 ps->table->addColumn(name, XMLGetAttrInt(attrs, "dataType", 0), displayName, XMLGetAttrBoolean(attrs, "isInstance", false));
182 ps->state = XML_STATE_COLUMN;
a3d3f9d5 183#ifdef UNICODE
63ff3c9d
VK
184 safe_free(name);
185 safe_free(displayName);
a3d3f9d5 186#endif
63ff3c9d
VK
187 }
188 else
189 {
190 ps->state = XML_STATE_ERROR;
191 }
a3d3f9d5 192 }
193 else if (!strcmp(name, "data"))
194 {
c637ecf7 195 ps->state = (ps->state == XML_STATE_TABLE) ? XML_STATE_DATA : XML_STATE_ERROR;
a3d3f9d5 196 }
197 else if (!strcmp(name, "tr"))
198 {
63ff3c9d
VK
199 if (ps->state == XML_STATE_DATA)
200 {
201 ps->table->addRow();
f1b8f11c 202 ps->table->setObjectId(ps->table->getNumRows() - 1, XMLGetAttrInt(attrs, "objectId", DEFAULT_OBJECT_ID));
63ff3c9d
VK
203 ps->column = 0;
204 ps->state = XML_STATE_ROW;
205 }
206 else
207 {
208 ps->state = XML_STATE_ERROR;
209 }
a3d3f9d5 210 }
211 else if (!strcmp(name, "td"))
212 {
c637ecf7 213 if (ps->state == XML_STATE_ROW)
63ff3c9d 214 {
f1b8f11c 215 ps->table->setStatus(ps->column, XMLGetAttrInt(attrs, "status", DEFAULT_STATUS));
63ff3c9d 216 ps->state = XML_STATE_CELL;
f58eac42 217 ps->buffer->clear();
63ff3c9d
VK
218 }
219 else
220 {
221 ps->state = XML_STATE_ERROR;
222 }
a3d3f9d5 223 }
224 else
225 {
226 ps->state = XML_STATE_ERROR;
227 }
228}
229
230/**
231 * Element end handler for XML parser
232 */
233static void EndElement(void *userData, const char *name)
234{
235 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
63ff3c9d
VK
236 if (ps->state == XML_STATE_ERROR)
237 return;
a3d3f9d5 238
239 if (!strcmp(name, "td"))
240 {
f58eac42 241 ps->table->set(ps->column, ps->buffer->getBuffer());
63ff3c9d
VK
242 ps->column++;
243 ps->state = XML_STATE_ROW;
a3d3f9d5 244 }
245 else if (!strcmp(name, "tr"))
246 {
63ff3c9d
VK
247 ps->column = -1;
248 ps->state = XML_STATE_DATA;
249 }
250 else if (!strcmp(name, "column"))
251 {
252 ps->state = XML_STATE_COLUMNS;
253 }
254 else if (!strcmp(name, "columns") || !strcmp(name, "data"))
255 {
256 ps->state = XML_STATE_TABLE;
a3d3f9d5 257 }
258}
259
260/**
261 * Data handler for XML parser
262 */
263static void CharData(void *userData, const XML_Char *s, int len)
264{
63ff3c9d 265 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
a3d3f9d5 266
63ff3c9d 267 if (ps->state == XML_STATE_CELL)
a3d3f9d5 268 {
f58eac42 269 ps->buffer->appendMBString(s, len, CP_UTF8);
a3d3f9d5 270 }
271}
272
63ff3c9d
VK
273/**
274 * Parse XML document with table data
275 */
276bool Table::parseXML(const char *xml)
a3d3f9d5 277{
278 XML_PARSER_STATE state;
279
280 XML_Parser parser = XML_ParserCreate(NULL);
281 XML_SetUserData(parser, &state);
282 XML_SetElementHandler(parser, StartElement, EndElement);
283 XML_SetCharacterDataHandler(parser, CharData);
284
285 state.table = this;
286 state.state = XML_STATE_INIT;
63ff3c9d 287 state.column = -1;
f58eac42 288 state.buffer = new String();
46750a00 289
63ff3c9d
VK
290 bool success = (XML_Parse(parser, xml, (int)strlen(xml), TRUE) != XML_STATUS_ERROR);
291 if (success)
292 success = (state.state != XML_STATE_ERROR);
a3d3f9d5 293 XML_ParserFree(parser);
f58eac42 294 delete state.buffer;
a3d3f9d5 295 return success;
296}
297
63ff3c9d
VK
298/**
299 * Create table from XML document
300 */
301Table *Table::createFromXML(const char *xml)
a3d3f9d5 302{
46750a00 303 Table *table = new Table();
63ff3c9d 304 if (table->parseXML(xml))
a3d3f9d5 305 {
46750a00 306 return table;
a3d3f9d5 307 }
63ff3c9d
VK
308 delete table;
309 return NULL;
a3d3f9d5 310}
311
63ff3c9d 312/**
f1b8f11c
VK
313 * Create table from packed XML document
314 */
315Table *Table::createFromPackedXML(const char *packedXml)
316{
317 char *compressedXml = NULL;
318 size_t compressedSize = 0;
319 base64_decode_alloc(packedXml, strlen(packedXml), &compressedXml, &compressedSize);
320 if (compressedXml == NULL)
321 return NULL;
322
323 size_t xmlSize = (size_t)ntohl(*((UINT32 *)compressedXml));
324 char *xml = (char *)malloc(xmlSize + 1);
325 uLongf uncompSize = (uLongf)xmlSize;
f5ed8ede 326 if (uncompress((BYTE *)xml, &uncompSize, (BYTE *)&compressedXml[4], (uLong)compressedSize - 4) != Z_OK)
f1b8f11c
VK
327 {
328 free(xml);
528c2730 329 free(compressedXml);
f1b8f11c
VK
330 return NULL;
331 }
332 xml[xmlSize] = 0;
528c2730 333 free(compressedXml);
f1b8f11c
VK
334
335 Table *table = new Table();
336 if (table->parseXML(xml))
337 {
338 free(xml);
339 return table;
340 }
341 free(xml);
342 delete table;
343 return NULL;
344}
345
346/**
63ff3c9d
VK
347 * Create XML document from table
348 */
349TCHAR *Table::createXML()
a3d3f9d5 350{
7a43a02e 351 String xml;
a7ebbd88 352 xml.appendFormattedString(_T("<table extendedFormat=\"%s\" source=\"%d\" name=\"%s\">\r\n"), m_extendedFormat ? _T("true") : _T("false"), m_source,
d1768b0b 353 (const TCHAR *)EscapeStringForXML2(m_title, -1));
7a43a02e 354 xml.append(_T("<columns>\r\n"));
5f22dd66
VK
355 int i;
356 for(i = 0; i < m_columns->size(); i++)
f19f0824 357 xml.appendFormattedString(_T("<column name=\"%s\" displayName=\"%s\" isInstance=\"%s\" dataType=\"%d\"/>\r\n"),
d1768b0b
VK
358 (const TCHAR *)EscapeStringForXML2(m_columns->get(i)->getName(), -1),
359 (const TCHAR *)EscapeStringForXML2(m_columns->get(i)->getDisplayName(), -1),
a7ebbd88 360 m_columns->get(i)->isInstanceColumn()? _T("true") : _T("false"), m_columns->get(i)->getDataType());
7a43a02e 361 xml.append(_T("</columns>\r\n"));
362 xml.append(_T("<data>\r\n"));
5f22dd66 363 for(i = 0; i < m_data->size(); i++)
7a43a02e 364 {
f1b8f11c
VK
365 UINT32 objectId = m_data->get(i)->getObjectId();
366 if (objectId != DEFAULT_OBJECT_ID)
367 xml.appendFormattedString(_T("<tr objectId=\"%u\">\r\n"), objectId);
368 else
369 xml.append(_T("<tr>\r\n"));
6135ecd1 370 for(int j = 0; j < m_columns->size(); j++)
7a43a02e 371 {
f1b8f11c
VK
372 int status = m_data->get(i)->getStatus(j);
373 if (status != DEFAULT_STATUS)
374 {
375 xml.append(_T("<td status=\""));
376 xml.append(status);
377 xml.append(_T("\">"));
378 }
379 else
380 {
381 xml.append(_T("<td>"));
382 }
383 xml.append((const TCHAR *)EscapeStringForXML2(m_data->get(i)->getValue(j), -1));
384 xml.append(_T("</td>\r\n"));
7a43a02e 385 }
386 xml.append(_T("</tr>\r\n"));
387 }
388 xml.append(_T("</data>\r\n"));
c637ecf7 389 xml.append(_T("</table>"));
7a43a02e 390 return _tcsdup(xml);
a3d3f9d5 391}
392
a0ddfb29 393/**
f1b8f11c 394 * Create packed XML document
0f506caa 395 */
f1b8f11c 396char *Table::createPackedXML()
0f506caa 397{
f1b8f11c
VK
398 TCHAR *xml = createXML();
399 if (xml == NULL)
400 return NULL;
401 char *utf8xml = UTF8StringFromTString(xml);
402 free(xml);
403 size_t len = strlen(utf8xml);
f5ed8ede 404 uLongf buflen = compressBound((uLong)len);
f1b8f11c 405 BYTE *buffer = (BYTE *)malloc(buflen + 4);
f5ed8ede 406 if (compress(&buffer[4], &buflen, (BYTE *)utf8xml, (uLong)len) != Z_OK)
f1b8f11c
VK
407 {
408 free(utf8xml);
409 free(buffer);
410 return NULL;
411 }
412 free(utf8xml);
413 char *encodedBuffer = NULL;
414 *((UINT32 *)buffer) = htonl((UINT32)len);
415 base64_encode_alloc((char *)buffer, buflen + 4, &encodedBuffer);
416 free(buffer);
417 return encodedBuffer;
0f506caa
VK
418}
419
420/**
421 * Create table from NXCP message
422 */
b368969c 423void Table::createFromMessage(NXCPMessage *msg)
6d738067
VK
424{
425 int i;
967893bb 426 UINT32 dwId;
6d738067 427
b368969c
VK
428 int rows = msg->getFieldAsUInt32(VID_TABLE_NUM_ROWS);
429 int columns = msg->getFieldAsUInt32(VID_TABLE_NUM_COLS);
430 m_title = msg->getFieldAsString(VID_TABLE_TITLE);
d6bbfa4e
VK
431 m_source = msg->getFieldAsInt16(VID_DCI_SOURCE_TYPE);
432 m_extendedFormat = msg->getFieldAsBoolean(VID_TABLE_EXTENDED_FORMAT);
6d738067 433
d6bbfa4e 434 for(i = 0, dwId = VID_TABLE_COLUMN_INFO_BASE; i < columns; i++, dwId += 10)
e31c8e60 435 {
967893bb 436 m_columns->add(new TableColumnDefinition(msg, dwId));
e31c8e60 437 }
5c44534b 438 if (msg->isFieldExist(VID_INSTANCE_COLUMN))
967893bb
VK
439 {
440 TCHAR name[MAX_COLUMN_NAME];
b368969c 441 msg->getFieldAsString(VID_INSTANCE_COLUMN, name, MAX_COLUMN_NAME);
967893bb
VK
442 for(i = 0; i < m_columns->size(); i++)
443 {
444 if (!_tcsicmp(m_columns->get(i)->getName(), name))
445 {
446 m_columns->get(i)->setInstanceColumn(true);
447 break;
448 }
449 }
450 }
6d738067 451
d6bbfa4e
VK
452 m_data = new ObjectArray<TableRow>(rows, 32, true);
453 for(i = 0, dwId = VID_TABLE_DATA_BASE; i < rows; i++)
454 {
455 TableRow *row = new TableRow(columns);
456 m_data->add(row);
457 if (m_extendedFormat)
458 {
b368969c 459 row->setObjectId(msg->getFieldAsUInt32(dwId++));
d6bbfa4e
VK
460 dwId += 9;
461 }
462 for(int j = 0; j < columns; j++)
463 {
b368969c 464 TCHAR *value = msg->getFieldAsString(dwId++);
d6bbfa4e
VK
465 if (m_extendedFormat)
466 {
43666be9
VK
467 int status = msg->getFieldAsInt16(dwId++);
468 UINT32 objectId = msg->getFieldAsUInt32(dwId++);
469 row->setPreallocated(j, value, status, objectId);
470 dwId += 7;
d6bbfa4e
VK
471 }
472 else
473 {
43666be9 474 row->setPreallocated(j, value, -1, 0);
d6bbfa4e
VK
475 }
476 }
477 }
6d738067
VK
478}
479
0f506caa
VK
480/**
481 * Update table from NXCP message
482 */
b368969c 483void Table::updateFromMessage(NXCPMessage *msg)
5039dede 484{
0f506caa 485 destroy();
d6bbfa4e 486 delete m_data; // will be re-created by createFromMessage
0f506caa 487 createFromMessage(msg);
5039dede
AK
488}
489
0f506caa
VK
490/**
491 * Fill NXCP message with table data
492 */
b368969c 493int Table::fillMessage(NXCPMessage &msg, int offset, int rowLimit)
6d738067 494{
967893bb 495 UINT32 id;
6d738067 496
b368969c
VK
497 msg.setField(VID_TABLE_TITLE, CHECK_NULL_EX(m_title));
498 msg.setField(VID_DCI_SOURCE_TYPE, (UINT16)m_source);
499 msg.setField(VID_TABLE_EXTENDED_FORMAT, (UINT16)(m_extendedFormat ? 1 : 0));
e31c8e60 500
3d5610eb
VK
501 if (offset == 0)
502 {
b368969c
VK
503 msg.setField(VID_TABLE_NUM_ROWS, (UINT32)m_data->size());
504 msg.setField(VID_TABLE_NUM_COLS, (UINT32)m_columns->size());
3d5610eb 505
d6bbfa4e
VK
506 id = VID_TABLE_COLUMN_INFO_BASE;
507 for(int i = 0; i < m_columns->size(); i++, id += 10)
967893bb 508 m_columns->get(i)->fillMessage(&msg, id);
3d5610eb 509 }
b368969c 510 msg.setField(VID_TABLE_OFFSET, (UINT32)offset);
3d5610eb 511
d6bbfa4e
VK
512 int stopRow = (rowLimit == -1) ? m_data->size() : min(m_data->size(), offset + rowLimit);
513 id = VID_TABLE_DATA_BASE;
514 for(int row = offset; row < stopRow; row++)
3d5610eb 515 {
3f4c195f
VK
516 TableRow *r = m_data->get(row);
517 if (m_extendedFormat)
518 {
b368969c 519 msg.setField(id++, r->getObjectId());
3f4c195f
VK
520 id += 9;
521 }
a3d3f9d5 522 for(int col = 0; col < m_columns->size(); col++)
c20b2798 523 {
3f4c195f 524 const TCHAR *tmp = r->getValue(col);
b368969c 525 msg.setField(id++, CHECK_NULL_EX(tmp));
d6bbfa4e
VK
526 if (m_extendedFormat)
527 {
b368969c 528 msg.setField(id++, (UINT16)r->getStatus(col));
43666be9
VK
529 msg.setField(id++, r->getCellObjectId(col));
530 id += 7;
d6bbfa4e 531 }
c20b2798 532 }
3d5610eb 533 }
b368969c 534 msg.setField(VID_NUM_ROWS, (UINT32)(stopRow - offset));
3d5610eb 535
d6bbfa4e 536 if (stopRow == m_data->size())
4af351c7 537 msg.setEndOfSequence();
d6bbfa4e 538 return stopRow;
6d738067
VK
539}
540
0f506caa
VK
541/**
542 * Add new column
543 */
8dba1eac 544int Table::addColumn(const TCHAR *name, INT32 dataType, const TCHAR *displayName, bool isInstance)
5039dede 545{
967893bb 546 m_columns->add(new TableColumnDefinition(name, displayName, dataType, isInstance));
d6bbfa4e
VK
547 for(int i = 0; i < m_data->size(); i++)
548 m_data->get(i)->addColumn();
549 return m_columns->size() - 1;
5039dede
AK
550}
551
55bdca5a
VK
552/**
553 * Get column index by name
554 *
555 * @param name column name
556 * @return column index or -1 if there are no such column
557 */
558int Table::getColumnIndex(const TCHAR *name)
559{
967893bb
VK
560 for(int i = 0; i < m_columns->size(); i++)
561 if (!_tcsicmp(name, m_columns->get(i)->getName()))
55bdca5a
VK
562 return i;
563 return -1;
564}
5039dede 565
55bdca5a
VK
566/**
567 * Add new row
568 */
e31c8e60 569int Table::addRow()
5039dede 570{
d6bbfa4e
VK
571 m_data->add(new TableRow(m_columns->size()));
572 return m_data->size() - 1;
5039dede
AK
573}
574
967893bb 575/**
25076d53
VK
576 * Delete row
577 */
578void Table::deleteRow(int row)
579{
d6bbfa4e 580 m_data->remove(row);
25076d53
VK
581}
582
583/**
584 * Delete column
585 */
586void Table::deleteColumn(int col)
587{
d6bbfa4e 588 if ((col < 0) || (col >= m_columns->size()))
25076d53
VK
589 return;
590
25076d53 591 m_columns->remove(col);
d6bbfa4e
VK
592 for(int i = 0; i < m_data->size(); i++)
593 m_data->get(i)->deleteColumn(col);
25076d53
VK
594}
595
596/**
967893bb
VK
597 * Set data at position
598 */
9e126e57 599void Table::setAt(int nRow, int nCol, const TCHAR *pszData)
5039dede 600{
d6bbfa4e
VK
601 TableRow *r = m_data->get(nRow);
602 if (r != NULL)
603 {
604 r->setValue(nCol, pszData);
605 }
5039dede
AK
606}
607
967893bb
VK
608/**
609 * Set pre-allocated data at position
610 */
e40e0fdc 611void Table::setPreallocatedAt(int nRow, int nCol, TCHAR *data)
9e126e57 612{
d6bbfa4e
VK
613 TableRow *r = m_data->get(nRow);
614 if (r != NULL)
615 {
e40e0fdc
VK
616 r->setPreallocatedValue(nCol, data);
617 }
618 else
619 {
620 free(data);
d6bbfa4e 621 }
9e126e57
VK
622}
623
967893bb
VK
624/**
625 * Set integer data at position
626 */
627void Table::setAt(int nRow, int nCol, INT32 nData)
5039dede
AK
628{
629 TCHAR szBuffer[32];
630
8d131dda 631 _sntprintf(szBuffer, 32, _T("%d"), (int)nData);
9e126e57 632 setAt(nRow, nCol, szBuffer);
5039dede
AK
633}
634
967893bb
VK
635/**
636 * Set unsigned integer data at position
637 */
638void Table::setAt(int nRow, int nCol, UINT32 dwData)
5039dede
AK
639{
640 TCHAR szBuffer[32];
641
8d131dda 642 _sntprintf(szBuffer, 32, _T("%u"), (unsigned int)dwData);
9e126e57 643 setAt(nRow, nCol, szBuffer);
5039dede
AK
644}
645
967893bb
VK
646/**
647 * Set 64 bit integer data at position
648 */
9e126e57 649void Table::setAt(int nRow, int nCol, INT64 nData)
5039dede
AK
650{
651 TCHAR szBuffer[32];
652
653 _sntprintf(szBuffer, 32, INT64_FMT, nData);
9e126e57 654 setAt(nRow, nCol, szBuffer);
5039dede
AK
655}
656
967893bb
VK
657/**
658 * Set unsigned 64 bit integer data at position
659 */
660void Table::setAt(int nRow, int nCol, UINT64 qwData)
5039dede
AK
661{
662 TCHAR szBuffer[32];
663
664 _sntprintf(szBuffer, 32, UINT64_FMT, qwData);
9e126e57 665 setAt(nRow, nCol, szBuffer);
5039dede
AK
666}
667
967893bb
VK
668/**
669 * Set floating point data at position
670 */
9e126e57 671void Table::setAt(int nRow, int nCol, double dData)
5039dede
AK
672{
673 TCHAR szBuffer[32];
674
675 _sntprintf(szBuffer, 32, _T("%f"), dData);
9e126e57 676 setAt(nRow, nCol, szBuffer);
5039dede
AK
677}
678
967893bb
VK
679/**
680 * Get data from position
681 */
9e126e57 682const TCHAR *Table::getAsString(int nRow, int nCol)
5039dede 683{
d6bbfa4e
VK
684 TableRow *r = m_data->get(nRow);
685 return (r != NULL) ? r->getValue(nCol) : NULL;
5039dede
AK
686}
687
967893bb 688INT32 Table::getAsInt(int nRow, int nCol)
5039dede 689{
9e126e57 690 const TCHAR *pszVal;
5039dede 691
9e126e57 692 pszVal = getAsString(nRow, nCol);
5039dede
AK
693 return pszVal != NULL ? _tcstol(pszVal, NULL, 0) : 0;
694}
695
967893bb 696UINT32 Table::getAsUInt(int nRow, int nCol)
5039dede 697{
9e126e57 698 const TCHAR *pszVal;
5039dede 699
9e126e57 700 pszVal = getAsString(nRow, nCol);
5039dede
AK
701 return pszVal != NULL ? _tcstoul(pszVal, NULL, 0) : 0;
702}
703
9e126e57 704INT64 Table::getAsInt64(int nRow, int nCol)
5039dede 705{
9e126e57 706 const TCHAR *pszVal;
5039dede 707
9e126e57 708 pszVal = getAsString(nRow, nCol);
5039dede
AK
709 return pszVal != NULL ? _tcstoll(pszVal, NULL, 0) : 0;
710}
711
967893bb 712UINT64 Table::getAsUInt64(int nRow, int nCol)
5039dede 713{
9e126e57 714 const TCHAR *pszVal;
5039dede 715
9e126e57 716 pszVal = getAsString(nRow, nCol);
5039dede
AK
717 return pszVal != NULL ? _tcstoull(pszVal, NULL, 0) : 0;
718}
719
9e126e57 720double Table::getAsDouble(int nRow, int nCol)
5039dede 721{
9e126e57 722 const TCHAR *pszVal;
5039dede 723
9e126e57 724 pszVal = getAsString(nRow, nCol);
5039dede
AK
725 return pszVal != NULL ? _tcstod(pszVal, NULL) : 0;
726}
55bdca5a
VK
727
728/**
d6bbfa4e
VK
729 * Set status of given cell
730 */
731void Table::setStatusAt(int row, int col, int status)
732{
733 TableRow *r = m_data->get(row);
734 if (r != NULL)
735 {
736 r->setStatus(col, status);
737 }
738}
739
740/**
741 * Get status of given cell
742 */
743int Table::getStatus(int nRow, int nCol)
744{
745 TableRow *r = m_data->get(nRow);
746 return (r != NULL) ? r->getStatus(nCol) : -1;
747}
748
749/**
43666be9
VK
750 * Set object ID of given cell
751 */
752void Table::setCellObjectIdAt(int row, int col, UINT32 objectId)
753{
754 TableRow *r = m_data->get(row);
755 if (r != NULL)
756 {
757 r->setCellObjectId(col, objectId);
758 }
759}
760
761/**
a0ddfb29 762 * Add all rows from another table.
55bdca5a
VK
763 * Identical table format assumed.
764 *
a0ddfb29 765 * @param src source table
55bdca5a 766 */
a0ddfb29 767void Table::addAll(Table *src)
55bdca5a 768{
d6bbfa4e
VK
769 int numColumns = min(m_columns->size(), src->m_columns->size());
770 for(int i = 0; i < src->m_data->size(); i++)
55bdca5a 771 {
d6bbfa4e
VK
772 TableRow *dstRow = new TableRow(m_columns->size());
773 TableRow *srcRow = src->m_data->get(i);
774 for(int j = 0; j < numColumns; j++)
55bdca5a 775 {
43666be9 776 dstRow->set(j, srcRow->getValue(j), srcRow->getStatus(j), srcRow->getCellObjectId(j));
55bdca5a 777 }
d6bbfa4e 778 m_data->add(dstRow);
55bdca5a 779 }
a0ddfb29
VK
780}
781
782/**
783 * Copy one row from source table
784 */
785void Table::copyRow(Table *src, int row)
786{
d6bbfa4e
VK
787 TableRow *srcRow = src->m_data->get(row);
788 if (srcRow == NULL)
a0ddfb29
VK
789 return;
790
d6bbfa4e
VK
791 int numColumns = min(m_columns->size(), src->m_columns->size());
792 TableRow *dstRow = new TableRow(m_columns->size());
a0ddfb29 793
d6bbfa4e 794 for(int j = 0; j < numColumns; j++)
a0ddfb29 795 {
43666be9 796 dstRow->set(j, srcRow->getValue(j), srcRow->getStatus(j), srcRow->getCellObjectId(j));
a0ddfb29 797 }
d6bbfa4e
VK
798
799 m_data->add(dstRow);
55bdca5a 800}
975f38fc
VK
801
802/**
22aaa779
VK
803 * Build instance string
804 */
805void Table::buildInstanceString(int row, TCHAR *buffer, size_t bufLen)
806{
d6bbfa4e
VK
807 TableRow *r = m_data->get(row);
808 if (r == NULL)
22aaa779
VK
809 {
810 buffer[0] = 0;
811 return;
812 }
813
22aaa779
VK
814 String instance;
815 bool first = true;
d6bbfa4e 816 for(int i = 0; i < m_columns->size(); i++)
22aaa779
VK
817 {
818 if (m_columns->get(i)->isInstanceColumn())
819 {
820 if (!first)
821 instance += _T("~~~");
822 first = false;
d6bbfa4e
VK
823 const TCHAR *value = r->getValue(i);
824 if (value != NULL)
825 instance += value;
22aaa779
VK
826 }
827 }
828 nx_strncpy(buffer, (const TCHAR *)instance, bufLen);
829}
830
831/**
a0ddfb29
VK
832 * Find row by instance value
833 *
834 * @return row number or -1 if no such row
835 */
836int Table::findRowByInstance(const TCHAR *instance)
837{
d6bbfa4e 838 for(int i = 0; i < m_data->size(); i++)
a0ddfb29
VK
839 {
840 TCHAR currInstance[256];
841 buildInstanceString(i, currInstance, 256);
842 if (!_tcscmp(instance, currInstance))
843 return i;
844 }
845 return -1;
846}
847
848/**
967893bb
VK
849 * Create new table column definition
850 */
851TableColumnDefinition::TableColumnDefinition(const TCHAR *name, const TCHAR *displayName, INT32 dataType, bool isInstance)
852{
853 m_name = _tcsdup(CHECK_NULL(name));
854 m_displayName = (displayName != NULL) ? _tcsdup(displayName) : _tcsdup(m_name);
855 m_dataType = dataType;
856 m_instanceColumn = isInstance;
857}
858
859/**
860 * Create copy of existing table column definition
861 */
862TableColumnDefinition::TableColumnDefinition(TableColumnDefinition *src)
863{
864 m_name = _tcsdup(src->m_name);
865 m_displayName = _tcsdup(src->m_displayName);
866 m_dataType = src->m_dataType;
867 m_instanceColumn = src->m_instanceColumn;
868}
869
870/**
871 * Create table column definition from NXCP message
872 */
b368969c 873TableColumnDefinition::TableColumnDefinition(NXCPMessage *msg, UINT32 baseId)
967893bb 874{
b368969c 875 m_name = msg->getFieldAsString(baseId);
967893bb
VK
876 if (m_name == NULL)
877 m_name = _tcsdup(_T("(null)"));
b368969c
VK
878 m_dataType = msg->getFieldAsUInt32(baseId + 1);
879 m_displayName = msg->getFieldAsString(baseId + 2);
967893bb
VK
880 if (m_displayName == NULL)
881 m_displayName = _tcsdup(m_name);
b368969c 882 m_instanceColumn = msg->getFieldAsUInt16(baseId + 3) ? true : false;
967893bb
VK
883}
884
885/**
886 * Destructor for table column definition
887 */
888TableColumnDefinition::~TableColumnDefinition()
889{
890 free(m_name);
891 free(m_displayName);
892}
893
894/**
895 * Fill message with table column definition data
975f38fc 896 */
b368969c 897void TableColumnDefinition::fillMessage(NXCPMessage *msg, UINT32 baseId)
975f38fc 898{
b368969c
VK
899 msg->setField(baseId, m_name);
900 msg->setField(baseId + 1, (UINT32)m_dataType);
901 msg->setField(baseId + 2, m_displayName);
902 msg->setField(baseId + 3, (WORD)(m_instanceColumn ? 1 : 0));
975f38fc 903}