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 #ifndef META_INTERFACE_ICONTAINER_H
17 #define META_INTERFACE_ICONTAINER_H
18 
19 #include <meta/api/iteration.h>
20 #include <meta/base/ids.h>
21 #include <meta/base/interface_utils.h>
22 #include <meta/base/object_traits.h>
23 #include <meta/interface/interface_macros.h>
24 #include <meta/interface/intf_iterable.h>
25 #include <meta/interface/intf_object.h>
26 #include <meta/interface/property/intf_property.h>
27 #include <meta/interface/simple_event.h>
28 
29 META_BEGIN_NAMESPACE()
30 
31 META_REGISTER_INTERFACE(IContainer, "55ce4604-91f4-4378-9c7d-9ab0fe97b3ce")
32 META_REGISTER_INTERFACE(IContainerProxyParent, "d57c03fc-23ac-4cba-8f9f-0363ca781bfa")
33 
34 META_REGISTER_INTERFACE(IOnChildChanged, "686e62cc-66b1-409d-821f-e5ac69e33863")
35 META_REGISTER_INTERFACE(IOnChildMoved, "5890fe47-4829-42bb-bdb1-03f8a399ba0e")
36 
37 struct IOnChildChangedInfo {
38     constexpr static BASE_NS::Uid UID { META_NS::InterfaceId::IOnChildChanged };
39     constexpr static char const* NAME { "OnChildChanged" };
40 };
41 
42 struct IOnMovedInfo {
43     constexpr static BASE_NS::Uid UID { META_NS::InterfaceId::IOnChildMoved };
44     constexpr static char const *NAME { "OnChildMoved" };
45 };
46 
47 class IContainer;
48 
49 /**
50  * @brief The ChildChangedInfo struct defines the parameters for an event which is invoked when
51  *        an object is added to or removed from an IContainer.
52  */
53 struct ChildChangedInfo {
54     IObject::Ptr object;                        ///< The object
55     size_t index;                               ///< IContainer index where the change happened
56     BASE_NS::weak_ptr<const IContainer> parent; ///< The parent IContainer for the change
57 };
58 
59 /**
60  * @brief The ChildChangedInfo struct defines the parameters for an event which is invoked when
61  *        an object is moved in an IContainer.
62  */
63 struct ChildMovedInfo {
64     IObject::Ptr object;                        ///< The object
65     size_t from;                                ///< IContainer index where the the object was moved from
66     size_t to;                                  ///< IContainer index where the object was moved to
67     BASE_NS::weak_ptr<const IContainer> parent; ///< The parent IContainer for the change
68 };
69 
70 using IOnChildChanged = META_NS::SimpleEvent<IOnChildChangedInfo, void(const ChildChangedInfo&)>;
71 using IOnChildMoved = META_NS::SimpleEvent<IOnMovedInfo, void(const ChildMovedInfo&)>;
72 
73 /**
74  * @brief An interface implemented by objects that may contain other objects.
75  * @note  The implementer shall use IObjectFlags::SERIALIZE_HIERARCHY flag to determine
76  *        if the children should be serialized when exporting the object. By default
77  *        IContainer implementations should serialize their child objects.
78  */
79 class IContainer : public CORE_NS::IInterface {
80     META_INTERFACE(CORE_NS::IInterface, IContainer)
81 public:
82     using SizeType = size_t;
83 
84     /**
85      * @brief The FindOptions struct defines a set of options that can be used to find objects from a container.
86      */
87     struct FindOptions {
88         /** Name of the object to find. If name is empty, only the uids are taken into account. */
89         BASE_NS::string name;
90         /** Search mode */
91         TraversalType behavior { TraversalType::BREADTH_FIRST_ORDER };
92         /** List of interface ids that the object must implement. If empty, no check is made */
93         BASE_NS::vector<TypeId> uids;
94         /** If true, the found object must implement all of the uids.
95          *  If false, any uid is enough to consider the object as a match */
96         bool strict { false };
97     };
98     /**
99      * @brief Returns the contained children.
100      */
101     virtual BASE_NS::vector<IObject::Ptr> GetAll() const = 0;
102     /**
103      * @brief Returns the child at given index.
104      * @param index Index of the child to return.
105      * @return The child at given index or nullptr in case of invalid index.
106      */
107     virtual IObject::Ptr GetAt(SizeType index) const = 0;
108     /**
109      * @brief Returns the number of children in the container. Equal to GetAll().size().
110      */
111     virtual SizeType GetSize() const = 0;
112     /**
113      * @brief Find all objects matching a search criteria from the container.
114      * @param options The options to use for finding.
115      * @return The child objects that match the search criteria.
116      */
117     virtual BASE_NS::vector<IObject::Ptr> FindAll(const FindOptions& options) const = 0;
118     /**
119      * @brief Returns the first found object matching a search criteria from the container.
120      * @note If the user is interested in finding some object that matches the criteria, it
121      *       is more efficient to call FindAny rather than FindAll as the implementation
122      *       can do an early exit when the first suitable object is found.
123      * @param options The options to use for finding.
124      * @return The first object which matches the search criteria.
125      */
126     virtual IObject::Ptr FindAny(const FindOptions& options) const = 0;
127     /**
128      * @brief Get child by name from the immediate children of the container.
129      * @param name Name of the child object to find.
130      * @note  Use FindAny to find from the hierarchy.
131      * @return The first child object with given name or null if non found.
132      */
133     virtual IObject::Ptr FindByName(BASE_NS::string_view name) const = 0;
134     /**
135      * @brief Add child object.
136      * @param object The object to add.
137      * @note An object can be added to the container only once. Subsequent add operations
138      *       for the same object will succeed but do not add anything to the container
139      *       (since the object was already there).
140      * @note Adding may fail due to e.g. the object not fulfilling the constraints set to
141      *       the interfaces each object in the container should implement (defined by calling
142      *       SetRequiredInterfaces).
143      * @return True if the object is in the container after the operation, false otherwise.
144      */
145     virtual bool Add(const IObject::Ptr& object) = 0;
146     /**
147      * @brief Inserts a child object at given index.
148      * @param index The index where the object should be added. If index >= container size, the
149      *              object is added at the end of the container.
150      * @param object The object to add.
151      * @note  If the object is already in the container, the function will move the object
152      *        to the given index.
153      * @return True if the object is in the container after the operation, false otherwise.
154      */
155     virtual bool Insert(SizeType index, const IObject::Ptr& object) = 0;
156     /**
157      * @brief Remove child object.
158      * @param index The index of child object to remove.
159      * @return True if the object was removed from the container, false otherwise.
160      */
161     virtual bool Remove(SizeType index) = 0;
162     /**
163      * @brief Remove child object.
164      * @param child The child object to remove.
165      * @return True if the object was removed from the container, false otherwise.
166      */
167     virtual bool Remove(const IObject::Ptr& child) = 0;
168     /**
169      * @brief Move a child object from one index to another.
170      * @note OnMoved event will be invoked only for the moved item, but not for other items in the
171      *       container whose index may change as a side effect of the move operation.
172      * @param fromIndex The index to move from.
173      * @param toIndex The index to move the object to. The target position is the position in the
174      *                container before the the object has been removed from its original position.
175      * @return True if the object was successfully moved, false otherwise.
176      */
177     virtual bool Move(SizeType fromIndex, SizeType toIndex) = 0;
178     /**
179      * @brief Move a child object to a new index.
180      * @note OnMoved event will be invoked only for the moved item, but not for other items in the
181      *       container whose index may change as a side effect of the move operation.
182      * @param child The child to move.
183      * @param toIndex The index to move the object to. The target position is the position in the
184      *                container before the the object has been removed from its original position.
185      * @return True if the object was successfully moved, false otherwise.
186      */
187     virtual bool Move(const IObject::Ptr& child, SizeType toIndex) = 0;
188     /**
189      * @brief Replace a child item with another one. The function will fail if child is not found.
190      * @note  Does nothing if the parameters point to the same object.
191      * @param child The child to replace. If the child is not found from the container, does nothing or
192      *        adds replaceWith to the container, depending on addAlways.
193      * @param replaceWith The item to replace the child with. If null, the child is removed.
194      * @return True if any changes were made in the container as a result of this call, or if child == replaceWith
195      *         and child is found from the container. In other cases returns false.
196      */
Replace(const IObject::Ptr & child,const IObject::Ptr & replaceWith)197     bool Replace(const IObject::Ptr& child, const IObject::Ptr& replaceWith)
198     {
199         return Replace(child, replaceWith, false);
200     }
201     /**
202      * @brief Replace a child item with another one.
203      * @note  Does nothing if the parameters point to the same object.
204      * @note  If both child and replaceWith are already in the container, child is removed and replaceWith
205      *        is moved to its location.
206      * @param child The child to replace. If the child is not found from the container, does nothing or
207      *        adds replaceWith to the container, depending on addAlways.
208      * @param replaceWith The item to replace the child with. If null, the child is removed.
209      * @param addAlways If true, and if child is not found from the container, adds replaceWith to
210      *        the end of the container. If false, the function will fail if child is not found.
211      * @return True if any changes were made in the container as a result of this call, or if child == replaceWith
212      *         and child is found from the container. In other cases returns false.
213      */
214     virtual bool Replace(const IObject::Ptr& child, const IObject::Ptr& replaceWith, bool addAlways) = 0;
215     /**
216      * @brief Removes all children from container.
217      */
218     virtual void RemoveAll() = 0;
219     /**
220      * @brief Invoked after an object was added to the container.
221      */
META_EVENT(IOnChildChanged,OnAdded)222     META_EVENT(IOnChildChanged, OnAdded)
223     /**
224      * @brief Invoked after an object was removed from the container.
225      */
226     META_EVENT(IOnChildChanged, OnRemoved)
227     /**
228      * @brief Invoked after an object was moved from one index to another in the container.
229      */
230     META_EVENT(IOnChildMoved, OnMoved)
231     /**
232      * @brief A helper template for finding a child item in this container that matches the name and implements
233      *        the interface given as a template parameter.
234      *
235      *        The template parameter interface is implicitly given as a parameter to the IContainer::FindAny function.
236      */
237     template<class T>
238     typename T::Ptr FindAny(BASE_NS::string_view name, TraversalType order) const
239     {
240         return interface_pointer_cast<T>(FindAny({ BASE_NS::string(name), order, { T::UID }, false }));
241     }
242     /**
243      * @brief A helper template for finding a child item in this container that matches the name.
244      */
245     template<class T>
FindByName(BASE_NS::string_view name)246     typename T::Ptr FindByName(BASE_NS::string_view name) const
247     {
248         return interface_pointer_cast<T>(FindByName(name));
249     }
250     /**
251      * @brief A helper template for finding a child item in this container that matches the name and implements
252      *        the interface given as a template parameter.
253      */
254     template<class T>
FindAnyFromHierarchy(BASE_NS::string_view name)255     typename T::Ptr FindAnyFromHierarchy(BASE_NS::string_view name) const
256     {
257         return FindAny<T>(name, TraversalType::BREADTH_FIRST_ORDER);
258     }
259     /**
260      * @brief Typed helper for IContainer::GetAll, returns all children which implement T.
261      */
262     template<class T>
GetAll()263     BASE_NS::vector<typename T::Ptr> GetAll() const
264     {
265         return PtrArrayCast<T>(GetAll());
266     }
267     /**
268      * @brief Checks if object is part of this container's hierarchy.
269      * @param object The object to check.
270      * @return True if object can be found from this container's hierarchy.
271      */
272     virtual bool IsAncestorOf(const IObject::ConstPtr& object) const = 0;
273 
274 public:
275     /**
276      * @brief Typed helper for IContainer::GetAt, returns object at index converted to T::Ptr.
277      */
278     template<class T>
GetAt(SizeType index)279     typename T::Ptr GetAt(SizeType index) const
280     {
281         return interface_pointer_cast<T>(GetAt(index));
282     }
283     /**
284      * @brief Typed helper for IContainer::Add
285      */
286     template<class T>
Add(const T & object)287     bool Add(const T& object)
288     {
289         return Add(interface_pointer_cast<IObject>(object));
290     }
291     /**
292      * @brief Typed helper for IContainer::Insert
293      */
294     template<class T>
Insert(SizeType index,const T & object)295     bool Insert(SizeType index, const T& object)
296     {
297         return Insert(index, interface_pointer_cast<IObject>(object));
298     }
299     /**
300      * @brief Typed helper for IContainer::Remove
301      */
302     template<class T, class = BASE_NS::enable_if_t<IsInterfacePtr_v<T>>>
Remove(const T & child)303     bool Remove(const T& child)
304     {
305         return Remove(interface_pointer_cast<IObject>(child));
306     }
307     /**
308      * @brief Typed helper for IContainer::Replace
309      */
310     template<class T1, class T2>
311     bool Replace(const T1& child, const T2& replaceWith, bool addAlways = false)
312     {
313         return Replace(interface_pointer_cast<IObject>(child), interface_pointer_cast<IObject>(replaceWith), addAlways);
314     }
315 };
316 
317 /**
318  * @brief Typed helper for IContainer::GetAll. Returns all children which implement T from container.
319  */
320 template<class T>
GetAll(const META_NS::IContainer::ConstPtr & container)321 BASE_NS::vector<typename T::Ptr> GetAll(const META_NS::IContainer::ConstPtr& container)
322 {
323     if (!container) {
324         return {};
325     }
326     if constexpr (IsIObjectPtr_v<typename T::Ptr>) {
327         return container->GetAll();
328     } else {
329         return container->GetAll<T>();
330     }
331 }
332 
333 /**
334  * @brief Returns true if a container contains a given child object.
335  * @param container The container to check against.
336  * @param child The child to find.
337  * @return True if the child can be found in the container, false otherwise.
338  */
339 template<class T, class = BASE_NS::enable_if_t<IsInterfacePtr_v<T>>>
ContainsObject(const META_NS::IContainer::ConstPtr & container,const T & child)340 bool ContainsObject(const META_NS::IContainer::ConstPtr& container, const T& child)
341 {
342     bool found = false;
343     if (const auto object = interface_pointer_cast<IObject>(child); container && object) {
344         IterateShared(container, [&](const IObject::Ptr& o) {
345             if (o == object) {
346                 found = true;
347             }
348             return !found;
349         });
350     }
351     return found;
352 }
353 
354 /**
355  * @brief The IContainerProxyParent interface can be used to defined a proxied parent
356  *        that an IContainer implementation sets to its child objects.
357  * @note  Child objects must implement IMutableContainable for the proxy to have effect.
358  */
359 class IContainerProxyParent : public CORE_NS::IInterface {
360     META_INTERFACE(CORE_NS::IInterface, IContainerProxyParent)
361 public:
362     /**
363      * @brief Sets the object that should be used as the parent object when an IContainer
364      *        implementation calls IMutableContainable::SetParent on objects that are
365      *        added to the container.
366      * @note  This call only affects the parent for objects added to the container
367      *        after SetProxyParent is called. Existing objects are not updated.
368      * @param parent The parent container to set.
369      * @return True if setting the parent was successful, false otherwise.
370      */
371     virtual bool SetProxyParent(const IContainer::Ptr& parent) = 0;
372 };
373 
374 META_REGISTER_INTERFACE(IContainerPreTransaction, "1a1d3ba0-302c-442f-8a13-0701938a5f4c")
375 META_REGISTER_INTERFACE(IOnChildChanging, "385aec1c-0d4b-4ca1-85b5-6235e13ded3e")
376 META_REGISTER_INTERFACE(IOnChildChangingCallable, "7977c014-8181-465d-8d86-48d30c754e6c")
377 
378 /**
379  * @brief IOnChildChanging defines the interface for IContainerPreTransaction events.
380  * @param info Information about the change that is about to happen
381  * @param success The callee can request canceling the operation by setting the parameter to false.
382  * @note Cancelling the operation may not always be possible.
383  * @note Cancelling the operation does not work for deferred event callbacks.
384  */
385 using IOnChildChanging = META_NS::SimpleEvent<IOnChildChangedInfo, void(const ChildChangedInfo& info, bool& success)>;
386 
387 /**
388  * @brief The IContainerPreTransaction interface can be implemenced by containers which support invoking events
389  *        about transactions within the container that are about to happen.
390  */
391 class IContainerPreTransaction : public CORE_NS::IInterface {
392     META_INTERFACE(CORE_NS::IInterface, IContainerPreTransaction)
393 public:
394     /**
395      * @brief Invoked when an object is about to be added to the container.
396      */
397     META_EVENT(IOnChildChanging, OnAdding)
398     /**
399      * @brief Invoked when an object is about to be removed from the container.
400      */
401     META_EVENT(IOnChildChanging, OnRemoving)
402 };
403 
404 META_END_NAMESPACE()
405 
406 META_TYPE(META_NS::IContainer::Ptr)
407 META_TYPE(META_NS::IContainer::WeakPtr)
408 META_TYPE(META_NS::TraversalType)
409 
410 #endif
411