1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "proxy_object.h"
17 
18 #include <algorithm>
19 
20 #include <meta/api/make_callback.h>
21 #include <meta/api/property/property_event_handler.h>
22 #include <meta/api/util.h>
23 
24 META_BEGIN_NAMESPACE()
25 
26 namespace Internal {
27 
~ProxyObject()28 ProxyObject::~ProxyObject()
29 {
30     if (GetMetadata()) {
31         ResetTargetListener();
32         for (auto&& p : Super::GetAllProperties()) {
33             p->OnChanged()->RemoveHandler(uintptr_t(this));
34         }
35     }
36 }
37 
Build(const IMetadata::Ptr & data)38 bool ProxyObject::Build(const IMetadata::Ptr& data)
39 {
40     bool ret = Super::Build(data);
41     if (ret) {
42         Dynamic()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
43             if (Dynamic()->GetValue()) {
44                 ListenTargetChanges();
45                 RefreshProperties();
46             } else {
47                 ResetTargetListener();
48             }
49         }));
50 
51         for (auto&& p : Super::GetAllProperties()) {
52             p->OnChanged()->AddHandler(MakeCallback<IOnChanged>(
53                                            [this](auto p) {
54                                                if (p) {
55                                                    OnPropertyChanged(p);
56                                                }
57                                            },
58                                            p),
59                 uintptr_t(this));
60         }
61 
62         if (auto meta = GetSelf<IMetadata>()) {
63             metaAdded_.Subscribe<IOnChildChanged>(
64                 meta->GetPropertyContainer()->OnAdded(), [this](const auto& i) { OnPropertyAdded(i); });
65             metaRemoved_.Subscribe<IOnChildChanged>(
66                 meta->GetPropertyContainer()->OnRemoved(), [this](const auto& i) { OnPropertyRemoved(i); });
67         }
68         UpdateSerializeState();
69     }
70     return ret;
71 }
72 
SerializeEmbeddedProxy(IProperty::Ptr p)73 static bool SerializeEmbeddedProxy(IProperty::Ptr p)
74 {
75     // Check for embedded proxy objects
76     if (auto proxy = GetPointer<IProxyObject>(p)) {
77         if (auto f = interface_cast<IObjectFlags>(proxy)) {
78             return static_cast<bool>(f->GetObjectFlags() & ObjectFlagBits::SERIALIZE);
79         }
80     }
81     return false;
82 }
83 
OnPropertyAdded(const ChildChangedInfo & info)84 void ProxyObject::OnPropertyAdded(const ChildChangedInfo& info)
85 {
86     if (auto p = interface_pointer_cast<IProperty>(info.object)) {
87         auto f = GetSelf<IObjectFlags>();
88         if (!updating_ && f && !(f->GetObjectFlags() & ObjectFlagBits::SERIALIZE)) {
89             META_NS::SetObjectFlags(f, ObjectFlagBits::SERIALIZE, ShouldSerialise(p));
90         }
91         p->OnChanged()->AddHandler(MakeCallback<IOnChanged>(
92                                        [this](auto p) {
93                                            if (p) {
94                                                OnPropertyChanged(p);
95                                            }
96                                        },
97                                        p),
98             uintptr_t(this));
99     }
100 }
101 
OnPropertyRemoved(const ChildChangedInfo & info)102 void ProxyObject::OnPropertyRemoved(const ChildChangedInfo& info)
103 {
104     if (auto p = interface_cast<IProperty>(info.object)) {
105         p->OnChanged()->RemoveHandler(uintptr_t(this));
106     }
107     auto f = GetSelf<IObjectFlags>();
108     if (!updating_ && f && f->GetObjectFlags() & ObjectFlagBits::SERIALIZE) {
109         UpdateSerializeState();
110     }
111 }
112 
OnPropertyChanged(const IProperty::Ptr & p)113 void ProxyObject::OnPropertyChanged(const IProperty::Ptr& p)
114 {
115     auto f = GetSelf<IObjectFlags>();
116     if (!updating_ && f) {
117         if (f->GetObjectFlags() & ObjectFlagBits::SERIALIZE) {
118             UpdateSerializeState();
119         } else {
120             META_NS::SetObjectFlags(f, ObjectFlagBits::SERIALIZE, ShouldSerialise(p));
121         }
122     }
123 }
124 
ListenTargetChanges()125 void ProxyObject::ListenTargetChanges()
126 {
127     if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
128         targetAddedListener_.Subscribe<IOnChildChanged>(
129             target->GetPropertyContainer()->OnAdded(), [&](auto) { RefreshProperties(); });
130         targetRemovedListener_.Subscribe<IOnChildChanged>(
131             target->GetPropertyContainer()->OnRemoved(), [&](auto) { RefreshProperties(); });
132     }
133 }
134 
ResetTargetListener()135 void ProxyObject::ResetTargetListener()
136 {
137     targetAddedListener_.Unsubscribe();
138     targetRemovedListener_.Unsubscribe();
139 }
140 
RefreshProperties()141 void ProxyObject::RefreshProperties()
142 {
143     updating_ = true;
144     if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
145         auto props = BASE_NS::move(proxyProperties_);
146         for (auto&& p : props) {
147             if (auto tp = target->GetPropertyByName(p.first)) {
148                 auto res = proxyProperties_.insert({ p.first, p.second });
149                 res.first->second.Bind(tp);
150             } else {
151                 Super::RemoveProperty(p.second.GetProperty());
152             }
153         }
154     } else {
155         // remove all we added
156         for (auto&& p : proxyProperties_) {
157             Super::RemoveProperty(p.second.GetProperty());
158         }
159         proxyProperties_.clear();
160     }
161     updating_ = false;
162     UpdateSerializeState();
163 }
164 
GetTarget() const165 const IObject::Ptr ProxyObject::GetTarget() const
166 {
167     return target_.lock();
168 }
169 
SetTarget(const IObject::Ptr & target)170 bool ProxyObject::SetTarget(const IObject::Ptr& target)
171 {
172     ResetTargetListener();
173     target_ = target;
174     if (Dynamic()->GetValue()) {
175         ListenTargetChanges();
176     }
177     RefreshProperties();
178     if (META_ACCESS_PROPERTY_VALUE(Mode) & ProxyMode::REFLECT_PROXY_HIERARCHY) {
179         ReflectHierarchy(target);
180     }
181     return true;
182 }
183 
ReflectHierarchy(const IObject::Ptr & target)184 void ProxyObject::ReflectHierarchy(const IObject::Ptr& target)
185 {
186     auto m = interface_pointer_cast<IMetadata>(target);
187     if (!m) {
188         return;
189     }
190     for (auto&& p : Super::GetAllProperties()) {
191         // reflect only non-proxyable properties
192         if (!proxyProperties_.count(p->GetName())) {
193             if (auto proxy = GetPointer<IProxyObject>(p)) {
194                 ReflectTargetForProperty(m, p->GetName(), proxy);
195             }
196         }
197     }
198 }
199 
ReflectTargetForProperty(const IMetadata::Ptr & m,BASE_NS::string_view name,const IProxyObject::Ptr & proxy)200 void ProxyObject::ReflectTargetForProperty(
201     const IMetadata::Ptr& m, BASE_NS::string_view name, const IProxyObject::Ptr& proxy)
202 {
203     if (auto p = m->GetPropertyByName(name)) {
204         if (auto tp = GetPointer<IObject>(p)) {
205             proxy->SetTarget(tp);
206         }
207     }
208 }
209 
GetOverrides() const210 BASE_NS::vector<IProperty::ConstPtr> ProxyObject::GetOverrides() const
211 {
212     BASE_NS::vector<IProperty::ConstPtr> res;
213     for (auto&& p : proxyProperties_) {
214         if (!p.second.IsDefaultValue()) {
215             res.push_back(p.second.GetProperty());
216         }
217     }
218     return res;
219 }
220 
GetOverride(BASE_NS::string_view name) const221 IProperty::ConstPtr ProxyObject::GetOverride(BASE_NS::string_view name) const
222 {
223     auto it = proxyProperties_.find(name);
224     return it != proxyProperties_.end() && !it->second.IsDefaultValue() ? it->second.GetProperty() : nullptr;
225 }
226 
GetProxyProperty(BASE_NS::string_view name) const227 IProperty::ConstPtr ProxyObject::GetProxyProperty(BASE_NS::string_view name) const
228 {
229     auto it = proxyProperties_.find(name);
230     return it != proxyProperties_.end() ? it->second.GetProperty() : nullptr;
231 }
232 
SetPropertyTarget(const IProperty::Ptr & property)233 IProperty::Ptr ProxyObject::SetPropertyTarget(const IProperty::Ptr& property)
234 {
235     if (!property) {
236         return nullptr;
237     }
238     IProperty::Ptr res;
239     bool add = true;
240     auto it = proxyProperties_.find(property->GetName());
241     if (it != proxyProperties_.end()) {
242         auto bprop = it->second.GetProperty();
243         add = !bprop || bprop->GetTypeId() != property->GetTypeId();
244         if (add) {
245             proxyProperties_.erase(it);
246         } else {
247             it->second.Bind(property);
248             res = it->second.GetProperty();
249         }
250     }
251     if (add && property) {
252         if (auto p = Super::GetPropertyByName(property->GetName())) {
253             auto r = proxyProperties_.insert_or_assign(p->GetName(), DefaultValueBind(p));
254             r.first->second.Bind(property);
255             res = p;
256         } else {
257             res = AddProxyProperty(property);
258         }
259     }
260     return res;
261 }
262 
AddProxyProperty(const IProperty::ConstPtr & tp)263 IProperty::Ptr ProxyObject::AddProxyProperty(const IProperty::ConstPtr& tp)
264 {
265     auto p = DuplicatePropertyType(META_NS::GetObjectRegistry(), tp);
266     if (p) {
267         auto res = proxyProperties_.insert_or_assign(p->GetName(), DefaultValueBind(p));
268         res.first->second.Bind(tp);
269         AddProperty(p);
270     }
271     return p;
272 }
273 
AddProxyProperty(BASE_NS::string_view name)274 IProperty::Ptr ProxyObject::AddProxyProperty(BASE_NS::string_view name)
275 {
276     IProperty::Ptr p;
277     if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
278         if (auto tp = target->GetPropertyByName(name)) {
279             p = AddProxyProperty(tp);
280         }
281     }
282     return p;
283 }
284 
PopulateAllProperties()285 void ProxyObject::PopulateAllProperties()
286 {
287     if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
288         for (auto&& p : target->GetAllProperties()) {
289             auto it = proxyProperties_.find(p->GetName());
290             if (it == proxyProperties_.end()) {
291                 AddProxyProperty(p);
292             }
293         }
294     }
295 }
296 
GetPropertyByName(BASE_NS::string_view name)297 IProperty::Ptr ProxyObject::GetPropertyByName(BASE_NS::string_view name)
298 {
299     auto p = Super::GetPropertyByName(name);
300     if (!p) {
301         p = AddProxyProperty(name);
302     }
303     return p;
304 }
305 
GetPropertyByName(BASE_NS::string_view name) const306 IProperty::ConstPtr ProxyObject::GetPropertyByName(BASE_NS::string_view name) const
307 {
308     return const_cast<ProxyObject*>(this)->GetPropertyByName(name);
309 }
310 
RemoveProperty(const IProperty::Ptr & p)311 void ProxyObject::RemoveProperty(const IProperty::Ptr& p)
312 {
313     Super::RemoveProperty(p);
314     proxyProperties_.erase(p->GetName());
315 }
316 
GetAllProperties()317 BASE_NS::vector<IProperty::Ptr> ProxyObject::GetAllProperties()
318 {
319     PopulateAllProperties();
320     return Super::GetAllProperties();
321 }
322 
GetAllProperties() const323 BASE_NS::vector<IProperty::ConstPtr> ProxyObject::GetAllProperties() const
324 {
325     const_cast<ProxyObject*>(this)->PopulateAllProperties();
326     return Super::GetAllProperties();
327 }
328 
ShouldSerialise(const IProperty::Ptr & p) const329 bool ProxyObject::ShouldSerialise(const IProperty::Ptr& p) const
330 {
331     auto s = interface_cast<IStackProperty>(p);
332     return (s && !s->GetValues({}, false).empty()) ||
333            (!IsFlagSet(p, ObjectFlagBits::NATIVE) && !proxyProperties_.count(p->GetName())) ||
334            SerializeEmbeddedProxy(p);
335 }
336 
UpdateSerializeState()337 void ProxyObject::UpdateSerializeState()
338 {
339     if (!updating_) {
340         bool serialise = !GetOverrides().empty();
341         if (!serialise) {
342             for (auto&& p : Super::GetAllProperties()) {
343                 serialise = ShouldSerialise(p);
344                 if (serialise) {
345                     break;
346                 }
347             }
348         }
349         META_NS::SetObjectFlags(GetSelf(), ObjectFlagBits::SERIALIZE, serialise);
350     }
351 }
352 
353 } // namespace Internal
354 
355 META_END_NAMESPACE()
356