Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "cafPdmReferenceHelper.h"
#include "cafPdmValueField.h"

#include <memory>
#include <vector>

class DemoPdmObject : public caf::PdmObjectHandle
Expand Down Expand Up @@ -1008,3 +1009,87 @@ TEST( BaseTest, ObjectValidation )

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() );
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
return executeCustomValidation();
}

} // End of namespace caf
20 changes: 20 additions & 0 deletions Fwk/AppFwk/cafProjectDataModel/cafPdmCore/cafPdmFieldHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ std::vector<QString> PdmFieldHandle::keywordAliases() 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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "cafPdmBase.h"
#include <QString>
#include <functional>
#include <vector>

namespace caf
Expand Down Expand Up @@ -61,9 +62,11 @@ class PdmFieldHandle
// Validation
virtual QString validate() const;
bool isValid() const;
void setCustomValidationCallback( std::function<QString()> callback );

protected:
bool isInitializedByInitFieldMacro() const { return m_ownerObject != nullptr; }
QString executeCustomValidation() const;
bool isInitializedByInitFieldMacro() const { return m_ownerObject != nullptr; }

private:
PDM_DISABLE_COPY_AND_ASSIGN( PdmFieldHandle );
Expand All @@ -77,6 +80,8 @@ class PdmFieldHandle
std::vector<QString> m_keywordAliases;

std::vector<std::pair<PdmFieldCapability*, bool>> m_capabilities;

std::function<QString()> m_customValidationCallback;
};

//--------------------------------------------------------------------------------------------------
Expand Down
41 changes: 28 additions & 13 deletions Fwk/AppFwk/cafTests/cafTestApplication/ValidationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,36 @@ ValidationTestObject::ValidationTestObject()
m_percentage.uiCapability()->setUiToolTip( "Valid range: 0 to 100%" );
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", "", "", "" );
m_count.uiCapability()->setUiToolTip( "Must be non-negative" );
m_count.uiCapability()->setUiToolTip( "Must be non-negative and even" );
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
CAF_PDM_InitField( &m_name, "name", QString( "John Doe" ), "Name", "", "", "" );
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( "[email protected]" ), "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
CAF_PDM_InitField( &m_rangeField, "rangeField", 0.0, "Range Field", "", "", "" );
Expand All @@ -54,15 +72,7 @@ std::map<QString, QString> ValidationTestObject::validate( const QString& config
auto errors = PdmObject::validate( configName );

// Add custom object-level validation

// Email validation (basic)
if ( !m_email().isEmpty() )
{
if ( !m_email().contains( "@" ) )
{
errors["email"] = "Email must contain @ symbol";
}
}
// Note: email validation is handled by a custom validation callback on the field itself

// Name validation
if ( m_name().isEmpty() )
Expand Down Expand Up @@ -135,6 +145,7 @@ void ValidationTestObject::performValidation()
qDebug() << "Age valid:" << m_age.isValid();
qDebug() << "Percentage valid:" << m_percentage.isValid();
qDebug() << "Count valid:" << m_count.isValid();
qDebug() << "Email valid:" << m_email.isValid();

// Show validation messages
if ( !m_temperature.isValid() )
Expand All @@ -153,4 +164,8 @@ void ValidationTestObject::performValidation()
{
qDebug() << " Count error:" << m_count.validate();
}
if ( !m_email.isValid() )
{
qDebug() << " Email error:" << m_email.validate();
}
}
2 changes: 1 addition & 1 deletion ThirdParty/vcpkg
Loading