• 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 
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->ContainerAboutToChange(), [this](const ChildChangedInfo& info, bool&) {
50                 if (info.type == ContainerChangeType::ADDING) {
51                     NotifyContainerChangeOp(info, HierarchyChangeType::ADDING, HierarchyChangeObjectType::CHILD);
52                 } else if (info.type == ContainerChangeType::REMOVING) {
53                     NotifyContainerChangeOp(info, HierarchyChangeType::REMOVING, HierarchyChangeObjectType::CHILD);
54                 }
55             });
56             containerPreTransaction_ = true;
57         }
58 
59         handlers_.emplace_back(container->OnContainerChanged(), [this](const ChildChangedInfo& info) {
60             if (info.type == ContainerChangeType::ADDED) {
61                 NotifyContainerChangeOp(info, HierarchyChangeType::ADDED, HierarchyChangeObjectType::CHILD);
62             } else if (info.type == ContainerChangeType::REMOVED) {
63                 NotifyContainerChangeOp(info, HierarchyChangeType::REMOVED, HierarchyChangeObjectType::CHILD);
64             } else if (info.type == ContainerChangeType::MOVED) {
65                 NotifyContainerMoveOp(info, HierarchyChangeType::MOVED, HierarchyChangeObjectType::CHILD);
66             }
67         });
68     }
69 }
70 
SubscribeAttachment(const IObject::Ptr & object)71 void ObjectChangeListener::SubscribeAttachment(const IObject::Ptr& object)
72 {
73     if (const auto attach = interface_cast<IAttach>(object)) {
74         if (const auto container = attach->GetAttachmentContainer(true)) {
75             if (const auto trans = interface_cast<IContainerPreTransaction>(container)) {
76                 handlers_.emplace_back(trans->ContainerAboutToChange(), [this](const ChildChangedInfo& info, bool&) {
77                     if (info.type == ContainerChangeType::ADDING) {
78                         NotifyContainerChangeOp(
79                             info, HierarchyChangeType::ADDING, HierarchyChangeObjectType::ATTACHMENT);
80                     } else if (info.type == ContainerChangeType::REMOVING) {
81                         NotifyContainerChangeOp(
82                             info, HierarchyChangeType::REMOVING, HierarchyChangeObjectType::ATTACHMENT);
83                     }
84                 });
85                 attachmentPreTransaction_ = true;
86             }
87             handlers_.emplace_back(container->OnContainerChanged(), [this](const ChildChangedInfo& info) {
88                 if (info.type == ContainerChangeType::ADDED) {
89                     NotifyContainerChangeOp(info, HierarchyChangeType::ADDED, HierarchyChangeObjectType::ATTACHMENT);
90                 } else if (info.type == ContainerChangeType::REMOVED) {
91                     NotifyContainerChangeOp(info, HierarchyChangeType::REMOVED, HierarchyChangeObjectType::ATTACHMENT);
92                 } else if (info.type == ContainerChangeType::MOVED) {
93                     NotifyContainerMoveOp(info, HierarchyChangeType::MOVED, HierarchyChangeObjectType::ATTACHMENT);
94                 }
95             });
96         }
97     }
98 }
99 
Subscribe(HierarchyChangeModeValue mode)100 bool ObjectChangeListener::Subscribe(HierarchyChangeModeValue mode)
101 {
102     if (const auto object = object_.lock()) {
103         // Figure out which hierarchical interfaces our target object implements and add listeners based on that
104         if (mode & HierarchyChangeMode::NOTIFY_CONTAINER) {
105             SubscribeContainer(object);
106         }
107         if (mode & HierarchyChangeMode::NOTIFY_CONTENT) {
108             if (const auto content = interface_cast<IContent>(object)) {
109                 content_ = META_NS::GetValue(content->Content());
110                 handlers_.emplace_back(
111                     content->Content()->OnChanged(), MakeCallback<IOnChanged>([this]() { NotifyContentChangeOp(); }));
112             }
113         }
114         if (mode & HierarchyChangeMode::NOTIFY_ATTACHMENT) {
115             SubscribeAttachment(object);
116         }
117         if (mode & HierarchyChangeMode::NOTIFY_OBJECT) {
118             if (auto i = interface_pointer_cast<INotifyOnChange>(object)) {
119                 handlers_.emplace_back(i->OnChanged(), MakeCallback<IOnChanged>([this]() { NotifyObjectChangedOp(); }));
120             }
121         }
122         return true;
123     }
124     return false;
125 }
126 
Unsubscribe()127 void ObjectChangeListener::Unsubscribe()
128 {
129     handlers_.clear();
130     content_.reset();
131 }
132 
NotifyObjectChangedOp()133 void ObjectChangeListener::NotifyObjectChangedOp()
134 {
135     if (auto object = object_.lock()) {
136         HierarchyChangedInfo change;
137         change.object = object;
138         change.change = HierarchyChangeType::CHANGED;
139         change.objectType = type_;
140         change.parent = parent_;
141         observer_->HierarchyChanged(change, this);
142     }
143 }
144 
NotifyContainerChangeOp(const ChildChangedInfo & info,HierarchyChangeType operation,HierarchyChangeObjectType objectType)145 void ObjectChangeListener::NotifyContainerChangeOp(
146     const ChildChangedInfo& info, HierarchyChangeType operation, HierarchyChangeObjectType objectType)
147 {
148     HierarchyChangedInfo change;
149     change.object = info.object;
150     change.change = operation;
151     change.objectType = objectType;
152     change.index = info.type == ContainerChangeType::ADDED ? info.to : info.from;
153     change.parent = object_;
154     if ((objectType == HierarchyChangeObjectType::CHILD && !containerPreTransaction_) ||
155         (objectType == HierarchyChangeObjectType::ATTACHMENT && !attachmentPreTransaction_)) {
156         // Our target does not support pre transaction notifications, generate ones.
157         if (operation == HierarchyChangeType::ADDED) {
158             change.change = HierarchyChangeType::ADDING;
159             observer_->HierarchyChanged(change, this);
160             change.change = operation;
161         } else if (operation == HierarchyChangeType::REMOVED) {
162             change.change = HierarchyChangeType::REMOVING;
163             observer_->HierarchyChanged(change, this);
164             change.change = operation;
165         }
166     }
167     observer_->HierarchyChanged(change, this);
168 }
169 
NotifyContainerMoveOp(const ChildChangedInfo & info,HierarchyChangeType operation,HierarchyChangeObjectType objectType)170 void ObjectChangeListener::NotifyContainerMoveOp(
171     const ChildChangedInfo& info, HierarchyChangeType operation, HierarchyChangeObjectType objectType)
172 {
173     HierarchyChangedInfo change;
174     change.object = info.object;
175     change.change = operation;
176     change.objectType = objectType;
177     change.parent = object_;
178     observer_->HierarchyChanged(change, this);
179 }
180 
NotifyContentChangeOp()181 void ObjectChangeListener::NotifyContentChangeOp()
182 {
183     if (const auto content = interface_pointer_cast<IContent>(object_)) {
184         const auto newContent = GetValue(content->Content());
185         const auto oldContent = content_;
186 
187         HierarchyChangedInfo change;
188         change.objectType = HierarchyChangeObjectType::CONTENT;
189         change.parent = object_;
190 
191         if (oldContent != newContent) {
192             if (oldContent) {
193                 change.object = oldContent;
194                 change.change = HierarchyChangeType::REMOVING;
195                 observer_->HierarchyChanged(change, this);
196                 change.change = HierarchyChangeType::REMOVED;
197                 observer_->HierarchyChanged(change, this);
198             }
199             content_ = newContent;
200             if (newContent) {
201                 change.object = newContent;
202                 change.change = HierarchyChangeType::ADDING;
203                 observer_->HierarchyChanged(change, this);
204                 change.change = HierarchyChangeType::ADDED;
205                 observer_->HierarchyChanged(change, this);
206             }
207         }
208     }
209 }
210 
211 // ContainerObserver
212 
Build(const IMetadata::Ptr & p)213 bool ObjectHierarchyObserver::Build(const IMetadata::Ptr& p)
214 {
215     bool ret = Super::Build(p);
216     if (ret) {
217         // we don't want to serialise observers
218         META_NS::SetObjectFlags(GetSelf(), ObjectFlagBits::SERIALIZE, false);
219     }
220     return ret;
221 }
222 
Destroy()223 void ObjectHierarchyObserver::Destroy()
224 {
225     ClearSubscriptions();
226 }
227 
SetTarget(const IObject::Ptr & root,HierarchyChangeModeValue mode)228 void ObjectHierarchyObserver::SetTarget(const IObject::Ptr& root, HierarchyChangeModeValue mode)
229 {
230     ClearSubscriptions();
231     mode_ = mode;
232     root_ = root;
233 
234     if (auto i = interface_cast<IAttach>(root)) {
235         i->Attach(GetSelf<IAttachment>());
236     }
237 
238     Subscribe(root, HierarchyChangeObjectType::ROOT);
239 }
240 
ClearSubscriptions()241 void ObjectHierarchyObserver::ClearSubscriptions()
242 {
243     // Delay subscription removal until outside lock
244     decltype(subscriptions_) subs;
245     decltype(immediateChildren_) immes;
246     {
247         std::unique_lock lock(mutex_);
248         subs = BASE_NS::move(subscriptions_);
249         immes = BASE_NS::move(immediateChildren_);
250     }
251     subs.clear();
252     immes.clear();
253 }
254 
GetTarget() const255 IObject::Ptr ObjectHierarchyObserver::GetTarget() const
256 {
257     return root_.lock();
258 }
259 
GetAllObserved() const260 BASE_NS::vector<IObject::Ptr> ObjectHierarchyObserver::GetAllObserved() const
261 {
262     std::shared_lock lock(mutex_);
263     BASE_NS::vector<IObject::Ptr> observed;
264     observed.reserve(subscriptions_.size());
265     for (auto&& sub : subscriptions_) {
266         if (auto object = sub.second.object_.lock()) {
267             observed.emplace_back(BASE_NS::move(object));
268         }
269     }
270     return observed;
271 }
272 
AddImmediateChild(const HierarchyChangedInfo & info)273 void ObjectHierarchyObserver::AddImmediateChild(const HierarchyChangedInfo& info)
274 {
275     std::unique_lock lock(mutex_);
276     if (info.index < immediateChildren_.size()) {
277         immediateChildren_.insert(
278             immediateChildren_.begin() + info.index, ImmediateChild { info.object, info.objectType });
279     } else {
280         immediateChildren_.push_back(ImmediateChild { info.object, info.objectType });
281     }
282 }
283 
RemoveImmediateChild(const HierarchyChangedInfo & info)284 void ObjectHierarchyObserver::RemoveImmediateChild(const HierarchyChangedInfo& info)
285 {
286     std::unique_lock lock(mutex_);
287     auto it = immediateChildren_.begin();
288     for (; it != immediateChildren_.end() && it->object.lock() != info.object; ++it) {
289     }
290     if (it != immediateChildren_.end()) {
291         immediateChildren_.erase(it);
292     }
293 }
294 
HierarchyChanged(const HierarchyChangedInfo & info,ObjectChangeListener * listener)295 void ObjectHierarchyObserver::HierarchyChanged(const HierarchyChangedInfo& info, ObjectChangeListener* listener)
296 {
297     if (info.object != GetSelf()) {
298         Invoke<IOnHierarchyChanged>(META_ACCESS_EVENT(OnHierarchyChanged), info);
299         if (info.objectType == HierarchyChangeObjectType::CHILD ||
300             info.objectType == HierarchyChangeObjectType::CONTENT) {
301             // We do not listen to attachments (other than monitoring for changes in an object's attachment list)
302             if (info.change == HierarchyChangeType::ADDING) {
303                 Subscribe(info.object, info.objectType, info.parent);
304                 if (keepTrackOfImmediate_ && listener->GetType() == HierarchyChangeObjectType::ROOT) {
305                     AddImmediateChild(info);
306                 }
307             } else if (info.change == HierarchyChangeType::REMOVED) {
308                 Unsubscribe(info.object);
309                 if (keepTrackOfImmediate_ && listener->GetType() == HierarchyChangeObjectType::ROOT) {
310                     RemoveImmediateChild(info);
311                 }
312             }
313         }
314     }
315 }
316 
Subscribe(const IObject::Ptr & root,HierarchyChangeObjectType type,const IObject::WeakPtr & parent)317 void ObjectHierarchyObserver::Subscribe(
318     const IObject::Ptr& root, HierarchyChangeObjectType type, const IObject::WeakPtr& parent)
319 {
320     if (!root) {
321         return;
322     }
323 
324     AddSubscription(root, type, parent);
325 
326     if (const auto container = interface_cast<IContainer>(root)) {
327         for (auto&& child : container->GetAll()) {
328             if (keepTrackOfImmediate_ && type == HierarchyChangeObjectType::ROOT) {
329                 std::unique_lock lock(mutex_);
330                 immediateChildren_.push_back(ImmediateChild { child, HierarchyChangeObjectType::CHILD });
331             }
332             Subscribe(child, HierarchyChangeObjectType::CHILD, root);
333         }
334     }
335     if (const auto content = interface_cast<IContent>(root)) {
336         if (auto object = GetValue(content->Content())) {
337             if (keepTrackOfImmediate_ && type == HierarchyChangeObjectType::ROOT) {
338                 std::unique_lock lock(mutex_);
339                 immediateChildren_.push_back(ImmediateChild { object, HierarchyChangeObjectType::CONTENT });
340             }
341             Subscribe(object, HierarchyChangeObjectType::CONTENT, root);
342         }
343     }
344 }
345 
AddSubscription(const IObject::Ptr & object,HierarchyChangeObjectType type,const IObject::WeakPtr & parent)346 void ObjectHierarchyObserver::AddSubscription(
347     const IObject::Ptr& object, HierarchyChangeObjectType type, const IObject::WeakPtr& parent)
348 {
349     auto listener = BASE_NS::make_unique<ObjectChangeListener>(object, type, parent, this, mode_);
350 
351     {
352         std::unique_lock lock(mutex_);
353         if (subscriptions_.find(listener.get()) != subscriptions_.end()) {
354             CORE_LOG_E(
355                 "Duplicate event subscription for %s:%s", object->GetClassName().data(), object->GetName().data());
356         }
357         subscriptions_.insert({ listener.get(), Subscription(object, BASE_NS::move(listener)) });
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 META_END_NAMESPACE()
453