• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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