BimRv SDK: Retrieve Calculated Table Cell Data

Evgeniy Tikhonov

June 02, 2023

This article explains how to collect calculated cell data from OdBmDBViewSchedule and OdBmPanelScheduleView elements.

Regular tables have a schema that defines a set of fields and conditions for elements that are displayed in the table. These fields may be simple (represent a parameter ID) or calculatable (formulas). A TableData object is built based on the table schema.

The OdBmDBViewSchedule elements have internal metadata used to build the values in its cells. Fields defined in a schedule schema link table cells to the respective parameters specified by the fields. These parameters are related to elements collected using conditions based on a schedule category or a category and schedule filter rules.

The OdBmPanelScheduleView elements don't have fields and schemas, but each cell of a panel schedule template has a corresponding parameter.

The simple process of value retrieval consists of two steps:

  1. Before extracting cell values from the table, you should recalculate them using the OdBmDBViewSchedule::refreshData() method for the OdBmDBViewSchedule class and the OdBmPanelScheduleView::refreshData() method for the OdBmPanelScheduleView class.
  2. To retrieve the calculated values of the desired table section, the OdBmPanelScheduleView and OdBmDBViewSchedule classes override the getCellText() method of their ancestor OdBmTableView: OdBmDBViewSchedule::getCellText() and OdBmPanelScheduleView::getCellText().

Table cells also can be merged with their neighboring cells and information about the merged area can be retrieved using the getCellMergedArea() method. To obtain a table cell style, use the getTableCellStyle() method. The OdBmTableCellStyle elements have properties related to the cell text font and can be accessed by the methods used in the example below.

Example

