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
24 META_BEGIN_NAMESPACE()
25
26 namespace Internal {
27
28 // ContainerChangeListener
29
ContainerChangeListener(const IContainer::Ptr & container)30 ContainerChangeListener::ContainerChangeListener(const IContainer::Ptr& container)
31 : changed_ { 0, {} }, container_(container)
32 {}
33
~ContainerChangeListener()34 ContainerChangeListener::~ContainerChangeListener()
35 {
36 Unsubscribe();
37 }
38
39 ContainerChangeListener::ContainerChangeListener(ContainerChangeListener&& other) = default;
40
operator =(ContainerChangeListener && other)41 ContainerChangeListener& ContainerChangeListener::operator=(ContainerChangeListener&& other)
42 {
43 if (&other == this) {
44 return *this;
45 }
46 Unsubscribe();
47
48 container_ = BASE_NS::move(container_);
49 changed_ = BASE_NS::move(changed_);
50
51 return *this;
52 }
53
operator ==(const ContainerChangeListener & other) const54 bool ContainerChangeListener::operator==(const ContainerChangeListener& other) const noexcept
55 {
56 return other.changed_.first == changed_.first;
57 }
58
Subscribe(const IOnChildChanged::InterfaceTypePtr & onChanged)59 bool ContainerChangeListener::Subscribe(const IOnChildChanged::InterfaceTypePtr& onChanged)
60 {
61 Unsubscribe();
62 if (const auto container = container_.lock()) {
63 changed_ = { container->OnContainerChanged()->AddHandler(onChanged), onChanged };
64 return changed_.first;
65 }
66 return false;
67 }
68
Unsubscribe()69 void ContainerChangeListener::Unsubscribe()
70 {
71 if (const auto container = container_.lock()) {
72 if (changed_.first) {
73 container->OnContainerChanged()->RemoveHandler(changed_.first);
74 }
75 }
76 changed_ = {};
77 }
78
79 // ContainerObserver
80
Build(const IMetadata::Ptr & p)81 bool ContainerObserver::Build(const IMetadata::Ptr& p)
82 {
83 bool ret = Super::Build(p);
84 if (ret) {
85 changedCallable_ = MakeCallback<IOnChildChanged>(this, &ContainerObserver::InvokeChanged);
86 }
87 return ret;
88 }
89
SetContainer(const IContainer::Ptr & container)90 void ContainerObserver::SetContainer(const IContainer::Ptr& container)
91 {
92 subscriptions_.clear();
93 container_ = container;
94 Subscribe(container_);
95 }
96
InvokeChanged(const ChildChangedInfo & info)97 void ContainerObserver::InvokeChanged(const ChildChangedInfo& info)
98 {
99 Invoke<IOnChildChanged>(META_ACCESS_EVENT(OnDescendantChanged), info);
100 if (info.type == ContainerChangeType::ADDED) {
101 Subscribe(interface_pointer_cast<IContainer>(info.object));
102 } else if (info.type == ContainerChangeType::REMOVED) {
103 Unsubscribe(interface_pointer_cast<IContainer>(info.object));
104 }
105 }
106
Subscribe(const IContainer::Ptr & base)107 void ContainerObserver::Subscribe(const IContainer::Ptr& base)
108 {
109 if (!base) {
110 return;
111 }
112 auto listener = ContainerChangeListener(base);
113 listener.Subscribe(changedCallable_);
114
115 subscriptions_.emplace_back(base, BASE_NS::move(listener));
116
117 for (const auto& child : base->GetAll<IContainer>()) {
118 Subscribe(child);
119 }
120 }
121
Unsubscribe(const IContainer::Ptr & base)122 void ContainerObserver::Unsubscribe(const IContainer::Ptr& base)
123 {
124 if (!base) {
125 return;
126 }
127 auto it = subscriptions_.begin();
128 while (it != subscriptions_.end()) {
129 const auto& sub = *it;
130 if (const auto c = sub.container.lock()) {
131 const auto object = interface_pointer_cast<const IObject>(c);
132 if (base == c || base->IsAncestorOf(object)) {
133 it = subscriptions_.erase(it);
134 continue;
135 }
136 }
137 ++it;
138 }
139 }
140
141 } // namespace Internal
142
143 META_END_NAMESPACE()
144