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