fixed memory leak in table unpack code; added Valgrind suppression file
[public/netxms.git] / src / libnetxms / table.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2014 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
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
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 **
15 ** You should have received a copy of the GNU Lesser General Public License
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"
24 #include <expat.h>
25 #include <zlib.h>
26
27 #define DEFAULT_OBJECT_ID (0)
28 #define DEFAULT_STATUS (-1)
29
30 /**
31 * Create empty table row
32 */
33 TableRow::TableRow(int columnCount)
34 {
35 m_cells = new ObjectArray<TableCell>(columnCount, 8, true);
36 for(int i = 0; i < columnCount; i++)
37 m_cells->add(new TableCell());
38 m_objectId = 0;
39 }
40
41 /**
42 * Table row copy constructor
43 */
44 TableRow::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 /**
53 * Create empty table
54 */
55 Table::Table() : RefCountObject()
56 {
57 m_data = new ObjectArray<TableRow>(32, 32, true);
58 m_title = NULL;
59 m_source = DS_INTERNAL;
60 m_columns = new ObjectArray<TableColumnDefinition>(8, 8, true);
61 m_extendedFormat = false;
62 }
63
64 /**
65 * Create table from NXCP message
66 */
67 Table::Table(NXCPMessage *msg) : RefCountObject()
68 {
69 m_columns = new ObjectArray<TableColumnDefinition>(8, 8, true);
70 createFromMessage(msg);
71 }
72
73 /**
74 * Copy constructor
75 */
76 Table::Table(Table *src) : RefCountObject()
77 {
78 m_extendedFormat = src->m_extendedFormat;
79 m_data = new ObjectArray<TableRow>(src->m_data->size(), 32, true);
80 int i;
81 for(i = 0; i < src->m_data->size(); i++)
82 m_data->add(new TableRow(src->m_data->get(i)));
83 m_title = (src->m_title != NULL) ? _tcsdup(src->m_title) : NULL;
84 m_source = src->m_source;
85 m_columns = new ObjectArray<TableColumnDefinition>(src->m_columns->size(), 8, true);
86 for(i = 0; i < src->m_columns->size(); i++)
87 m_columns->add(new TableColumnDefinition(src->m_columns->get(i)));
88 }
89
90 /**
91 * Table destructor
92 */
93 Table::~Table()
94 {
95 destroy();
96 delete m_columns;
97 delete m_data;
98 }
99
100 /**
101 * Destroy table data
102 */
103 void Table::destroy()
104 {
105 m_columns->clear();
106 m_data->clear();
107 safe_free(m_title);
108 }
109
110 /**
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
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
122
123 /**
124 * State information for XML parser
125 */
126 typedef struct
127 {
128 Table *table;
129 int state;
130 String *buffer;
131 int column;
132 } XML_PARSER_STATE;
133
134 /**
135 * Element start handler for XML parser
136 */
137 static 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 {
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 {
150 #ifdef UNICODE
151 WCHAR *wtitle = WideStringFromUTF8String(title);
152 ps->table->setTitle(wtitle);
153 free(wtitle);
154 #else
155 ps->table->setTitle(title);
156 #endif
157 }
158 ps->state = XML_STATE_TABLE;
159 }
160 else
161 {
162 ps->state = XML_STATE_ERROR;
163 }
164 }
165 else if (!strcmp(name, "columns"))
166 {
167 ps->state = (ps->state == XML_STATE_TABLE) ? XML_STATE_COLUMNS : XML_STATE_ERROR;
168 }
169 else if (!strcmp(name, "column"))
170 {
171 if (ps->state == XML_STATE_COLUMNS)
172 {
173 #ifdef UNICODE
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;
177 #else
178 const char *name = CHECK_NULL_A(XMLGetAttr(attrs, "name"));
179 const char *displayName = XMLGetAttr(attrs, "displayName");
180 #endif
181 ps->table->addColumn(name, XMLGetAttrInt(attrs, "dataType", 0), displayName, XMLGetAttrBoolean(attrs, "isInstance", false));
182 ps->state = XML_STATE_COLUMN;
183 #ifdef UNICODE
184 safe_free(name);
185 safe_free(displayName);
186 #endif
187 }
188 else
189 {
190 ps->state = XML_STATE_ERROR;
191 }
192 }
193 else if (!strcmp(name, "data"))
194 {
195 ps->state = (ps->state == XML_STATE_TABLE) ? XML_STATE_DATA : XML_STATE_ERROR;
196 }
197 else if (!strcmp(name, "tr"))
198 {
199 if (ps->state == XML_STATE_DATA)
200 {
201 ps->table->addRow();
202 ps->table->setObjectId(ps->table->getNumRows() - 1, XMLGetAttrInt(attrs, "objectId", DEFAULT_OBJECT_ID));
203 ps->column = 0;
204 ps->state = XML_STATE_ROW;
205 }
206 else
207 {
208 ps->state = XML_STATE_ERROR;
209 }
210 }
211 else if (!strcmp(name, "td"))
212 {
213 if (ps->state == XML_STATE_ROW)
214 {
215 ps->table->setStatus(ps->column, XMLGetAttrInt(attrs, "status", DEFAULT_STATUS));
216 ps->state = XML_STATE_CELL;
217 ps->buffer->clear();
218 }
219 else
220 {
221 ps->state = XML_STATE_ERROR;
222 }
223 }
224 else
225 {
226 ps->state = XML_STATE_ERROR;
227 }
228 }
229
230 /**
231 * Element end handler for XML parser
232 */
233 static void EndElement(void *userData, const char *name)
234 {
235 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
236 if (ps->state == XML_STATE_ERROR)
237 return;
238
239 if (!strcmp(name, "td"))
240 {
241 ps->table->set(ps->column, ps->buffer->getBuffer());
242 ps->column++;
243 ps->state = XML_STATE_ROW;
244 }
245 else if (!strcmp(name, "tr"))
246 {
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;
257 }
258 }
259
260 /**
261 * Data handler for XML parser
262 */
263 static void CharData(void *userData, const XML_Char *s, int len)
264 {
265 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
266
267 if (ps->state == XML_STATE_CELL)
268 {
269 ps->buffer->appendMBString(s, len, CP_UTF8);
270 }
271 }
272
273 /**
274 * Parse XML document with table data
275 */
276 bool Table::parseXML(const char *xml)
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;
287 state.column = -1;
288 state.buffer = new String();
289
290 bool success = (XML_Parse(parser, xml, (int)strlen(xml), TRUE) != XML_STATUS_ERROR);
291 if (success)
292 success = (state.state != XML_STATE_ERROR);
293 XML_ParserFree(parser);
294 delete state.buffer;
295 return success;
296 }
297
298 /**
299 * Create table from XML document
300 */
301 Table *Table::createFromXML(const char *xml)
302 {
303 Table *table = new Table();
304 if (table->parseXML(xml))
305 {
306 return table;
307 }
308 delete table;
309 return NULL;
310 }
311
312 /**
313 * Create table from packed XML document
314 */
315 Table *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;
326 if (uncompress((BYTE *)xml, &uncompSize, (BYTE *)&compressedXml[4], (uLong)compressedSize - 4) != Z_OK)
327 {
328 free(xml);
329 free(compressedXml);
330 return NULL;
331 }
332 xml[xmlSize] = 0;
333 free(compressedXml);
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 /**
347 * Create XML document from table
348 */
349 TCHAR *Table::createXML()
350 {
351 String xml;
352 xml.appendFormattedString(_T("<table extendedFormat=\"%s\" source=\"%d\" name=\"%s\">\r\n"), m_extendedFormat ? _T("true") : _T("false"), m_source,
353 (const TCHAR *)EscapeStringForXML2(m_title, -1));
354 xml.append(_T("<columns>\r\n"));
355 int i;
356 for(i = 0; i < m_columns->size(); i++)
357 xml.appendFormattedString(_T("<column name=\"%s\" displayName=\"%s\" isInstance=\"%s\" dataType=\"%d\"/>\r\n"),
358 (const TCHAR *)EscapeStringForXML2(m_columns->get(i)->getName(), -1),
359 (const TCHAR *)EscapeStringForXML2(m_columns->get(i)->getDisplayName(), -1),
360 m_columns->get(i)->isInstanceColumn()? _T("true") : _T("false"), m_columns->get(i)->getDataType());
361 xml.append(_T("</columns>\r\n"));
362 xml.append(_T("<data>\r\n"));
363 for(i = 0; i < m_data->size(); i++)
364 {
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"));
370 for(int j = 0; j < m_columns->size(); j++)
371 {
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"));
385 }
386 xml.append(_T("</tr>\r\n"));
387 }
388 xml.append(_T("</data>\r\n"));
389 xml.append(_T("</table>"));
390 return _tcsdup(xml);
391 }
392
393 /**
394 * Create packed XML document
395 */
396 char *Table::createPackedXML()
397 {
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);
404 uLongf buflen = compressBound((uLong)len);
405 BYTE *buffer = (BYTE *)malloc(buflen + 4);
406 if (compress(&buffer[4], &buflen, (BYTE *)utf8xml, (uLong)len) != Z_OK)
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;
418 }
419
420 /**
421 * Create table from NXCP message
422 */
423 void Table::createFromMessage(NXCPMessage *msg)
424 {
425 int i;
426 UINT32 dwId;
427
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);
431 m_source = msg->getFieldAsInt16(VID_DCI_SOURCE_TYPE);
432 m_extendedFormat = msg->getFieldAsBoolean(VID_TABLE_EXTENDED_FORMAT);
433
434 for(i = 0, dwId = VID_TABLE_COLUMN_INFO_BASE; i < columns; i++, dwId += 10)
435 {
436 m_columns->add(new TableColumnDefinition(msg, dwId));
437 }
438 if (msg->isFieldExist(VID_INSTANCE_COLUMN))
439 {
440 TCHAR name[MAX_COLUMN_NAME];
441 msg->getFieldAsString(VID_INSTANCE_COLUMN, name, MAX_COLUMN_NAME);
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 }
451
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 {
459 row->setObjectId(msg->getFieldAsUInt32(dwId++));
460 dwId += 9;
461 }
462 for(int j = 0; j < columns; j++)
463 {
464 TCHAR *value = msg->getFieldAsString(dwId++);
465 if (m_extendedFormat)
466 {
467 int status = msg->getFieldAsInt16(dwId++);
468 UINT32 objectId = msg->getFieldAsUInt32(dwId++);
469 row->setPreallocated(j, value, status, objectId);
470 dwId += 7;
471 }
472 else
473 {
474 row->setPreallocated(j, value, -1, 0);
475 }
476 }
477 }
478 }
479
480 /**
481 * Update table from NXCP message
482 */
483 void Table::updateFromMessage(NXCPMessage *msg)
484 {
485 destroy();
486 delete m_data; // will be re-created by createFromMessage
487 createFromMessage(msg);
488 }
489
490 /**
491 * Fill NXCP message with table data
492 */
493 int Table::fillMessage(NXCPMessage &msg, int offset, int rowLimit)
494 {
495 UINT32 id;
496
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));
500
501 if (offset == 0)
502 {
503 msg.setField(VID_TABLE_NUM_ROWS, (UINT32)m_data->size());
504 msg.setField(VID_TABLE_NUM_COLS, (UINT32)m_columns->size());
505
506 id = VID_TABLE_COLUMN_INFO_BASE;
507 for(int i = 0; i < m_columns->size(); i++, id += 10)
508 m_columns->get(i)->fillMessage(&msg, id);
509 }
510 msg.setField(VID_TABLE_OFFSET, (UINT32)offset);
511
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++)
515 {
516 TableRow *r = m_data->get(row);
517 if (m_extendedFormat)
518 {
519 msg.setField(id++, r->getObjectId());
520 id += 9;
521 }
522 for(int col = 0; col < m_columns->size(); col++)
523 {
524 const TCHAR *tmp = r->getValue(col);
525 msg.setField(id++, CHECK_NULL_EX(tmp));
526 if (m_extendedFormat)
527 {
528 msg.setField(id++, (UINT16)r->getStatus(col));
529 msg.setField(id++, r->getCellObjectId(col));
530 id += 7;
531 }
532 }
533 }
534 msg.setField(VID_NUM_ROWS, (UINT32)(stopRow - offset));
535
536 if (stopRow == m_data->size())
537 msg.setEndOfSequence();
538 return stopRow;
539 }
540
541 /**
542 * Add new column
543 */
544 int Table::addColumn(const TCHAR *name, INT32 dataType, const TCHAR *displayName, bool isInstance)
545 {
546 m_columns->add(new TableColumnDefinition(name, displayName, dataType, isInstance));
547 for(int i = 0; i < m_data->size(); i++)
548 m_data->get(i)->addColumn();
549 return m_columns->size() - 1;
550 }
551
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 */
558 int Table::getColumnIndex(const TCHAR *name)
559 {
560 for(int i = 0; i < m_columns->size(); i++)
561 if (!_tcsicmp(name, m_columns->get(i)->getName()))
562 return i;
563 return -1;
564 }
565
566 /**
567 * Add new row
568 */
569 int Table::addRow()
570 {
571 m_data->add(new TableRow(m_columns->size()));
572 return m_data->size() - 1;
573 }
574
575 /**
576 * Delete row
577 */
578 void Table::deleteRow(int row)
579 {
580 m_data->remove(row);
581 }
582
583 /**
584 * Delete column
585 */
586 void Table::deleteColumn(int col)
587 {
588 if ((col < 0) || (col >= m_columns->size()))
589 return;
590
591 m_columns->remove(col);
592 for(int i = 0; i < m_data->size(); i++)
593 m_data->get(i)->deleteColumn(col);
594 }
595
596 /**
597 * Set data at position
598 */
599 void Table::setAt(int nRow, int nCol, const TCHAR *pszData)
600 {
601 TableRow *r = m_data->get(nRow);
602 if (r != NULL)
603 {
604 r->setValue(nCol, pszData);
605 }
606 }
607
608 /**
609 * Set pre-allocated data at position
610 */
611 void Table::setPreallocatedAt(int nRow, int nCol, TCHAR *data)
612 {
613 TableRow *r = m_data->get(nRow);
614 if (r != NULL)
615 {
616 r->setPreallocatedValue(nCol, data);
617 }
618 else
619 {
620 free(data);
621 }
622 }
623
624 /**
625 * Set integer data at position
626 */
627 void Table::setAt(int nRow, int nCol, INT32 nData)
628 {
629 TCHAR szBuffer[32];
630
631 _sntprintf(szBuffer, 32, _T("%d"), (int)nData);
632 setAt(nRow, nCol, szBuffer);
633 }
634
635 /**
636 * Set unsigned integer data at position
637 */
638 void Table::setAt(int nRow, int nCol, UINT32 dwData)
639 {
640 TCHAR szBuffer[32];
641
642 _sntprintf(szBuffer, 32, _T("%u"), (unsigned int)dwData);
643 setAt(nRow, nCol, szBuffer);
644 }
645
646 /**
647 * Set 64 bit integer data at position
648 */
649 void Table::setAt(int nRow, int nCol, INT64 nData)
650 {
651 TCHAR szBuffer[32];
652
653 _sntprintf(szBuffer, 32, INT64_FMT, nData);
654 setAt(nRow, nCol, szBuffer);
655 }
656
657 /**
658 * Set unsigned 64 bit integer data at position
659 */
660 void Table::setAt(int nRow, int nCol, UINT64 qwData)
661 {
662 TCHAR szBuffer[32];
663
664 _sntprintf(szBuffer, 32, UINT64_FMT, qwData);
665 setAt(nRow, nCol, szBuffer);
666 }
667
668 /**
669 * Set floating point data at position
670 */
671 void Table::setAt(int nRow, int nCol, double dData)
672 {
673 TCHAR szBuffer[32];
674
675 _sntprintf(szBuffer, 32, _T("%f"), dData);
676 setAt(nRow, nCol, szBuffer);
677 }
678
679 /**
680 * Get data from position
681 */
682 const TCHAR *Table::getAsString(int nRow, int nCol)
683 {
684 TableRow *r = m_data->get(nRow);
685 return (r != NULL) ? r->getValue(nCol) : NULL;
686 }
687
688 INT32 Table::getAsInt(int nRow, int nCol)
689 {
690 const TCHAR *pszVal;
691
692 pszVal = getAsString(nRow, nCol);
693 return pszVal != NULL ? _tcstol(pszVal, NULL, 0) : 0;
694 }
695
696 UINT32 Table::getAsUInt(int nRow, int nCol)
697 {
698 const TCHAR *pszVal;
699
700 pszVal = getAsString(nRow, nCol);
701 return pszVal != NULL ? _tcstoul(pszVal, NULL, 0) : 0;
702 }
703
704 INT64 Table::getAsInt64(int nRow, int nCol)
705 {
706 const TCHAR *pszVal;
707
708 pszVal = getAsString(nRow, nCol);
709 return pszVal != NULL ? _tcstoll(pszVal, NULL, 0) : 0;
710 }
711
712 UINT64 Table::getAsUInt64(int nRow, int nCol)
713 {
714 const TCHAR *pszVal;
715
716 pszVal = getAsString(nRow, nCol);
717 return pszVal != NULL ? _tcstoull(pszVal, NULL, 0) : 0;
718 }
719
720 double Table::getAsDouble(int nRow, int nCol)
721 {
722 const TCHAR *pszVal;
723
724 pszVal = getAsString(nRow, nCol);
725 return pszVal != NULL ? _tcstod(pszVal, NULL) : 0;
726 }
727
728 /**
729 * Set status of given cell
730 */
731 void 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 */
743 int 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 /**
750 * Set object ID of given cell
751 */
752 void 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 /**
762 * Add all rows from another table.
763 * Identical table format assumed.
764 *
765 * @param src source table
766 */
767 void Table::addAll(Table *src)
768 {
769 int numColumns = min(m_columns->size(), src->m_columns->size());
770 for(int i = 0; i < src->m_data->size(); i++)
771 {
772 TableRow *dstRow = new TableRow(m_columns->size());
773 TableRow *srcRow = src->m_data->get(i);
774 for(int j = 0; j < numColumns; j++)
775 {
776 dstRow->set(j, srcRow->getValue(j), srcRow->getStatus(j), srcRow->getCellObjectId(j));
777 }
778 m_data->add(dstRow);
779 }
780 }
781
782 /**
783 * Copy one row from source table
784 */
785 void Table::copyRow(Table *src, int row)
786 {
787 TableRow *srcRow = src->m_data->get(row);
788 if (srcRow == NULL)
789 return;
790
791 int numColumns = min(m_columns->size(), src->m_columns->size());
792 TableRow *dstRow = new TableRow(m_columns->size());
793
794 for(int j = 0; j < numColumns; j++)
795 {
796 dstRow->set(j, srcRow->getValue(j), srcRow->getStatus(j), srcRow->getCellObjectId(j));
797 }
798
799 m_data->add(dstRow);
800 }
801
802 /**
803 * Build instance string
804 */
805 void Table::buildInstanceString(int row, TCHAR *buffer, size_t bufLen)
806 {
807 TableRow *r = m_data->get(row);
808 if (r == NULL)
809 {
810 buffer[0] = 0;
811 return;
812 }
813
814 String instance;
815 bool first = true;
816 for(int i = 0; i < m_columns->size(); i++)
817 {
818 if (m_columns->get(i)->isInstanceColumn())
819 {
820 if (!first)
821 instance += _T("~~~");
822 first = false;
823 const TCHAR *value = r->getValue(i);
824 if (value != NULL)
825 instance += value;
826 }
827 }
828 nx_strncpy(buffer, (const TCHAR *)instance, bufLen);
829 }
830
831 /**
832 * Find row by instance value
833 *
834 * @return row number or -1 if no such row
835 */
836 int Table::findRowByInstance(const TCHAR *instance)
837 {
838 for(int i = 0; i < m_data->size(); i++)
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 /**
849 * Create new table column definition
850 */
851 TableColumnDefinition::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 */
862 TableColumnDefinition::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 */
873 TableColumnDefinition::TableColumnDefinition(NXCPMessage *msg, UINT32 baseId)
874 {
875 m_name = msg->getFieldAsString(baseId);
876 if (m_name == NULL)
877 m_name = _tcsdup(_T("(null)"));
878 m_dataType = msg->getFieldAsUInt32(baseId + 1);
879 m_displayName = msg->getFieldAsString(baseId + 2);
880 if (m_displayName == NULL)
881 m_displayName = _tcsdup(m_name);
882 m_instanceColumn = msg->getFieldAsUInt16(baseId + 3) ? true : false;
883 }
884
885 /**
886 * Destructor for table column definition
887 */
888 TableColumnDefinition::~TableColumnDefinition()
889 {
890 free(m_name);
891 free(m_displayName);
892 }
893
894 /**
895 * Fill message with table column definition data
896 */
897 void TableColumnDefinition::fillMessage(NXCPMessage *msg, UINT32 baseId)
898 {
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));
903 }