use crate::controls::{INameScope, NameScopeRef};
use crate::data::compiled_binding::{
binding_properties, create_data_context_node, explicit_source, resolve_defaults_from_metadata,
};
use crate::data::converters::IValueConverter;
use crate::data::core::expression_nodes::{ExpressionNode, NamedElementNode};
use crate::data::core::parsers::{BindingExpressionGrammar, ExpressionNodeFactory, TypeResolver};
use crate::data::core::{BindingExpression, BindingExpressionOptions, ExpressionParseException, TargetTypeConverter};
use crate::data::{
BindingBase, BindingExpressionBase, BindingMode, BindingPriority, RelativeSource, UpdateSourceTrigger,
};
use crate::utilities::CultureInfo;
use crate::{BoxedValue, FerroObject, FerroProperty, Ref, WeakRef};
use std::rc::{Rc, Weak};
binding_properties! {
pub struct ReflectionBinding {
converter / set_converter / with_converter_value: Option<Rc<dyn IValueConverter>> = None,
converter_culture / set_converter_culture / with_converter_culture: Option<CultureInfo> = None,
converter_parameter / set_converter_parameter / with_converter_parameter: Option<BoxedValue> = None,
delay / set_delay / with_delay: i32 = 0,
element_name / set_element_name / with_element_name: Option<String> = None,
fallback_value / set_fallback_value / with_fallback_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
mode / set_mode / with_mode: BindingMode = BindingMode::Default,
path / set_path / with_path: String = String::new(),
priority / set_priority / with_priority: BindingPriority = BindingPriority::LocalValue,
relative_source / set_relative_source / with_relative_source: Option<Rc<RelativeSource>> = None,
source / set_source / with_source: Option<BoxedValue> = Some(FerroProperty::unset_value()),
string_format / set_string_format / with_string_format: Option<String> = None,
target_null_value / set_target_null_value / with_target_null_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
update_source_trigger / set_update_source_trigger / with_update_source_trigger: UpdateSourceTrigger = UpdateSourceTrigger::Default,
type_resolver / set_type_resolver / with_type_resolver: Option<TypeResolver> = None,
default_anchor / set_default_anchor / with_default_anchor: Option<WeakRef<FerroObject>> = None,
name_scope / set_name_scope / with_name_scope: Option<Weak<dyn INameScope>> = None,
}
}
impl ReflectionBinding {
pub fn empty() -> Rc<Self> {
Rc::new(Self::default())
}
pub fn new(path: &str) -> Rc<Self> {
Rc::new(Self::construct(path))
}
pub fn construct(path: &str) -> Self {
let result = Self::default();
result.set_path(path.to_string());
result
}
pub fn with_converter(self: Rc<Self>, converter: Rc<dyn IValueConverter>) -> Rc<Self> {
self.with_converter_value(Some(converter))
}
fn get_name_scope(&self) -> Option<NameScopeRef> {
self.name_scope().as_ref().and_then(Weak::upgrade).map(NameScopeRef)
}
fn create_source_node(&self, target_property: Option<&'static FerroProperty>) -> Option<Rc<dyn ExpressionNode>> {
let element_name = self.element_name();
if let Some(element_name) = element_name.as_deref().filter(|n| !n.is_empty()) {
let name_scope =
self.get_name_scope().expect("Cannot create ElementName binding when NameScope is null");
return Some(NamedElementNode::new(Some(&name_scope), element_name));
}
if let Some(relative_source) = self.relative_source() {
return ExpressionNodeFactory::create_relative_source(&relative_source);
}
Some(create_data_context_node(target_property))
}
pub fn try_create_instance(
&self,
target: &FerroObject,
target_property: Option<&'static FerroProperty>,
anchor: Option<&Ref<FerroObject>>,
) -> Result<Rc<BindingExpression>, ExpressionParseException> {
let mut nodes: Vec<Rc<dyn ExpressionNode>> = Vec::new();
let mut is_rooted = false;
let enable_data_validation =
target_property.is_some_and(|p| p.get_metadata_for(target).enable_data_validation() == Some(true));
let path = self.path();
let explicit_source = explicit_source(self.source());
if !path.is_empty() {
let (ast, _source_mode) = BindingExpressionGrammar::parse(&path)?;
let name_scope = self.get_name_scope();
is_rooted = ExpressionNodeFactory::create_from_ast(
&ast,
self.type_resolver().as_ref(),
name_scope.as_ref(),
&mut nodes,
)?;
}
if explicit_source.is_none() && !is_rooted {
if let Some(source_node) = self.create_source_node(target_property) {
nodes.insert(0, source_node);
}
}
let target_ref = target.to_ref();
let default_anchor = self.default_anchor().as_ref().and_then(WeakRef::upgrade);
let anchor = anchor.or(default_anchor.as_ref());
let source = match nodes.first().and_then(|n| n.as_source_node()) {
Some(source_node) => source_node.select_source(explicit_source.as_ref(), &target_ref, anchor),
None => match &explicit_source {
Some(source) => source.clone(),
None => Some(Rc::new(target_ref.clone()) as BoxedValue),
},
};
let (mode, trigger) =
resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, target_property);
Ok(BindingExpression::new(
source,
nodes,
BindingExpressionOptions {
delay: self.delay(),
fallback_value: self.fallback_value(),
converter: self.converter(),
converter_culture: self.converter_culture(),
converter_parameter: self.converter_parameter(),
enable_data_validation,
mode,
priority: self.priority(),
string_format: self.string_format(),
target_null_value: self.target_null_value(),
target_property,
target_type_converter: Some(TargetTypeConverter::get_reflection_converter()),
update_source_trigger: trigger,
},
))
}
}
impl BindingBase for ReflectionBinding {
fn as_any(&self) -> Option<&dyn std::any::Any> {
Some(self)
}
fn create_instance(
&self,
target: &FerroObject,
target_property: Option<&'static FerroProperty>,
anchor: Option<&Ref<FerroObject>>,
) -> Rc<dyn BindingExpressionBase> {
match self.try_create_instance(target, target_property, anchor) {
Ok(expression) => expression,
Err(e) => panic!("{e}"),
}
}
}