The sample code in this section is an approximation of the default behavior of deepClone(). The deep clone operation has two main stages:
During the cloning stage in this example, information about the old object is copied to the new object using a specific type of filer to write out the object and read it back. The filer keeps track of objects owned by the primary object so that they can be copied as well.
If this is not a primary object, you would normally add it to the database using addAcDbObject() and then identify its owner using setOwnerId(). To establish ownership, the owner must file out the ID of the owned object using the appropriate ownership type.
The following sample code illustrates these steps:
Acad::ErrorStatus AsdkPoly::subDeepClone(AcDbObject* pOwner, AcDbObject*& pClonedObject, AcDbIdMapping& idMap, Adesk::Boolean isPrimary) const { // You should always pass back pClonedObject == NULL // if, for any reason, you do not actually clone it // during this call. The caller should pass it in // as NULL, but to be safe, we set it here as well. // pClonedObject = NULL; // If this object is in the idMap and is already // cloned, then return. // bool isPrim = false; if (isPrimary) isPrim = true; AcDbIdPair idPair(objectId(), (AcDbObjectId)NULL, false, isPrim); if (idMap.compute(idPair) && (idPair.value() != NULL)) return Acad::eOk; // Create the clone // AsdkPoly *pClone = (AsdkPoly*)isA()->create(); if (pClone != NULL) pClonedObject = pClone; // set the return value else return Acad::eOutOfMemory; AcDbDeepCloneFiler filer; dwgOut(&filer); filer.seek(0L, AcDb::kSeekFromStart); pClone->dwgIn(&filer); bool bOwnerXlated = false; if (isPrimary) { AcDbBlockTableRecord *pBTR = AcDbBlockTableRecord::cast(pOwner); if (pBTR != NULL) { pBTR->appendAcDbEntity(pClone); bOwnerXlated = true; } else { pOwner->database()->addAcDbObject(pClone); } } else { pOwner->database()->addAcDbObject(pClone); pClone->setOwnerId(pOwner->objectId()); bOwnerXlated = true; } // This must be called for all newly created objects // in deepClone. It is turned off by endDeepClone() // after it has translated the references to their // new values. // pClone->setAcDbObjectIdsInFlux(); pClone->disableUndoRecording(true); // Add the new information to the idMap. We can use // the idPair started above. // idPair.setValue(pClonedObject->objectId()); idPair.setIsCloned(Adesk::kTrue); idPair.setIsOwnerXlated(bOwnerXlated); idMap.assign(idPair); // Using the filer list created above, find and clone // any owned objects. // AcDbObjectId id; while (filer.getNextOwnedObject(id)) { AcDbObject *pSubObject; AcDbObject *pClonedSubObject; // Some object's references may be set to NULL, // so don't try to clone them. // if (id == NULL) continue; // Open the object and clone it. Note that we now // set "isPrimary" to kFalse here because the object // is being cloned, not as part of the primary set, // but because it is owned by something in the // primary set. // acdbOpenAcDbObject(pSubObject, id, AcDb::kForRead); pClonedSubObject = NULL; pSubObject->deepClone(pClonedObject, pClonedSubObject, idMap, Adesk::kFalse); // If this is a kDcInsert context, the objects // may be "cheapCloned". In this case, they are // "moved" instead of cloned. The result is that // pSubObject and pClonedSubObject will point to // the same object. So, we only want to close // pSubObject if it really is a different object // than its clone. // if (pSubObject != pClonedSubObject) pSubObject->close(); // The pSubObject may either already have been // cloned, or for some reason has chosen not to be // cloned. In that case, the returned pointer will // be NULL. Otherwise, since we have no immediate // use for it now, we can close the clone. // if (pClonedSubObject != NULL) pClonedSubObject->close(); } // Leave pClonedObject open for the caller // return Acad::eOk; }