1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 CERN
5  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
6  * @author Maciej Suminski <maciej.suminski@cern.ch>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "property_mgr.h"
23 #include "property.h"
24 
25 #include <algorithm>
26 #include <utility>
27 
28 static wxString EMPTY_STRING( wxEmptyString );
29 
30 
RegisterType(TYPE_ID aType,const wxString & aName)31 void PROPERTY_MANAGER::RegisterType( TYPE_ID aType, const wxString& aName )
32 {
33     wxASSERT( m_classNames.count( aType ) == 0 );
34     m_classNames.emplace( aType, aName );
35 }
36 
37 
ResolveType(TYPE_ID aType) const38 const wxString& PROPERTY_MANAGER::ResolveType( TYPE_ID aType ) const
39 {
40     auto it = m_classNames.find( aType );
41     return it != m_classNames.end() ? it->second : EMPTY_STRING;
42 }
43 
44 
GetProperty(TYPE_ID aType,const wxString & aProperty) const45 PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aProperty ) const
46 {
47     if( m_dirty )
48         const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
49 
50     auto it = m_classes.find( aType );
51 
52     if( it == m_classes.end() )
53         return nullptr;
54 
55     const CLASS_DESC& classDesc = it->second;
56 
57     for( PROPERTY_BASE* property : classDesc.m_allProperties )
58     {
59         if( !aProperty.CmpNoCase( property->Name() ) )
60             return property;
61     }
62 
63     return nullptr;
64 }
65 
66 
GetProperties(TYPE_ID aType) const67 const PROPERTY_LIST& PROPERTY_MANAGER::GetProperties( TYPE_ID aType ) const
68 {
69     if( m_dirty )
70         const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
71 
72     static const PROPERTY_LIST empty;
73     auto it = m_classes.find( aType );
74 
75     if( it == m_classes.end() )
76         return empty;
77 
78     return it->second.m_allProperties;
79 }
80 
81 
TypeCast(const void * aSource,TYPE_ID aBase,TYPE_ID aTarget) const82 const void* PROPERTY_MANAGER::TypeCast( const void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const
83 {
84     if( aBase == aTarget )
85         return aSource;
86 
87     auto classDesc = m_classes.find( aBase );
88 
89     if( classDesc == m_classes.end() )
90         return aSource;
91 
92     auto& converters = classDesc->second.m_typeCasts;
93     auto converter = converters.find( aTarget );
94 
95     if( converter == converters.end() )     // explicit type cast not found
96         return IsOfType( aBase, aTarget ) ? aSource : nullptr;
97 
98     return (*converter->second)( aSource );
99 }
100 
101 
AddProperty(PROPERTY_BASE * aProperty)102 void PROPERTY_MANAGER::AddProperty( PROPERTY_BASE* aProperty )
103 {
104     const wxString& name = aProperty->Name();
105     TYPE_ID hash = aProperty->OwnerHash();
106     CLASS_DESC& classDesc = getClass( hash );
107     classDesc.m_ownProperties.emplace( name, aProperty );
108     m_dirty = true;
109 }
110 
111 
ReplaceProperty(size_t aBase,const wxString & aName,PROPERTY_BASE * aNew)112 void PROPERTY_MANAGER::ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew )
113 {
114     wxASSERT( aBase == aNew->BaseHash() );
115     CLASS_DESC& classDesc = getClass( aNew->OwnerHash() );
116     classDesc.m_replaced.insert( std::make_pair( aBase, aName ) );
117     AddProperty( aNew );
118 }
119 
120 
AddTypeCast(TYPE_CAST_BASE * aCast)121 void PROPERTY_MANAGER::AddTypeCast( TYPE_CAST_BASE* aCast )
122 {
123     TYPE_ID derivedHash = aCast->DerivedHash();
124     CLASS_DESC& classDesc = getClass( aCast->BaseHash() );
125     auto& typeCasts = classDesc.m_typeCasts;
126     wxASSERT_MSG( typeCasts.count( derivedHash ) == 0, "Such converter already exists" );
127     typeCasts.emplace( derivedHash, aCast );
128 }
129 
130 
InheritsAfter(TYPE_ID aDerived,TYPE_ID aBase)131 void PROPERTY_MANAGER::InheritsAfter( TYPE_ID aDerived, TYPE_ID aBase )
132 {
133     wxASSERT_MSG( aDerived != aBase, "Class cannot inherit from itself" );
134 
135     CLASS_DESC& derived = getClass( aDerived );
136     CLASS_DESC& base = getClass( aBase );
137     derived.m_bases.push_back( base );
138     m_dirty = true;
139 
140     wxASSERT_MSG( derived.m_bases.size() == 1 || derived.m_typeCasts.count( aBase ) == 1,
141                   "You need to add a TYPE_CAST for classes inheriting from multiple bases" );
142 }
143 
144 
IsOfType(TYPE_ID aDerived,TYPE_ID aBase) const145 bool PROPERTY_MANAGER::IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const
146 {
147     if( aDerived == aBase )
148         return true;
149 
150     auto derived = m_classes.find( aDerived );
151     wxCHECK( derived != m_classes.end(), false );   // missing class description
152 
153     // traverse the hierarchy seeking for the base class
154     for( auto& base : derived->second.m_bases )
155     {
156         if( IsOfType( base.get().m_id, aBase ) )
157             return true;
158     }
159 
160     return false;
161 }
162 
163 
Rebuild()164 void PROPERTY_MANAGER::Rebuild()
165 {
166     for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
167         classEntry.second.rebuild();
168 
169     m_dirty = false;
170 }
171 
172 
getClass(TYPE_ID aTypeId)173 PROPERTY_MANAGER::CLASS_DESC& PROPERTY_MANAGER::getClass( TYPE_ID aTypeId )
174 {
175     auto it = m_classes.find( aTypeId );
176 
177     if( it == m_classes.end() )
178         tie( it, std::ignore ) = m_classes.emplace( aTypeId, CLASS_DESC( aTypeId ) );
179 
180     return it->second;
181 }
182 
183 
rebuild()184 void PROPERTY_MANAGER::CLASS_DESC::rebuild()
185 {
186     PROPERTY_SET replaced( m_replaced );
187     m_allProperties.clear();
188     collectPropsRecur( m_allProperties, replaced );
189     // We need to keep properties sorted to be able to use std::set_* functions
190     sort( m_allProperties.begin(), m_allProperties.end() );
191 }
192 
193 
collectPropsRecur(PROPERTY_LIST & aResult,PROPERTY_SET & aReplaced) const194 void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( PROPERTY_LIST& aResult,
195                                                       PROPERTY_SET& aReplaced ) const
196 {
197     for( const std::pair<size_t, wxString>& replacedEntry : m_replaced )
198         aReplaced.emplace( replacedEntry );
199 
200     for( const std::pair<const wxString, std::unique_ptr<PROPERTY_BASE>>& prop : m_ownProperties )
201     {
202         PROPERTY_BASE* property = prop.second.get();
203 
204         // Do not store replaced properties
205         if( aReplaced.count( std::make_pair( property->OwnerHash(), property->Name() ) ) == 0 )
206             aResult.push_back( property );
207     }
208 
209     for( const std::reference_wrapper<CLASS_DESC>& base : m_bases )
210         base.get().collectPropsRecur( aResult, aReplaced );
211 }
212 
213 
GetMatchingClasses(PROPERTY_BASE * aProperty)214 std::vector<TYPE_ID> PROPERTY_MANAGER::GetMatchingClasses( PROPERTY_BASE* aProperty )
215 {
216     std::vector<TYPE_ID> ids;
217 
218 /*
219     for( auto& cls : m_classes )
220     {
221         CLASS_INFO info;
222 
223         for( auto prop : cls.second.m_allProperties )
224             info.properties.push_back(prop);
225 
226 
227     }
228  */
229 
230     return ids;
231 }
232 
233 
GetAllClasses()234 PROPERTY_MANAGER::CLASSES_INFO PROPERTY_MANAGER::GetAllClasses()
235 {
236     CLASSES_INFO rv;
237 
238     for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
239     {
240         CLASS_INFO info;
241 
242         info.type = classEntry.first;
243         info.name = m_classNames[classEntry.first];
244 
245         for( PROPERTY_BASE* prop : classEntry.second.m_allProperties )
246             info.properties.push_back( prop );
247 
248         rv.push_back( info );
249     }
250 
251     return rv;
252 }
253