• 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.h"
17 
18 #include <algorithm>
19 
20 #include <base/math/mathf.h>
21 
22 #include <meta/base/interface_utils.h>
23 #include <meta/interface/intf_containable.h>
24 #include <meta/interface/intf_content.h>
25 #include <meta/interface/property/intf_property.h>
26 
META_BEGIN_NAMESPACE()27 META_BEGIN_NAMESPACE()
28 
29 IObject::Ptr Container::FindAny(const META_NS::IContainer::FindOptions& options) const
30 {
31     return ContainerBase::FindAnyImpl(options, false);
32 }
33 
FindAll(const META_NS::IContainer::FindOptions & options) const34 BASE_NS::vector<IObject::Ptr> Container::FindAll(const META_NS::IContainer::FindOptions& options) const
35 {
36     return ContainerBase::FindAllImpl(options, false);
37 }
38 
Add(const META_NS::IObject::Ptr & object)39 bool Container::Add(const META_NS::IObject::Ptr& object)
40 {
41     return Container::Insert(-1, object);
42 }
43 
Insert(SizeType index,const IObject::Ptr & object)44 bool Container::Insert(SizeType index, const IObject::Ptr& object)
45 {
46     if (!object) {
47         return false;
48     }
49     {
50         std::unique_lock lock(mutex_);
51         if (!IsCompatible(object) || !CheckLoop(object)) {
52             return false;
53         }
54         if (auto it = std::find(children_.begin(), children_.end(), object); it != children_.end()) {
55             // Already in the container, fail
56             return false;
57         }
58         index = BASE_NS::Math::min(index, children_.size());
59         children_.insert(children_.begin() + index, object);
60     }
61     ChildChangedInfo info { ContainerChangeType::ADDED, object, parent_, size_t(-1), index };
62     SetObjectParent(object, interface_pointer_cast<IObject>(parent_));
63     Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING), info);
64     return true;
65 }
66 
Replace(const META_NS::IObject::Ptr & child,const META_NS::IObject::Ptr & replaceWith,bool addAlways)67 bool Container::Replace(const META_NS::IObject::Ptr& child, const META_NS::IObject::Ptr& replaceWith, bool addAlways)
68 {
69     IObject::Ptr removed;
70     IObject::Ptr added;
71     IObject::Ptr moved;
72     IContainer::SizeType fromIndex = 0;
73     IContainer::SizeType toIndex = 0;
74     bool changed = false; // True if any changes were made
75     {
76         std::unique_lock lock(mutex_);
77         if (replaceWith && (!IsCompatible(replaceWith) || !CheckLoop(replaceWith))) {
78             return false;
79         }
80         auto removedIt = children_.end();
81         auto movedIt = children_.end();
82         SizeType index = 0;
83         // Check if 'child' and 'replaceWith' can already be found from the container
84         for (auto it = children_.begin(); it != children_.end(); ++it, ++index) {
85             if (*it == child) {
86                 removedIt = it;
87                 removed = *it;
88                 toIndex = index;
89             }
90             if (*it == replaceWith) {
91                 movedIt = it;
92                 moved = *it;
93                 fromIndex = index;
94             }
95             if (removed && moved) {
96                 break;
97             }
98         }
99         if (removed && removed == moved) {
100             // 'child' and 'replaceWith' are the same item (which is already in the container)
101             return true;
102         }
103         if (removed) {
104             // 'child' was found
105             if (replaceWith) {
106                 *removedIt = replaceWith;
107                 if (moved) {
108                     // 'replaceWith' was already in the container, remove it from its previous position
109                     children_.erase(movedIt);
110                 } else {
111                     // 'replaceWith' was added
112                     added = replaceWith;
113                 }
114             } else {
115                 // 'replaceWith' == null, remove 'child'
116                 children_.erase(removedIt);
117             }
118             changed = true;
119         } else {
120             moved.reset();
121         }
122         if (!changed && addAlways && replaceWith && movedIt == children_.end()) {
123             // 'child' was not found in container but addAlways is specified with a valid new item which
124             // was not previously in the container, add it
125             toIndex = children_.size();
126             children_.push_back(replaceWith);
127             added = replaceWith;
128             changed = true;
129         }
130     }
131     if (changed) {
132         // We did some changes to the container
133         ChildChangedInfo addedInfo { ContainerChangeType::ADDED, added, parent_, size_t(-1), toIndex };
134         ChildChangedInfo removedInfo { ContainerChangeType::REMOVED, removed, parent_, toIndex };
135         SetObjectParent(removed, nullptr);
136         SetObjectParent(added, interface_pointer_cast<IObject>(parent_));
137         if (removed) {
138             Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING), removedInfo);
139         }
140         if (added) {
141             Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING), addedInfo);
142         }
143         if (moved) {
144             Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING),
145                 ChildChangedInfo { ContainerChangeType::MOVED, moved, parent_, fromIndex, toIndex });
146         }
147     }
148     return changed;
149 }
150 
SetObjectParent(const IObject::Ptr & object,const IObject::Ptr & parent) const151 void Container::SetObjectParent(const IObject::Ptr& object, const IObject::Ptr& parent) const
152 {
153     const auto set = interface_cast<IMutableContainable>(object);
154     if (!set) {
155         // Object does not support setting a parent
156         return;
157     }
158     if (const auto cont = interface_cast<IContainable>(object)) {
159         // Remove from old parent (if any)
160         if (const auto old = interface_pointer_cast<IContainer>(cont->GetParent())) {
161             if (old == interface_pointer_cast<IContainer>(parent)) {
162                 // The object is already a child of the new parent container
163                 return;
164             }
165             old->Remove(object);
166         }
167     }
168     set->SetParent(parent);
169 }
170 
CheckLoop(const IObject::Ptr & object) const171 bool Container::CheckLoop(const IObject::Ptr& object) const
172 {
173     // Check if adding object to "this" would lead to a loop in the hierarchy.
174     // This can only happen if object itself is a container which happens to be
175     // an ancestor of ours.
176     if (const auto cc = interface_cast<const IContainer>(object)) {
177         if (const auto me = interface_cast<IObjectInstance>(impl_)) {
178             if (cc->IsAncestorOf(me->GetSelf())) {
179                 CORE_LOG_E("Adding '%s' to '%s' would lead to a loop in the container", object->GetName().c_str(),
180                     me->GetName().c_str());
181                 return false;
182             }
183         }
184     }
185     return true;
186 }
187 
188 META_END_NAMESPACE()
189