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 "object.h"
16
17#include <algorithm>
18#include <limits>
19
20#include <base/util/uid_util.h>
21#include <core/plugin/intf_class_factory.h>
22
23#include <meta/api/iteration.h>
24#include <meta/interface/builtin_objects.h>
25#include <meta/interface/intf_object_registry.h>
26#include <meta/interface/intf_proxy_object.h>
27#include <meta/interface/intf_required_interfaces.h>
28#include <meta/interface/property/intf_property_internal.h>
29
30#include "ref_uri_util.h"
31
32META_BEGIN_NAMESPACE()
33namespace Internal {
34
35// ILifecycle
36bool Object::Build(const IMetadata::Ptr& data)
37{
38    return Super::Build(data);
39}
40
41void Object::Destroy()
42{
43    if (attachments_) {
44        attachments_->RemoveAllAttachments();
45        attachments_.reset();
46    }
47    Super::Destroy();
48}
49
50// IAttach
51bool Object::Attach(const IObject::Ptr& attachment, const IObject::Ptr& dataContext)
52{
53    ValidateAttachmentContainer();
54    if (attachments_) {
55        return attachments_->Attach(attachment, dataContext);
56    }
57    return false;
58}
59
60bool Object::Detach(const IObject::Ptr& attachment)
61{
62    if (attachments_) {
63        return attachments_->Detach(attachment);
64    }
65    return false;
66}
67
68BASE_NS::vector<IObject::Ptr> Object::GetAttachments(const BASE_NS::vector<TypeId>& uids, bool strict) const
69{
70    if (attachments_) {
71        return attachments_->GetAttachments(uids, strict);
72    }
73    return {};
74}
75bool Object::HasAttachments() const
76{
77    if (const auto container = interface_cast<IContainer>(attachments_)) {
78        return container->GetSize() > 0;
79    }
80    return false;
81}
82IContainer::Ptr Object::GetAttachmentContainer(bool initializeAlways) const
83{
84    if (initializeAlways) {
85        ValidateAttachmentContainer();
86    }
87    return interface_pointer_cast<IContainer>(attachments_);
88}
89
90void Object::ValidateAttachmentContainer() const
91{
92    if (!attachments_) {
93        if (attachments_ =
94                META_NS::GetObjectRegistry().Create<META_NS::IAttachmentContainer>(ClassId::AttachmentContainer);
95            attachments_) {
96            attachments_->Initialize(GetSelf<META_NS::IAttach>());
97        } else {
98            CORE_LOG_E("Failed to create container for attachments");
99        }
100    }
101}
102
103bool CheckRequiredInterfaces(const IContainer::Ptr& container, const BASE_NS::vector<BASE_NS::Uid>& uids)
104{
105    if (uids.empty()) {
106        return true;
107    }
108    if (auto req = interface_cast<IRequiredInterfaces>(container)) {
109        const auto reqs = req->GetRequiredInterfaces();
110        if (reqs.empty()) {
111            return true; // Container has no requirements related to the interfaces it accepts
112        }
113        size_t matches = 0;
114        for (const auto& uid : uids) {
115            if (std::find(reqs.begin(), reqs.end(), uid) != reqs.end()) {
116                matches++;
117            }
118        }
119        return matches == uids.size();
120    }
121
122    // If container is valid but it does not implement IRequiredInterfaces, anything goes
123    return container.operator bool();
124}
125
126BASE_NS::vector<IContainer::Ptr> Object::FindAllContainers(const ContainerFindOptions& options) const
127{
128    BASE_NS::vector<IContainer::Ptr> containers;
129    const auto maxCount = options.maxCount ? options.maxCount : std::numeric_limits<size_t>::max();
130    const auto& uids = options.uids;
131    const auto addIfMatches = [&containers, &uids](const IContainer::Ptr& container) {
132        if (container) {
133            if (CheckRequiredInterfaces(container, uids)) {
134                containers.push_back(container);
135            }
136        }
137    };
138    if (const auto me = GetSelf<IContainer>()) {
139        // This object is itself a container
140        addIfMatches(me);
141    }
142    if (containers.size() < maxCount) {
143        if (HasAttachments()) {
144            // Check the attachment container
145            addIfMatches(interface_pointer_cast<IContainer>(attachments_));
146            // Check the attachments themselves
147            if (containers.size() < maxCount) {
148                IterateShared(attachments_, [&addIfMatches, &containers, &maxCount](const IObject::Ptr& object) {
149                    addIfMatches(interface_pointer_cast<IContainer>(object));
150                    return containers.size() < maxCount;
151                });
152            }
153        } else {
154            // No attachments, but the user has requested IAttachment so we need to create the container
155            if (uids.empty() || std::find(uids.begin(), uids.end(), IAttachment::UID) != uids.end()) {
156                ValidateAttachmentContainer();
157                addIfMatches(interface_pointer_cast<IContainer>(attachments_));
158            }
159        }
160    }
161    return containers;
162}
163
164const StaticObjectMetadata& Object::GetStaticMetadata() const
165{
166    return GetStaticObjectMetadata();
167}
168
169} // namespace Internal
170
171META_END_NAMESPACE()
172