* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "proxy_object.h"
#include <algorithm>
#include <meta/api/make_callback.h>
#include <meta/api/property/property_event_handler.h>
#include <meta/api/util.h>
META_BEGIN_NAMESPACE()
namespace Internal {
ProxyObject::~ProxyObject()
{
if (meta_) {
ResetTargetListener();
for (auto&& p : meta_->GetProperties()) {
p->OnChanged()->RemoveHandler(uintptr_t(this));
}
}
}
bool ProxyObject::Build(const IMetadata::Ptr& data)
{
bool ret = Super::Build(data);
if (ret) {
meta_ = interface_cast<META_NS::IMetadata>(Super::GetAttachmentContainer(true));
if (!meta_) {
return false;
}
Dynamic()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
if (Dynamic()->GetValue()) {
ListenTargetChanges();
RefreshProperties();
} else {
ResetTargetListener();
}
}));
for (auto&& p : meta_->GetProperties()) {
p->OnChanged()->AddHandler(MakeCallback<IOnChanged>(
[this](auto p) {
if (p) {
OnPropertyChanged(p);
}
},
p),
uintptr_t(this));
}
if (auto meta = GetSelf<IAttach>()) {
metaAdded_.Subscribe<IOnChildChanged>(
meta->GetAttachmentContainer(true)->OnContainerChanged(), [this](const auto& i) {
if (interface_cast<IProperty>(i.object)) {
if (i.type == ContainerChangeType::ADDED) {
OnPropertyAdded(i);
} else if (i.type == ContainerChangeType::REMOVED) {
OnPropertyRemoved(i);
}
}
});
}
UpdateSerializeState();
}
return ret;
}
static bool SerializeEmbeddedProxy(IProperty::Ptr p)
{
if (auto proxy = GetPointer<IProxyObject>(p)) {
if (auto f = interface_cast<IObjectFlags>(proxy)) {
return static_cast<bool>(f->GetObjectFlags() & ObjectFlagBits::SERIALIZE);
}
}
return false;
}
void ProxyObject::OnPropertyAdded(const ChildChangedInfo& info)
{
if (auto p = interface_pointer_cast<IProperty>(info.object)) {
auto f = GetSelf<IObjectFlags>();
if (!updating_ && f && !(f->GetObjectFlags() & ObjectFlagBits::SERIALIZE)) {
META_NS::SetObjectFlags(f, ObjectFlagBits::SERIALIZE, ShouldSerialise(p));
}
p->OnChanged()->AddHandler(MakeCallback<IOnChanged>(
[this](auto p) {
if (p) {
OnPropertyChanged(p);
}
},
p),
uintptr_t(this));
}
}
void ProxyObject::OnPropertyRemoved(const ChildChangedInfo& info)
{
if (auto p = interface_cast<IProperty>(info.object)) {
p->OnChanged()->RemoveHandler(uintptr_t(this));
}
auto f = GetSelf<IObjectFlags>();
if (!updating_ && f && f->GetObjectFlags() & ObjectFlagBits::SERIALIZE) {
UpdateSerializeState();
}
}
void ProxyObject::OnPropertyChanged(const IProperty::Ptr& p)
{
auto f = GetSelf<IObjectFlags>();
if (!updating_ && f) {
if (f->GetObjectFlags() & ObjectFlagBits::SERIALIZE) {
UpdateSerializeState();
} else {
META_NS::SetObjectFlags(f, ObjectFlagBits::SERIALIZE, ShouldSerialise(p));
}
}
}
void ProxyObject::ListenTargetChanges()
{
if (auto target = interface_pointer_cast<IAttach>(GetTarget())) {
targetAddedListener_.Subscribe<IOnChildChanged>(
target->GetAttachmentContainer(true)->OnContainerChanged(), [&](auto i) {
if (interface_cast<IProperty>(i.object)) {
RefreshProperties();
}
});
}
}
void ProxyObject::ResetTargetListener()
{
targetAddedListener_.Unsubscribe();
targetRemovedListener_.Unsubscribe();
}
void ProxyObject::RefreshProperties()
{
if (!GetTarget()) {
RemoveAllProxyProperties();
return;
}
updating_ = true;
auto propertiesToRemove = BASE_NS::vector<BASE_NS::string>();
auto propertiesToAdd = BASE_NS::vector<BASE_NS::string>();
for (auto&& property : proxyProperties_) {
const auto& name = property.first;
META_NS::IProperty::Ptr sourceProperty;
if (auto internalSourceName = internalBindings_.find(name); internalSourceName != internalBindings_.end()) {
auto targetSourceProperty = interface_cast<IMetadata>(GetTarget())->GetProperty(internalSourceName->second);
if (targetSourceProperty) {
propertiesToAdd.push_back(name);
continue;
}
internalBindings_.erase(internalSourceName);
sourceProperty = interface_cast<IMetadata>(GetTarget())->GetProperty(name);
if (!sourceProperty) {
propertiesToRemove.push_back(name);
propertiesToAdd.push_back(name);
continue;
}
} else {
sourceProperty = interface_cast<IMetadata>(GetTarget())->GetProperty(name);
if (!sourceProperty) {
propertiesToRemove.push_back(name);
continue;
}
}
auto& valueBind = proxyProperties_.find(name)->second;
const auto bindingResult = valueBind.Bind(sourceProperty);
if (!bindingResult) {
propertiesToRemove.push_back(name);
propertiesToAdd.push_back(name);
}
}
for (const auto& remove : propertiesToRemove) {
RemoveProxyProperty(remove);
}
for (const auto& name : propertiesToAdd) {
AddProxyProperty(name);
}
updating_ = false;
UpdateSerializeState();
}
const IObject::Ptr ProxyObject::GetTarget() const
{
return target_.lock();
}
bool ProxyObject::SetTarget(const IObject::Ptr& target)
{
ResetTargetListener();
target_ = target;
if (Dynamic()->GetValue()) {
ListenTargetChanges();
}
RefreshProperties();
if (META_ACCESS_PROPERTY_VALUE(Mode) & ProxyMode::REFLECT_PROXY_HIERARCHY) {
ReflectHierarchy(target);
}
return true;
}
void ProxyObject::ReflectHierarchy(const IObject::Ptr& target)
{
auto m = interface_pointer_cast<IMetadata>(target);
if (!m) {
return;
}
for (auto&& p : meta_->GetProperties()) {
if (!proxyProperties_.count(p->GetName())) {
if (auto proxy = GetPointer<IProxyObject>(p)) {
ReflectTargetForProperty(m, p->GetName(), proxy);
}
}
}
}
void ProxyObject::ReflectTargetForProperty(
const IMetadata::Ptr& m, BASE_NS::string_view name, const IProxyObject::Ptr& proxy)
{
if (auto p = m->GetProperty(name)) {
if (auto tp = GetPointer<IObject>(p)) {
proxy->SetTarget(tp);
}
}
}
BASE_NS::vector<IProperty::ConstPtr> ProxyObject::GetOverrides() const
{
BASE_NS::vector<IProperty::ConstPtr> res;
for (auto&& p : proxyProperties_) {
if (!p.second.IsDefaultValue()) {
res.push_back(p.second.GetProperty());
}
}
return res;
}
IProperty::ConstPtr ProxyObject::GetOverride(BASE_NS::string_view name) const
{
auto it = proxyProperties_.find(name);
return it != proxyProperties_.end() && !it->second.IsDefaultValue() ? it->second.GetProperty() : nullptr;
}
IProperty::ConstPtr ProxyObject::GetProxyProperty(BASE_NS::string_view name) const
{
auto it = proxyProperties_.find(name);
return it != proxyProperties_.end() ? it->second.GetProperty() : nullptr;
}
IProperty::Ptr ProxyObject::SetPropertyTarget(const IProperty::Ptr& property)
{
if (!property) {
return nullptr;
}
IProperty::Ptr res;
bool add = true;
auto it = proxyProperties_.find(property->GetName());
if (it != proxyProperties_.end()) {
auto bprop = it->second.GetProperty();
add = !bprop || bprop->GetTypeId() != property->GetTypeId();
if (add) {
proxyProperties_.erase(it);
} else {
it->second.Bind(property);
res = it->second.GetProperty();
}
}
if (add && property) {
if (auto p = meta_->GetProperty(property->GetName())) {
proxyProperties_.insert_or_assign(p->GetName(), DefaultValueBind(p, property));
res = p;
} else {
res = AddProxyProperty(property->GetName(), property);
}
}
return res;
}
IProperty::Ptr ProxyObject::AddProxyProperty(BASE_NS::string_view name, const IProperty::ConstPtr& tp)
{
auto p = meta_->GetProperty(name);
if (!p) {
p = DuplicatePropertyType(META_NS::GetObjectRegistry(), tp, name);
}
if (p) {
proxyProperties_.insert_or_assign(p->GetName(), DefaultValueBind(p, tp));
AddProperty(p);
}
return p;
}
IProperty::Ptr ProxyObject::AddProxyProperty(BASE_NS::string_view name)
{
IProperty::Ptr p;
if (internalBindings_.contains(name)) {
auto source = GetProperty(internalBindings_[name]);
if (!source) {
return {};
}
p = AddProxyProperty(name, source);
} else if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
if (auto tp = target->GetProperty(name)) {
p = AddProxyProperty(name, tp);
}
}
return p;
}
void ProxyObject::PopulateAllProperties()
{
if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
for (auto&& p : target->GetProperties()) {
auto it = proxyProperties_.find(p->GetName());
if (it == proxyProperties_.end()) {
AddProxyProperty(p->GetName(), p);
}
}
}
}
IProperty::Ptr ProxyObject::GetProperty(BASE_NS::string_view name, MetadataQuery q)
{
auto p = meta_->GetProperty(name, q);
if (!p) {
p = AddProxyProperty(name);
}
return p;
}
IProperty::ConstPtr ProxyObject::GetProperty(BASE_NS::string_view name, MetadataQuery q) const
{
return const_cast<ProxyObject*>(this)->GetProperty(name, q);
}
bool ProxyObject::RemoveProperty(const IProperty::Ptr& p)
{
bool res = meta_->RemoveProperty(p);
proxyProperties_.erase(p->GetName());
return res;
}
BASE_NS::vector<IProperty::Ptr> ProxyObject::GetProperties()
{
PopulateAllProperties();
return meta_->GetProperties();
}
BASE_NS::vector<IProperty::ConstPtr> ProxyObject::GetProperties() const
{
const_cast<ProxyObject*>(this)->PopulateAllProperties();
return const_cast<const IMetadata*>(meta_)->GetProperties();
}
bool ProxyObject::ShouldSerialise(const IProperty::Ptr& p) const
{
auto s = interface_cast<IStackProperty>(p);
return (s && !s->GetValues({}, false).empty()) ||
(!IsFlagSet(p, ObjectFlagBits::NATIVE) && !proxyProperties_.count(p->GetName())) ||
SerializeEmbeddedProxy(p);
}
void ProxyObject::UpdateSerializeState()
{
if (!updating_) {
bool serialise = !GetOverrides().empty();
if (!serialise) {
for (auto&& p : meta_->GetProperties()) {
serialise = ShouldSerialise(p);
if (serialise) {
break;
}
}
}
META_NS::SetObjectFlags(GetSelf(), ObjectFlagBits::SERIALIZE, serialise);
}
}
void ProxyObject::RemoveProxyProperty(const BASE_NS::string& name)
{
meta_->RemoveProperty(GetProperty(name));
proxyProperties_.erase(name);
internalBindings_.erase(name);
}
void ProxyObject::RemoveAllProxyProperties()
{
for (const auto& p : meta_->GetProperties()) {
RemoveProxyProperty(p->GetName());
}
}
void ProxyObject::AddInternalProxy(BASE_NS::string_view propertyPropertyName, BASE_NS::string_view sourcePropertyName)
{
auto internalSourceProperty = GetProperty(sourcePropertyName);
if (!internalSourceProperty) {
return;
}
if (internalBindings_.contains(propertyPropertyName)) {
if (internalBindings_[propertyPropertyName] == sourcePropertyName) {
return;
}
internalBindings_.erase(propertyPropertyName);
}
internalBindings_.insert({BASE_NS::string(propertyPropertyName), BASE_NS::string(sourcePropertyName)});
if (proxyProperties_.contains(propertyPropertyName)) {
proxyProperties_.erase(propertyPropertyName);
}
auto bindProperty = meta_->GetProperty(propertyPropertyName);
if (!bindProperty) {
return;
}
proxyProperties_.insert({propertyPropertyName, DefaultValueBind(bindProperty, internalSourceProperty)});
}
}
META_END_NAMESPACE()