Properties replacement method
Adds a possibility to replace properties inherited from base types with a more specific ones. For example, such properties may have: - different meaning which should be reflected in property name (e.g. TRACK::{G,S}etWidth() sets actual track width, but VIA::{G,S}etWidth() modifies the diameter) - different set of possible values (e.g. BOARD_CONNECTED_ITEM::SetLayer() should accept any copper layer, but MODULE::SetLayer() works only with F.Cu and B.Cu)
This commit is contained in:
parent
a988cab84e
commit
f084a86601
|
@ -46,6 +46,7 @@ PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aPr
|
|||
{
|
||||
wxASSERT_MSG( !m_dirty, "Have not called PROPERTY_MANAGER::Rebuild(), "
|
||||
"property list not up-to-date" );
|
||||
|
||||
auto it = m_classes.find( aType );
|
||||
|
||||
if( it == m_classes.end() )
|
||||
|
@ -108,6 +109,15 @@ void PROPERTY_MANAGER::AddProperty( PROPERTY_BASE* aProperty )
|
|||
}
|
||||
|
||||
|
||||
void PROPERTY_MANAGER::ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew )
|
||||
{
|
||||
wxASSERT( aBase == aNew->BaseHash() );
|
||||
CLASS_DESC& classDesc = getClass( aNew->OwnerHash() );
|
||||
classDesc.m_replaced.insert( std::make_pair( aBase, aName ) );
|
||||
AddProperty( aNew );
|
||||
}
|
||||
|
||||
|
||||
void PROPERTY_MANAGER::AddTypeCast( TYPE_CAST_BASE* aCast )
|
||||
{
|
||||
TYPE_ID derivedHash = aCast->DerivedHash();
|
||||
|
@ -169,7 +179,7 @@ PROPERTY_MANAGER::CLASS_DESC& PROPERTY_MANAGER::getClass( TYPE_ID aTypeId )
|
|||
auto it = m_classes.find( aTypeId );
|
||||
|
||||
if( it == m_classes.end() )
|
||||
tie( it, std::ignore ) = m_classes.emplace( std::make_pair( aTypeId, CLASS_DESC( aTypeId ) ) );
|
||||
tie( it, std::ignore ) = m_classes.emplace( aTypeId, CLASS_DESC( aTypeId ) );
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
@ -177,18 +187,28 @@ PROPERTY_MANAGER::CLASS_DESC& PROPERTY_MANAGER::getClass( TYPE_ID aTypeId )
|
|||
|
||||
void PROPERTY_MANAGER::CLASS_DESC::rebuild()
|
||||
{
|
||||
PROPERTY_SET replaced( m_replaced );
|
||||
m_allProperties.clear();
|
||||
collectPropsRecur( m_allProperties );
|
||||
collectPropsRecur( m_allProperties, replaced );
|
||||
// We need to keep properties sorted to be able to use std::set_* functions
|
||||
sort( m_allProperties.begin(), m_allProperties.end() );
|
||||
}
|
||||
|
||||
|
||||
void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( PROPERTY_LIST& aResult ) const
|
||||
void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( PROPERTY_LIST& aResult, PROPERTY_SET& aReplaced ) const
|
||||
{
|
||||
for( const auto& base : m_bases )
|
||||
base.get().collectPropsRecur( aResult );
|
||||
for( const auto& replacedEntry : m_replaced )
|
||||
aReplaced.emplace( replacedEntry );
|
||||
|
||||
for( auto& property : m_ownProperties )
|
||||
aResult.push_back( property.second.get() );
|
||||
for( auto& propertyData : m_ownProperties )
|
||||
{
|
||||
PROPERTY_BASE* property = propertyData.second.get();
|
||||
|
||||
// Do not store replaced properties
|
||||
if( aReplaced.count( std::make_pair( property->OwnerHash(), property->Name() ) ) == 0 )
|
||||
aResult.push_back( property );
|
||||
}
|
||||
|
||||
for( const auto& base : m_bases )
|
||||
base.get().collectPropsRecur( aResult, aReplaced );
|
||||
}
|
||||
|
|
|
@ -165,11 +165,16 @@ public:
|
|||
|
||||
class PROPERTY_BASE
|
||||
{
|
||||
private:
|
||||
///> Used to generate unique IDs
|
||||
size_t nextId = 0;
|
||||
|
||||
public:
|
||||
PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = DEFAULT )
|
||||
: m_name( aName ), m_display( aDisplay ),
|
||||
: m_id( nextId ), m_name( aName ), m_display( aDisplay ),
|
||||
m_availFunc( [](INSPECTABLE*)->bool { return true; } )
|
||||
{
|
||||
++nextId;
|
||||
}
|
||||
|
||||
virtual ~PROPERTY_BASE()
|
||||
|
@ -191,6 +196,14 @@ public:
|
|||
return empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the possible values for for the property.
|
||||
*/
|
||||
virtual void SetChoices( const wxPGChoices& aChoices )
|
||||
{
|
||||
wxFAIL; // only possible for PROPERTY_ENUM
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this PROPERTY has a limited set of possible values.
|
||||
* @see PROPERTY_BASE::Choices()
|
||||
|
@ -217,15 +230,28 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the type-id of the Owner class.
|
||||
* Returns type-id of the Owner class.
|
||||
*/
|
||||
virtual size_t OwnerHash() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the type-id of the property type.
|
||||
* Returns type-id of the Base class.
|
||||
*/
|
||||
virtual size_t BaseHash() const = 0;
|
||||
|
||||
/**
|
||||
* Returns type-id of the property type.
|
||||
*/
|
||||
virtual size_t TypeHash() const = 0;
|
||||
|
||||
/**
|
||||
* Returns unique ID of the property.
|
||||
*/
|
||||
size_t Id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
virtual bool IsReadOnly() const = 0;
|
||||
|
||||
PROPERTY_DISPLAY GetDisplay() const
|
||||
|
@ -246,7 +272,7 @@ protected:
|
|||
{
|
||||
wxAny a = getter( aObject );
|
||||
|
||||
if ( !(std::is_enum<T>::value && a.CheckType<int>() ) && !a.CheckType<T>() )
|
||||
if ( !( std::is_enum<T>::value && a.CheckType<int>() ) && !a.CheckType<T>() )
|
||||
throw std::invalid_argument("Invalid requested type");
|
||||
|
||||
return wxANY_AS(a, T);
|
||||
|
@ -256,6 +282,7 @@ protected:
|
|||
virtual wxAny getter( void* aObject ) const = 0;
|
||||
|
||||
private:
|
||||
const size_t m_id;
|
||||
const wxString m_name;
|
||||
const PROPERTY_DISPLAY m_display;
|
||||
|
||||
|
@ -296,6 +323,11 @@ public:
|
|||
return m_ownerHash;
|
||||
}
|
||||
|
||||
size_t BaseHash() const override
|
||||
{
|
||||
return m_baseHash;
|
||||
}
|
||||
|
||||
size_t TypeHash() const override
|
||||
{
|
||||
return m_typeHash;
|
||||
|
@ -310,7 +342,8 @@ protected:
|
|||
PROPERTY( const wxString& aName, SETTER_BASE<Owner, T>* s, GETTER_BASE<Owner, T>* g,
|
||||
PROPERTY_DISPLAY aDisplay )
|
||||
: PROPERTY_BASE( aName, aDisplay ), m_setter( s ), m_getter( g ),
|
||||
m_ownerHash( TYPE_HASH( Owner ) ), m_typeHash( TYPE_HASH( BASE_TYPE ) )
|
||||
m_ownerHash( TYPE_HASH( Owner ) ), m_baseHash( TYPE_HASH( Base ) ),
|
||||
m_typeHash( TYPE_HASH( BASE_TYPE ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -344,6 +377,9 @@ protected:
|
|||
///> Owner class type-id
|
||||
const size_t m_ownerHash;
|
||||
|
||||
///> Base class type-id
|
||||
const size_t m_baseHash;
|
||||
|
||||
///> Property value type-id
|
||||
const size_t m_typeHash;
|
||||
};
|
||||
|
@ -357,22 +393,28 @@ public:
|
|||
PROPERTY_ENUM( const wxString& aName,
|
||||
void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(),
|
||||
PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT )
|
||||
: PROPERTY<Owner, T>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
|
||||
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ),
|
||||
m_choices( ENUM_MAP<T>::Instance().Choices() )
|
||||
: PROPERTY<Owner, T, Base>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
|
||||
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay )
|
||||
{
|
||||
wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" );
|
||||
if ( std::is_enum<T>::value )
|
||||
{
|
||||
m_choices = ENUM_MAP<T>::Instance().Choices();
|
||||
wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" );
|
||||
}
|
||||
}
|
||||
|
||||
template<typename SetType, typename GetType>
|
||||
PROPERTY_ENUM( const wxString& aName,
|
||||
void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const,
|
||||
PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT )
|
||||
: PROPERTY<Owner, T>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
|
||||
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ),
|
||||
m_choices( ENUM_MAP<T>::Instance().Choices() )
|
||||
: PROPERTY<Owner, T, Base>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
|
||||
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay )
|
||||
{
|
||||
wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" );
|
||||
if ( std::is_enum<T>::value )
|
||||
{
|
||||
m_choices = ENUM_MAP<T>::Instance().Choices();
|
||||
wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" );
|
||||
}
|
||||
}
|
||||
|
||||
virtual void setter( void* obj, wxAny& v ) override
|
||||
|
@ -408,6 +450,11 @@ public:
|
|||
return m_choices;
|
||||
}
|
||||
|
||||
void SetChoices( const wxPGChoices& aChoices ) override
|
||||
{
|
||||
m_choices = aChoices;
|
||||
}
|
||||
|
||||
bool HasChoices() const override
|
||||
{
|
||||
return m_choices.GetCount() > 0;
|
||||
|
@ -478,11 +525,6 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
m_choices.Clear();
|
||||
}
|
||||
|
||||
const wxString& ToString( T value ) const
|
||||
{
|
||||
return m_choices.GetLabel( static_cast<int>( value ) );
|
||||
|
@ -493,11 +535,6 @@ public:
|
|||
return m_choices;
|
||||
}
|
||||
|
||||
void SetChoices( const wxPGChoices& aChoices )
|
||||
{
|
||||
m_choices = aChoices;
|
||||
}
|
||||
|
||||
private:
|
||||
wxPGChoices m_choices;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <common.h>
|
||||
|
@ -38,6 +39,8 @@ using TYPE_ID = size_t;
|
|||
|
||||
using PROPERTY_LIST = std::vector<PROPERTY_BASE*>;
|
||||
|
||||
using PROPERTY_SET = std::set<std::pair<size_t, wxString>>;
|
||||
|
||||
/**
|
||||
* Provides class metadata. Each class handled by PROPERTY_MANAGER
|
||||
* needs to be described using AddProperty(), AddTypeCast() and InheritsAfter() methods.
|
||||
|
@ -121,6 +124,18 @@ public:
|
|||
*/
|
||||
void AddProperty( PROPERTY_BASE* aProperty );
|
||||
|
||||
/**
|
||||
* Replaces an existing property for a specific type.
|
||||
*
|
||||
* It is used to modify a property that has been inherited from a base class.
|
||||
* This method is used instead of AddProperty().
|
||||
*
|
||||
* @param aBase is the base class type the delivers the original property.
|
||||
* @param aName is the name of the replaced property.
|
||||
* @param aNew is the property replacing the inherited one.
|
||||
*/
|
||||
void ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew );
|
||||
|
||||
/**
|
||||
* Registers a type converter. Required prior TypeCast() usage.
|
||||
*
|
||||
|
@ -186,12 +201,15 @@ private:
|
|||
///> All properties (both unique to the type and inherited)
|
||||
std::vector<PROPERTY_BASE*> m_allProperties;
|
||||
|
||||
///> Replaced properties (TYPE_ID / name)
|
||||
PROPERTY_SET m_replaced;
|
||||
|
||||
///> Recreates the list of properties
|
||||
void rebuild();
|
||||
|
||||
///> Traverses the class inheritance hierarchy bottom-to-top, gathering
|
||||
///> all properties available to a type
|
||||
void collectPropsRecur( PROPERTY_LIST& aResult ) const;
|
||||
void collectPropsRecur( PROPERTY_LIST& aResult, PROPERTY_SET& aReplaced ) const;
|
||||
};
|
||||
|
||||
///> Returns metadata for a specific type
|
||||
|
|
|
@ -100,6 +100,10 @@ public:
|
|||
int m_cond;
|
||||
};
|
||||
|
||||
class E : public D
|
||||
{
|
||||
};
|
||||
|
||||
static struct ENUM_GLOB_DESC
|
||||
{
|
||||
ENUM_GLOB_DESC()
|
||||
|
@ -162,6 +166,9 @@ static struct CLASS_D_DESC
|
|||
propMgr.AddProperty( new PROPERTY_ENUM<D, D::enum_class>( "enumClass", &D::setClassEnum, &D::getClassEnum ) );
|
||||
propMgr.AddProperty( new PROPERTY<D, wxPoint, A>( "point_alias", &D::setPoint, &D::getPoint ) );
|
||||
|
||||
propMgr.ReplaceProperty( TYPE_HASH( C ), "bool",
|
||||
new PROPERTY<D, bool, C>( "replaced_bool", &D::setBool, &D::getBool ) );
|
||||
|
||||
// lines below are needed to indicate multiple inheritance
|
||||
propMgr.AddTypeCast( new TYPE_CAST<D, A> );
|
||||
propMgr.AddTypeCast( new TYPE_CAST<D, C> );
|
||||
|
@ -174,6 +181,26 @@ static struct CLASS_D_DESC
|
|||
}
|
||||
} _CLASS_D_DESC;
|
||||
|
||||
static struct CLASS_E_DESC
|
||||
{
|
||||
CLASS_E_DESC()
|
||||
{
|
||||
wxArrayInt values;
|
||||
values.Add( enum_glob::TEST1 );
|
||||
values.Add( enum_glob::TEST3 );
|
||||
wxArrayString labels;
|
||||
labels.Add( "T1" );
|
||||
labels.Add( "T3" );
|
||||
wxPGChoices newChoices( labels, values );
|
||||
|
||||
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||
auto prop = new PROPERTY_ENUM<E, enum_glob, D>( "enumGlob",
|
||||
&E::setGlobEnum, &E::getGlobEnum );
|
||||
prop->SetChoices( newChoices );
|
||||
propMgr.ReplaceProperty( TYPE_HASH( D ), "enumGlob", prop );
|
||||
}
|
||||
} _CLASS_E_DESC;
|
||||
|
||||
ENUM_TO_WXANY( D::enum_class );
|
||||
|
||||
|
||||
|
@ -229,6 +256,7 @@ BOOST_AUTO_TEST_CASE( VirtualMethods )
|
|||
// Non-existing properties
|
||||
BOOST_AUTO_TEST_CASE( NotexistingProperties )
|
||||
{
|
||||
ptr = &d;
|
||||
BOOST_CHECK_EQUAL( ptr->Set<int>( "does not exist", 5 ), false );
|
||||
BOOST_CHECK_EQUAL( ptr->Get<int>( "neither" ).has_value(), false );
|
||||
}
|
||||
|
@ -236,7 +264,8 @@ BOOST_AUTO_TEST_CASE( NotexistingProperties )
|
|||
// Request data using incorrect type
|
||||
BOOST_AUTO_TEST_CASE( IncorrectType )
|
||||
{
|
||||
BOOST_CHECK_THROW( ptr->Get<wxPoint>( "bool" ), std::invalid_argument );
|
||||
ptr = &d;
|
||||
BOOST_CHECK_THROW( ptr->Get<wxPoint>( "A" ), std::invalid_argument );
|
||||
}
|
||||
|
||||
// Type-casting (for types with multiple inheritance)
|
||||
|
@ -256,9 +285,9 @@ BOOST_AUTO_TEST_CASE( EnumGlob )
|
|||
BOOST_CHECK( prop->HasChoices() );
|
||||
|
||||
wxArrayInt values;
|
||||
values.Add( static_cast<int>( enum_glob::TEST1 ) );
|
||||
values.Add( static_cast<int>( enum_glob::TEST2 ) );
|
||||
values.Add( static_cast<int>( enum_glob::TEST3 ) );
|
||||
values.Add( enum_glob::TEST1 );
|
||||
values.Add( enum_glob::TEST2 );
|
||||
values.Add( enum_glob::TEST3 );
|
||||
wxArrayString labels;
|
||||
labels.Add( "TEST1" );
|
||||
labels.Add( "TEST2" );
|
||||
|
@ -285,9 +314,9 @@ BOOST_AUTO_TEST_CASE( EnumClass )
|
|||
BOOST_CHECK( prop->HasChoices() );
|
||||
|
||||
wxArrayInt values;
|
||||
values.Add( static_cast<int>( D::enum_class::TESTA ) );
|
||||
values.Add( static_cast<int>( D::enum_class::TESTB ) );
|
||||
values.Add( static_cast<int>( D::enum_class::TESTC ) );
|
||||
values.Add( D::enum_class::TESTA );
|
||||
values.Add( D::enum_class::TESTB );
|
||||
values.Add( D::enum_class::TESTC );
|
||||
wxArrayString labels;
|
||||
labels.Add( "TESTA" );
|
||||
labels.Add( "TESTB" );
|
||||
|
@ -322,7 +351,7 @@ BOOST_AUTO_TEST_CASE( Availability )
|
|||
BOOST_CHECK( propCond->Available( ptr ) );
|
||||
}
|
||||
|
||||
// Using a different name for a parent propety
|
||||
// Using a different name for a parent property
|
||||
BOOST_AUTO_TEST_CASE( Alias )
|
||||
{
|
||||
ptr = &d;
|
||||
|
@ -336,4 +365,44 @@ BOOST_AUTO_TEST_CASE( Alias )
|
|||
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 300, 300 ) );
|
||||
}
|
||||
|
||||
// Property renaming
|
||||
BOOST_AUTO_TEST_CASE( Rename )
|
||||
{
|
||||
PROPERTY_BASE* prop;
|
||||
|
||||
prop = propMgr.GetProperty( TYPE_HASH( D ), "bool" );
|
||||
BOOST_CHECK_EQUAL( prop, nullptr );
|
||||
|
||||
prop = propMgr.GetProperty( TYPE_HASH( D ), "replaced_bool" );
|
||||
BOOST_CHECK( prop );
|
||||
}
|
||||
|
||||
// Different subset of enum values for a property
|
||||
BOOST_AUTO_TEST_CASE( AlternativeEnum )
|
||||
{
|
||||
PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( E ), "enumGlob" );
|
||||
BOOST_CHECK( prop->HasChoices() );
|
||||
|
||||
wxArrayInt values;
|
||||
values.Add( enum_glob::TEST1 );
|
||||
values.Add( enum_glob::TEST3 );
|
||||
wxArrayString labels;
|
||||
labels.Add( "T1" );
|
||||
labels.Add( "T3" );
|
||||
|
||||
const wxPGChoices& v = prop->Choices();
|
||||
BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
|
||||
BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
|
||||
|
||||
for (int i = 0; i < values.GetCount(); ++i )
|
||||
{
|
||||
BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
|
||||
}
|
||||
|
||||
for (int i = 0; i < labels.GetCount(); ++i )
|
||||
{
|
||||
BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
Loading…
Reference in New Issue