This example shows the implementation of the OdBmTableViewToHTMLHelper class from the sample located in BimRv/Examples/TB_Commands/Dump/TableDump.

  1. Define and implement utility methods that will be used later.
    // Gets the HTML string representing this horizontal alignment.
    static OdString getAlignString(OdBm::HorizontalAlignmentStyle::Enum hAlignment) {
      switch (hAlignment)
      {
      case OdBm::HorizontalAlignmentStyle::Left:
        return L" align=\"left\"";
      case OdBm::HorizontalAlignmentStyle::Center:
        return L" align=\"center\"";
      case OdBm::HorizontalAlignmentStyle::Right:
        return " align=\"right\"";
      }
      return OdString::kEmpty;
    }
    static OdString colorToHtml(OdBmCmColor color) {
      return OdString().format(L"\"#%2.2X%2.2X%2.2X\"", color.red(), color.green(), color.blue());
    }
    static bool isWhiteColor(const OdBmCmColor& color) {
      return color.red() == 255 && color.green() == 255 && color.blue() == 255;
    }
    OdBmTableViewToHTMLHelper::OdBmTableViewToHTMLHelper(const OdBmTableViewPtr& pTableView)
      : m_pTableView(pTableView) {
    }
    void replaceIllegalCharacters(OdString& sText) {
      sText.replace(L"&", L"&");
      sText.replace(L"<", L"<");
      sText.replace(L">", L">");
      sText.replace(L"\"", L""");
      sText.replace(L"'", L"'");
      if (sText.isEmpty())
        sText = L" ";
    }
  2. Define the class and necessary methods.
    class OdBmTableViewToHTMLHelper
    {
    public:
      /** \details
        Default constructor of OdBmTableViewToHTMLHelper class.
      */
      OdBmTableViewToHTMLHelper(const OdBmTableViewPtr&);
      /** \details
        Exports the table to the HTML file.
        \returns String representing the result of the export
      */
      OdString exportTable() const;
    private:
      OdString exportCell(OdBm::SectionType::Enum sectionId, OdUInt32 row, OdUInt32 col) const;
      OdString exportSection(OdBm::SectionType::Enum) const;
      OdBmTableViewPtr m_pTableView;
      mutable std::set<std::pair<OdUInt32, OdUInt32>> writtenCells;
    };

    The constructor accepts a smart pointer to an OdBmTableView object and its descendants. The exportTable() method simply runs exportSection(OdBm::SectionType::Enum) on all possible sections in a table view (header, body, summary, and footer).

  3. Implement the exportSection() method.
    OdString OdBmTableViewToHTMLHelper::exportSection(OdBm::SectionType::Enum sectionId) const {
      OdBmTableSectionDataPtr pSection = m_pTableView->getData()->getSectionDataItem(sectionId);
      if (pSection.isNull())
        return OdString::kEmpty;
      OdString sSectionLeader = L"<table border=\"1\">\n    ";
      OdString sSectionTrailer = L"\n  </table>";
      OdString sRowLeader = L"<tr>\n";
      OdString sRowTrailer = L"\n    </tr>";
      OdString sSection;
      for (OdUInt32 row = 0; row < pSection->getNumberOfRows(); row++) {
        OdString sSectionRow = sRowLeader+L"      ";
        for (OdUInt32 col = pSection->getFirstColumnNumber(); col < pSection->getNumberOfColumns(); col++) {
          if (std::find(writtenCells.begin(), writtenCells.end(), std::make_pair(row, col)) != writtenCells.end())
            continue;
          sSectionRow += exportCell(sectionId, row, col);
        }
        sSection += sSectionRow + sRowTrailer;
      }
      writtenCells.clear();
      return sSectionLeader + sSection + sSectionTrailer;
    }
    
    
  4. Implement the exportCell() method.
    
    OdString OdBmTableViewToHTMLHelper::exportCell(OdBm::SectionType::Enum sectionId, OdUInt32 row, OdUInt32 col) const {
      OdString sCellLeader = L"<td";
      OdString sCellTrailer = L"</td>";
      OdString sCellLeaderTrailer = L">";
      OdString sText = m_pTableView->getCellText(sectionId, row, col);
      replaceIllegalCharacters(sText);
      OdBmTableSectionDataPtr pSection = m_pTableView->getData()->getSectionDataItem(sectionId);
      OdBmTableCellStylePtr pCellStyle;
      OdBmTableCellMergedArea mergedArea;
      OdString sCellStyle;
      OdString sColSpan;
      OdString sRowSpan;
      OdString sFontStyleLeader;
      OdString sFontStyleTrailer;
      if (eOk == pSection->getCellMergedArea(row, col, mergedArea)) {
        // If merged cell spans multiple columns
        if (mergedArea.getLeft() != mergedArea.getRight()) {
          sColSpan = OdString().format(L" colspan=\"%d\" ", mergedArea.getRight() - mergedArea.getLeft() + 1);
        }
        // If merged cell spans multiple rows
        if (mergedArea.getTop() != mergedArea.getBottom()) {
          sColSpan = OdString().format(L" rowspan=\"%d\" ", mergedArea.getBottom() - mergedArea.getTop() + 1);
        }
        // Remember all written cells related to the merge 
        for (OdUInt32 iMergedRow = mergedArea.getTop(); iMergedRow <= mergedArea.getBottom(); iMergedRow++) {
          for (OdUInt32 iMergedCol = mergedArea.getLeft(); iMergedCol <= mergedArea.getRight(); iMergedCol++) {
            writtenCells.insert(std::make_pair(iMergedRow, iMergedCol));
          }
        }
      }
      if (eOk == pSection->getTableCellStyle(row, col, pCellStyle)) {
        // Horizontal alignment
        sCellStyle += getAlignString(pCellStyle->getHorizontalAlignment());
        // Write formatting attributes for the upcoming cell
        // Background color
        if (!isWhiteColor(pCellStyle->getBackgroundColor())) {
          sCellStyle += L" bgcolor=" + colorToHtml(pCellStyle->getBackgroundColor());
        }
        // Write subtags for the cell
        // Underline
        if (pCellStyle->isFontUnderline()) {
          sFontStyleLeader += L"<u>";
          sFontStyleTrailer += L"</u>";
        }
        //Italic
        if (pCellStyle->isFontItalic()) {
          sFontStyleLeader += L"<i>";
          sFontStyleTrailer += L"</i>";
        }
        //Bold
        if (pCellStyle->isFontBold()) {
          sFontStyleLeader += L"<b>";
          sFontStyleTrailer += L"</b>";
        }
      }
      return sCellLeader + sColSpan + sRowSpan + sCellStyle + sCellLeaderTrailer + sFontStyleLeader + sText + sFontStyleTrailer + sCellTrailer;
    }