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