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 "attachment_container.h"
16 
17 #include <meta/api/make_callback.h>
18 #include <meta/api/util.h>
19 
20 META_BEGIN_NAMESPACE()
21 
22 AttachmentContainer::AttachmentContainer() = default;
23 
~AttachmentContainer()24 AttachmentContainer::~AttachmentContainer()
25 {
26     RemoveAllAttachments();
27 }
28 
SetSuperInstance(const IObject::Ptr & aggr,const IObject::Ptr & super)29 void AttachmentContainer::SetSuperInstance(const IObject::Ptr& aggr, const IObject::Ptr& super)
30 {
31     Super::SetSuperInstance(aggr, super);
32     transaction_ = interface_cast<IContainerPreTransaction>(super);
33     CORE_ASSERT(transaction_);
34 }
35 
Build(const IMetadata::Ptr & data)36 bool AttachmentContainer::Build(const IMetadata::Ptr& data)
37 {
38     // Always set null as the parent of all our attachments
39     ObjectContainerFwd::SetProxyParent({});
40 
41     OnRemoved()->AddHandler(MakeCallback<IOnChildChanged>(this, &AttachmentContainer::RemovedFromContainer));
42     OnAdding()->AddHandler(MakeCallback<IOnChildChanging>(this, &AttachmentContainer::AddingToContainer));
43     return true;
44 }
45 
Add(const IObject::Ptr & object)46 bool AttachmentContainer::Add(const IObject::Ptr& object)
47 {
48     return Attach(N_POS, object, {});
49 }
50 
Insert(IContainer::SizeType index,const IObject::Ptr & object)51 bool AttachmentContainer::Insert(IContainer::SizeType index, const IObject::Ptr& object)
52 {
53     return Attach(index, object, {});
54 }
55 
Remove(IContainer::SizeType index)56 bool AttachmentContainer::Remove(IContainer::SizeType index)
57 {
58     return Remove(GetAt(index));
59 }
60 
Remove(const IObject::Ptr & child)61 bool AttachmentContainer::Remove(const IObject::Ptr& child)
62 {
63     return Detach(child);
64 }
65 
Replace(const IObject::Ptr & child,const IObject::Ptr & replaceWith,bool addAlways)66 bool AttachmentContainer::Replace(const IObject::Ptr& child, const IObject::Ptr& replaceWith, bool addAlways)
67 {
68     const auto owner = owner_.lock();
69     if (child && AlreadyAttached(replaceWith)) {
70         return true;
71     }
72     return ObjectContainerFwd::Replace(child, replaceWith, addAlways);
73 }
74 
RemoveAll()75 void AttachmentContainer::RemoveAll()
76 {
77     RemoveAllAttachments();
78 }
79 
SetRequiredInterfaces(const BASE_NS::vector<TypeId> & interfaces)80 bool AttachmentContainer::SetRequiredInterfaces(const BASE_NS::vector<TypeId>& interfaces)
81 {
82     CORE_LOG_E("Setting the required interfaces of an attachment container is not allowed.");
83     return false;
84 }
85 
Initialize(const META_NS::IAttach::Ptr & owner)86 bool AttachmentContainer::Initialize(const META_NS::IAttach::Ptr& owner)
87 {
88     if (!owner) {
89         return false;
90     }
91     owner_ = owner;
92     return true;
93 }
94 
Attach(const IObject::Ptr & attachment,const IObject::Ptr & dataContext)95 bool AttachmentContainer::Attach(const IObject::Ptr& attachment, const IObject::Ptr& dataContext)
96 {
97     return Attach(N_POS, attachment, dataContext);
98 }
99 
Attach(IContainer::SizeType pos,const IObject::Ptr & attachment,const IObject::Ptr & dataContext)100 bool AttachmentContainer::Attach(
101     IContainer::SizeType pos, const IObject::Ptr& attachment, const IObject::Ptr& dataContext)
102 {
103     const auto object = interface_pointer_cast<IObject>(attachment);
104     if (!object) {
105         return false;
106     }
107     bool result = false;
108     if (const auto owner = owner_.lock()) {
109         // If attachment is attached to something, detach it
110         if (const auto att = interface_cast<IAttachment>(attachment)) {
111             if (const auto current = GetValue(att->AttachedTo()).lock()) {
112                 if (current == owner) {
113                     // Already attached to this
114                     return true;
115                 }
116                 if (!current->Detach(attachment)) {
117                     return false;
118                 }
119             }
120         }
121         // If no data context given, use this as the data context
122         const auto context = dataContext ? dataContext : interface_pointer_cast<IObject>(owner);
123         addingContexts_.emplace_back(BASE_NS::pair<IObject*, IObject::WeakPtr> { attachment.get(), context });
124         result = ObjectContainerFwd::Insert(pos, object);
125         if (!result) {
126             addingContexts_.pop_back();
127         }
128     }
129     return result;
130 }
131 
Detach(const IObject::Ptr & attachment)132 bool AttachmentContainer::Detach(const IObject::Ptr& attachment)
133 {
134     if (!attachment) {
135         return false;
136     }
137     if (const auto owner = owner_.lock()) {
138         if (const auto att = interface_cast<IAttachment>(attachment)) {
139             const auto current = GetValue(att->AttachedTo()).lock();
140             if (current != owner) {
141                 return false;
142             }
143         }
144         return ObjectContainerFwd::Remove(attachment);
145     }
146     return false;
147 }
148 
AddingToContainer(const ChildChangedInfo & info,bool & success)149 void AttachmentContainer::AddingToContainer(const ChildChangedInfo& info, bool& success)
150 {
151     if (const auto owner = owner_.lock()) {
152         if (auto object = info.object) {
153             IObject::Ptr context;
154             for (auto it = addingContexts_.begin(); it != addingContexts_.end(); it++) {
155                 if (it->first == object.get()) {
156                     context = it->second.lock();
157                     addingContexts_.erase(it);
158                     break;
159                 }
160             }
161             if (auto attachment = interface_pointer_cast<IAttachment>(info.object)) {
162                 if (GetValue(attachment->AttachedTo()).lock() == owner) {
163                     // Already attached to this
164                     return;
165                 }
166                 if (!attachment->Attaching(owner, context)) {
167                     success = false;
168                 }
169             } else {
170                 success = true;
171             }
172         }
173     }
174 }
RemovedFromContainer(const ChildChangedInfo & info)175 void AttachmentContainer::RemovedFromContainer(const ChildChangedInfo& info)
176 {
177     if (const auto owner = owner_.lock()) {
178         if (auto attachment = interface_pointer_cast<IAttachment>(info.object)) {
179             attachment->Detaching(owner);
180         }
181     }
182 }
183 
GetAttachments(const BASE_NS::vector<TypeId> & uids,bool strict)184 BASE_NS::vector<IObject::Ptr> AttachmentContainer::GetAttachments(const BASE_NS::vector<TypeId>& uids, bool strict)
185 {
186     return ObjectContainerFwd::FindAll({ "", TraversalType::NO_HIERARCHY, uids, strict });
187 }
188 
RemoveAllAttachments()189 void AttachmentContainer::RemoveAllAttachments()
190 {
191     const auto owner = owner_.lock();
192     const auto all = ObjectContainerFwd::GetAll();
193     for (const auto& object : all) {
194         if (auto att = interface_cast<IAttachment>(object)) {
195             // Ignore result
196             att->Detaching(owner);
197         }
198     }
199     ObjectContainerFwd::RemoveAll();
200 }
201 
FindByName(const BASE_NS::string & name) const202 IObject::Ptr AttachmentContainer::FindByName(const BASE_NS::string& name) const
203 {
204     return ObjectContainerFwd::FindByName(name);
205 }
206 
AlreadyAttached(const IObject::Ptr & object)207 bool AttachmentContainer::AlreadyAttached(const IObject::Ptr& object)
208 {
209     if (const auto attachment = interface_pointer_cast<IAttachment>(object)) {
210         if (const auto owner = owner_.lock(); attachment && owner) {
211             if (const auto current = GetValue(attachment->AttachedTo()).lock()) {
212                 if (current == owner) {
213                     // Already attached to this
214                     return true;
215                 }
216             }
217         }
218     } else {
219         return META_NS::ContainsObject(GetSelf<IContainer>(), object);
220     }
221     return false;
222 }
223 
224 META_END_NAMESPACE()
225