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 "container_observer.h"
17
18 #include <algorithm>
19
20 #include <meta/api/make_callback.h>
21 #include <meta/base/interface_utils.h>
22 #include <meta/interface/intf_containable.h>
23
META_BEGIN_NAMESPACE()24 META_BEGIN_NAMESPACE()
25
26 // ContainerChangeListener
27
28 ContainerChangeListener::ContainerChangeListener(const IContainer::Ptr& container)
29 : added_ { 0, {} }, removed_ { 0, {} }, moved_ { 0, {} }, container_(container)
30 {}
31
~ContainerChangeListener()32 ContainerChangeListener::~ContainerChangeListener()
33 {
34 Unsubscribe();
35 }
36
37 ContainerChangeListener::ContainerChangeListener(ContainerChangeListener&& other) = default;
38
operator =(ContainerChangeListener && other)39 ContainerChangeListener& ContainerChangeListener::operator=(ContainerChangeListener&& other)
40 {
41 if (&other == this) {
42 return *this;
43 }
44 Unsubscribe();
45
46 container_ = BASE_NS::move(container_);
47 added_ = BASE_NS::move(added_);
48 removed_ = BASE_NS::move(removed_);
49 moved_ = BASE_NS::move(moved_);
50
51 return *this;
52 }
53
operator ==(const ContainerChangeListener & other) const54 bool ContainerChangeListener::operator==(const ContainerChangeListener& other) const noexcept
55 {
56 return other.added_.first == added_.first && other.removed_.first == removed_.first &&
57 other.moved_.first == moved_.first;
58 }
59
Subscribe(const IOnChildChanged::InterfaceTypePtr & onAdded,const IOnChildChanged::InterfaceTypePtr & onRemoved,const IOnChildMoved::InterfaceTypePtr & onMoved)60 bool ContainerChangeListener::Subscribe(const IOnChildChanged::InterfaceTypePtr& onAdded,
61 const IOnChildChanged::InterfaceTypePtr& onRemoved, const IOnChildMoved::InterfaceTypePtr& onMoved)
62 {
63 Unsubscribe();
64 if (const auto container = container_.lock()) {
65 added_ = { container->OnAdded()->AddHandler(onAdded), onAdded };
66 removed_ = { container->OnRemoved()->AddHandler(onRemoved), onRemoved };
67 moved_ = { container->OnMoved()->AddHandler(onMoved), onMoved };
68 return added_.first && removed_.first && moved_.first;
69 }
70 return false;
71 }
72
Unsubscribe()73 void ContainerChangeListener::Unsubscribe()
74 {
75 if (const auto container = container_.lock()) {
76 if (added_.first) {
77 container->OnAdded()->RemoveHandler(added_.first);
78 }
79 if (removed_.first) {
80 container->OnRemoved()->RemoveHandler(removed_.first);
81 }
82 if (moved_.first) {
83 container->OnMoved()->RemoveHandler(moved_.first);
84 }
85 }
86 added_ = {};
87 removed_ = {};
88 moved_ = {};
89 }
90
91 // ContainerObserver
92
Build(const IMetadata::Ptr & p)93 bool ContainerObserver::Build(const IMetadata::Ptr& p)
94 {
95 bool ret = Super::Build(p);
96 if (ret) {
97 addedCallable_ = MakeCallback<IOnChildChanged>(this, &ContainerObserver::InvokeAdded);
98 removedCallable_ = MakeCallback<IOnChildChanged>(this, &ContainerObserver::InvokeRemoved);
99 movedCallable_ = MakeCallback<IOnChildMoved>(this, &ContainerObserver::InvokeMoved);
100 }
101 return ret;
102 }
103
SetContainer(const IContainer::Ptr & container)104 void ContainerObserver::SetContainer(const IContainer::Ptr& container)
105 {
106 subscriptions_.clear();
107 container_ = container;
108 Subscribe(container_);
109 }
110
InvokeAdded(const ChildChangedInfo & info)111 void ContainerObserver::InvokeAdded(const ChildChangedInfo& info)
112 {
113 META_ACCESS_EVENT(OnDescendantAdded)->Invoke(info);
114 Subscribe(interface_pointer_cast<IContainer>(info.object));
115 }
116
InvokeRemoved(const ChildChangedInfo & info)117 void ContainerObserver::InvokeRemoved(const ChildChangedInfo& info)
118 {
119 META_ACCESS_EVENT(OnDescendantRemoved)->Invoke(info);
120 Unsubscribe(interface_pointer_cast<IContainer>(info.object));
121 }
122
InvokeMoved(const ChildMovedInfo & info)123 void ContainerObserver::InvokeMoved(const ChildMovedInfo& info)
124 {
125 META_ACCESS_EVENT(OnDescendantMoved)->Invoke(info);
126 }
127
Subscribe(const IContainer::Ptr & base)128 void ContainerObserver::Subscribe(const IContainer::Ptr& base)
129 {
130 if (!base) {
131 return;
132 }
133 auto listener = ContainerChangeListener(base);
134 listener.Subscribe(addedCallable_, removedCallable_, movedCallable_);
135
136 subscriptions_.emplace_back(base, BASE_NS::move(listener));
137
138 for (const auto& child : base->GetAll<IContainer>()) {
139 Subscribe(child);
140 }
141 }
142
Unsubscribe(const IContainer::Ptr & base)143 void ContainerObserver::Unsubscribe(const IContainer::Ptr& base)
144 {
145 if (!base) {
146 return;
147 }
148 auto it = subscriptions_.begin();
149 while (it != subscriptions_.end()) {
150 const auto& sub = *it;
151 if (const auto c = sub.container.lock()) {
152 const auto object = interface_pointer_cast<const IObject>(c);
153 if (base == c || base->IsAncestorOf(object)) {
154 it = subscriptions_.erase(it);
155 continue;
156 }
157 }
158 ++it;
159 }
160 }
161
162 META_END_NAMESPACE()
163