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