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