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:
Maciej Suminski 2020-02-05 15:59:45 +01:00 committed by Tomasz Wlostowski
parent a988cab84e
commit f084a86601
4 changed files with 183 additions and 39 deletions

View File

@ -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(), " wxASSERT_MSG( !m_dirty, "Have not called PROPERTY_MANAGER::Rebuild(), "
"property list not up-to-date" ); "property list not up-to-date" );
auto it = m_classes.find( aType ); auto it = m_classes.find( aType );
if( it == m_classes.end() ) 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 ) void PROPERTY_MANAGER::AddTypeCast( TYPE_CAST_BASE* aCast )
{ {
TYPE_ID derivedHash = aCast->DerivedHash(); 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 ); auto it = m_classes.find( aTypeId );
if( it == m_classes.end() ) 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; return it->second;
} }
@ -177,18 +187,28 @@ PROPERTY_MANAGER::CLASS_DESC& PROPERTY_MANAGER::getClass( TYPE_ID aTypeId )
void PROPERTY_MANAGER::CLASS_DESC::rebuild() void PROPERTY_MANAGER::CLASS_DESC::rebuild()
{ {
PROPERTY_SET replaced( m_replaced );
m_allProperties.clear(); m_allProperties.clear();
collectPropsRecur( m_allProperties ); collectPropsRecur( m_allProperties, replaced );
// We need to keep properties sorted to be able to use std::set_* functions // We need to keep properties sorted to be able to use std::set_* functions
sort( m_allProperties.begin(), m_allProperties.end() ); 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 ) for( const auto& replacedEntry : m_replaced )
base.get().collectPropsRecur( aResult ); aReplaced.emplace( replacedEntry );
for( auto& property : m_ownProperties ) for( auto& propertyData : m_ownProperties )
aResult.push_back( property.second.get() ); {
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 );
} }

View File

@ -165,11 +165,16 @@ public:
class PROPERTY_BASE class PROPERTY_BASE
{ {
private:
///> Used to generate unique IDs
size_t nextId = 0;
public: public:
PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = DEFAULT ) 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; } ) m_availFunc( [](INSPECTABLE*)->bool { return true; } )
{ {
++nextId;
} }
virtual ~PROPERTY_BASE() virtual ~PROPERTY_BASE()
@ -191,6 +196,14 @@ public:
return empty; 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. * Returns true if this PROPERTY has a limited set of possible values.
* @see PROPERTY_BASE::Choices() * @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; 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; virtual size_t TypeHash() const = 0;
/**
* Returns unique ID of the property.
*/
size_t Id() const
{
return m_id;
}
virtual bool IsReadOnly() const = 0; virtual bool IsReadOnly() const = 0;
PROPERTY_DISPLAY GetDisplay() const PROPERTY_DISPLAY GetDisplay() const
@ -246,7 +272,7 @@ protected:
{ {
wxAny a = getter( aObject ); 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"); throw std::invalid_argument("Invalid requested type");
return wxANY_AS(a, T); return wxANY_AS(a, T);
@ -256,6 +282,7 @@ protected:
virtual wxAny getter( void* aObject ) const = 0; virtual wxAny getter( void* aObject ) const = 0;
private: private:
const size_t m_id;
const wxString m_name; const wxString m_name;
const PROPERTY_DISPLAY m_display; const PROPERTY_DISPLAY m_display;
@ -296,6 +323,11 @@ public:
return m_ownerHash; return m_ownerHash;
} }
size_t BaseHash() const override
{
return m_baseHash;
}
size_t TypeHash() const override size_t TypeHash() const override
{ {
return m_typeHash; return m_typeHash;
@ -310,7 +342,8 @@ protected:
PROPERTY( const wxString& aName, SETTER_BASE<Owner, T>* s, GETTER_BASE<Owner, T>* g, PROPERTY( const wxString& aName, SETTER_BASE<Owner, T>* s, GETTER_BASE<Owner, T>* g,
PROPERTY_DISPLAY aDisplay ) PROPERTY_DISPLAY aDisplay )
: PROPERTY_BASE( aName, aDisplay ), m_setter( s ), m_getter( g ), : 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 ///> Owner class type-id
const size_t m_ownerHash; const size_t m_ownerHash;
///> Base class type-id
const size_t m_baseHash;
///> Property value type-id ///> Property value type-id
const size_t m_typeHash; const size_t m_typeHash;
}; };
@ -357,22 +393,28 @@ public:
PROPERTY_ENUM( const wxString& aName, PROPERTY_ENUM( const wxString& aName,
void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(), void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(),
PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT ) PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT )
: PROPERTY<Owner, T>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ), : PROPERTY<Owner, T, Base>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ), METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay )
m_choices( ENUM_MAP<T>::Instance().Choices() )
{ {
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> template<typename SetType, typename GetType>
PROPERTY_ENUM( const wxString& aName, PROPERTY_ENUM( const wxString& aName,
void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const,
PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT ) PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT )
: PROPERTY<Owner, T>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ), : PROPERTY<Owner, T, Base>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ), METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay )
m_choices( ENUM_MAP<T>::Instance().Choices() )
{ {
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 virtual void setter( void* obj, wxAny& v ) override
@ -408,6 +450,11 @@ public:
return m_choices; return m_choices;
} }
void SetChoices( const wxPGChoices& aChoices ) override
{
m_choices = aChoices;
}
bool HasChoices() const override bool HasChoices() const override
{ {
return m_choices.GetCount() > 0; return m_choices.GetCount() > 0;
@ -478,11 +525,6 @@ public:
return *this; return *this;
} }
void Reset()
{
m_choices.Clear();
}
const wxString& ToString( T value ) const const wxString& ToString( T value ) const
{ {
return m_choices.GetLabel( static_cast<int>( value ) ); return m_choices.GetLabel( static_cast<int>( value ) );
@ -493,11 +535,6 @@ public:
return m_choices; return m_choices;
} }
void SetChoices( const wxPGChoices& aChoices )
{
m_choices = aChoices;
}
private: private:
wxPGChoices m_choices; wxPGChoices m_choices;

