mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 22:50:25 -05:00
Add support for custom validation callbacks on fields
* Add support for custom validation callbacks on fields Introduce setCustomValidationCallback to PdmFieldHandle, allowing custom validation logic via user-provided lambdas or functions. The validate() method now invokes this callback if set, in addition to built-in checks. Includes comprehensive unit tests for custom and combined validation scenarios. Backward compatible: default behavior unchanged if no callback is set.
This commit is contained in:
1 parent
93bd0b9c9d
commit
cb8a069ccd
6 files changed
+141
-16
No files matched your search
+85
@@ -12,6 +12,7 @@
|
|||||||
#include "cafPdmReferenceHelper.h"
|
#include "cafPdmReferenceHelper.h"
|
||||||
#include "cafPdmValueField.h"
|
#include "cafPdmValueField.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class DemoPdmObject : public caf::PdmObjectHandle
|
class DemoPdmObject : public caf::PdmObjectHandle
|
||||||
@@ -1008,3 +1009,87 @@ TEST( BaseTest, ObjectValidation )
|
|||||||
|
|
||||||
delete customObj;
|
delete customObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
/// Test of custom validation callback
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
TEST( BaseTest, CustomValidationCallback )
|
||||||
|
{
|
||||||
|
class TestObject : public caf::PdmObjectHandle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestObject()
|
||||||
|
{
|
||||||
|
this->addField( &m_value, "value" );
|
||||||
|
this->addField( &m_rangedValue, "rangedValue" );
|
||||||
|
}
|
||||||
|
|
||||||
|
caf::PdmDataValueField<int> m_value;
|
||||||
|
caf::PdmDataValueField<int> m_rangedValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto obj = std::make_unique<TestObject>();
|
||||||
|
|
||||||
|
// A field with no callback is valid by default
|
||||||
|
obj->m_value.setValue( 42 );
|
||||||
|
EXPECT_TRUE( obj->m_value.isValid() );
|
||||||
|
EXPECT_TRUE( obj->m_value.validate().isEmpty() );
|
||||||
|
|
||||||
|
// Registering a callback that returns an error string makes the field invalid
|
||||||
|
obj->m_value.setCustomValidationCallback( []() -> QString { return "Custom error"; } );
|
||||||
|
EXPECT_FALSE( obj->m_value.isValid() );
|
||||||
|
EXPECT_EQ( QString( "Custom error" ), obj->m_value.validate() );
|
||||||
|
|
||||||
|
// Registering a callback that returns empty string keeps the field valid
|
||||||
|
obj->m_value.setCustomValidationCallback( []() -> QString { return QString(); } );
|
||||||
|
EXPECT_TRUE( obj->m_value.isValid() );
|
||||||
|
EXPECT_TRUE( obj->m_value.validate().isEmpty() );
|
||||||
|
|
||||||
|
// The callback can access the field value for conditional validation
|
||||||
|
obj->m_value.setCustomValidationCallback(
|
||||||
|
[&obj]() -> QString
|
||||||
|
{
|
||||||
|
if ( obj->m_value.value() % 2 != 0 )
|
||||||
|
{
|
||||||
|
return "Value must be even";
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
} );
|
||||||
|
|
||||||
|
obj->m_value.setValue( 4 );
|
||||||
|
EXPECT_TRUE( obj->m_value.isValid() );
|
||||||
|
|
||||||
|
obj->m_value.setValue( 7 );
|
||||||
|
EXPECT_FALSE( obj->m_value.isValid() );
|
||||||
|
EXPECT_EQ( QString( "Value must be even" ), obj->m_value.validate() );
|
||||||
|
|
||||||
|
// Custom callback works alongside range validation (both must pass)
|
||||||
|
obj->m_rangedValue.setRange( 0, 100 );
|
||||||
|
obj->m_rangedValue.setCustomValidationCallback(
|
||||||
|
[&obj]() -> QString
|
||||||
|
{
|
||||||
|
if ( obj->m_rangedValue.value() % 2 != 0 )
|
||||||
|
{
|
||||||
|
return "Value must be even";
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Both range and custom pass
|
||||||
|
obj->m_rangedValue.setValue( 50 );
|
||||||
|
EXPECT_TRUE( obj->m_rangedValue.isValid() );
|
||||||
|
|
||||||
|
// Range fails (custom would pass)
|
||||||
|
obj->m_rangedValue.setValue( 150 );
|
||||||
|
EXPECT_FALSE( obj->m_rangedValue.isValid() );
|
||||||
|
EXPECT_TRUE( obj->m_rangedValue.validate().contains( "exceeds maximum" ) );
|
||||||
|
|
||||||
|
// Custom fails (range would pass)
|
||||||
|
obj->m_rangedValue.setValue( 51 );
|
||||||
|
EXPECT_FALSE( obj->m_rangedValue.isValid() );
|
||||||
|
EXPECT_EQ( QString( "Value must be even" ), obj->m_rangedValue.validate() );
|
||||||
|
|
||||||
|
// Both pass
|
||||||
|
obj->m_rangedValue.setValue( 50 );
|
||||||
|
EXPECT_TRUE( obj->m_rangedValue.isValid() );
|
||||||
|
}
|
||||||
@@ -259,7 +259,7 @@ QString caf::PdmDataValueField<DataType>::validate() const
|
|||||||
return QString( "Value %1 exceeds maximum %2" ).arg( m_fieldValue ).arg( m_maxValue.value() );
|
return QString( "Value %1 exceeds maximum %2" ).arg( m_fieldValue ).arg( m_maxValue.value() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QString();
|
return executeCustomValidation();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // End of namespace caf
|
} // End of namespace caf
|
||||||
@@ -135,6 +135,26 @@ std::vector<QString> PdmFieldHandle::keywordAliases() const
|
|||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
QString PdmFieldHandle::validate() const
|
QString PdmFieldHandle::validate() const
|
||||||
{
|
{
|
||||||
|
return executeCustomValidation();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
///
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
void PdmFieldHandle::setCustomValidationCallback( std::function<QString()> callback )
|
||||||
|
{
|
||||||
|
m_customValidationCallback = std::move( callback );
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
///
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
QString PdmFieldHandle::executeCustomValidation() const
|
||||||
|
{
|
||||||
|
if ( m_customValidationCallback )
|
||||||
|
{
|
||||||
|
return m_customValidationCallback();
|
||||||
|
}
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "cafPdmBase.h"
|
#include "cafPdmBase.h"
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace caf
|
namespace caf
|
||||||
@@ -61,9 +62,11 @@ public:
|
|||||||
// Validation
|
// Validation
|
||||||
virtual QString validate() const;
|
virtual QString validate() const;
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
void setCustomValidationCallback( std::function<QString()> callback );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool isInitializedByInitFieldMacro() const { return m_ownerObject != nullptr; }
|
QString executeCustomValidation() const;
|
||||||
|
bool isInitializedByInitFieldMacro() const { return m_ownerObject != nullptr; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PDM_DISABLE_COPY_AND_ASSIGN( PdmFieldHandle );
|
PDM_DISABLE_COPY_AND_ASSIGN( PdmFieldHandle );
|
||||||
@@ -77,6 +80,8 @@ private:
|
|||||||
std::vector<QString> m_keywordAliases;
|
std::vector<QString> m_keywordAliases;
|
||||||
|
|
||||||
std::vector<std::pair<PdmFieldCapability*, bool>> m_capabilities;
|
std::vector<std::pair<PdmFieldCapability*, bool>> m_capabilities;
|
||||||
|
|
||||||
|
std::function<QString()> m_customValidationCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -26,18 +26,36 @@ ValidationTestObject::ValidationTestObject()
|
|||||||
m_percentage.uiCapability()->setUiToolTip( "Valid range: 0 to 100%" );
|
m_percentage.uiCapability()->setUiToolTip( "Valid range: 0 to 100%" );
|
||||||
m_percentage.setRange( 0.0, 100.0 );
|
m_percentage.setRange( 0.0, 100.0 );
|
||||||
|
|
||||||
// Count field: minimum value only (>= 0)
|
// Count field: minimum value only (>= 0) and must be even (custom callback)
|
||||||
CAF_PDM_InitField( &m_count, "count", 0, "Count", "", "", "" );
|
CAF_PDM_InitField( &m_count, "count", 0, "Count", "", "", "" );
|
||||||
m_count.uiCapability()->setUiToolTip( "Must be non-negative" );
|
m_count.uiCapability()->setUiToolTip( "Must be non-negative and even" );
|
||||||
m_count.setMinValue( 0 );
|
m_count.setMinValue( 0 );
|
||||||
|
m_count.setCustomValidationCallback(
|
||||||
|
[this]() -> QString
|
||||||
|
{
|
||||||
|
if ( m_count() % 2 != 0 )
|
||||||
|
{
|
||||||
|
return "Count must be an even number";
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
} );
|
||||||
|
|
||||||
// Name field: no validation
|
// Name field: no validation
|
||||||
CAF_PDM_InitField( &m_name, "name", QString( "John Doe" ), "Name", "", "", "" );
|
CAF_PDM_InitField( &m_name, "name", QString( "John Doe" ), "Name", "", "", "" );
|
||||||
m_name.uiCapability()->setUiToolTip( "Free text field" );
|
m_name.uiCapability()->setUiToolTip( "Free text field" );
|
||||||
|
|
||||||
// Email field: no validation (but could add custom validation in validate())
|
// Email field: custom validation callback
|
||||||
CAF_PDM_InitField( &m_email, "email", QString( "john@example.com" ), "Email", "", "", "" );
|
CAF_PDM_InitField( &m_email, "email", QString( "john@example.com" ), "Email", "", "", "" );
|
||||||
m_email.uiCapability()->setUiToolTip( "Email address" );
|
m_email.uiCapability()->setUiToolTip( "Email address (must contain @)" );
|
||||||
|
m_email.setCustomValidationCallback(
|
||||||
|
[this]() -> QString
|
||||||
|
{
|
||||||
|
if ( !m_email().isEmpty() && !m_email().contains( "@" ) )
|
||||||
|
{
|
||||||
|
return "Email must contain @ symbol";
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
} );
|
||||||
|
|
||||||
// Range field: -100.0 to 100.0
|
// Range field: -100.0 to 100.0
|
||||||
CAF_PDM_InitField( &m_rangeField, "rangeField", 0.0, "Range Field", "", "", "" );
|
CAF_PDM_InitField( &m_rangeField, "rangeField", 0.0, "Range Field", "", "", "" );
|
||||||
@@ -54,15 +72,7 @@ std::map<QString, QString> ValidationTestObject::validate( const QString& config
|
|||||||
auto errors = PdmObject::validate( configName );
|
auto errors = PdmObject::validate( configName );
|
||||||
|
|
||||||
// Add custom object-level validation
|
// Add custom object-level validation
|
||||||
|
// Note: email validation is handled by a custom validation callback on the field itself
|
||||||
// Email validation (basic)
|
|
||||||
if ( !m_email().isEmpty() )
|
|
||||||
{
|
|
||||||
if ( !m_email().contains( "@" ) )
|
|
||||||
{
|
|
||||||
errors["email"] = "Email must contain @ symbol";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name validation
|
// Name validation
|
||||||
if ( m_name().isEmpty() )
|
if ( m_name().isEmpty() )
|
||||||
@@ -135,6 +145,7 @@ void ValidationTestObject::performValidation()
|
|||||||
qDebug() << "Age valid:" << m_age.isValid();
|
qDebug() << "Age valid:" << m_age.isValid();
|
||||||
qDebug() << "Percentage valid:" << m_percentage.isValid();
|
qDebug() << "Percentage valid:" << m_percentage.isValid();
|
||||||
qDebug() << "Count valid:" << m_count.isValid();
|
qDebug() << "Count valid:" << m_count.isValid();
|
||||||
|
qDebug() << "Email valid:" << m_email.isValid();
|
||||||
|
|
||||||
// Show validation messages
|
// Show validation messages
|
||||||
if ( !m_temperature.isValid() )
|
if ( !m_temperature.isValid() )
|
||||||
@@ -153,4 +164,8 @@ void ValidationTestObject::performValidation()
|
|||||||
{
|
{
|
||||||
qDebug() << " Count error:" << m_count.validate();
|
qDebug() << " Count error:" << m_count.validate();
|
||||||
}
|
}
|
||||||
|
if ( !m_email.isValid() )
|
||||||
|
{
|
||||||
|
qDebug() << " Email error:" << m_email.validate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Vendored
+1
-1
Submodule ThirdParty/vcpkg updated: 04bec276db...61e1f600c1.
Reference in new issue
Block a user