/*
* 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 "stack_property.h"
#include
#include
#include
#include
#include "../any.h"
#include "dependencies.h"
META_BEGIN_NAMESPACE()
namespace Internal {
StackProperty::StackProperty(BASE_NS::string name)
: Super(BASE_NS::move(name)), onChangedCallback_(MakeCallback([this] { InternalOnChanged(); }))
{}
StackProperty::~StackProperty()
{
CleanUp();
}
void StackProperty::CleanUp()
{
if (auto i = interface_cast(defaultValue_)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
for (auto&& m : modifiers_) {
if (auto i = interface_cast(m)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
}
modifiers_.clear();
for (auto&& v : values_) {
if (auto i = interface_cast(v)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
}
values_.clear();
}
AnyReturnValue StackProperty::SetValueInValueStack(const IAny& value)
{
AnyReturnValue res = AnyReturn::FAIL;
// find first value that accepts the new value, all non-accepting values are removed
for (int i = int(values_.size()) - 1; i >= 0; --i) {
{
InterfaceUniqueLock lock { values_[i] };
res = values_[i]->SetValue(value);
if (res) {
break;
}
}
if (auto noti = interface_cast(values_[i])) {
noti->OnChanged()->RemoveHandler(uintptr_t(this));
}
values_.pop_back();
}
// if there was no any to set the new value, create one
if (!res) {
if (auto c = interface_pointer_cast(value.Clone(true))) {
if (auto i = interface_cast(c)) {
i->OnChanged()->AddHandler(onChangedCallback_, uintptr_t(this));
}
values_.push_back(c);
res = AnyReturn::SUCCESS;
} else {
res = AnyReturn::INCOMPATIBLE_TYPE;
}
}
return res;
}
AnyReturnValue StackProperty::SetValueToStack(const IAny::Ptr& internal)
{
// first go through modifiers and let them alter the value
for (auto it = modifiers_.rbegin(); it != modifiers_.rend(); ++it) {
auto m = (*it)->ProcessOnSet(*internal, GetValueFromStack());
if (m & EVAL_ERROR) {
return AnyReturn::FAIL;
}
if (m & EVAL_RETURN) {
if (m & EVAL_VALUE_CHANGED) {
NotifyChange();
}
return AnyReturn::SUCCESS;
}
}
auto res = SetValueInValueStack(*internal);
if (res != AnyReturn::NOTHING_TO_DO) {
NotifyChange();
}
return res;
}
AnyReturnValue StackProperty::SetValue(const IAny& value)
{
if (evaluating_) {
CORE_LOG_E("Recursive property evaluation requested [property name=%s]", GetName().c_str());
return AnyReturn::RECURSIVE_CALL;
}
evaluating_ = true;
AnyReturnValue res = AnyReturn::SUCCESS;
auto v = GetData();
if (!v) {
CORE_LOG_D("Initializing internal any with SetValue");
res = SetInternalAny(value.Clone(false));
if (res) {
v = GetData();
}
}
if (res) {
CORE_ASSERT(v);
res = SetInternalValue(value);
if (res) {
res = SetValueToStack(v);
}
}
evaluating_ = false;
return res;
}
const IAny& StackProperty::GetValueFromStack() const
{
if (requiresEvaluation_) {
AnyReturnValue res = AnyReturn::FAIL;
if (values_.empty()) {
res = currentValue_->CopyFrom(*defaultValue_);
} else {
auto& v = values_.back();
InterfaceSharedLock lock { v };
res = currentValue_->CopyFrom(v->GetValue());
}
if (!res) {
CORE_LOG_E("Invalid value in stack, could not copy from [property name=%s]", GetName().c_str());
return INVALID_ANY;
}
for (auto&& m : modifiers_) {
auto mres = m->ProcessOnGet(*currentValue_);
if (mres & EVAL_RETURN) {
break;
}
}
requiresEvaluation_ = false;
}
return *currentValue_;
}
const IAny& StackProperty::RawGetValue() const
{
if (evaluating_) {
CORE_LOG_E("Recursive property evaluation requested [property name=%s]", GetName().c_str());
return INVALID_ANY;
}
if (!currentValue_) {
CORE_LOG_E("GetValue called for not initialized property [property name=%s]", GetName().c_str());
return INVALID_ANY;
}
evaluating_ = true;
const IAny& res = GetValueFromStack();
evaluating_ = false;
return res;
}
const IAny& StackProperty::GetValue() const
{
bool isActive = false;
if constexpr (ENABLE_DEPENDENCY_CHECK) {
auto& d = GetDeps();
if (d.IsActive()) {
if (!d.AddDependency(self_.lock())) {
return INVALID_ANY;
}
// force evaluation since we are checking dependencies
requiresEvaluation_ = true;
isActive = true;
d.Start();
}
}
const IAny& res = RawGetValue();
if constexpr (ENABLE_DEPENDENCY_CHECK) {
if (isActive) {
GetDeps().End();
}
}
return res;
}
ReturnError StackProperty::PushValue(const IValue::Ptr& value)
{
auto& internal = GetData();
if (!internal || !value || !IsValueGetCompatible(*internal, *value)) {
CORE_LOG_W("Incompatible value");
return GenericError::INCOMPATIBLE_TYPES;
}
values_.push_back(value);
// if it is property, see that there is no circular dependency
if (interface_cast(value)) {
requiresEvaluation_ = true;
if (!RawGetValue().GetTypeId().IsValid()) {
values_.pop_back();
return GenericError::RECURSIVE_CALL;
}
}
if (auto i = interface_cast(value)) {
i->OnChanged()->AddHandler(onChangedCallback_, uintptr_t(this));
}
NotifyChange();
return GenericError::SUCCESS;
}
ReturnError StackProperty::PopValue()
{
if (!values_.empty()) {
if (auto i = interface_cast(values_.back())) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
values_.pop_back();
NotifyChange();
return GenericError::SUCCESS;
}
return GenericError::NOT_FOUND;
}
IValue::Ptr StackProperty::TopValue() const
{
return !values_.empty() ? values_.back() : nullptr;
}
ReturnError StackProperty::RemoveValue(const IValue::Ptr& value)
{
size_t index = 0;
for (auto m = values_.rbegin(); m != values_.rend(); ++m, ++index) {
if (*m == value) {
if (auto i = interface_cast(*m)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
values_.erase(m.base() - 1);
// notify if the top-most value was removed (i.e. pop value)
if (index == 0) {
NotifyChange();
}
return GenericError::SUCCESS;
}
}
return GenericError::NOT_FOUND;
}
BASE_NS::vector StackProperty::GetValues(const BASE_NS::array_view& ids, bool strict) const
{
BASE_NS::vector ret;
for (auto&& v : values_) {
if (CheckInterfaces(interface_pointer_cast(v), ids, strict)) {
ret.push_back(v);
}
}
return ret;
}
ReturnError StackProperty::InsertModifier(IndexType pos, const IModifier::Ptr& mod)
{
auto& internal = GetData();
if (!internal || !mod || !IsModifierGetCompatible(*internal, *mod)) {
CORE_LOG_W("Incompatible modifier");
return GenericError::INCOMPATIBLE_TYPES;
}
if (auto i = interface_cast(mod)) {
i->OnChanged()->AddHandler(onChangedCallback_, uintptr_t(this));
}
IndexType i = pos < modifiers_.size() ? pos : modifiers_.size();
modifiers_.insert(modifiers_.begin() + i, mod);
NotifyChange();
return GenericError::SUCCESS;
}
IModifier::Ptr StackProperty::RemoveModifier(IndexType pos)
{
IModifier::Ptr p;
if (pos < modifiers_.size()) {
p = modifiers_[pos];
modifiers_.erase(modifiers_.begin() + pos);
if (auto i = interface_cast(p)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
NotifyChange();
}
return p;
}
ReturnError StackProperty::RemoveModifier(const IModifier::Ptr& mod)
{
for (auto m = modifiers_.begin(); m != modifiers_.end(); ++m) {
if (*m == mod) {
if (auto i = interface_cast(*m)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
modifiers_.erase(m);
NotifyChange();
return GenericError::SUCCESS;
}
}
return GenericError::NOT_FOUND;
}
BASE_NS::vector StackProperty::GetModifiers(
const BASE_NS::array_view& ids, bool strict) const
{
BASE_NS::vector ret;
for (auto&& m : modifiers_) {
if (CheckInterfaces(interface_pointer_cast(m), ids, strict)) {
ret.push_back(m);
}
}
return ret;
}
AnyReturnValue StackProperty::SetDefaultValue(const IAny& value)
{
CORE_ASSERT_MSG(defaultValue_, "SetInternalAny not called");
AnyReturnValue res = defaultValue_->CopyFrom(value);
if (res && values_.empty()) {
NotifyChange();
}
return res;
}
const IAny& StackProperty::GetDefaultValue() const
{
CORE_ASSERT_MSG(defaultValue_, "SetInternalAny not called");
return *defaultValue_;
}
AnyReturnValue StackProperty::SetInternalAny(IAny::Ptr any)
{
auto res = Super::SetInternalAny(any);
if (res) {
defaultValue_ = any->Clone(false);
if (auto i = interface_cast(defaultValue_)) {
i->OnChanged()->AddHandler(onChangedCallback_, uintptr_t(this));
}
currentValue_ = defaultValue_->Clone(true);
requiresEvaluation_ = true;
values_.clear();
modifiers_.clear();
}
return res;
}
void StackProperty::NotifyChange() const
{
requiresEvaluation_ = true;
CallOnChanged();
}
template
bool StackProperty::ProcessResetables(Vec& vec)
{
for (int i = int(vec.size()) - 1; i >= 0; --i) {
ResetResult res = ResetResult::RESET_REMOVE_ME;
if (auto rable = interface_cast(vec[i])) {
res = rable->ProcessOnReset(*defaultValue_);
}
if (res & RESET_REMOVE_ME) {
vec.erase(vec.begin() + i);
}
if (res & RESET_STOP) {
return false;
}
}
return true;
}
void StackProperty::ResetValue()
{
if (ProcessResetables(modifiers_)) {
ProcessResetables(values_);
}
// reset the currentValue_ and internal value so we don't accidentally keep any shared_ptrs alive
currentValue_ = defaultValue_->Clone(false);
SetInternalValue(*currentValue_);
NotifyChange();
}
void StackProperty::RemoveAll()
{
values_.clear();
modifiers_.clear();
// reset the currentValue_ and internal value so we don't accidentally keep any shared_ptrs alive
currentValue_ = defaultValue_->Clone(false);
SetInternalValue(*currentValue_);
NotifyChange();
}
ReturnError StackProperty::Export(IExportContext& c) const
{
return Serializer(c) & NamedValue("defaultValue", defaultValue_) & NamedValue("values", values_) &
NamedValue("modifiers", modifiers_);
}
ReturnError StackProperty::Import(IImportContext& c)
{
CleanUp();
BASE_NS::vector values;
BASE_NS::vector modifiers;
Serializer ser(c);
ser& NamedValue("defaultValue", defaultValue_) & NamedValue("values", values) & NamedValue("modifiers", modifiers);
if (ser) {
if (!defaultValue_) {
return GenericError::FAIL;
}
if (auto i = interface_cast(defaultValue_)) {
i->OnChanged()->AddHandler(onChangedCallback_, uintptr_t(this));
}
if (auto res = Super::SetInternalAny(defaultValue_->Clone(false))) {
currentValue_ = defaultValue_->Clone(true);
requiresEvaluation_ = true;
}
for (auto&& i : values) {
if (auto v = interface_pointer_cast(i)) {
if (auto i = interface_cast(v)) {
i->OnChanged()->AddHandler(onChangedCallback_, uintptr_t(this));
}
values_.push_back(BASE_NS::move(v));
}
}
for (auto&& i : modifiers) {
if (auto v = interface_pointer_cast(i)) {
if (auto i = interface_cast(v)) {
i->OnChanged()->AddHandler(onChangedCallback_, uintptr_t(this));
}
modifiers_.push_back(BASE_NS::move(v));
}
}
}
return ser;
}
bool StackProperty::IsDefaultValue() const
{
return values_.empty();
}
} // namespace Internal
META_END_NAMESPACE()