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 const auto& props = pm.GetProperties( TYPE_HASH( *aBefore ) );
74
75 // IsAvailableFor and Get take a non-const INSPECTABLE*; we treat both items
76 // as read-only inspections.
77 INSPECTABLE* mutBefore = const_cast<INSPECTABLE*>( aBefore );
78 INSPECTABLE* mutAfter = const_cast<INSPECTABLE*>( aAfter );
79
80 for( PROPERTY_BASE* prop : props )
81 {
82 if( !prop || prop->IsHiddenFromPropertiesManager() )
83 continue;
84
85 // Skip properties that aren't applicable to this particular item state
86 // (e.g. context-dependent visibility).
87 if( !pm.IsAvailableFor( TYPE_HASH( *aBefore ), prop, mutBefore ) )
88 continue;
89
90 if( !pm.IsAvailableFor( TYPE_HASH( *aAfter ), prop, mutAfter ) )
91 continue;
92
93 wxAny beforeVal = mutBefore->Get( prop );
94 wxAny afterVal = mutAfter->Get( prop );
95
96 if( KiWxAnyEquals( beforeVal, afterVal, prop ) )
97 continue;
98
99 DIFF_VALUE beforeDV = WxAnyToDiffValue( beforeVal, prop );
100 DIFF_VALUE afterDV = WxAnyToDiffValue( afterVal, prop );
101
102 // Skip if conversion failed for both sides (unsupported type) — we can't
103 // produce a meaningful delta. If only one side converted, we still emit
104 // because the user benefits from knowing "something changed".
105 if( beforeDV.GetType() == DIFF_VALUE::T::NONE && afterDV.GetType() == DIFF_VALUE::T::NONE )
106 continue;
107
109 d.name = prop->Name();
110 d.before = beforeDV;
111 d.after = afterDV;
112 deltas.push_back( std::move( d ) );
113 }
114
115 // Deterministic order: alphabetical by property name.
116 std::sort( deltas.begin(), deltas.end(),
117 []( const PROPERTY_DELTA& aL, const PROPERTY_DELTA& aR )
118 {
119 return aL.name < aR.name;
120 } );
121
122 return deltas;
123}
124
125
131inline std::vector<PROPERTY_DELTA> ItemProperties( const INSPECTABLE* aItem, bool aAsAfter )
132{
133 std::vector<PROPERTY_DELTA> deltas;
134
135 if( !aItem )
136 return deltas;
137
139 const auto& props = pm.GetProperties( TYPE_HASH( *aItem ) );
140
141 INSPECTABLE* mutItem = const_cast<INSPECTABLE*>( aItem );
142
143 for( PROPERTY_BASE* prop : props )
144 {
145 if( !prop || prop->IsHiddenFromPropertiesManager() )
146 continue;
147
148 if( !pm.IsAvailableFor( TYPE_HASH( *aItem ), prop, mutItem ) )
149 continue;
150
151 DIFF_VALUE dv = WxAnyToDiffValue( mutItem->Get( prop ), prop );
152
153 if( dv.GetType() == DIFF_VALUE::T::NONE )
154 continue;
155
156 // An unset string property (e.g. an unassigned Component Class) carries
157 // no information for an added or removed item, so skip it.
158 if( dv.GetType() == DIFF_VALUE::T::STRING && dv.AsString().IsEmpty() )
159 continue;
160
162 d.name = prop->Name();
163
164 if( aAsAfter )
165 d.after = dv;
166 else
167 d.before = dv;
168
169 deltas.push_back( std::move( d ) );
170 }
171
172 std::sort( deltas.begin(), deltas.end(),
173 []( const PROPERTY_DELTA& aL, const PROPERTY_DELTA& aR )
174 {
175 return aL.name < aR.name;
176 } );
177
178 return deltas;
179}
180
181
184{
185 std::size_t applied = 0;
186 std::size_t failed = 0;
187};
188
189
203 INSPECTABLE* aTarget, const std::vector<PROPERTY_RESOLUTION>& aProps,
204 const INSPECTABLE* aOurs, const INSPECTABLE* aTheirs, const INSPECTABLE* aAncestor )
205{
207
208 if( !aTarget || aProps.empty() )
209 return counts;
210
212
213 for( const PROPERTY_RESOLUTION& res : aProps )
214 {
215 PROPERTY_BASE* prop = pm.GetProperty( TYPE_HASH( *aTarget ), res.name );
216
217 if( !prop )
218 {
219 wxLogTrace( traceDiffMerge, wxT( "applier: property '%s' not found on target type" ),
220 res.name );
221 ++counts.failed;
222 continue;
223 }
224
225 if( !prop->Writeable( aTarget ) )
226 {
227 wxLogTrace( traceDiffMerge, wxT( "applier: property '%s' is read-only on target" ),
228 res.name );
229 ++counts.failed;
230 continue;
231 }
232
233 wxAny value;
234
235 if( res.kind == PROP_RES::CUSTOM )
236 {
237 // Custom values come from the resolution payload itself — the UI
238 // flow stores the user-edited value in
239 // PROPERTY_RESOLUTION.customValue. Convert DIFF_VALUE back into a
240 // wxAny the property can consume; a T::NONE payload carries nothing
241 // and counts as failed.
242 if( !DiffValueToWxAny( res.customValue, value ) )
243 {
244 ++counts.failed;
245 continue;
246 }
247 }
248 else
249 {
250 const INSPECTABLE* source = nullptr;
251
252 switch( res.kind )
253 {
254 case PROP_RES::OURS: source = aOurs; break;
255 case PROP_RES::THEIRS: source = aTheirs; break;
256 case PROP_RES::ANCESTOR: source = aAncestor; break;
257 case PROP_RES::CUSTOM: break;
258 }
259
260 if( !source )
261 {
262 ++counts.failed;
263 continue;
264 }
265
266 value = const_cast<INSPECTABLE*>( source )->Get( prop );
267 }
268
269 if( !aTarget->Set( prop, value ) )
270 {
271 wxLogTrace( traceDiffMerge, wxT( "applier: Set failed for property '%s'" ),
272 res.name );
273 ++counts.failed;
274 continue;
275 }
276
277 ++counts.applied;
278 }
279
280 return counts;
281}
282
283} // namespace KICAD_DIFF
284
285#endif // KICAD_DIFF_PROPERTY_DIFF_H
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:38
wxAny Get(PROPERTY_BASE *aProperty) const
bool Set(PROPERTY_BASE *aProperty, wxAny &aValue, bool aNotify=true)
A typed sum value used to carry the before/after of any single property.
virtual bool Writeable(INSPECTABLE *aObject) const
Definition property.h:282
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.