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