ODA IFC SDK Developer's Guide > IFC Data Management > Common Data Access
Common Data Access

Common Data Access

The Common Data Access (CDA) mechanism is used by various applications (e.g., OpenIFCViewer, ExIfcCDAWalker, etc.) to access structural information and object properties of imported (open) databases in the most abstract manner without the need to know about the imported format. The structure is provided as a tree, where the nodes of the tree are connected with imported instances. Specific levels of the hierarchy in the tree depend on the domain, as well as the connection of this perceived hierarchy with the database content. CDA technology is based on the RxProperties mechanism. To get more information, see Work with Non-COM Properties.

Before working with CDA, make sure the corresponding RxProperties module is built for your current ODA version. To use the CDA mechanism, call the odIfcInitialize() global function with the first parameter set to true. This function initializes the IfcCore module, loads the RxProperties module and performs the necessary calls for working with Common Data Access. You can also check whether CDA is enabled by using one of the following methods:

  • Call the odIfcIsCDAInitialized global function which returns true if CDA is initialized and can be used for IFC data.
  • Call the OdIfcCoreModule::isCDAEnabled method which returns true if the Common Data Access interface is available for the core IFC module.

The rest of the API for working with CDA is part of Kernel SDK. It is strongly recommended to read the Common Data Access section before proceeding with the example.

Example of CDA Usage

