diff --git a/ApplicationLibCode/Commands/ExportCommands/RicAdvancedSnapshotExportFeature.cpp b/ApplicationLibCode/Commands/ExportCommands/RicAdvancedSnapshotExportFeature.cpp index ec0779ed12..9a19c27824 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicAdvancedSnapshotExportFeature.cpp +++ b/ApplicationLibCode/Commands/ExportCommands/RicAdvancedSnapshotExportFeature.cpp @@ -165,9 +165,7 @@ void RicAdvancedSnapshotExportFeature::exportMultipleSnapshots( const QString& f copyOfGeoMechView->setGeoMechCase( geomCase ); - // Resolve references after reservoir view has been inserted into Rim structures - copyOfGeoMechView->resolveReferencesRecursively(); - copyOfGeoMechView->initAfterReadRecursively(); + copyOfGeoMechView->initAfterInsert(); copyOfGeoMechView->loadDataAndUpdate(); diff --git a/ApplicationLibCode/Commands/GridCrossPlotCommands/RicPasteGridCrossPlotDataSetFeature.cpp b/ApplicationLibCode/Commands/GridCrossPlotCommands/RicPasteGridCrossPlotDataSetFeature.cpp index 76a883014a..9e8e2ac8b1 100644 --- a/ApplicationLibCode/Commands/GridCrossPlotCommands/RicPasteGridCrossPlotDataSetFeature.cpp +++ b/ApplicationLibCode/Commands/GridCrossPlotCommands/RicPasteGridCrossPlotDataSetFeature.cpp @@ -61,8 +61,7 @@ void RicPasteGridCrossPlotDataSetFeature::onActionTriggered( bool isChecked ) { auto newDataSet = dataSet->copyObject(); crossPlot->addDataSet( newDataSet ); - newDataSet->resolveReferencesRecursively(); - newDataSet->initAfterReadRecursively(); + newDataSet->initAfterInsert(); objectToSelect = newDataSet; } diff --git a/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteEclipseViewsFeature.cpp b/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteEclipseViewsFeature.cpp index cbd5a6ce89..98d390c83c 100644 --- a/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteEclipseViewsFeature.cpp +++ b/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteEclipseViewsFeature.cpp @@ -112,10 +112,8 @@ void RicPasteEclipseViewsFeature::onActionTriggered( bool isChecked ) rimReservoirView->setEclipseCase( eclipseCase ); - // Resolve references after reservoir view has been inserted into Rim structures - // Intersections referencing a well path/ simulation well requires this - rimReservoirView->resolveReferencesRecursively(); - rimReservoirView->initAfterReadRecursively(); + // Intersections referencing a well path/ simulation well requires resolving after insert + rimReservoirView->initAfterInsert(); eclipseCase->intersectionViewCollection()->syncFromExistingIntersections( false ); rimReservoirView->loadDataAndUpdate(); diff --git a/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteGeoMechViewsFeature.cpp b/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteGeoMechViewsFeature.cpp index 243cb3e958..de55c04637 100644 --- a/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteGeoMechViewsFeature.cpp +++ b/ApplicationLibCode/Commands/OperationsUsingObjReferences/RicPasteGeoMechViewsFeature.cpp @@ -88,10 +88,8 @@ void RicPasteGeoMechViewsFeature::onActionTriggered( bool isChecked ) rimReservoirView->setGeoMechCase( geomCase ); - // Resolve references after reservoir view has been inserted into Rim structures - // Intersections referencing a well path requires this - rimReservoirView->resolveReferencesRecursively(); - rimReservoirView->initAfterReadRecursively(); + // Intersections referencing a well path requires resolving after insert + rimReservoirView->initAfterInsert(); caf::PdmDocument::updateUiIconStateRecursively( rimReservoirView ); diff --git a/ApplicationLibCode/Commands/RicDeleteItemExec.cpp b/ApplicationLibCode/Commands/RicDeleteItemExec.cpp index 21b948cace..a1f9003602 100644 --- a/ApplicationLibCode/Commands/RicDeleteItemExec.cpp +++ b/ApplicationLibCode/Commands/RicDeleteItemExec.cpp @@ -88,8 +88,7 @@ void RicDeleteItemExec::undo() listField->insertAt( m_commandData.m_indexToObject, obj ); - obj->xmlCapability()->initAfterReadRecursively(); - obj->xmlCapability()->resolveReferencesRecursively(); + obj->xmlCapability()->initAfterInsert(); listField->uiCapability()->updateConnectedEditors(); listField->ownerObject()->uiCapability()->updateConnectedEditors(); diff --git a/ApplicationLibCode/Commands/RicNewContourMapViewFeature.cpp b/ApplicationLibCode/Commands/RicNewContourMapViewFeature.cpp index 76518564be..20a79f2196 100644 --- a/ApplicationLibCode/Commands/RicNewContourMapViewFeature.cpp +++ b/ApplicationLibCode/Commands/RicNewContourMapViewFeature.cpp @@ -225,9 +225,7 @@ RimEclipseContourMapView* contourMap->setName( QString( "Contour Map %1" ).arg( i + 1 ) ); eclipseCase->contourMapCollection()->addView( contourMap ); - // Resolve references after contour map has been inserted into Rim structures - contourMap->resolveReferencesRecursively(); - contourMap->initAfterReadRecursively(); + contourMap->initAfterInsert(); return contourMap; } @@ -269,9 +267,7 @@ RimEclipseContourMapView* RicNewContourMapViewFeature::createEclipseContourMapFr contourMap->synchronizeLocalAnnotationsFromGlobal(); - // Resolve references after contour map has been inserted into Rim structures - contourMap->resolveReferencesRecursively(); - contourMap->initAfterReadRecursively(); + contourMap->initAfterInsert(); eclipseCase->contourMapCollection()->updateConnectedEditors(); @@ -330,9 +326,7 @@ RimEclipseContourMapView* RicNewContourMapViewFeature::createEclipseContourMap( auto col = RiuGuiTheme::getColorByVariableName( "backgroundColor2" ); contourMap->setBackgroundColor( RiaColorTools::fromQColorTo3f( col ) ); // Ignore original view background - // Resolve references after contour map has been inserted into Rim structures - contourMap->resolveReferencesRecursively(); - contourMap->initAfterReadRecursively(); + contourMap->initAfterInsert(); return contourMap; } @@ -358,9 +352,7 @@ RimGeoMechContourMapView* contourMap->setName( QString( "Contour Map %1" ).arg( i + 1 ) ); geoMechCase->contourMapCollection()->addView( contourMap ); - // Resolve references after contour map has been inserted into Rim structures - contourMap->resolveReferencesRecursively(); - contourMap->initAfterReadRecursively(); + contourMap->initAfterInsert(); return contourMap; } @@ -390,9 +382,7 @@ RimGeoMechContourMapView* RicNewContourMapViewFeature::createGeoMechContourMapFr geoMechCase->contourMapCollection()->addView( contourMap ); - // Resolve references after contour map has been inserted into Rim structures - contourMap->resolveReferencesRecursively(); - contourMap->initAfterReadRecursively(); + contourMap->initAfterInsert(); return contourMap; } @@ -415,9 +405,7 @@ RimGeoMechContourMapView* RicNewContourMapViewFeature::createGeoMechContourMap( auto col = RiuGuiTheme::getColorByVariableName( "backgroundColor2" ); contourMap->setBackgroundColor( RiaColorTools::fromQColorTo3f( col ) ); // Ignore original view background - // Resolve references after contour map has been inserted into Rim structures - contourMap->resolveReferencesRecursively(); - contourMap->initAfterReadRecursively(); + contourMap->initAfterInsert(); return contourMap; } diff --git a/ApplicationLibCode/Commands/SummaryPlotCommands/RicDuplicateSummaryTableFeature.cpp b/ApplicationLibCode/Commands/SummaryPlotCommands/RicDuplicateSummaryTableFeature.cpp index 408e53d6c0..24e6ef3582 100644 --- a/ApplicationLibCode/Commands/SummaryPlotCommands/RicDuplicateSummaryTableFeature.cpp +++ b/ApplicationLibCode/Commands/SummaryPlotCommands/RicDuplicateSummaryTableFeature.cpp @@ -69,9 +69,7 @@ void RicDuplicateSummaryTableFeature::copyTableAndAddToCollection( RimSummaryTab // Add table to collection summaryTableColl->addTable( newSummaryTable ); - // Resolve references after object has been inserted into the data model - newSummaryTable->resolveReferencesRecursively(); - newSummaryTable->initAfterReadRecursively(); + newSummaryTable->initAfterInsert(); // Update name QString nameOfCopy = QString( "Copy of " ) + sourceTable->description(); diff --git a/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteAsciiDataCurveFeature.cpp b/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteAsciiDataCurveFeature.cpp index f1444210ca..ec3b58728e 100644 --- a/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteAsciiDataCurveFeature.cpp +++ b/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteAsciiDataCurveFeature.cpp @@ -69,15 +69,12 @@ void RicPasteAsciiDataCurveFeature::onActionTriggered( bool isChecked ) summaryPlot->addAsciiDataCruve( newObject ); - // Resolve references after object has been inserted into the project data model - newObject->resolveReferencesRecursively(); + newObject->initAfterInsert(); // If source curve is part of a curve filter, resolve of references to the summary case does not // work when pasting the new curve into a plot. Must set summary case manually. // newObject->setSummaryCase(sourceObjects[i]->summaryCase()); - newObject->initAfterReadRecursively(); - newObject->loadDataAndUpdate( true ); newObject->updateConnectedEditors(); diff --git a/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteEnsembleCurveSetFeature.cpp b/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteEnsembleCurveSetFeature.cpp index 72cb7135a0..6e6d3a8dd7 100644 --- a/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteEnsembleCurveSetFeature.cpp +++ b/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteEnsembleCurveSetFeature.cpp @@ -50,9 +50,7 @@ RimEnsembleCurveSet* RicPasteEnsembleCurveSetFeature::copyCurveSetAndAddToCollec curveSetCollection->addCurveSet( newCurveSet ); - // Resolve references after object has been inserted into the project data model - newCurveSet->resolveReferencesRecursively(); - newCurveSet->initAfterReadRecursively(); + newCurveSet->initAfterInsert(); newCurveSet->loadDataAndUpdate( false ); newCurveSet->updateConnectedEditors(); diff --git a/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteTimeHistoryCurveFeature.cpp b/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteTimeHistoryCurveFeature.cpp index fdc48c2818..ae014c0e02 100644 --- a/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteTimeHistoryCurveFeature.cpp +++ b/ApplicationLibCode/Commands/SummaryPlotCommands/RicPasteTimeHistoryCurveFeature.cpp @@ -77,15 +77,12 @@ void RicPasteTimeHistoryCurveFeature::onActionTriggered( bool isChecked ) summaryPlot->addGridTimeHistoryCurve( newObject ); - // Resolve references after object has been inserted into the project data model - newObject->resolveReferencesRecursively(); + newObject->initAfterInsert(); // If source curve is part of a curve filter, resolve of references to the summary case does not // work when pasting the new curve into a plot. Must set summary case manually. // newObject->setSummaryCase(sourceObjects[i]->summaryCase()); - newObject->initAfterReadRecursively(); - newObject->loadDataAndUpdate( true ); newObject->updateConnectedEditors(); diff --git a/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogCurveFeature.cpp b/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogCurveFeature.cpp index c81b329491..d65bad5a47 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogCurveFeature.cpp +++ b/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogCurveFeature.cpp @@ -110,9 +110,7 @@ void RicPasteWellLogCurveFeature::onActionTriggered( bool isChecked ) wellLogTrack->addCurve( newObject ); - // Resolve references after object has been inserted into the project data model - newObject->resolveReferencesRecursively(); - newObject->initAfterReadRecursively(); + newObject->initAfterInsert(); newObject->loadDataAndUpdate( true ); diff --git a/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogPlotFeature.cpp b/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogPlotFeature.cpp index 9c9d9926f0..2c296c069f 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogPlotFeature.cpp +++ b/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogPlotFeature.cpp @@ -78,9 +78,7 @@ void RicPasteWellLogPlotFeature::onActionTriggered( bool isChecked ) newObject->resetDockWindowId(); wellLogPlotCollection->addWellLogPlot( newObject ); - // Resolve references after object has been inserted into the project data model - newObject->resolveReferencesRecursively(); - newObject->initAfterReadRecursively(); + newObject->initAfterInsert(); QString customName = "Copy of " + newObject->nameConfig()->customName(); newObject->nameConfig()->setCustomName( customName ); diff --git a/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogTrackFeature.cpp b/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogTrackFeature.cpp index 9da0965df7..0b12ca08de 100644 --- a/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogTrackFeature.cpp +++ b/ApplicationLibCode/Commands/WellLogCommands/RicPasteWellLogTrackFeature.cpp @@ -80,9 +80,7 @@ void RicPasteWellLogTrackFeature::onActionTriggered( bool isChecked ) newObject->setDescription( QString( "Track %1" ).arg( wellLogPlot->plotCount() ) ); - // Resolve references after object has been inserted into the project data model - newObject->resolveReferencesRecursively(); - newObject->initAfterReadRecursively(); + newObject->initAfterInsert(); newObject->loadDataAndUpdate(); diff --git a/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechCase.cpp b/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechCase.cpp index e73685a6a4..db21bd4f89 100644 --- a/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/GeoMech/RimGeoMechCase.cpp @@ -251,9 +251,7 @@ RimGeoMechView* RimGeoMechCase::createCopyAndAddView( const RimGeoMechView* sour geoMechViews.push_back( rimGeoMechView ); - // Resolve references after reservoir view has been inserted into Rim structures - rimGeoMechView->resolveReferencesRecursively(); - rimGeoMechView->initAfterReadRecursively(); + rimGeoMechView->initAfterInsert(); return rimGeoMechView; } diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseCase.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseCase.cpp index 4da1d7a45d..93ede63c18 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseCase.cpp @@ -410,9 +410,7 @@ RimEclipseView* RimEclipseCase::createCopyAndAddView( const RimEclipseView* sour viewColl->addView( rimEclipseView ); - // Resolve references after reservoir view has been inserted into Rim structures - rimEclipseView->resolveReferencesRecursively(); - rimEclipseView->initAfterReadRecursively(); + rimEclipseView->initAfterInsert(); return rimEclipseView; } diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryMultiPlotCollection.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryMultiPlotCollection.cpp index 124c1ed968..3e046a2e5f 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryMultiPlotCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryMultiPlotCollection.cpp @@ -210,8 +210,7 @@ RimSummaryMultiPlot* RimSummaryMultiPlotCollection::duplicatePlot( RimSummaryMul plotCopy->resetDockWindowId(); addSummaryMultiPlot( plotCopy ); - plotCopy->resolveReferencesRecursively(); - plotCopy->initAfterReadRecursively(); + plotCopy->initAfterInsert(); plotCopy->updateAllRequiredEditors(); plotCopy->loadDataAndUpdate(); diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.cpp b/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.cpp index 2677836b34..03d6300b4f 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.cpp +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.cpp @@ -501,6 +501,20 @@ void PdmXmlObjectHandle::resolveReferencesRecursively( } } +//-------------------------------------------------------------------------------------------------- +/// Call after the object has been inserted into the document tree, typically after a copy by XML +/// serialization. Resolves pointer references (which requires the object to be reachable from the +/// document root) and then re-runs initAfterRead() so overrides can use the resolved pointers. +//-------------------------------------------------------------------------------------------------- +void PdmXmlObjectHandle::initAfterInsert() +{ + // The object must be inserted into the document tree before references can be resolved + CAF_ASSERT( m_owner && m_owner->parentField() ); + + resolveReferencesRecursively(); + initAfterReadRecursively(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.h b/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.h index 31f6c79fd0..df90990d35 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.h +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.h @@ -62,6 +62,11 @@ public: // resolving might fail. The object needs to be fully inserted into the document before resolving references. void resolveReferencesRecursively( std::vector* fieldWithFailingResolve = nullptr ); + // Call after the object has been inserted into the document tree, typically after a copy by XML serialization. + // Resolves pointer references (which requires the object to be reachable from the document root) and then + // re-runs initAfterRead() so overrides can use the resolved pointers. + void initAfterInsert(); + bool inheritsClassWithKeyword( const QString& testClassKeyword ) const; const std::list& classInheritanceStack() const; diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXml_UnitTests/cafPdmAdvancedTemplateTest.cpp b/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXml_UnitTests/cafPdmAdvancedTemplateTest.cpp index 10821807a2..46e251371c 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXml_UnitTests/cafPdmAdvancedTemplateTest.cpp +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXml_UnitTests/cafPdmAdvancedTemplateTest.cpp @@ -110,6 +110,9 @@ public: return m_doubleMember; } double m_doubleMember; + + void initAfterRead() override { m_initAfterReadCount++; } + int m_initAfterReadCount = 0; }; CAF_PDM_XML_SOURCE_INIT( DemoPdmObjectA, "DemoPdmObjectA" ); @@ -268,3 +271,38 @@ TEST( AdvancedObjectTest, CopyOfObjects ) } } } + +//-------------------------------------------------------------------------------------------------- +/// initAfterInsert() resolves references and re-runs initAfterRead() after the object has been +/// inserted into the document tree +//-------------------------------------------------------------------------------------------------- +TEST( AdvancedObjectTest, InitAfterInsert ) +{ + ContainerPdmObject* root = new ContainerPdmObject; + ContainerPdmObject* container = new ContainerPdmObject; + ContainerPdmObject* sibling = new ContainerPdmObject; + root->m_containers.push_back( container ); + root->m_containers.push_back( sibling ); + + ItemPdmObject* item = new ItemPdmObject(); + item->m_name = "Obj A"; + container->m_items.push_back( item ); + + DemoPdmObjectA* a = new DemoPdmObjectA; + sibling->m_demoObjs.push_back( a ); + a->m_pointerToItem = item; + + auto* objCopy = dynamic_cast( + a->xmlCapability()->copyByXmlSerialization( caf::PdmDefaultObjectFactory::instance() ) ); + ASSERT_TRUE( objCopy != nullptr ); + + // The pointer field is not resolvable until the copy is inserted into the document tree + objCopy->m_pointerToItem = nullptr; + sibling->m_demoObjs.push_back( objCopy ); + + const int initCountBeforeInsert = objCopy->m_initAfterReadCount; + objCopy->xmlCapability()->initAfterInsert(); + + ASSERT_TRUE( objCopy->m_pointerToItem() == item ); + ASSERT_EQ( initCountBeforeInsert + 1, objCopy->m_initAfterReadCount ); +}