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 28META_BEGIN_NAMESPACE() 29 30// ContainerChangeListener 31 32ObjectChangeListener::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 40ObjectChangeListener::~ObjectChangeListener() 41{ 42 Unsubscribe(); 43} 44 45void 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 70void 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 96bool 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 123void ObjectChangeListener::Unsubscribe() 124{ 125 handlers_.clear(); 126 content_.reset(); 127} 128 129void 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 141void 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 166void 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 177void 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 209bool 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 219void ObjectHierarchyObserver::Destroy() 220{ 221 ClearSubscriptions(); 222} 223 224void 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 237void 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 251IObject::Ptr ObjectHierarchyObserver::GetTarget() const 252{ 253 return root_.lock(); 254} 255 256BASE_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 269void 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 280void 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 291void 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 313void 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 342void 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 357void 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 383void 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 403void 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 426bool 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} 436bool 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 448META_END_NAMESPACE() 449