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 "object_hierarchy_observer.h"
17
18 #include <algorithm>
19
20 #include <meta/api/iteration.h>
21 #include <meta/api/make_callback.h>
22 #include <meta/api/property/property_event_handler.h>
23 #include <meta/api/util.h>
24 #include <meta/base/interface_utils.h>
25 #include <meta/interface/intf_containable.h>
26 #include <meta/interface/intf_content.h>
27
28 META_BEGIN_NAMESPACE()
29
30 namespace Internal {
31
32 // ContainerChangeListener
33
ObjectChangeListener(const IObject::Ptr & object,HierarchyChangeObjectType myType,const IObject::WeakPtr & parent,Internal::ObjectHierarchyObserver * observer,HierarchyChangeModeValue mode)34 ObjectChangeListener::ObjectChangeListener(const IObject::Ptr& object, HierarchyChangeObjectType myType,
35 const IObject::WeakPtr& parent, Internal::ObjectHierarchyObserver* observer, HierarchyChangeModeValue mode)
36 : object_(object), type_(myType), parent_(parent), observer_(observer)
37 {
38 CORE_ASSERT_MSG(observer_ && object, "ObjectChangeListener constructed with null objects");
39 Subscribe(mode);
40 }
41
~ObjectChangeListener()42 ObjectChangeListener::~ObjectChangeListener()
43 {
44 Unsubscribe();
45 }
46
SubscribeContainer(const IObject::Ptr & object)47 void ObjectChangeListener::SubscribeContainer(const IObject::Ptr& object)
48 {
49 if (const auto container = interface_cast<IContainer>(object)) {
50 if (auto trans = interface_cast<IContainerPreTransaction>(container)) {
51 handlers_.emplace_back(trans->ContainerAboutToChange(), [this](const ChildChangedInfo& info, bool&) {
52 if (info.type == ContainerChangeType::ADDING) {
53 NotifyContainerChangeOp(info, HierarchyChangeType::ADDING, HierarchyChangeObjectType::CHILD);
54 } else if (info.type == ContainerChangeType::REMOVING) {
55 NotifyContainerChangeOp(info, HierarchyChangeType::REMOVING, HierarchyChangeObjectType::CHILD);
56 }
57 });
58 containerPreTransaction_ = true;
59 }
60
61 handlers_.emplace_back(container->OnContainerChanged(), [this](const ChildChangedInfo& info) {
62 if (info.type == ContainerChangeType::ADDED) {
63 NotifyContainerChangeOp(info, HierarchyChangeType::ADDED, HierarchyChangeObjectType::CHILD);
64 } else if (info.type == ContainerChangeType::REMOVED) {
65 NotifyContainerChangeOp(info, HierarchyChangeType::REMOVED, HierarchyChangeObjectType::CHILD);
66 } else if (info.type == ContainerChangeType::MOVED) {
67 NotifyContainerMoveOp(info, HierarchyChangeType::MOVED, HierarchyChangeObjectType::CHILD);
68 }
69 });
70 }
71 }
72
SubscribeAttachment(const IObject::Ptr & object)73 void ObjectChangeListener::SubscribeAttachment(const IObject::Ptr& object)
74 {
75 if (const auto attach = interface_cast<IAttach>(object)) {
76 if (const auto container = attach->GetAttachmentContainer(true)) {
77 if (const auto trans = interface_cast<IContainerPreTransaction>(container)) {
78 handlers_.emplace_back(trans->ContainerAboutToChange(), [this](const ChildChangedInfo& info, bool&) {
79 if (info.type == ContainerChangeType::ADDING) {
80 NotifyContainerChangeOp(
81 info, HierarchyChangeType::ADDING, HierarchyChangeObjectType::ATTACHMENT);
82 } else if (info.type == ContainerChangeType::REMOVING) {
83 NotifyContainerChangeOp(
84 info, HierarchyChangeType::REMOVING, HierarchyChangeObjectType::ATTACHMENT);
85 }
86 });
87 attachmentPreTransaction_ = true;
88 }
89 handlers_.emplace_back(container->OnContainerChanged(), [this](const ChildChangedInfo& info) {
90 if (info.type == ContainerChangeType::ADDED) {
91 NotifyContainerChangeOp(info, HierarchyChangeType::ADDED, HierarchyChangeObjectType::ATTACHMENT);
92 } else if (info.type == ContainerChangeType::REMOVED) {
93 NotifyContainerChangeOp(info, HierarchyChangeType::REMOVED, HierarchyChangeObjectType::ATTACHMENT);
94 } else if (info.type == ContainerChangeType::MOVED) {
95 NotifyContainerMoveOp(info, HierarchyChangeType::MOVED, HierarchyChangeObjectType::ATTACHMENT);
96 }
97 });
98 }
99 }
100 }
101
Subscribe(HierarchyChangeModeValue mode)102 bool ObjectChangeListener::Subscribe(HierarchyChangeModeValue mode)
103 {
104 if (const auto object = object_.lock()) {
105 // Figure out which hierarchical interfaces our target object implements and add listeners based on that
106 if (mode & HierarchyChangeMode::NOTIFY_CONTAINER) {
107 SubscribeContainer(object);
108 }
109 if (mode & HierarchyChangeMode::NOTIFY_CONTENT) {
110 if (const auto content = interface_cast<IContent>(object)) {
111 content_ = META_NS::GetValue(content->Content());
112 handlers_.emplace_back(
113 content->Content()->OnChanged(), MakeCallback<IOnChanged>([this]() { NotifyContentChangeOp(); }));
114 }
115 }
116 if (mode & HierarchyChangeMode::NOTIFY_ATTACHMENT) {
117 SubscribeAttachment(object);
118 }
119 if (mode & HierarchyChangeMode::NOTIFY_OBJECT) {
120 if (auto i = interface_pointer_cast<INotifyOnChange>(object)) {
121 handlers_.emplace_back(i->OnChanged(), MakeCallback<IOnChanged>([this]() { NotifyObjectChangedOp(); }));
122 }
123 }
124 return true;
125 }
126 return false;
127 }
128
Unsubscribe()129 void ObjectChangeListener::Unsubscribe()
130 {
131 handlers_.clear();
132 content_.reset();
133 }
134
NotifyObjectChangedOp()135 void ObjectChangeListener::NotifyObjectChangedOp()
136 {
137 if (auto object = object_.lock()) {
138 HierarchyChangedInfo change;
139 change.object = object;
140 change.change = HierarchyChangeType::CHANGED;
141 change.objectType = type_;
142 change.parent = parent_;
143 observer_->HierarchyChanged(change, this);
144 }
145 }
146
NotifyContainerChangeOp(const ChildChangedInfo & info,HierarchyChangeType operation,HierarchyChangeObjectType objectType)147 void ObjectChangeListener::NotifyContainerChangeOp(
148 const ChildChangedInfo& info, HierarchyChangeType operation, HierarchyChangeObjectType objectType)
149 {
150 HierarchyChangedInfo change;
151 change.object = info.object;
152 change.change = operation;
153 change.objectType = objectType;
154 change.index = info.type == ContainerChangeType::ADDED ? info.to : info.from;
155 change.parent = object_;
156 if ((objectType == HierarchyChangeObjectType::CHILD && !containerPreTransaction_) ||
157 (objectType == HierarchyChangeObjectType::ATTACHMENT && !attachmentPreTransaction_)) {
158 // Our target does not support pre transaction notifications, generate ones.
159 if (operation == HierarchyChangeType::ADDED) {
160 change.change = HierarchyChangeType::ADDING;
161 observer_->HierarchyChanged(change, this);
162 change.change = operation;
163 } else if (operation == HierarchyChangeType::REMOVED) {
164 change.change = HierarchyChangeType::REMOVING;
165 observer_->HierarchyChanged(change, this);
166 change.change = operation;
167 }
168 }
169 observer_->HierarchyChanged(change, this);
170 }
171
NotifyContainerMoveOp(const ChildChangedInfo & info,HierarchyChangeType operation,HierarchyChangeObjectType objectType)172 void ObjectChangeListener::NotifyContainerMoveOp(
173 const ChildChangedInfo& info, HierarchyChangeType operation, HierarchyChangeObjectType objectType)
174 {
175 HierarchyChangedInfo change;
176 change.object = info.object;
177 change.change = operation;
178 change.objectType = objectType;
179 change.parent = object_;
180 observer_->HierarchyChanged(change, this);
181 }
182
NotifyContentChangeOp()183 void ObjectChangeListener::NotifyContentChangeOp()
184 {
185 if (const auto content = interface_pointer_cast<IContent>(object_)) {
186 const auto newContent = GetValue(content->Content());
187 const auto oldContent = content_;
188
189 HierarchyChangedInfo change;
190 change.objectType = HierarchyChangeObjectType::CONTENT;
191 change.parent = object_;
192
193 if (oldContent != newContent) {
194 if (oldContent) {
195 change.object = oldContent;
196 change.change = HierarchyChangeType::REMOVING;
197 observer_->HierarchyChanged(change, this);
198 change.change = HierarchyChangeType::REMOVED;
199 observer_->HierarchyChanged(change, this);
200 }
201 content_ = newContent;
202 if (newContent) {
203 change.object = newContent;
204 change.change = HierarchyChangeType::ADDING;
205 observer_->HierarchyChanged(change, this);
206 change.change = HierarchyChangeType::ADDED;
207 observer_->HierarchyChanged(change, this);
208 }
209 }
210 }
211 }
212
213 // ContainerObserver
Build(const IMetadata::Ptr & p)214 bool ObjectHierarchyObserver::Build(const IMetadata::Ptr& p)
215 {
216 bool ret = Super::Build(p);
217 if (ret) {
218 // we don't want to serialise observers
219 META_NS::SetObjectFlags(GetSelf(), ObjectFlagBits::SERIALIZE, false);
220 }
221 return ret;
222 }
223
Destroy()224 void ObjectHierarchyObserver::Destroy()
225 {
226 ClearSubscriptions();
227 }
228
SetTarget(const IObject::Ptr & root,HierarchyChangeModeValue mode)229 void ObjectHierarchyObserver::SetTarget(const IObject::Ptr& root, HierarchyChangeModeValue mode)
230 {
231 ClearSubscriptions();
232 mode_ = mode;
233 root_ = root;
234
235 if (auto i = interface_cast<IAttach>(root)) {
236 i->Attach(GetSelf<IAttachment>());
237 }
238
239 Subscribe(root, HierarchyChangeObjectType::ROOT);
240 }
241
ClearSubscriptions()242 void ObjectHierarchyObserver::ClearSubscriptions()
243 {
244 // Delay subscription removal until outside lock
245 decltype(subscriptions_) subs;
246 decltype(immediateChildren_) immes;
247 {
248 std::unique_lock lock(mutex_);
249 subs = BASE_NS::move(subscriptions_);
250 immes = BASE_NS::move(immediateChildren_);
251 }
252 subs.clear();
253 immes.clear();
254 }
255
GetTarget() const256 IObject::Ptr ObjectHierarchyObserver::GetTarget() const
257 {
258 return root_.lock();
259 }
260
GetAllObserved() const261 BASE_NS::vector<IObject::Ptr> ObjectHierarchyObserver::GetAllObserved() const
262 {
263 std::shared_lock lock(mutex_);
264 BASE_NS::vector<IObject::Ptr> observed;
265 observed.reserve(subscriptions_.size());
266 for (auto&& sub : subscriptions_) {
267 if (auto object = sub.second.object_.lock()) {
268 observed.emplace_back(BASE_NS::move(object));
269 }
270 }
271 return observed;
272 }
273
AddImmediateChild(const HierarchyChangedInfo & info)274 void ObjectHierarchyObserver::AddImmediateChild(const HierarchyChangedInfo& info)
275 {
276 std::unique_lock lock(mutex_);
277 if (info.index < immediateChildren_.size()) {
278 immediateChildren_.insert(
279 immediateChildren_.begin() + info.index, ImmediateChild { info.object, info.objectType });
280 } else {
281 immediateChildren_.push_back(ImmediateChild { info.object, info.objectType });
282 }
283 }
284
RemoveImmediateChild(const HierarchyChangedInfo & info)285 void ObjectHierarchyObserver::RemoveImmediateChild(const HierarchyChangedInfo& info)
286 {
287 std::unique_lock lock(mutex_);
288 auto it = immediateChildren_.begin();
289 for (; it != immediateChildren_.end() && it->object.lock() != info.object; ++it) {
290 }
291 if (it != immediateChildren_.end()) {
292 immediateChildren_.erase(it);
293 }
294 }
295
HierarchyChanged(const HierarchyChangedInfo & info,ObjectChangeListener * listener)296 void ObjectHierarchyObserver::HierarchyChanged(const HierarchyChangedInfo& info, ObjectChangeListener* listener)
297 {
298 if (info.object != GetSelf()) {
299 Invoke<IOnHierarchyChanged>(META_ACCESS_EVENT(OnHierarchyChanged), info);
300 if (info.objectType == HierarchyChangeObjectType::CHILD ||
301 info.objectType == HierarchyChangeObjectType::CONTENT) {
302 // We do not listen to attachments (other than monitoring for changes in an object's attachment list)
303 if (info.change == HierarchyChangeType::ADDING) {
304 Subscribe(info.object, info.objectType, info.parent);
305 if (keepTrackOfImmediate_ && listener->GetType() == HierarchyChangeObjectType::ROOT) {
306 AddImmediateChild(info);
307 }
308 } else if (info.change == HierarchyChangeType::REMOVED) {
309 Unsubscribe(info.object);
310 if (keepTrackOfImmediate_ && listener->GetType() == HierarchyChangeObjectType::ROOT) {
311 RemoveImmediateChild(info);
312 }
313 }
314 }
315 }
316 }
317
Subscribe(const IObject::Ptr & root,HierarchyChangeObjectType type,const IObject::WeakPtr & parent)318 void ObjectHierarchyObserver::Subscribe(
319 const IObject::Ptr& root, HierarchyChangeObjectType type, const IObject::WeakPtr& parent)
320 {
321 if (!root) {
322 return;
323 }
324
325 AddSubscription(root, type, parent);
326
327 if (const auto container = interface_cast<IContainer>(root)) {
328 for (auto&& child : container->GetAll()) {
329 if (keepTrackOfImmediate_ && type == HierarchyChangeObjectType::ROOT) {
330 std::unique_lock lock(mutex_);
331 immediateChildren_.push_back(ImmediateChild { child, HierarchyChangeObjectType::CHILD });
332 }
333 Subscribe(child, HierarchyChangeObjectType::CHILD, root);
334 }
335 }
336 if (const auto content = interface_cast<IContent>(root)) {
337 if (auto object = GetValue(content->Content())) {
338 if (keepTrackOfImmediate_ && type == HierarchyChangeObjectType::ROOT) {
339 std::unique_lock lock(mutex_);
340 immediateChildren_.push_back(ImmediateChild { object, HierarchyChangeObjectType::CONTENT });
341 }
342 Subscribe(object, HierarchyChangeObjectType::CONTENT, root);
343 }
344 }
345 }
346
AddSubscription(const IObject::Ptr & object,HierarchyChangeObjectType type,const IObject::WeakPtr & parent)347 void ObjectHierarchyObserver::AddSubscription(
348 const IObject::Ptr& object, HierarchyChangeObjectType type, const IObject::WeakPtr& parent)
349 {
350 auto listener = BASE_NS::make_unique<ObjectChangeListener>(object, type, parent, this, mode_);
351 {
352 std::unique_lock lock(mutex_);
353 auto* ptr = listener.get();
354 if (!subscriptions_.insert({ ptr, Subscription(object, BASE_NS::move(listener)) }).second) {
355 CORE_LOG_E(
356 "Duplicate event subscription for %s:%s", object->GetClassName().data(), object->GetName().data());
357 }
358 }
359 }
360
Unsubscribe(const IObject::Ptr & root)361 void ObjectHierarchyObserver::Unsubscribe(const IObject::Ptr& root)
362 {
363 if (!root) {
364 return;
365 }
366
367 RemoveSubscription(root);
368
369 if (root->GetInterface(IIterable::UID)) {
370 ForEachShared(
371 root, [this](const IObject::Ptr& object) { RemoveSubscription(object); },
372 TraversalType::DEPTH_FIRST_PRE_ORDER);
373 } else {
374 if (const auto container = interface_cast<IContainer>(root)) {
375 for (auto&& child : container->GetAll()) {
376 Unsubscribe(child);
377 }
378 }
379 if (const auto content = interface_cast<IContent>(root)) {
380 if (auto object = GetValue(content->Content())) {
381 Unsubscribe(object);
382 }
383 }
384 }
385 }
386
RemoveSubscription(const IObject::Ptr & object)387 void ObjectHierarchyObserver::RemoveSubscription(const IObject::Ptr& object)
388 {
389 // Delay subscription destruction until we're out of the lock
390 BASE_NS::vector<Subscription> subs;
391 {
392 std::unique_lock lock(mutex_);
393 auto it = subscriptions_.begin();
394 while (it != subscriptions_.end()) {
395 const auto o = it->second.object_.lock();
396 if (!o || o == object) {
397 subs.emplace_back(BASE_NS::move(it->second));
398 it = subscriptions_.erase(it);
399 } else {
400 ++it;
401 }
402 }
403 }
404 subs.clear();
405 }
406
NotifyOnDetach()407 void ObjectHierarchyObserver::NotifyOnDetach()
408 {
409 // If we were attached to the target and we are getting detached when the target already died, lets
410 // notify about the removing the immediate children as those are not otherwise notified when destroying
411 // container
412 decltype(immediateChildren_) ic;
413 {
414 std::unique_lock lock(mutex_);
415 ic = BASE_NS::move(immediateChildren_);
416 }
417 if (root_.expired()) {
418 for (auto&& c : ic) {
419 if (auto o = c.object.lock()) {
420 // Generate pre transaction notifications (even if coming in wrong time).
421 HierarchyChangedInfo info { o, HierarchyChangeType::REMOVING, c.type, size_t(-1), nullptr };
422 Invoke<IOnHierarchyChanged>(META_ACCESS_EVENT(OnHierarchyChanged), info);
423 info.change = HierarchyChangeType::REMOVED;
424 Invoke<IOnHierarchyChanged>(META_ACCESS_EVENT(OnHierarchyChanged), info);
425 }
426 }
427 }
428 }
429
Attaching(const META_NS::IAttach::Ptr & target,const META_NS::IObject::Ptr & dataContext)430 bool ObjectHierarchyObserver::Attaching(const META_NS::IAttach::Ptr& target, const META_NS::IObject::Ptr& dataContext)
431 {
432 META_ACCESS_PROPERTY(AttachedTo)->SetValue(target);
433 META_ACCESS_PROPERTY(DataContext)->SetValue(dataContext);
434 {
435 std::unique_lock lock(mutex_);
436 keepTrackOfImmediate_ = true;
437 }
438 return true;
439 }
Detaching(const META_NS::IAttach::Ptr & target)440 bool ObjectHierarchyObserver::Detaching(const META_NS::IAttach::Ptr& target)
441 {
442 NotifyOnDetach();
443 {
444 std::unique_lock lock(mutex_);
445 keepTrackOfImmediate_ = false;
446 }
447 META_ACCESS_PROPERTY(AttachedTo)->SetValue({});
448 META_ACCESS_PROPERTY(DataContext)->SetValue({});
449 return true;
450 }
451
452 } // namespace Internal
453
454 META_END_NAMESPACE()
455