KiCad PCB EDA Suite
Loading...
Searching...
No Matches
property_diff.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#ifndef KICAD_DIFF_PROPERTY_DIFF_H
25#define KICAD_DIFF_PROPERTY_DIFF_H
26
30
31#include <properties/property.h>
34#include <inspectable.h>
35#include <trace_helpers.h>
36
37#include <wx/log.h>
38
39#include <algorithm>
40#include <cstddef>
41#include <typeinfo>
42#include <vector>
43
44
45namespace KICAD_DIFF
46{
47
64inline std::vector<PROPERTY_DELTA> DiffItemProperties( const INSPECTABLE* aBefore,
65 const INSPECTABLE* aAfter )
66{
67 std::vector<PROPERTY_DELTA> deltas;
68
69 if( !aBefore || !aAfter || typeid( *aBefore ) != typeid( *aAfter ) )
70 return deltas;
71
73 std::vector<PROPERTY_BASE*> props = pm.GetProperties( aBefore );
74
75 for( PROPERTY_BASE* dynAfter : aAfter->GetDynamicProperties() )
76 {
77 if( std::ranges::none_of( props,
78 [&]( PROPERTY_BASE* p )
79 {
80 return p->Name() == dynAfter->Name();
81 } ) )
82 {
83 props.push_back( dynAfter );
84 }
85 }
86
87 // IsAvailableFor and Get take a non-const INSPECTABLE*; we treat both items
88 // as read-only inspections.
89 INSPECTABLE* mutBefore = const_cast<INSPECTABLE*>( aBefore );
90 INSPECTABLE* mutAfter = const_cast<INSPECTABLE*>( aAfter );
91
92 for( PROPERTY_BASE* prop : props )
93 {
94 if( !prop || prop->IsHiddenFromPropertiesManager() )
95 continue;
96
97 // Skip properties that aren't applicable to this particular item state
98 // (e.g. context-dependent visibility).
99 if( !pm.IsAvailableFor( TYPE_HASH( *aBefore ), prop, mutBefore ) )
100 continue;
101
102 if( !pm.IsAvailableFor( TYPE_HASH( *aAfter ), prop, mutAfter ) )
103 continue;
104
105 wxAny beforeVal = mutBefore->Get( prop );
106 wxAny afterVal = mutAfter->Get( prop );
107
108 if( KiWxAnyEquals( beforeVal, afterVal, prop ) )
109 continue;
110
111 DIFF_VALUE beforeDV = WxAnyToDiffValue( beforeVal, prop );
112 DIFF_VALUE afterDV = WxAnyToDiffValue( afterVal, prop );
113
114 // Skip if conversion failed for both sides (unsupported type) — we can't
115 // produce a meaningful delta. If only one side converted, we still emit
116 // because the user benefits from knowing "something changed".
117 if( beforeDV.GetType() == DIFF_VALUE::T::NONE && afterDV.GetType() == DIFF_VALUE::T::NONE )
118 continue;
119
121 d.name = prop->Name();
122 d.before = beforeDV;
123 d.after = afterDV;
124 deltas.push_back( std::move( d ) );
125 }
126
127 // Deterministic order: alphabetical by property name.
128 std::sort( deltas.begin(), deltas.end(),
129 []( const PROPERTY_DELTA& aL, const PROPERTY_DELTA& aR )
130 {
131 return aL.name < aR.name;
132 } );
133
134 return deltas;
135}
136
137
143inline std::vector<PROPERTY_DELTA> ItemProperties( const INSPECTABLE* aItem, bool aAsAfter )
144{
145 std::vector<PROPERTY_DELTA> deltas;
146
147 if( !aItem )
148 return deltas;
149
151 std::vector<PROPERTY_BASE*> props = pm.GetProperties( aItem );
152
153 INSPECTABLE* mutItem = const_cast<INSPECTABLE*>( aItem );
154
155 for( PROPERTY_BASE* prop : props )
156 {
157 if( !prop || prop->IsHiddenFromPropertiesManager() )
158 continue;
159
160 if( !pm.IsAvailableFor( TYPE_HASH( *aItem ), prop, mutItem ) )
161 continue;
162
163 DIFF_VALUE dv = WxAnyToDiffValue( mutItem->Get( prop ), prop );
164
165 if( dv.GetType() == DIFF_VALUE::T::NONE )
166 continue;
167
168 // An unset string property (e.g. an unassigned Component Class) carries
169 // no information for an added or removed item, so skip it.
170 if( dv.GetType() == DIFF_VALUE::T::STRING && dv.AsString().IsEmpty() )
171 continue;
172
174 d.name = prop->Name();
175
176 if( aAsAfter )
177 d.after = dv;
178 else
179 d.before = dv;
180
181 deltas.push_back( std::move( d ) );
182 }
183
184 std::sort( deltas.begin(), deltas.end(),
185 []( const PROPERTY_DELTA& aL, const PROPERTY_DELTA& aR )
186 {
187 return aL.name < aR.name;
188 } );
189
190 return deltas;
191}
192
193
196{
197 std::size_t applied = 0;
198 std::size_t failed = 0;
199};
200
201
215 INSPECTABLE* aTarget, const std::vector<PROPERTY_RESOLUTION>& aProps,
216 const INSPECTABLE* aOurs, const INSPECTABLE* aTheirs, const INSPECTABLE* aAncestor )
217{
219
220 if( !aTarget || aProps.empty() )
221 return counts;
222
224
225 for( const PROPERTY_RESOLUTION& res : aProps )
226 {
227 PROPERTY_BASE* prop = pm.GetProperty( aTarget, res.name );
228
229 if( !prop )
230 {
231 wxLogTrace( traceDiffMerge, wxT( "applier: property '%s' not found on target" ),
232 res.name );
233 ++counts.failed;
234 continue;
235 }
236
237 if( !prop->Writeable( aTarget ) )
238 {
239 wxLogTrace( traceDiffMerge, wxT( "applier: property '%s' is read-only on target" ),
240 res.name );
241 ++counts.failed;
242 continue;
243 }
244
245 wxAny value;
246
247 if( res.kind == PROP_RES::CUSTOM )
248 {
249 // Custom values come from the resolution payload itself — the UI
250 // flow stores the user-edited value in
251 // PROPERTY_RESOLUTION.customValue. Convert DIFF_VALUE back into a
252 // wxAny the property can consume; a T::NONE payload carries nothing
253 // and counts as failed.
254 if( !DiffValueToWxAny( res.customValue, value ) )
255 {
256 ++counts.failed;
257 continue;
258 }
259 }
260 else
261 {
262 const INSPECTABLE* source = nullptr;
263
264 switch( res.kind )
265 {
266 case PROP_RES::OURS: source = aOurs; break;
267 case PROP_RES::THEIRS: source = aTheirs; break;
268 case PROP_RES::ANCESTOR: source = aAncestor; break;
269 case PROP_RES::CUSTOM: break;
270 }
271
272 if( !source )
273 {
274 ++counts.failed;
275 continue;
276 }
277
278 value = const_cast<INSPECTABLE*>( source )->Get( prop );
279 }
280
281 if( !aTarget->Set( prop, value ) )
282 {
283 wxLogTrace( traceDiffMerge, wxT( "applier: Set failed for property '%s'" ),
284 res.name );
285 ++counts.failed;
286 continue;
287 }
288
289 ++counts.applied;
290 }
291
292 return counts;
293}
294
295} // namespace KICAD_DIFF
296
297#endif // KICAD_DIFF_PROPERTY_DIFF_H
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
wxAny Get(PROPERTY_BASE *aProperty) const
bool Set(PROPERTY_BASE *aProperty, wxAny &aValue, bool aNotify=true)
virtual std::vector< PROPERTY_BASE * > GetDynamicProperties() const
Return dynamically-computed properties specific to this object instance (e.g.
Definition inspectable.h:53
A typed sum value used to carry the before/after of any single property.
virtual bool Writeable(INSPECTABLE *aObject) const
Definition property.h:288
const wxString & Name() const
Definition property.h:221
Provide class metadata.Helper macro to map type hashes to names.
const std::vector< PROPERTY_BASE * > & GetProperties(TYPE_ID aType) const
Return all properties for a specific type.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE * GetProperty(TYPE_ID aType, const wxString &aProperty) const
Return a property for a specific type.
bool IsAvailableFor(TYPE_ID aItemClass, PROPERTY_BASE *aProp, INSPECTABLE *aItem)
Checks overriden availability and original availability of a property, returns false if the property ...
const wxChar *const traceDiffMerge
Flag to enable diff/merge engine and renderer debugging output.
std::vector< PROPERTY_DELTA > DiffItemProperties(const INSPECTABLE *aBefore, const INSPECTABLE *aAfter)
Enumerate the property deltas between two items of the same dynamic type.
bool DiffValueToWxAny(const DIFF_VALUE &aValue, wxAny &aOut)
Convert a DIFF_VALUE back into a wxAny a PROPERTY_BASE setter can consume.
std::vector< PROPERTY_DELTA > ItemProperties(const INSPECTABLE *aItem, bool aAsAfter)
List one item's properties as one-sided deltas for an added or removed item.
PROPERTY_APPLY_COUNTS ApplyPropertyResolutions(INSPECTABLE *aTarget, const std::vector< PROPERTY_RESOLUTION > &aProps, const INSPECTABLE *aOurs, const INSPECTABLE *aTheirs, const INSPECTABLE *aAncestor)
Apply per-property merge resolutions to aTarget, sourcing OURS/THEIRS/ANCESTOR values from the matchi...
DIFF_VALUE WxAnyToDiffValue(const wxAny &aValue, PROPERTY_BASE *aProperty)
Convert a wxAny value read from a PROPERTY_BASE getter into a DIFF_VALUE that the engine can store,...
#define TYPE_HASH(x)
Definition property.h:74
Applied/failed tallies from ApplyPropertyResolutions, folded into a caller's report.
Single (name, before, after) triple for one mutated property on an item.
VECTOR3I res
wxLogTrace helper definitions.
bool KiWxAnyEquals(const wxAny &aA, const wxAny &aB, const PROPERTY_BASE *aProperty)
Compare two wxAny values for equality across the KiCad property type set.