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 #include "hierarchy_controller.h"
16 
17 #include <meta/api/future.h>
18 #include <meta/api/make_callback.h>
19 #include <meta/api/task.h>
20 #include <meta/interface/intf_content.h>
21 #include <meta/interface/intf_task_queue_registry.h>
22 
23 using namespace META_NS;
24 
SCENE_BEGIN_NAMESPACE()25 SCENE_BEGIN_NAMESPACE()
26 
27 bool NodeHierarchyController::Build(const IMetadata::Ptr& data)
28 {
29     observer_ = GetObjectRegistry().Create<IObjectHierarchyObserver>(META_NS::ClassId::ObjectHierarchyObserver);
30     CORE_ASSERT(observer_);
31 
32     // Do not serialize the observer.
33     META_NS::SetObjectFlags(observer_, META_NS::ObjectFlagBits::SERIALIZE, false);
34 
35     observer_->OnHierarchyChanged()->AddHandler(
36         MakeCallback<IOnHierarchyChanged>(this, &NodeHierarchyController::HierarchyChanged));
37 
38     return true;
39 }
40 
Destroy()41 void NodeHierarchyController::Destroy()
42 {
43     SetTarget({}, {});
44     observer_.reset();
45 }
46 
SetTarget(const IObject::Ptr & hierarchyRoot,HierarchyChangeModeValue mode)47 void NodeHierarchyController::SetTarget(const IObject::Ptr& hierarchyRoot, HierarchyChangeModeValue mode)
48 {
49     if (!observer_) {
50         return;
51     }
52     target_ = hierarchyRoot;
53 
54     if (!hierarchyRoot) {
55         DetachAll();
56     }
57 
58     observer_->SetTarget(hierarchyRoot, mode);
59 
60     if (hierarchyRoot) {
61         AttachAll();
62     }
63 }
64 
GetTarget() const65 IObject::Ptr NodeHierarchyController::GetTarget() const
66 {
67     return observer_->GetTarget();
68 }
69 
GetAllObserved() const70 BASE_NS::vector<IObject::Ptr> NodeHierarchyController::GetAllObserved() const
71 {
72     return observer_->GetAllObserved();
73 }
74 
AttachAll()75 bool NodeHierarchyController::AttachAll()
76 {
77     if (const auto root = target_.lock()) {
78         return RunOperation({ NodeOperation::ATTACH, target_ });
79     }
80     return false;
81 }
82 
DetachAll()83 bool NodeHierarchyController::DetachAll()
84 {
85     if (auto root = target_.lock()) {
86         return RunOperation({ NodeOperation::DETACH, target_ });
87     }
88     return false;
89 }
90 
HierarchyChanged(const HierarchyChangedInfo & info)91 void NodeHierarchyController::HierarchyChanged(const HierarchyChangedInfo& info)
92 {
93     if (info.change == HierarchyChangeType::ADDED) {
94         RunOperation({ NodeOperation::ATTACH, info.object });
95     } else if (info.change == HierarchyChangeType::REMOVING) {
96         RunOperation({ NodeOperation::DETACH, info.object });
97     }
98 }
99 
100 template<class T, class Callback>
IterateChildren(const BASE_NS::vector<T> & children,bool reverse,Callback && callback)101 void IterateChildren(const BASE_NS::vector<T>& children, bool reverse, Callback&& callback)
102 {
103     if (reverse) {
104         for (auto it = children.rbegin(); it != children.rend(); ++it) {
105             callback(*it);
106         }
107     } else {
108         for (auto&& child : children) {
109             callback(child);
110         }
111     }
112 }
113 
114 template<class Callback>
IterateHierarchy(const IObject::Ptr & root,bool reverse,Callback && callback)115 void IterateHierarchy(const IObject::Ptr& root, bool reverse, Callback&& callback)
116 {
117     if (const auto container = interface_cast<IContainer>(root)) {
118         IterateChildren(container->GetAll(), reverse, callback);
119     }
120     if (const auto content = interface_cast<IContent>(root)) {
121         if (auto object = GetValue(content->Content())) {
122             callback(object);
123         }
124     }
125 }
126 
GetAllNodes() const127 BASE_NS::vector<INode::Ptr> NodeHierarchyController::GetAllNodes() const
128 {
129     BASE_NS::vector<INode::Ptr> nodes;
130     auto add = [&nodes](const INode::Ptr& node) { nodes.push_back(node); };
131     if (const auto root = target_.lock()) {
132         IterateShared(
133             root, [&add](const IObject::Ptr& object) { return true; }, TraversalType::DEPTH_FIRST_POST_ORDER);
134     }
135     return nodes;
136 }
137 
AttachHierarchy(const IObject::Ptr & root)138 void NodeHierarchyController::AttachHierarchy(const IObject::Ptr& root)
139 {
140     if (!root) {
141         return;
142     }
143 
144     AttachNode(interface_cast<INode>(root));
145     IterateHierarchy(root, false, [this](const IObject::Ptr& object) { AttachHierarchy(object); });
146 }
147 
AttachNode(INode * const node)148 void NodeHierarchyController::AttachNode(INode* const node)
149 {
150     if (node) {
151         const auto state = node->GetAttachedState();
152         if (state == NodeState::DETACHED) {
153             node->AttachToHierarchy();
154         }
155     }
156 }
157 
DetachHierarchy(const IObject::Ptr & root)158 void NodeHierarchyController::DetachHierarchy(const IObject::Ptr& root)
159 {
160     if (!root) {
161         return;
162     }
163 
164     IterateHierarchy(root, true, [this](const IObject::Ptr& object) { DetachHierarchy(object); });
165     DetachNode(interface_cast<INode>(root));
166 }
167 
DetachNode(INode * const node)168 void NodeHierarchyController::DetachNode(INode* const node)
169 {
170     if (node) {
171         const auto state = node->GetAttachedState();
172         if (state == NodeState::ATTACHED) {
173             node->DetachFromHierarchy();
174         }
175     }
176 }
177 
RunOperation(NodeOperation operation)178 bool NodeHierarchyController::RunOperation(NodeOperation operation)
179 {
180     auto object = operation.root_.lock();
181     if (!object) {
182         return false;
183     }
184 
185     // This may potentially end up calling Attach/DetachHierarchy several times
186     // for the same subtrees, but we will accept that. Attach/Detach will only
187     // be called once since the functions check for current state.
188     if (auto root = operation.root_.lock()) {
189         if (operation.operation_ == NodeOperation::ATTACH) {
190             AttachHierarchy(root);
191         } else if (operation.operation_ == NodeOperation::DETACH) {
192             DetachHierarchy(root);
193         }
194     }
195 
196     return true;
197 }
198 
RegisterNodeHierarchyController()199 void RegisterNodeHierarchyController()
200 {
201     META_NS::GetObjectRegistry().RegisterObjectType<NodeHierarchyController>();
202 }
UnregisterNodeHierarchyController()203 void UnregisterNodeHierarchyController()
204 {
205     META_NS::GetObjectRegistry().UnregisterObjectType<NodeHierarchyController>();
206 }
207 
208 SCENE_END_NAMESPACE()
209