The ExIfcCDAWalker example application shows how to traverse through the spatial structure of an IFC model from a specified file using the CDA mechanism. Example location: Ifc/Examples/ExIfcCDAWalker/ExIfcCDAWalker.cpp. The example has two parts: part one — preparations and application setup (points 1-7), and part two — traversing through the CDA tree (point 8 and its sub-points). Overview of the application:

  1. The AbstractNotifier interface defines the notification methods to be called in the following situations:
    • When a new object hierarchy level begins in a CDA tree.
    • When a new object hierarchy level ends.
    • When the CDA Hierarchy attribute is found.
    • When the Geometrical Representation Representation for the currently walked node is found.
    
    class AbstractNotifier
    {
    public:
    
      virtual ~AbstractNotifier() {};
    
      virtual void onLevelBegin(const OdRxObject *inst) = 0;
      virtual void onLevelEnd(const OdRxObject *inst) = 0;
      virtual void onHierarchyAttributeFound(const OdRxObject *inst, OdRxMember *member) = 0;
      virtual void onHasRepresentation(const OdRxObject *inst, const OdRxObject *reprInst) = 0;
    };
    
    
  2. The CDATreePrinter class is designed to print information about nodes that are traversed. This class inherits AbstractNotifier and implements the notification methods defined in AbstractNotifier.
    
    class CDATreePrinter : public AbstractNotifier
    {
      int m_level;
    
    public:
    
      CDATreePrinter()
        : m_level(0)
      {}
    
      virtual void onLevelBegin(const OdRxObject *inst)
      {
        ++m_level;
    
        odifcPrintConsoleString(L"%sHierarchy level: %d\n", tabulation(m_level).c_str(), m_level);
    
        const OdString &className = inst->isA()->name();
        odifcPrintConsoleString(L"%sClass: %s\n", tabulation(m_level).c_str(), className.c_str());
    
        OdString entityName;
    
        OdRxPropertyPtr propName = OdRxMemberQueryEngine::theEngine()->find(inst, L"Name");
        if (!propName.isNull())
        {
          OdRxValue valName;
          (eOk == propName->getValue(inst, valName)) &&
            (valName >> entityName);
        }
    
        odifcPrintConsoleString(L"%sName: `%s`\n", tabulation(m_level).c_str(), entityName.c_str());
      }
    
      virtual void onLevelEnd(const OdRxObject *inst)
      {
        --m_level;
      }
    
      virtual void onHierarchyAttributeFound(const OdRxObject *inst, OdRxMember *member)
      {
        const OdString &propName = member->name();
        odifcPrintConsoleString(L"%sHierarchy property name: %s\n", tabulation(m_level).c_str(), propName.c_str());
      }
    
      virtual void onHasRepresentation(const OdRxObject *inst, const OdRxObject *reprInst)
      {
        const OdString &reprClassName = reprInst->isA()->name();
        odifcPrintConsoleString(L"%sCan be vectorized here (Representation: %s)\n", tabulation(m_level).c_str(), reprClassName.c_str());
        // vectorize(inst); // not reprInst
      }
    
    protected:
    
      OdString tabulation(int tab)
      {
        if (tab < 0)
          tab = 0;
        OdString res;
        while (tab > 0)
        {
          res += OD_T("  ");
          --tab;
        }
        return res;
      }
    
    };
    
    
  3. The CDAWalker class is designed to traverse the CDA tree and has a pointer to a notifier object as a private field. The public method run() walks through the CDA tree.
    
    class CDAWalker
    {
      AbstractNotifier *m_pNotifier;
    
    public:
    
      CDAWalker(AbstractNotifier *notifier)
        : m_pNotifier(notifier)
      {}
    
      ~CDAWalker()
      {
        delete m_pNotifier;
      }
    
      void run(const OdRxObject *inst)
      {
        return walkCDA(inst);
      }
    
    protected:
    
      void walkCDA(const OdRxObject *inst)
      {
        ...
      }
    
    };
    
    
  4. Define the main function, and get command line arguments. The command line argument should be a full path to the IFC file for which you want to print CDA tree information. Also, display information about the application to the console.
    
    #if defined(OD_USE_WMAIN)
    int wmain(int argc, wchar_t* argv[])
    #else
    int main(int argc, char* argv[])
    #endif
    {
      int nRes = 0; // Return value for main
    
    #if defined(TARGET_OS_MAC) && !defined(__MACH__)
      argc = ccommand(&argv);
    #endif
    
      setlocale(LC_TIME, ""); // set current user locale (not OD_T("C")), for strftime
    
      /**********************************************************************/
      /* Create a Services object                                           */
      /**********************************************************************/
      OdStaticRxObject<MyServices> svcs;
    
      /**********************************************************************/
      /* Display the Product and Version that created the executable        */
      /**********************************************************************/
      odPrintConsoleString(L"\nExIfcCDAWalker developed using %ls ver %ls", 
        svcs.product().c_str(), svcs.versionString().c_str());
      
      ...
      
    }// main
    
    
  5. Parse command line arguments, and define the static map, if needed.
    
      /**********************************************************************/
      /* Parse Command Line inputs                                          */
      /**********************************************************************/
      bool bInvalidArgs = (argc != 2);
      if (bInvalidArgs)
      {
        bInvalidArgs = true;
        nRes  = 1;
      }
    
      if (bInvalidArgs)    
      {
        odPrintConsoleString(L"\n\nusage: ExIfcCDAWalker <filename>");
        odPrintConsoleString(L"\n<filename> - input .ifc file\n");
        return nRes;
      }
    
    #if !defined(_TOOLKIT_IN_DLL_) 
      ODRX_INIT_STATIC_MODULE_MAP();
    #endif
    
    
  6. Initialize ODA SDKs and initialize the IfcCore module using the odIfcInitialize() function. Note that the first passed parameter to this function should be true.
    
      /**********************************************************************/
      /* Initialize ODA SDK                                                 */
      /**********************************************************************/
      odrxInitialize(&svcs);
    
      odDbRootInitialize();
    
      /**********************************************************************/
      /* Initialize IfcCore                                                 */
      /**********************************************************************/
      odIfcInitialize(true /* CDA */, false /* No geometry calculation needed */);
    
    
  7. Define the try - catch block that tries to run CDAWalker for the specified file and handles errors, if they occur.
    
    try
      {
        OdString ifcFileName(argv[1]);
    
        OdIfcFilePtr pIfcFile = svcs.createDatabase();
        OdResult res = pIfcFile->readFile(ifcFileName);
    
        if (res == eOk)
        {
          odPrintConsoleString(L"\nFile opened successfully.");
        }
        else
        {
          odPrintConsoleString(L"\nFile open error.");
        }
    
        odPrintConsoleString(L"\n");
        CDAWalker walker(new CDATreePrinter);
        walker.run(pIfcFile);
    
        std::cout << "Press any key.";
        getchar();
      }
      catch(OdError& e)
      {
        odPrintConsoleString(L"\n\nError: %ls", e.description().c_str());
        nRes = -1;
      }
      catch(...)
      {
        odPrintConsoleString(L"\n\nUnexpected error.");
        nRes = -1;
        throw;
      }
    
    
  8. Before exiting the main() function, uninitialize modules and SDKs in reverse order.
    
    /**********************************************************************/
      /* Uninitialize IfcCore                                               */
      /**********************************************************************/
      odIfcUninitialize();
    
      odDbRootUninitialize();
      /**********************************************************************/
      /* Uninitialize ODA SDK                                               */
      /**********************************************************************/
      odrxUninitialize();
      return nRes;
      
    } //main
    
    
  9. The walkCDA() method is a protected method of CDAWalker. It is called from the run() method and performs the following actions:
    1. Calls the onLevelBegin() method of a notifier. This method is called each time a new object hierarchy level is entered. It gets and prints information about the hierarchy level, class name, and the name property of an entity (this property is part of IfcRoot, e.g., OdIfc4x3_rc3::IfcRoot).
      
      void walkCDA(const OdRxObject *inst)
      {
          if (m_pNotifier)
            m_pNotifier->onLevelBegin(inst);
            
        ...
      
      
    2. Attempts to find the Representation attribute of an instance. If such an attribute exists and has a value, the onHasRepresentation() method is called. For more information about the Representation attribute, see Access to Representation Body Geometry. The onHasRepresentation() method finds the class name of the passed OdRxObject object (that is a representation instance in this case) and prints it to the console.
      
        ... // void walkCDA(const OdRxObject *inst)
      
        //
        // Try to find attribute called Representation, if it isn't null, the instance can be vectorized.
        //
        OdRxMember *memRepr = OdRxMemberQueryEngine::theEngine()->find(inst, L"Representation");
        if (memRepr)
        {
          if (memRepr->isKindOf(OdRxProperty::desc()))
          {
            OdRxProperty *propRepr = OdRxProperty::cast(memRepr);
      
            OdRxValue idRepresentation;
            if (eOk == propRepr->getValue(inst, idRepresentation))
            {
              const IOdRxReferenceType *reference = idRepresentation.type().reference();
              if (reference)
              {
                OdRxObjectPtr reprInst = reference->dereference(idRepresentation, IOdRxReferenceType::kForRead);
                if (!reprInst.isNull())
                {
                  if (m_pNotifier)
                    m_pNotifier->onHasRepresentation(inst, reprInst);
                }
              }
            }
          }
        }
        
        ...
      
      
    3. Traverses through all Rx properties of an instance and searches for the Hierarchy attribute. When a Hierarchy attribute is found, the onHierarchyAttributeFound() method is called which prints the name of the hierarchy property.
      
        ... // void walkCDA(const OdRxObject *inst)
      
          //
          // Walk along all Rx Properties of instance and try to find Hierarchy Attribute
          //
          OdRxMemberIteratorPtr it = OdRxMemberQueryEngine::theEngine()->newMemberIterator(inst);
          for (; !it->done(); it->next())
          {
            OdRxMember *member = it->current();
      
            OdRxAttributeCollection &attrs = member->attributes();
      
            int numAttrs = attrs.count();
            for (int i = 0; i < numAttrs; ++i)
            {
              OdRxAttribute *attr = attrs.getAt(i);
      
              OdRxClass* pRx = attr->isA();
              if (pRx->isDerivedFrom(OdRxHierarchyLevelAttribute::desc()))
              {
                if (m_pNotifier)
                  m_pNotifier->onHierarchyAttributeFound(inst, member);
      
                if (member->isKindOf(OdRxProperty::desc()))
                {
                  OdRxProperty *prop = dynamic_cast<OdRxProperty*>(member);
      
                  OdRxValue idHierarchy;
                  if (eOk == prop->getValue(inst, idHierarchy))
                  {
                    const IOdRxReferenceType *reference = idHierarchy.type().reference();
                    if (reference)
                    {
                      OdGiDrawablePtr pInst = reference->dereference(idHierarchy, IOdRxReferenceType::kForRead);
                      walkCDA(pInst);
                    }
                  }
                }
                else
                  if (member->isKindOf(OdRxCollectionProperty::desc()))
                  {
                    OdRxCollectionProperty *collection = dynamic_cast<OdRxCollectionProperty*>(member);
                    OdRxValueIteratorPtr itValues = collection->newValueIterator(inst);
      
                    if (!itValues.isNull())
                      for (; !itValues->done(); itValues->next())
                      {
                        OdRxValue treeNodeId = itValues->current();
                        const IOdRxReferenceType *reference = treeNodeId.type().reference();
                        if (reference)
                        {
                          OdGiDrawablePtr pInst = reference->dereference(treeNodeId, IOdRxReferenceType::kForRead);
                          walkCDA(pInst);
                        }
                      }
                  }
              }
            }
          }
        
        ...
      
      
      Note: The currently referenced member can be a single property or a collection of properties. To check these cases, use the following code:
      
      // Check if current member is a single property
      if(member->isKindOf(OdRxProperty::desc()))
      ...
      
      // Check if current member is a collection of properties
      if(member->isKindOf(OdRxCollectionProperty::desc()))
      ...
      
      
    4. Calls the onLevelEnd() method to decrement the current hierarchy level value when the current tree node and all its sub-nodes, if they exist, are traversed.
      
        ... // void walkCDA(const OdRxObject *inst)
      
          if (m_pNotifier)
            m_pNotifier->onLevelEnd(inst);
      } //walkCDA
      
      

