Commit f084a866 authored by Maciej Sumiński's avatar Maciej Sumiński 👽 Committed by Tomasz Wlostowski
Browse files

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)
parent a988cab8
......@@ -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()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment