Named Object Dictionary

The named object dictionary has soft ownership of its entries. The entries are thus not cloned by wblockClone(). It is up to the application to copy those objects if necessary.

During the INSERT command, application-defined entries in the named object dictionary are not copied. The application must perform the desired cloning during the beginDeepCloneXlation() phase, adding the object IDs to the ID map and adding a new entry to the destination named object dictionary. For more information on beginDeepCloneXlation(), see Editor Reactor Notification Functions.

During the WBLOCK command, all IDs in the original named object dictionary are brought over to the destination named object dictionary, but the objects pointed to are not automatically copied. If the object an ID points to is not cloned by the application, the ID is removed from the destination dictionary during endDeepClone() translation. Again, the application needs to clone the objects during beginDeepCloneXlation and add the IDs to the ID map. It does not need to add a new ID to the destination named object dictionary because this task was performed automatically.

The following example shows how you might write an AcEditorReactor::beginDeepCloneXlation() function for a user-defined dictionary of objects that is placed in the named object dictionary. The example refers only to the kDcWblock and kDcInsert contexts.

// This example demonstrates a way to handle objects in
// the named object dictionary for WBLOCK and INSERT. 
// Our object is an AcDbDictionary, which is called
// "AsdkDictionary" in the named objects dictionary,
// containing our custom objects.
//
const char *kpDictionary = "AsdkDictionary";
// AsdkNODEdReactor is derived from AcEditorReactor.
//
void
AsdkNODEdReactor::beginDeepCloneXlation(
    AcDbIdMapping& idMap,
    Acad::ErrorStatus* pRetStat)
{
    Acad::ErrorStatus es;
    AcDbObjectId dictId;
    if (   idMap.deepCloneContext() != AcDb::kDcWblock
        && idMap.deepCloneContext() != AcDb::kDcInsert)
        return;
    // Get the "from" and "to" databases. 
    //
    AcDbDatabase *pFrom, *pTo;
    idMap.origDb(pFrom);
    idMap.destDb(pTo);
    // See if the "from" database has our dictionary, and
    // open it.  If it doesn't have one, we are done.
    //
    AcDbDictionary *pSrcNamedObjDict;
    pFrom->getNamedObjectsDictionary(pSrcNamedObjDict,
                                     AcDb::kForRead);
    es = pSrcNamedObjDict->getAt(kpDictionary, dictId);
    pSrcNamedObjDict->close();
    if (es == Acad::eKeyNotFound) 
        return;
    AcDbDictionary *pSrcDict;
    acdbOpenObject(pSrcDict, dictId, AcDb::kForRead);
    AcDbObject *pClone;
    switch (idMap.deepCloneContext()) {
    case AcDb::kDcWblock:
        // WBLOCK clones all or part of a drawing into a
        // newly created drawing.  This means that the
        // named object dictionary is always cloned, and
        // its AcDbObjectIds are in flux.  Therefore, you
        // cannot use getAt() or setAt() on the dictionary
        // in the new database.  This is because the
        // cloned dictionary references all refer to the 
        // original objects.  During deep clone translation,
        // all cloned entries will be translated to the
        // new objects, and entries not cloned will be
        // "removed" by getting "translated" to NULL.
        //
        // The cloning of entries in our own dictionary are
        // not handled here. If all are to be cloned, then
        // call setTreatElementsAsHard(Adesk::kTrue) on the
        // dictionary. Otherwise, only those entries that
        // are referred to by hard references in other
        // wblocked objects will have been cloned via
        // those references.
        // In this example, we will always write out all of
        // the records.  Since TreatElementsAsHard is not
        // currently persistent, we reset it here each time.
        //
        pSrcDict->upgradeOpen();
        pSrcDict->setTreatElementsAsHard(Adesk::kTrue);
        pClone = NULL;
        pSrcDict->wblockClone(pTo, pClone, idMap,
                              Adesk::kFalse);
        if (pClone != NULL) 
            pClone->close();
        break;
    case AcDb::kDcInsert:
        // In INSERT, an entire drawing is cloned, and
        // "merged" into a pre-existing drawing.  This
        // means that the destination drawing may already
        // have our dictionary, in which case we have to
        // merge our entries into the destination
        // dictionary. First we must find out if
        // the destination named objects dictionary contains
        // our dictionary.
        //
        AcDbDictionary *pDestNamedDict;
        pTo->getNamedObjectsDictionary(pDestNamedDict,
                                       AcDb::kForWrite);
        // Since INSERT does not clone the destination
        // named object dictionary, we can use getAt()
        // on it.
        //
        es = pDestNamedDict->getAt(kpDictionary, dictId);
        // If our dictionary does not yet exist in the
        // named object dictionary, which is not itself
        // cloned, we have to both clone and add our
        // dictionary to it.  Since dictionary entries are
        // ownership references, all of our entries will
        // also be cloned at this point, so we are done.
        //
        if (es == Acad::eKeyNotFound) {
            pClone = NULL;
            pSrcDict->deepClone(pDestNamedDict,
                                pClone, idMap);
            // Unless we have overridden the deepClone()
            // of our dictionary, we should expect it to
            // always be cloned here.
            //
            if (pClone == NULL) {
                *pRetStat = Acad::eNullObjectId;
                break;
            }
            pDestNamedDict->setAt(kpDictionary,
                                  pClone, dictId);
            pDestNamedDict->close();
            pClone->close();
            break;
        }
        pDestNamedDict->close();
        // Our dictionary already exists in the destination
        // database, so now we must "merge" the entries
        // into it.  Since we have not cloned our
        // destination dictionary, its object IDs are not in
        // flux, and we can use getAt() and setAt() on it.
        //
        AcDbDictionary *pDestDict;
        acdbOpenObject(pDestDict, dictId, AcDb::kForWrite);
        AcDbObject *pObj, *pObjClone;
        AcDbDictionaryIterator* pIter;
        pIter = pSrcDict->newIterator();
        for (; !pIter->done(); pIter->next()) {
            const char *pName = pIter->name();
            pIter->getObject(pObj, AcDb::kForRead);
            // If the dictionary contains any references
            // and/or other objects have references to it,
            // you must either use deepClone() or put the
            // ID pairs into the ID map here, so that they
            // will be in the map for translation.
            //
            pObjClone = NULL;
            pObj->deepClone(pDestDict, pObjClone, idMap);
            // INSERT usually uses a method of cloning
            // called CheapClone, where it "moves" objects
            // into the destination database instead of
            // actually cloning them.  When this happens,
            // pObj and pObjClone are pointers to the
            // same object.  We only want to close pObj
            // here if it really is a different object.
            // 
            if (pObj != pObjClone)
                pObj->close();
            if (pObjClone == NULL)
                continue;
            // If the name already exists in our
            // destination dictionary, it must be changed
            // to something unique.  In this example, the
            // name is changed to an anonymous entry. 
            // The setAt() method will automatically append
            // a unique identifier to each name beginning
            // with "*", for example: "*S04".
            //
            if (   pDestDict->getAt(pName, dictId)
                == Acad::eKeyNotFound) 
                pDestDict->setAt(pName, pObjClone, dictId);
            else 
                pDestDict->setAt("*S", pObjClone, dictId);
            pObjClone->close();
        }
        delete pIter;
        pDestDict->close();
        break;
    default:
        break;
    }
    pSrcDict->close();
}