/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "OdaCommon.h" #include "StaticRxObject.h" #include "RxDynamicModule.h" #include "IfcCore.h" #include "IfcExamplesCommon.h" // // Define module map for statically linked modules: // #if !defined(_TOOLKIT_IN_DLL_) using namespace OdIfc; \ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdSDAIModule); \ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdIfcCoreModule); \ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdIfc2x3Module); \ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdIfc4Module); \ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdRxThreadPoolService); \ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdRxPropertiesModule); \ ODRX_BEGIN_STATIC_MODULE_MAP() \ ODRX_DEFINE_STATIC_APPMODULE(OdSDAIModuleName, OdSDAIModule) \ ODRX_DEFINE_STATIC_APPMODULE(OdIfcCoreModuleName, OdIfcCoreModule) \ ODRX_DEFINE_STATIC_APPMODULE(OdIfc2x3ModuleName, OdIfc2x3Module) \ ODRX_DEFINE_STATIC_APPMODULE(OdIfc4ModuleName, OdIfc4Module) \ ODRX_DEFINE_STATIC_APPMODULE(OdThreadPoolModuleName, OdRxThreadPoolService) \ ODRX_DEFINE_STATIC_APPMODULE(RxPropertiesModuleName, OdRxPropertiesModule) \ ODRX_END_STATIC_MODULE_MAP() #endif #include #include 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; virtual void onAttribute(const OdRxMember *member, const OdRxValue &rxVal, const OdRxObject *inst) = 0; }; class LevelSaver { int& m_level; public: LevelSaver(int& level) : m_level(level) { ++m_level; } ~LevelSaver() { --m_level; } }; class CDATreePrinter : public AbstractNotifier { int m_level; public: CDATreePrinter() : m_level(0) {} void onLevelBegin(const OdRxObject *inst) override { ++m_level; odPrintConsoleString(OD_T("%sHierarchy level: %d\n"), tabulation(m_level).c_str(), m_level); const OdString &className = inst->isA()->name(); odPrintConsoleString(OD_T("%sClass: %s\n"), tabulation(m_level).c_str(), className.c_str()); OdString entityName; OdRxPropertyPtr propName = OdRxMemberQueryEngine::theEngine()->find(inst, OD_T("Name")); if (!propName.isNull()) { OdRxValue valName; (eOk == propName->getValue(inst, valName)) && (valName >> entityName); } odPrintConsoleString(OD_T("%sName: `%s`\n"), tabulation(m_level).c_str(), entityName.c_str()); } void onLevelEnd(const OdRxObject *inst) override { --m_level; } void onHierarchyAttributeFound(const OdRxObject *inst, OdRxMember *member) override { const OdString &propName = member->name(); odPrintConsoleString(OD_T("%sHierarchy property name: %s\n"), tabulation(m_level).c_str(), propName.c_str()); } void onHasRepresentation(const OdRxObject *inst, const OdRxObject *reprInst) override { const OdString &reprClassName = reprInst->isA()->name(); odPrintConsoleString(OD_T("%sCan be vectorized here (Representation: %s)\n"), tabulation(m_level).c_str(), reprClassName.c_str()); // vectorize(inst); // not reprInst, inst should be casted appropriately to OdGiDrawable/OdIfcInstance } void onAttribute(const OdRxMember* member, const OdRxValue& rxVal, const OdRxObject *inst) override { OdString memberName = member->name(); odPrintConsoleString(OD_T("\n%s%s: "), tabulation(m_level).c_str(), memberName.c_str()); const OdRxValueType &vt = rxVal.type(); if (vt == OdRxValueType::Desc::value()) { OdDAIObjectId idVal = *rxvalue_cast(&rxVal); odPrintConsoleString(OD_T("#%llu"), (OdUInt64)idVal.getHandle()); } else if (vt == OdRxValueType::Desc::value()) { OdAnsiString strVal = *rxvalue_cast(&rxVal); odPrintConsoleString(OD_T("'%hs'"), strVal.c_str()); } else if (vt == OdRxValueType::Desc::value()) { OdString strVal = *rxvalue_cast(&rxVal); odPrintConsoleString(OD_T("'%s'"), strVal.c_str()); } else if (vt == OdRxValueType::Desc::value()) { double dblVal = *rxvalue_cast(&rxVal); odPrintConsoleString(OD_T("%.4f"), dblVal); } if (vt.isEnum()) { OdAnsiString strVal; rxVal >> strVal; odPrintConsoleString(OD_T("'%hs'"), strVal.c_str()); } if (vt.isSelect()) { OdString typeValue = rxVal.typePath(); typeValue.makeUpper(); OdTCKind kind; rxVal >> kind; OdAnsiString sVal; switch (kind) { case tkDouble: { double dVal; if (rxVal >> dVal) sVal.format("(%f)", dVal); break; } default: ; } } // ... // Children attributes processing const OdArray *children = member->children(); if (member->children() && children->size()) { odPrintConsoleString(OD_T(", child attributes:")); LevelSaver saveLevel(m_level); if (rxVal.type() == OdRxValueType::Desc::value()) { OdDAIObjectId idVal = *rxvalue_cast(&rxVal); OdDAI::ApplicationInstancePtr instParentSet = idVal.openObject(); ODA_ASSERT_ONCE(instParentSet); for (const OdRxMemberPtr& childAttribute : *children) { if (childAttribute->isKindOf(OdRxProperty::desc())) { const OdRxPropertyPtr rxProperty = OdRxProperty::cast(childAttribute); OdRxValue childValue; if (eOk == rxProperty->getValue(instParentSet, childValue)) { onAttribute(childAttribute, childValue, instParentSet); } else { odPrintConsoleString(OD_T("\n%s%s: Can not get value."), tabulation(m_level).c_str(), childAttribute->name().c_str()); } } else if (childAttribute->isKindOf(OdRxCollectionProperty::desc())) { // // Process collection // const OdRxCollectionProperty* collection = dynamic_cast(childAttribute.get()); // Haven't seen before } } } else { for (const OdRxMemberPtr& childAttribute : *children) { if (childAttribute->isKindOf(OdRxProperty::desc())) { OdRxProperty* childProp = OdRxProperty::cast(childAttribute); OdRxValue childRxVal; if (eOk == childProp->getValue(inst, childRxVal)) { onAttribute(childProp, childRxVal, inst); } } else { // Haven't seen before } } } } } protected: OdString tabulation(int tab) { if (tab < 0) tab = 0; OdString res; while (tab > 0) { res += OD_T(" "); --tab; } return res; } }; 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 walkAttributes(const OdRxObject *inst) { OdRxMemberIteratorPtr memberIterator = OdRxMemberQueryEngine::theEngine()->newMemberIterator(inst); if (memberIterator) { OdUInt64 memberCount = memberIterator->size(); for (; !memberIterator->done(); memberIterator->next()) { OdRxMember *member = memberIterator->current(); if (member->isKindOf(OdRxProperty::desc())) { // // Process single property // OdRxProperty *prop = OdRxProperty::cast(member); // dynamic_cast(member); OdRxValue rxVal; if (eOk == prop->getValue(inst, rxVal)) { m_pNotifier->onAttribute(member, rxVal, inst); } } else if (member->isKindOf(OdRxCollectionProperty::desc())) { // // Process collection // OdRxCollectionProperty *collection = dynamic_cast(member); OdRxValueIteratorPtr itValues = collection->newValueIterator(inst); if (!itValues.isNull()) { for (; !itValues->done(); itValues->next()) { OdRxValue rxVal = itValues->current(); // Typed values extraction } } } } } } void walkCDA(const OdRxObject *inst) { if (m_pNotifier) m_pNotifier->onLevelBegin(inst); // // Uncomment to get OdRx class name. // //OdString rxClassName = inst->isA()->name(); // // Uncomment this line for dump attributes/properties of an instance // //walkAttributes(inst); // // Try to find attribute called Representation, if it isn't null, the instance can be vectorized. // OdRxMember *memRepr = OdRxMemberQueryEngine::theEngine()->find(inst, OD_T("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); } } } } } // // 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(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 { for (const auto& it : *prop->children()) { // } } } } else if (member->isKindOf(OdRxCollectionProperty::desc())) { OdRxCollectionProperty *collection = dynamic_cast(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); } } } } } } if (m_pNotifier) m_pNotifier->onLevelEnd(inst); } }; /************************************************************************/ /* Main */ /************************************************************************/ #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 #ifdef _MSC_VER SetConsoleOutputCP(CP_UTF8); #endif setlocale(LC_TIME, ""); // set current user locale (not OD_T("C")), for strftime /**********************************************************************/ /* Create a Services object */ /**********************************************************************/ OdStaticRxObject svcs; /**********************************************************************/ /* Display the Product and Version that created the executable */ /**********************************************************************/ odPrintConsoleString(OD_T("\nExIfcCDAWalker developed using %ls ver %ls\n"), svcs.product().c_str(), svcs.versionString().c_str()); /**********************************************************************/ /* Parse Command Line inputs */ /**********************************************************************/ const OdString noWaitKeyword = OD_T("-noWait"); bool noWait = false; bool bInvalidArgs = (argc < 2 || argc > 3); if (bInvalidArgs) { odPrintConsoleString(OD_T("\nusage: ExIfcCDAWalker [%s]\n"), noWaitKeyword.c_str()); odPrintConsoleString(OD_T("\n - input .ifc file")); odPrintConsoleString(OD_T("\n[%s] - optional disable \"Press ENTER to continue\" on finish\n"), noWaitKeyword.c_str()); return eInvalidInput; } for (int argIndex = 2; argIndex < argc; ++argIndex) { if (noWaitKeyword == argv[argIndex]) { noWait = true; continue; } } #if !defined(_TOOLKIT_IN_DLL_) ODRX_INIT_STATIC_MODULE_MAP(); #endif /**********************************************************************/ /* Initialize ODA SDK */ /**********************************************************************/ odrxInitialize(&svcs); /**********************************************************************/ /* Initialize IfcCore */ /**********************************************************************/ odIfcInitialize(true /* CDA */, false /* No geometry calculation needed */); try { OdString ifcFileName(argv[1]); OdIfcFilePtr pIfcFile = svcs.createDatabase(); OdResult res = pIfcFile->readFile(ifcFileName); if (res == eOk) { odPrintConsoleString(OD_T("\nFile opened successfully.")); odPrintConsoleString(OD_T("\n")); CDAWalker walker(new CDATreePrinter); #define _WHOLE_MODEL #ifdef _WHOLE_MODEL walker.run(pIfcFile); #else OdDAI::ModelPtr model = pIfcFile->getModel(); OdDAI::ApplicationInstancePtr inst = model->getEntityInstance(826908).openObject(); walker.run(inst); #endif } else { odPrintConsoleString(OD_T("\nFile open error.")); nRes = res; } if (!noWait) { odPrintConsoleString(OD_T("\nPress ENTER to continue.\n")); getchar(); } } catch(OdError& e) { odPrintConsoleString(OD_T("\n\nError: %ls"), e.description().c_str()); nRes = -1; } catch(...) { odPrintConsoleString(OD_T("\n\nUnexpected error.")); nRes = -1; throw; } /**********************************************************************/ /* Uninitialize IfcCore */ /**********************************************************************/ odIfcUninitialize(); /**********************************************************************/ /* Uninitialize ODA SDK */ /**********************************************************************/ odrxUninitialize(); return nRes; }