/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2019, 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-2019 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 "DbImpAssocNetwork.h" #include "DbAssocNetwork.h" #include "DbFiler.h" #include "DbDictionary.h" #include "DbImpAssocAction.h" #include "DbAssocAction.h" #include "DbAssoc2dConstraintGroup.h" #include "DbAssocNetCloneCtx.h" #include "DbAssocVariable.h" #include "DbHostAppServices.h" #include "DbAudit.h" #include "DbAssocManager.h" #include "DebugStuff.h" #include "SaveState.h" OdDbImpAssocNetwork::OdDbImpAssocNetwork() : OdDbImpAssocAction() , m_maxChildActionIdx(0) , m_fixStatusConsistency(false) { m_isBase = false; } OdDbObjectIdArray OdDbImpAssocNetwork::getActions() const { return m_actions; } OdDbObjectIdArray OdDbImpAssocNetwork::getActionsToEvaluate() const { return m_actionsToEvaluate; } OdResult OdDbImpAssocNetwork::addAction(OdDbAssocNetwork* thisNetwork, const OdDbObjectId& actionId, bool alsoSetAsDatabaseOwner) { OdDbObjectId networkId = thisNetwork->objectId(); OdDbObjectPtr pObj = actionId.openObject(OdDb::kForWrite); if ( !pObj->isKindOf(OdDbAssocAction::desc()) ) return eInvalidInput; OdDbAssocActionPtr pAction = OdDbAssocAction::cast(pObj); if ( networkId== pAction->owningNetwork() && (!alsoSetAsDatabaseOwner || networkId == pAction->ownerId()) && m_actions.contains(actionId)) { ODA_FAIL_ONCE(); // temp test marker // #11465 fix audit problem via cloning (select + drag) return eInvalidInput; // already added } pAction->setOwningNetwork(networkId, alsoSetAsDatabaseOwner); if(alsoSetAsDatabaseOwner) pAction->setOwnerId(networkId); m_actions.push_back(actionId); m_isOwnedAction.push_back(alsoSetAsDatabaseOwner); if( isEvaluationRequest(pAction->status()) ) { m_actionsToEvaluate.push_back(actionId); thisNetwork->setStatus(kChangedDirectlyAssocStatus); } m_maxChildActionIdx++; OdDbImpAssocAction *pImpAction = OdDbImpAssocAction::getImpObject(pAction); pImpAction->setActionIndex(m_maxChildActionIdx); return eOk; } OdResult OdDbImpAssocNetwork::removeAction(OdDbAssocNetwork* thisNetwork, const OdDbObjectId& actionId, bool alsoEraseIt) { OdDbAssocActionPtr pAction = OdDbAssocAction::cast(actionId.openObject(OdDb::kForWrite)); if (pAction.isNull()) alsoEraseIt = false; else pAction->setOwningNetwork(OdDbObjectId::kNull, false); int i = m_actions.size(); while (i-- > 0) { if (m_actions[i] == actionId) { m_actions.removeAt(i); m_isOwnedAction.removeAt(i); m_actionsToEvaluate.remove(actionId); thisNetwork->setStatus(kChangedDirectlyAssocStatus); break; } } if ( alsoEraseIt ) { pAction->erase(); pAction->downgradeOpen(); } return eOk; } OdResult OdDbImpAssocNetwork::ownedActionStatusChanged(OdDbObject* pThisNetwork, OdDbAssocAction* pOwnedAction, OdDbAssocStatus previousStatus) { OdDbAssocStatus newStatus = pOwnedAction->status(); ODA_ASSERT(newStatus != previousStatus); bool new_toEvaluate = isEvaluationRequest(newStatus); bool old_toEvaluate = isEvaluationRequest(previousStatus); if (m_status == kErasedAssocStatus) { return eOk; } else if (newStatus == kErasedAssocStatus) { setStatus(pThisNetwork, kChangedDirectlyAssocStatus, true, false); } else if (old_toEvaluate && !new_toEvaluate) { pThisNetwork->assertWriteEnabled(); m_actionsToEvaluate.remove(pOwnedAction->objectId()); if(m_actionsToEvaluate.isEmpty()) setStatus(pThisNetwork, kIsUpToDateAssocStatus, true, false); } else if (!old_toEvaluate && new_toEvaluate) { pThisNetwork->assertWriteEnabled(); m_actionsToEvaluate.append(pOwnedAction->objectId()); if (isActionEvaluationInProgress()) { OdDbAssocManager::requestToEvaluate(pOwnedAction->objectId()); } setStatus(pThisNetwork, kChangedDirectlyAssocStatus, true, false); } return eOk; } void odaaEnableStatusConsistencyCheck(OdDbAssocNetwork* network, bool doIt) { static_cast(OdDbImpAssocAction::getImpObject(network))->m_fixStatusConsistency = doIt; } OdResult OdDbImpAssocNetwork::dwgInFields(OdDbAssocAction *pSelf, OdDbDwgFiler* pFiler) { OdResult res = OdDbImpAssocAction::dwgInFields(pSelf, pFiler); if ( res != eOk) return res; #ifdef _DEBUG int val = pFiler->rdInt16(); ODA_ASSERT(val == 0); // Version? #else pFiler->rdInt16(); #endif // _DEBUG m_maxChildActionIdx = pFiler->rdInt32(); OdUInt32 nRefs = pFiler->rdInt32(); m_actions.clear(); m_actions.reserve(nRefs); m_isOwnedAction.clear(); m_isOwnedAction.reserve(nRefs); OdDbObjectId id; bool fileFiler = pFiler->filerType()==OdDbFiler::kFileFiler; while (nRefs--) { bool owned = pFiler->rdBool(); if (owned) id = pFiler->rdHardOwnershipId(); else id = pFiler->rdSoftPointerId(); if(!fileFiler || !id.isNull()) { m_isOwnedAction.append(owned); m_actions.append(id); } } // Some more references nRefs = pFiler->rdInt32(); m_actionsToEvaluate.clear(); m_actionsToEvaluate.reserve(nRefs); while (nRefs--) { id = pFiler->rdSoftPointerId(); if(!fileFiler || !id.isNull()) m_actionsToEvaluate.append(id); } if (pFiler->filerType() == OdDbFiler::kFileFiler && !m_actions.empty()) ::odaaEnableStatusConsistencyCheck(static_cast(pSelf)); return eOk; } void OdDbImpAssocNetwork::dwgOutFields(OdDbDwgFiler* pFiler, OdDbObjectId objectId) const { OdDbImpAssocAction::dwgOutFields(pFiler, objectId); pFiler->wrInt16(0); // Version? pFiler->wrInt32(m_maxChildActionIdx); OdUInt32 nRefs = m_actions.size(); pFiler->wrInt32(nRefs); const OdDbObjectId* pId = m_actions.asArrayPtr(); const bool* pOwned = m_isOwnedAction.asArrayPtr(); while (nRefs--) { pFiler->wrBool(*pOwned); if (*pOwned++) { pFiler->wrHardOwnershipId(*pId++); } else { pFiler->wrSoftPointerId(*pId++); } } //Some more references nRefs = m_actionsToEvaluate.size(); pFiler->wrInt32(nRefs); pId = m_actionsToEvaluate.asArrayPtr(); while (nRefs--) { pFiler->wrSoftPointerId(*pId++); } } #define NEXT_CODE(code) \ if (pFiler->nextItem() != code) \ { \ ODA_FAIL(); \ return eBadDxfSequence; \ } OdResult OdDbImpAssocNetwork::dxfInFields(OdDbDxfFiler* pFiler, OdDbObjectId objectId) { OdResult res = OdDbImpAssocAction::dxfInFields(pFiler, objectId); if (res != eOk) return res; // Check that we are at the correct subclass data if( !pFiler->atSubclassData( OdDbAssocNetwork::desc()->name() )) { return eBadDxfSequence; } NEXT_CODE(90) #ifdef ODA_DIAGNOSTICS int val = #endif // ODA_DIAGNOSTICS pFiler->rdInt32(); ODA_ASSERT(val == 0); // Version? NEXT_CODE(90) m_maxChildActionIdx = pFiler->rdInt32(); NEXT_CODE(90) OdUInt32 nRefs = pFiler->rdInt32(); m_actions.clear(); m_actions.reserve(nRefs); m_isOwnedAction.clear(); m_isOwnedAction.reserve(nRefs); OdDbObjectId id; bool fileFiler = pFiler->filerType() == OdDbFiler::kFileFiler; bool owned; while (nRefs--) { switch(pFiler->nextItem()) { case 330: owned = false; break; case 360: owned = true; break; default: ODA_FAIL(); throw OdError(eBadDxfSequence); } id = pFiler->rdObjectId(); if (!fileFiler || !id.isNull()) { m_isOwnedAction.append(owned); m_actions.append(id); } } // More references NEXT_CODE(90) nRefs = pFiler->rdInt32(); m_actionsToEvaluate.clear(); m_actionsToEvaluate.reserve(nRefs); while (nRefs--) { NEXT_CODE(330) id = pFiler->rdObjectId(); if (!fileFiler || !id.isNull()) { m_isOwnedAction.append(owned); m_actions.append(id); } } if (pFiler->filerType() == OdDbFiler::kFileFiler && !m_actions.empty()) ::odaaEnableStatusConsistencyCheck(OdDbAssocNetworkPtr(objectId.safeOpenObject())); return eOk; } void OdDbImpAssocNetwork::dxfOutFields(OdDbDxfFiler* pFiler, OdDbObjectId objectId) const { OdDbImpAssocAction::dxfOutFields(pFiler, objectId); pFiler->wrSubclassMarker( OdDbAssocNetwork::desc()->name() ); pFiler->wrInt32(90, 0); // Version ? pFiler->wrInt32(90, m_maxChildActionIdx); OdUInt32 nRefs = m_actions.size(); pFiler->wrInt32(90, nRefs); const OdDbObjectId* pId = m_actions.asArrayPtr(); const bool* pOwned = m_isOwnedAction.asArrayPtr(); while (nRefs--) { if (*pOwned++) { pFiler->wrObjectId(360, *pId++); } else { pFiler->wrObjectId(330, *pId++); } } // More references nRefs = m_actionsToEvaluate.size(); pFiler->wrInt32(90, nRefs); pId = m_actionsToEvaluate.asArrayPtr(); while (nRefs--) { pFiler->wrObjectId(330, *pId++); } } OdResult OdDbImpAssocNetwork::setStatus(OdDbObject* pThisNetwork, OdDbAssocStatus newStatus, bool notifyOwningNetwork, bool setInOwnedActions) { OdDbImpAssocAction::setStatus(pThisNetwork, newStatus, notifyOwningNetwork, setInOwnedActions); if ( setInOwnedActions ) { OdDbObjectIdArray attachedActions = getActions(); for (unsigned int i = 0; i < attachedActions.size(); i++) { OdDbAssocActionPtr curAction = OdDbAssocAction::cast(attachedActions[i].openObject(OdDb::kForWrite)); if (!curAction.isNull()) curAction->setStatus(newStatus, false, setInOwnedActions); } } return eOk; } class AssocNetEvalGraphNode : public OdRxObject { OdUInt32 flags; OdDbObjectId id; OdDbAssocStatus saved; mutable OdDbAssocEvaluationPriority evalPriority; public: static int compare(const void* v1, const void* v2) { // dna: should it take into account write dependency order here? return (*(AssocNetEvalGraphNode**)v1)->priority() - (*(AssocNetEvalGraphNode**)v2)->priority(); } # ifndef NDEBUG OdUInt64 dbgHandle; # endif enum { kIsNew = 1, kIsAction = 2, kIsProcessed = 4, }; AssocNetEvalGraphNode() { flags = kIsNew; evalPriority = kCannotDermineAssocEvaluationPriority; } ~AssocNetEvalGraphNode() { } OdDbObjectId objectId() const { return id; } void setObjectId(OdDbObjectId id) { this->id = id; } bool isNew() const { return GETBIT(flags, kIsNew); } bool isAction() const { return GETBIT(flags, kIsAction); } void setIsNew(bool bNew) { SETBIT(flags, kIsNew, bNew); } void setIsAction(bool bAction) { SETBIT(flags, kIsAction, bAction); } bool isProcessed() const { return GETBIT(flags, kIsProcessed); } void setIsProcessed() { SETBIT(flags, kIsProcessed, true); } OdDbAssocEvaluationPriority priority() const { if (evalPriority == kCannotDermineAssocEvaluationPriority) { OdDbAssocActionPtr action = id.safeOpenObject(OdDb::kForWrite); evalPriority = action->evaluationPriority(); } return evalPriority; } void suppressAction() { OdDbAssocActionPtr action = id.safeOpenObject(OdDb::kForWrite); saved = action->status(); action->setStatus(kSuppressedAssocStatus, false); } void unsuppressAction() { OdDbAssocActionPtr action = id.safeOpenObject(OdDb::kForWrite); action->setStatus(kIsUpToDateAssocStatus, false); action->setStatus(saved, false); } void resetPriority() { evalPriority = kCannotDermineAssocEvaluationPriority; } }; void clearIfErased(OdDbAssocAction* action) { OdDbAssocNetworkPtr net = OdDbAssocNetwork::cast(action->owningNetwork().openObject(OdDb::kForWrite)); if (net.get()) net->removeAction(action->objectId(), true); else action->erase(true); } #ifndef NDEBUG void DBG_DUMP_OBJID(const OdDbObjectId& id) { OdDbObjectPtr object = id.safeOpenObject(OdDb::kForRead, true); OdDbAssocActionPtr action = OdDbAssocAction::cast(object); OdString className; if (action.get()) { OdDbObjectPtr body = action->actionBody().openObject(OdDb::kForRead, true); if (body.get()) className.format(L"%ls:[%ls]", object->isA()->name().c_str(), body->isA()->name().c_str()); } if (className.isEmpty()) className = object->isA()->name(); ODA_TRACE3("%" PRIX64W L":%ls%ls", (OdUInt64)id.getHandle(), className.c_str(), (id.isErased() ? L" (erased)" : L"")); } #else #define DBG_DUMP_OBJID(id) #endif class AssocNetEvalGraph : public OdDbActionsToEvaluateCallback { typedef AssocNetEvalGraphNode EvalGrapNode; typedef OdSmartPtr EvalGrapNodePtr; typedef OdHashMap > Id2NodeHash; Id2NodeHash ids; typedef OdArray > EvalGrapNodePtrArray; EvalGrapNodePtrArray actionsToEvaluate; EvalGrapNode* lookupNode(const OdDbObjectId& objectId) { Id2NodeHash::iterator it = ids.find(objectId); if(it!=ids.end()) return it->second.get(); return 0; } EvalGrapNode* newNode(const OdDbObjectId& objectId) { OdSmartPtr node = OdRxObjectImpl::createObject(); ids.insert(std::pair(objectId, node)); node->setObjectId(objectId); # ifndef NDEBUG node->dbgHandle = objectId.getHandle(); # endif return node; } EvalGrapNode* mapNode(const OdDbObjectId& objectId) { EvalGrapNode* node = lookupNode(objectId); if (!node) node = newNode(objectId); return node; } public: AssocNetEvalGraph() { } void reset() { actionsToEvaluate.clear(); ids.clear(); notEvaluated.clear(); } virtual void dependencyNeedsToEvaluate(OdDbAssocDependency* dep, OdDbAssocStatus newStatus) { if (isEvaluationRequest(newStatus) && dep->isWriteDependency()) { OdDbObjectPtr obj = dep->dependentOnObject().openObject(OdDb::kForRead); if(obj.get()) objectNeedsToEvaluate(obj, kChangedTransitivelyAssocStatus); } } virtual void actionNeedsToEvaluate(OdDbAssocAction* action, OdDbAssocStatus newStatus) { ODA_TRACE(L"* actionNeedsToEvaluate( "); DBG_DUMP_OBJID(action->objectId()); ODA_TRACE(L" )\n"); action->upgradeOpen(); if (action->status() == kErasedAssocStatus) { ODA_TRACE0("*** Remove erased action: "); DBG_DUMP_OBJID(action->objectId()); ODA_TRACE0("\n"); removeErasedAction(action); } else if (isEvaluationRequest(newStatus)) { action->setStatus(newStatus); EvalGrapNode* node = mapNode(action->objectId()); if (node->isNew()) { node->setIsAction(true); node->setIsNew(false); action->getDependentActionsToEvaluate(this); actionsToEvaluate.append(node); } } } virtual void objectNeedsToEvaluate(const OdDbObject* object, OdDbAssocStatus newStatus) { ODA_TRACE(L"* objectNeedsToEvaluate( "); DBG_DUMP_OBJID(object->objectId()); ODA_TRACE(L" )\n"); if (isEvaluationRequest(newStatus)) { EvalGrapNode* node = mapNode(object->objectId()); if (node->isNew()) { node->setIsNew(false); OdDbAssocDependencyPtr dep = OdDbAssocDependency::getFirstDependencyOnObject(object).openObject(); while (dep.get()) { if (dep->isReadDependency()) { OdDbAssocActionPtr action = dep->owningAction().openObject(); if (action.get()) actionNeedsToEvaluate(action, newStatus); } dep = dep->nextDependencyOnObject().openObject(); } } } } OdDbAssocNetwork* topAssocNetwork; EvalGrapNodePtrArray notEvaluated; void evaluateActions(OdDbAssocNetwork* topAssocNetwork) { if (actionsToEvaluate.empty()) return; this->topAssocNetwork = topAssocNetwork; ODA_TRACE0("\n************ Evaluating network of actions ************\n"); ODA_TRACE0("*** Sorting actions to evaluate... \n"); sortNodesByPriority(); int evaluated = 0; do { EvalGrapNode* node = actionsToEvaluate.last(); // highest priority node OdDbAssocEvaluationPriority priority = node->priority(); if (priority > 0 || actionsToEvaluate.size()==1) { ODA_TRACE0("*** Evaluating: \n"); ODA_TRACE1("%+10d : ", priority); DBG_DUMP_OBJID(node->objectId()); ODA_TRACE0("\n"); evaluateActionNode(node); ++evaluated; actionsToEvaluate.removeLast(); } else if (evaluated) { evaluated = 0; ODA_TRACE0("*** Updating priorities for: \n"); #ifndef NDEBUG for (int dd = actionsToEvaluate.size() - 1; dd >= 0; --dd) { node = actionsToEvaluate[dd]; priority = node->priority(); ODA_TRACE1("%+10d : ", priority); DBG_DUMP_OBJID(node->objectId()); ODA_TRACE0("\n"); } #endif ODA_TRACE0("*** Resorting actions to evaluate... \n"); resortNodesByPriority(); } else { ODA_TRACE0("\n*** breaking deadlock... \n"); node = actionsToEvaluate.first(); // least priority node #ifndef NDEBUG priority = node->priority(); ODA_TRACE1("%+10d : ", priority); DBG_DUMP_OBJID(node->objectId()); ODA_TRACE0("\n"); #endif OdDbAssocActionPtr action = node->objectId().safeOpenObject(OdDb::kForWrite); OdDbAssocManager::requestToEvaluate(action->objectId()); action->setStatus(kSuppressedAssocStatus); actionsToEvaluate.removeFirst(); resortNodesByPriority(); } } while (!actionsToEvaluate.empty()); ODA_TRACE0("******************* Evaluation done *******************\n"); } void unsupressNotEvaluatedActions() { if (notEvaluated.size()) { do { notEvaluated.last()->unsuppressAction(); notEvaluated.removeLast(); } while (notEvaluated.size()); } } void sortNodesByPriority() { ::qsort(actionsToEvaluate.asArrayPtr(), actionsToEvaluate.size(), sizeof(EvalGrapNode*), &EvalGrapNode::compare); } static void removeErasedAction(OdDbAssocAction* action) { action->erase(true); } void evaluateActionNode(EvalGrapNode* actionNode) { OdDbObjectId id = actionNode->objectId(); OdDbAssocActionPtr action = id.openObject(OdDb::kForWrite); if (action.get()) { if (action->status() == kErasedAssocStatus) removeErasedAction(action); else if(isEvaluationRequest(action->status())) { action->evaluate(0); // Force notifications inside a transaction action->xmitPropagateModify(); OdDbAssocStatus status = action->status(); if (status == kErasedAssocStatus) removeErasedAction(action); else if (isEvaluationRequest(status)) { actionNode->suppressAction(); notEvaluated.append(actionNode); } } } } void suppressAction(EvalGrapNode* actionNode) { OdDbObjectId id = actionNode->objectId(); OdDbAssocActionPtr action = id.openObject(OdDb::kForWrite); if (action.get()) { action->setStatus(kSuppressedAssocStatus); OdDbAssocStatus status = action->status(); if (status == kErasedAssocStatus) topAssocNetwork->removeAction(id, true); } } static void reset_priority(EvalGrapNode* actionNode) { actionNode->resetPriority(); } void resortNodesByPriority() { std::for_each(actionsToEvaluate.begin(), actionsToEvaluate.end(), &reset_priority); sortNodesByPriority(); } void assembly(OdDbAssocNetwork* topAssocNetwork) { ODA_TRACE(L"\n*** assembly evaluation graph ***\n"); reset(); topAssocNetwork->getDependentActionsToEvaluate(this); } }; class RequestedToEvaluate : public OdDbObjectReactor { typedef OdHashMap, OdHashSet_PtrHasher > Id2Params; Id2Params buf; static void processRequest(const Id2Params::value_type& req) { doRequest(req.first, req.second.first, req.second.second); } template static void setStatus(OdDbAssocStatus newStatus, T *object) { if (newStatus == kErasedAssocStatus) { object->setStatus(kChangedDirectlyAssocStatus, true); object->setStatus(newStatus, false); } else object->setStatus(newStatus); } static void doRequest(const OdDbObjectId &objectId, OdDbAssocStatus newStatus, bool ownedActionsAlso) { OdDbObjectPtr object = objectId.openObject(OdDb::kForWrite); if (object.get()) { if (object->isKindOf(OdDbAssocAction::desc())) { setStatus(newStatus, static_cast(object.get())); if (object->isKindOf(OdDbAssocNetwork::desc()) && ownedActionsAlso) { OdDbObjectIdArray actions = static_cast(object.get())->getActions(); for (OdDbObjectIdArray::const_iterator it = actions.begin(), end = actions.end(); it != end; ++it) doRequest(*it, newStatus, ownedActionsAlso); } } else if (object->isKindOf(OdDbAssocDependency::desc())) static_cast(object.get())->setStatus(newStatus); } } static RequestedToEvaluate* getFromDb(OdDbDatabase* db, bool create = false) { const OdDbObjectReactorArray reactors = db->getTransientReactors(); for (OdUInt32 i = 0, n = reactors.size(); i < n; ++i) { const OdRxObject* r = reactors[i]; if (r->isKindOf(RequestedToEvaluate::desc())) return (RequestedToEvaluate*)r; } if (create) { OdSmartPtr req = RequestedToEvaluate::createObject(); db->OdDbObject::addReactor(req); return req; } return 0; } static void removeFromDb(OdDbDatabase* db) { RequestedToEvaluate* r = getFromDb(db); if (r) db->OdDbObject::removeReactor(r); } static bool isEvaluationInProgress(const OdDbObjectId& objectId) { if (OdDbAssocManager::isActionEvaluationInProgress(objectId.database())) { return true; } if (objectId.objectClass()->isDerivedFrom(OdDbAssocAction::desc())) { OdDbObjectPtr object = objectId.openObject(); OdDbAssocAction* action = static_cast(object.get()); OdDbAssocNetworkPtr network = OdDbAssocNetwork::cast(action->owningNetwork().openObject()); if (network.get()) { return network->isActionEvaluationInProgress(); } } return false; } public: ODRX_DECLARE_MEMBERS(RequestedToEvaluate); static OdResult requestToEvaluate(const OdDbObjectId& objectId, OdDbAssocStatus newStatus, bool ownedActionsAlso) { OdDbDatabase* db = objectId.database(); if (isEvaluationInProgress(objectId)) { RequestedToEvaluate* rte = getFromDb(db, true); if(!rte) return eExtendedError; rte->buf.insert(std::pair >( objectId, std::pair(newStatus, ownedActionsAlso) ) ); } else { doRequest(objectId, newStatus, ownedActionsAlso); } return eOk; } static bool process(OdDbDatabase *db) { RequestedToEvaluate* rte = getFromDb(db); if (rte && rte->buf.size()) { std::for_each(rte->buf.begin(), rte->buf.end(), &processRequest); bool res = rte->buf.empty(); rte->buf.clear(); removeFromDb(db); return true; } return false; } }; void RequestedToEvaluate_rxInit() { RequestedToEvaluate::rxInit(); } void RequestedToEvaluate_rxUninit() { RequestedToEvaluate::rxUninit(); } OdResult OdDbAssocManager::requestToEvaluate(const OdDbObjectId& objectId, OdDbAssocStatus newStatus, bool ownedActionsAlso) { return RequestedToEvaluate::requestToEvaluate(objectId, newStatus, ownedActionsAlso); } ODRX_CONS_DEFINE_MEMBERS(RequestedToEvaluate, OdDbObjectReactor, RXIMPL_CONSTR); void OdDbImpAssocNetwork::evaluate(OdDbAssocAction *pThisNetwork, OdDbAssocEvaluationCallback* pEvaluationCallback) { OdDbAssocNetworkPtr owningNet = OdDbAssocNetwork::cast(owningNetwork().openObject()); if (owningNet.isNull() || !owningNet->isActionEvaluationInProgress()) { // top level network AssocNetEvalGraph evalGraph; do { evalGraph.assembly(static_cast(pThisNetwork)); evalGraph.evaluateActions(static_cast(pThisNetwork)); } while (RequestedToEvaluate::process(pThisNetwork->database())); evalGraph.unsupressNotEvaluatedActions(); evalGraph.reset(); } if (m_actionsToEvaluate.empty()) setStatus(pThisNetwork, m_actions.empty() ? kErasedAssocStatus : kIsUpToDateAssocStatus, true, false); } unsigned int removeAll(const OdDbObjectId& id, OdDbObjectIdArray &from, unsigned int start = 0) { unsigned int found, removed = 0; while (start < from.size() && from.find(id, found, start)) { from.removeAt(found); start = found; ++removed; } return removed; } inline unsigned int removeNulls(OdDbObjectIdArray &ids) { return removeAll(OdDbObjectId::kNull, ids); } void OdDbImpAssocNetwork::audit(OdDbObject* pObj, OdDbAuditInfo* pAuditInfo) { OdDbImpAssocAction::audit(pObj, pAuditInfo); auditEvaluationQueue(pObj, pAuditInfo); OdDb::OpenMode mode = pAuditInfo->fixErrors() ? OdDb::kForWrite : OdDb::kForRead; int i = m_actions.size(); while (i-- > 0) { OdDbObjectId id = m_actions[i]; OdDbObjectPtr pObj = id.openObject(mode); bool bRemove; if (pObj.isNull()) { bRemove = true; } else { pObj->audit(pAuditInfo); // For permanently erased object with Null objectId pObj->isErased() returns false bRemove = pObj->objectId().isErased(); } if (bRemove) { m_actions.removeAt(i); m_isOwnedAction.removeAt(i); m_actionsToEvaluate.remove(id); } } } static OdDbObjectId getOrCreateExtensionDict(OdDbObject& obj) { OdDbObjectId extDictId = obj.extensionDictionary(); if (extDictId.isNull()) { obj.createExtensionDictionary(); extDictId = obj.extensionDictionary(); } return extDictId; } static OdDbObjectId getOrCreateAssocNetworkDict(OdDbDictionary& extDict) { const OdString ACAD_ASSOCNETWORK = OD_T("ACAD_ASSOCNETWORK"); OdDbObjectId netDictId = extDict.getAt(ACAD_ASSOCNETWORK); if (netDictId.isNull()) { OdDbDictionaryPtr pNetDict = OdDbDictionary::createObject(); netDictId = extDict.setAt(ACAD_ASSOCNETWORK, pNetDict); } return netDictId; } void OdDbImpAssocNetwork::appendToOwner(OdDbAssocAction* pThisAction, OdDbIdPair& idPair, OdDbObject* owner, OdDbIdMapping& idMap) { ODA_ASSERT(!idPair.isOwnerXlated()); if (owner && owner->isA()->isDerivedFrom(OdDbBlockTableRecord::desc())) { const OdDbObjectId extDictId = getOrCreateExtensionDict(*owner); OdDbDictionaryPtr pExtDict = OdDbDictionary::cast(extDictId.openObject(OdDb::kForWrite)); if (pExtDict.get()) { const OdDbObjectId netDictId = getOrCreateAssocNetworkDict(*pExtDict); OdDbDictionaryPtr pNetDict = OdDbDictionary::cast(netDictId.openObject(OdDb::kForWrite)); if (pNetDict.get()) { const OdString ACAD_ASSOCNETWORK = OD_T("ACAD_ASSOCNETWORK"); OdDbObjectId existingNetId = pNetDict->getAt(ACAD_ASSOCNETWORK); if (!existingNetId.isNull()) { ODA_ASSERT(false && "Cannot append assoc network to a dictionary already containing one"); return; } pNetDict->setAt(ACAD_ASSOCNETWORK, pThisAction); idPair.setOwnerXlated(true); idMap.assign(idPair); OdDbObjectPtr original = idPair.key().openObject(); if (original.get()) { OdDbIdPair ownerMapping(original->ownerId()); if (idMap.compute(ownerMapping) && !ownerMapping.isCloned()) { ownerMapping.setValue(netDictId); idMap.assign(ownerMapping); } } } } } else { pThisAction->OdDbObject::appendToOwner(idPair, owner, idMap); } } unsigned int removeDuplicates(OdDbObjectIdArray &ids) { unsigned int removed = 0; for (unsigned int i = 0; i < ids.size(); ++i) removed += removeAll(ids[i], ids, i + 1); return removed; } void reportAuditError(OdDbAuditInfo* ctx, OdDbObject* obj, OdUInt32 sidValueName, OdUInt32 sidValidation, OdUInt32 sidRepair, OdUInt32 errFound = 1, OdUInt32 errFixed = 0); void OdDbImpAssocNetwork::auditEvaluationQueue(OdDbObject* obj, OdDbAuditInfo* ctx) { if(m_actionsToEvaluate.isEmpty()) return; bool fix = ctx->fixErrors(); unsigned int errors = 0; if (fix) { errors = removeDuplicates(m_actionsToEvaluate) + removeNulls(m_actionsToEvaluate); } else { for (unsigned int i = 0, found; i < m_actionsToEvaluate.size()-1; ++i) { if (m_actionsToEvaluate.find(m_actionsToEvaluate[i], found, i + 1)) { ++errors; break; } } if (m_actionsToEvaluate.contains(OdDbObjectId::kNull)) ++errors; } if(!m_actionsToEvaluate.empty()) { OdDbAssocActionPtr net = obj; if (!isEvaluationRequest(net->status())) { ++errors; if (fix) net->setStatus(kChangedDirectlyAssocStatus); } } if (errors) reportAuditError(ctx, obj, sidEvaluationQueue, sidVarValidInvalid, sidVarDefRepair, 1, fix ? 1 : 0); } void OdDbImpAssocNetwork::fixStatusConsistency(OdDbAssocAction* self) { if (m_fixStatusConsistency) { bool hasDirtyActions = false; for (unsigned int i = 0; i < m_actionsToEvaluate.length(); i++) { OdDbAssocActionPtr action = m_actionsToEvaluate[i].openObject(); if (action.get() && action->status() == kIsUpToDateAssocStatus) { action->upgradeOpen(); action->setStatus(kChangedDirectlyAssocStatus); ODA_TRACE(L"Status consistency fix: 'changed directly' : "); DBG_DUMP_OBJID(action->objectId()); ODA_TRACE(L"\n"); hasDirtyActions = true; } } for (unsigned int i = 0; i < m_actions.length(); i++) { OdDbAssocActionPtr action = m_actions[i].openObject(); if (action.get() && action->status() == kIsUpToDateAssocStatus) { ::odaaFixStatusConsistency(action); if(!hasDirtyActions) hasDirtyActions = isEvaluationRequest(action->status()); } } if(hasDirtyActions) { self->upgradeOpen(); self->setStatus(kChangedDirectlyAssocStatus); ODA_TRACE(L"Status consistency fix: 'changed directly' : "); DBG_DUMP_OBJID(self->objectId()); ODA_TRACE(L"\n"); } m_fixStatusConsistency = false; } } void OdDbImpAssocNetwork::getDependentActionsToEvaluate(OdDbActionsToEvaluateCallback* pActionsToEvaluateCallback) const { if (isEvaluationRequest(status())) { OdDbObjectIdArray::const_iterator iter = m_actionsToEvaluate.begin(); OdDbObjectIdArray::const_iterator end = m_actionsToEvaluate.end(); while (iter != end) { OdDbAssocActionPtr action = OdDbAssocAction::cast(iter->openObject()); if (action.get()) pActionsToEvaluateCallback->actionNeedsToEvaluate(action, action->status()); ODA_ASSERT_ONCE(L"Attempt to evaluate null action" && !action.isNull()); ++iter; } } } OdDbAssocEvaluationPriority OdDbImpAssocNetwork::evaluationPriority() const { if (m_actionsToEvaluate.empty()) return OdDbAssocEvaluationPriority(kCanBeEvaluatedAssocEvaluationPriority / 2 + evaluationRequestSeverityLevel(status())); return OdDbAssocEvaluationPriority(kCannotBeEvaluatedAssocEvaluationPriority / 2 - m_actionsToEvaluate.size()); }