• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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