View File

@ -26,6 +26,7 @@
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <set>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <common.h> #include <common.h>
@ -38,6 +39,8 @@ using TYPE_ID = size_t;
using PROPERTY_LIST = std::vector<PROPERTY_BASE*>; 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 * Provides class metadata. Each class handled by PROPERTY_MANAGER
* needs to be described using AddProperty(), AddTypeCast() and InheritsAfter() methods. * needs to be described using AddProperty(), AddTypeCast() and InheritsAfter() methods.
@ -121,6 +124,18 @@ public:
*/ */
void AddProperty( PROPERTY_BASE* aProperty ); 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. * Registers a type converter. Required prior TypeCast() usage.
* *
@ -186,12 +201,15 @@ private:
///> All properties (both unique to the type and inherited) ///> All properties (both unique to the type and inherited)
std::vector<PROPERTY_BASE*> m_allProperties; std::vector<PROPERTY_BASE*> m_allProperties;
///> Replaced properties (TYPE_ID / name)
PROPERTY_SET m_replaced;
///> Recreates the list of properties ///> Recreates the list of properties
void rebuild(); void rebuild();
///> Traverses the class inheritance hierarchy bottom-to-top, gathering ///> Traverses the class inheritance hierarchy bottom-to-top, gathering
///> all properties available to a type ///> 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 ///> Returns metadata for a specific type

View File

@ -100,6 +100,10 @@ public:
int m_cond; int m_cond;
}; };
class E : public D
{
};
static struct ENUM_GLOB_DESC static struct ENUM_GLOB_DESC
{ {
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_ENUM<D, D::enum_class>( "enumClass", &D::setClassEnum, &D::getClassEnum ) );
propMgr.AddProperty( new PROPERTY<D, wxPoint, A>( "point_alias", &D::setPoint, &D::getPoint ) ); 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 // lines below are needed to indicate multiple inheritance
propMgr.AddTypeCast( new TYPE_CAST<D, A> ); propMgr.AddTypeCast( new TYPE_CAST<D, A> );
propMgr.AddTypeCast( new TYPE_CAST<D, C> ); propMgr.AddTypeCast( new TYPE_CAST<D, C> );
@ -174,6 +181,26 @@ static struct CLASS_D_DESC
} }
} _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 ); ENUM_TO_WXANY( D::enum_class );
@ -229,6 +256,7 @@ BOOST_AUTO_TEST_CASE( VirtualMethods )
// Non-existing properties // Non-existing properties
BOOST_AUTO_TEST_CASE( NotexistingProperties ) BOOST_AUTO_TEST_CASE( NotexistingProperties )
{ {
ptr = &d;
BOOST_CHECK_EQUAL( ptr->Set<int>( "does not exist", 5 ), false ); BOOST_CHECK_EQUAL( ptr->Set<int>( "does not exist", 5 ), false );
BOOST_CHECK_EQUAL( ptr->Get<int>( "neither" ).has_value(), 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 // Request data using incorrect type
BOOST_AUTO_TEST_CASE( IncorrectType ) 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) // Type-casting (for types with multiple inheritance)
@ -256,9 +285,9 @@ BOOST_AUTO_TEST_CASE( EnumGlob )
BOOST_CHECK( prop->HasChoices() ); BOOST_CHECK( prop->HasChoices() );
wxArrayInt values; wxArrayInt values;
values.Add( static_cast<int>( enum_glob::TEST1 ) ); values.Add( enum_glob::TEST1 );
values.Add( static_cast<int>( enum_glob::TEST2 ) ); values.Add( enum_glob::TEST2 );
values.Add( static_cast<int>( enum_glob::TEST3 ) ); values.Add( enum_glob::TEST3 );
wxArrayString labels; wxArrayString labels;
labels.Add( "TEST1" ); labels.Add( "TEST1" );
labels.Add( "TEST2" ); labels.Add( "TEST2" );
@ -285,9 +314,9 @@ BOOST_AUTO_TEST_CASE( EnumClass )
BOOST_CHECK( prop->HasChoices() ); BOOST_CHECK( prop->HasChoices() );
wxArrayInt values; wxArrayInt values;
values.Add( static_cast<int>( D::enum_class::TESTA ) ); values.Add( D::enum_class::TESTA );
values.Add( static_cast<int>( D::enum_class::TESTB ) ); values.Add( D::enum_class::TESTB );
values.Add( static_cast<int>( D::enum_class::TESTC ) ); values.Add( D::enum_class::TESTC );
wxArrayString labels; wxArrayString labels;
labels.Add( "TESTA" ); labels.Add( "TESTA" );
labels.Add( "TESTB" ); labels.Add( "TESTB" );
@ -322,7 +351,7 @@ BOOST_AUTO_TEST_CASE( Availability )
BOOST_CHECK( propCond->Available( ptr ) ); 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 ) BOOST_AUTO_TEST_CASE( Alias )
{ {
ptr = &d; ptr = &d;
@ -336,4 +365,44 @@ BOOST_AUTO_TEST_CASE( Alias )
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 300, 300 ) ); 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() BOOST_AUTO_TEST_SUITE_END()