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