/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 CERN * @author Tomasz Wlostowski * @author Maciej Suminski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "property_mgr.h" #include "property.h" #include #include static wxString EMPTY_STRING( wxEmptyString ); void PROPERTY_MANAGER::RegisterType( TYPE_ID aType, const wxString& aName ) { wxASSERT( m_classNames.count( aType ) == 0 ); m_classNames.emplace( aType, aName ); } const wxString& PROPERTY_MANAGER::ResolveType( TYPE_ID aType ) const { auto it = m_classNames.find( aType ); return it != m_classNames.end() ? it->second : EMPTY_STRING; } PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aProperty ) const { if( m_dirty ) const_cast( this )->Rebuild(); auto it = m_classes.find( aType ); if( it == m_classes.end() ) return nullptr; const CLASS_DESC& classDesc = it->second; for( PROPERTY_BASE* property : classDesc.m_allProperties ) { if( !aProperty.CmpNoCase( property->Name() ) ) return property; } return nullptr; } const PROPERTY_LIST& PROPERTY_MANAGER::GetProperties( TYPE_ID aType ) const { if( m_dirty ) const_cast( this )->Rebuild(); static const PROPERTY_LIST empty; auto it = m_classes.find( aType ); if( it == m_classes.end() ) return empty; return it->second.m_allProperties; } const void* PROPERTY_MANAGER::TypeCast( const void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const { if( aBase == aTarget ) return aSource; auto classDesc = m_classes.find( aBase ); if( classDesc == m_classes.end() ) return aSource; auto& converters = classDesc->second.m_typeCasts; auto converter = converters.find( aTarget ); if( converter == converters.end() ) // explicit type cast not found return IsOfType( aBase, aTarget ) ? aSource : nullptr; return (*converter->second)( aSource ); } void PROPERTY_MANAGER::AddProperty( PROPERTY_BASE* aProperty ) { const wxString& name = aProperty->Name(); TYPE_ID hash = aProperty->OwnerHash(); CLASS_DESC& classDesc = getClass( hash ); classDesc.m_ownProperties.emplace( name, aProperty ); m_dirty = true; } 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(); CLASS_DESC& classDesc = getClass( aCast->BaseHash() ); auto& typeCasts = classDesc.m_typeCasts; wxASSERT_MSG( typeCasts.count( derivedHash ) == 0, wxT( "Such converter already exists" ) ); typeCasts.emplace( derivedHash, aCast ); } void PROPERTY_MANAGER::InheritsAfter( TYPE_ID aDerived, TYPE_ID aBase ) { wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot inherit from itself" ) ); CLASS_DESC& derived = getClass( aDerived ); CLASS_DESC& base = getClass( aBase ); derived.m_bases.push_back( base ); m_dirty = true; wxASSERT_MSG( derived.m_bases.size() == 1 || derived.m_typeCasts.count( aBase ) == 1, wxT( "You need to add a TYPE_CAST for classes inheriting from multiple bases" ) ); } bool PROPERTY_MANAGER::IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const { if( aDerived == aBase ) return true; auto derived = m_classes.find( aDerived ); wxCHECK( derived != m_classes.end(), false ); // missing class description // traverse the hierarchy seeking for the base class for( auto& base : derived->second.m_bases ) { if( IsOfType( base.get().m_id, aBase ) ) return true; } return false; } void PROPERTY_MANAGER::Rebuild() { for( std::pair& classEntry : m_classes ) classEntry.second.rebuild(); m_dirty = false; } 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( aTypeId, CLASS_DESC( aTypeId ) ); return it->second; } void PROPERTY_MANAGER::CLASS_DESC::rebuild() { PROPERTY_SET replaced( m_replaced ); m_allProperties.clear(); 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, PROPERTY_SET& aReplaced ) const { for( const std::pair& replacedEntry : m_replaced ) aReplaced.emplace( replacedEntry ); for( const std::pair>& prop : m_ownProperties ) { PROPERTY_BASE* property = prop.second.get(); // Do not store replaced properties if( aReplaced.count( std::make_pair( property->OwnerHash(), property->Name() ) ) == 0 ) aResult.push_back( property ); } for( const std::reference_wrapper& base : m_bases ) base.get().collectPropsRecur( aResult, aReplaced ); } std::vector PROPERTY_MANAGER::GetMatchingClasses( PROPERTY_BASE* aProperty ) { std::vector ids; /* for( auto& cls : m_classes ) { CLASS_INFO info; for( auto prop : cls.second.m_allProperties ) info.properties.push_back(prop); } */ return ids; } PROPERTY_MANAGER::CLASSES_INFO PROPERTY_MANAGER::GetAllClasses() { CLASSES_INFO rv; for( std::pair& classEntry : m_classes ) { CLASS_INFO info; info.type = classEntry.first; info.name = m_classNames[classEntry.first]; for( PROPERTY_BASE* prop : classEntry.second.m_allProperties ) info.properties.push_back( prop ); rv.push_back( info ); } return rv; }