Example of successful application execution:

Additionally, you can use the following code example to process object properties of the CDA tree. The hierarchy can be started from OdIfcFile instance, and then continued as in Ifc/Examples/ExIFCCDAWalker. Every instance can be checked with this sample code:


OdRxMemberIteratorPtr memberIterator = OdRxMemberQueryEngine::theEngine()->newMemberIterator(inst); // inst is any OdRxClass, including OdDAI::ApplicationInstance
if (memberIterator)
{
  OdUInt64 memberCount = memberIterator->size();
  for (; !memberIterator->done(); memberIterator->next())
  {
    OdRxMember *member = memberIterator->current();
    OdString name = member->name();
    odPrintConsoleString(L"\nAttribute %s: ", name.c_str());

    if (member->isKindOf(OdRxProperty::desc()))
    {
      //
      // Process single property
      //
      OdRxProperty *prop = OdRxProperty::cast(member); // dynamic_cast<OdRxProperty*>(member);

      OdRxValue rxVal;
      if (eOk == prop->getValue(inst, rxVal))
      {
        const OdRxValueType &vt = rxVal.type();
        if (vt == OdRxValueType::Desc<OdDAIObjectId>::value())
        {
          OdDAIObjectId idVal = *rxvalue_cast<OdDAIObjectId>(&rxVal);
          odPrintConsoleString(L"#%ul", idVal.getHandle());
        }
        if (vt == OdRxValueType::Desc<OdAnsiString>::value())
        {
          OdAnsiString strVal = *rxvalue_cast<OdAnsiString>(&rxVal);
          odPrintConsoleString(L"'%hs'", strVal.c_str());
        }
        if (vt == OdRxValueType::Desc<OdString>::value())
        {
          OdString strVal = *rxvalue_cast<OdString>(&rxVal);
          odPrintConsoleString(L"'%s'", strVal.c_str());
        }
        if (vt == OdRxValueType::Desc<double>::value())
        {
          double dblVal = *rxvalue_cast<double>(&rxVal);
          odPrintConsoleString(L"%.4f", dblVal);
        }
        if (vt.isEnum())
        {
          OdAnsiString strVal;
          rxVal >> strVal;
          odPrintConsoleString(L"'%hs'", strVal.c_str());
        }
        // ...
      }
    }
    else
    if (member->isKindOf(OdRxCollectionProperty::desc()))
    {
      //
      // Process collection
      //
      OdRxCollectionProperty *collection = dynamic_cast<OdRxCollectionProperty*>(member);
      OdRxValueIteratorPtr itValues = collection->newValueIterator(inst);

      if (!itValues.isNull())
      {
        for (; !itValues->done(); itValues->next())
        {
          OdRxValue rxVal = itValues->current();
          // Typed values extraction as for single attribute
        }
      }
    }
  }
}

See Also

ExIfcCDAWalker Sample Application
Run-Time Type Identification
Work with Non-COM Properties
Copyright © 2002 – 2021. Open Design Alliance. All rights reserved.