1use crate::controls::{INameScope, NameScopeRef};
2use crate::data::compiled_binding::{
3 binding_properties, create_data_context_node, explicit_source, resolve_defaults_from_metadata,
4};
5use crate::data::converters::IValueConverter;
6use crate::data::core::expression_nodes::{ExpressionNode, NamedElementNode};
7use crate::data::core::parsers::{BindingExpressionGrammar, ExpressionNodeFactory, TypeResolver};
8use crate::data::core::{BindingExpression, BindingExpressionOptions, ExpressionParseException, TargetTypeConverter};
9use crate::data::{
10 BindingBase, BindingExpressionBase, BindingMode, BindingPriority, RelativeSource, UpdateSourceTrigger,
11};
12use crate::utilities::CultureInfo;
13use crate::{BoxedValue, FerroObject, FerroProperty, Ref, WeakRef};
14use std::rc::{Rc, Weak};
15
16binding_properties! {
17 pub struct ReflectionBinding {
30 converter / set_converter / with_converter_value: Option<Rc<dyn IValueConverter>> = None,
32 converter_culture / set_converter_culture / with_converter_culture: Option<CultureInfo> = None,
35 converter_parameter / set_converter_parameter / with_converter_parameter: Option<BoxedValue> = None,
37 delay / set_delay / with_delay: i32 = 0,
40 element_name / set_element_name / with_element_name: Option<String> = None,
42 fallback_value / set_fallback_value / with_fallback_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
45 mode / set_mode / with_mode: BindingMode = BindingMode::Default,
47 path / set_path / with_path: String = String::new(),
49 priority / set_priority / with_priority: BindingPriority = BindingPriority::LocalValue,
51 relative_source / set_relative_source / with_relative_source: Option<Rc<RelativeSource>> = None,
53 source / set_source / with_source: Option<BoxedValue> = Some(FerroProperty::unset_value()),
56 string_format / set_string_format / with_string_format: Option<String> = None,
58 target_null_value / set_target_null_value / with_target_null_value: Option<BoxedValue> = Some(FerroProperty::unset_value()),
61 update_source_trigger / set_update_source_trigger / with_update_source_trigger: UpdateSourceTrigger = UpdateSourceTrigger::Default,
64 type_resolver / set_type_resolver / with_type_resolver: Option<TypeResolver> = None,
67 default_anchor / set_default_anchor / with_default_anchor: Option<WeakRef<FerroObject>> = None,
69 name_scope / set_name_scope / with_name_scope: Option<Weak<dyn INameScope>> = None,
71 }
72}
73
74impl ReflectionBinding {
75 pub fn empty() -> Rc<Self> {
77 Rc::new(Self::default())
78 }
79
80 pub fn new(path: &str) -> Rc<Self> {
82 Rc::new(Self::construct(path))
83 }
84
85 pub fn construct(path: &str) -> Self {
88 let result = Self::default();
89 result.set_path(path.to_string());
90 result
91 }
92
93 pub fn with_converter(self: Rc<Self>, converter: Rc<dyn IValueConverter>) -> Rc<Self> {
96 self.with_converter_value(Some(converter))
97 }
98
99 fn get_name_scope(&self) -> Option<NameScopeRef> {
100 self.name_scope().as_ref().and_then(Weak::upgrade).map(NameScopeRef)
101 }
102
103 fn create_source_node(&self, target_property: Option<&'static FerroProperty>) -> Option<Rc<dyn ExpressionNode>> {
104 let element_name = self.element_name();
105 if let Some(element_name) = element_name.as_deref().filter(|n| !n.is_empty()) {
106 let name_scope =
107 self.get_name_scope().expect("Cannot create ElementName binding when NameScope is null");
108 return Some(NamedElementNode::new(Some(&name_scope), element_name));
109 }
110 if let Some(relative_source) = self.relative_source() {
111 return ExpressionNodeFactory::create_relative_source(&relative_source);
112 }
113 Some(create_data_context_node(target_property))
114 }
115
116 pub fn try_create_instance(
119 &self,
120 target: &FerroObject,
121 target_property: Option<&'static FerroProperty>,
122 anchor: Option<&Ref<FerroObject>>,
123 ) -> Result<Rc<BindingExpression>, ExpressionParseException> {
124 let mut nodes: Vec<Rc<dyn ExpressionNode>> = Vec::new();
125 let mut is_rooted = false;
126 let enable_data_validation =
127 target_property.is_some_and(|p| p.get_metadata_for(target).enable_data_validation() == Some(true));
128
129 let path = self.path();
131 let explicit_source = explicit_source(self.source());
132 if !path.is_empty() {
133 let (ast, _source_mode) = BindingExpressionGrammar::parse(&path)?;
134 let name_scope = self.get_name_scope();
135 is_rooted = ExpressionNodeFactory::create_from_ast(
136 &ast,
137 self.type_resolver().as_ref(),
138 name_scope.as_ref(),
139 &mut nodes,
140 )?;
141 }
142
143 if explicit_source.is_none() && !is_rooted {
148 if let Some(source_node) = self.create_source_node(target_property) {
149 nodes.insert(0, source_node);
150 }
151 }
152
153 let target_ref = target.to_ref();
157 let default_anchor = self.default_anchor().as_ref().and_then(WeakRef::upgrade);
158 let anchor = anchor.or(default_anchor.as_ref());
159 let source = match nodes.first().and_then(|n| n.as_source_node()) {
160 Some(source_node) => source_node.select_source(explicit_source.as_ref(), &target_ref, anchor),
161 None => match &explicit_source {
162 Some(source) => source.clone(),
163 None => Some(Rc::new(target_ref.clone()) as BoxedValue),
164 },
165 };
166
167 let (mode, trigger) =
168 resolve_defaults_from_metadata(self.mode(), self.update_source_trigger(), target, target_property);
169
170 Ok(BindingExpression::new(
171 source,
172 nodes,
173 BindingExpressionOptions {
174 delay: self.delay(),
175 fallback_value: self.fallback_value(),
176 converter: self.converter(),
177 converter_culture: self.converter_culture(),
178 converter_parameter: self.converter_parameter(),
179 enable_data_validation,
180 mode,
181 priority: self.priority(),
182 string_format: self.string_format(),
183 target_null_value: self.target_null_value(),
184 target_property,
185 target_type_converter: Some(TargetTypeConverter::get_reflection_converter()),
186 update_source_trigger: trigger,
187 },
188 ))
189 }
190}
191
192impl BindingBase for ReflectionBinding {
193 fn as_any(&self) -> Option<&dyn std::any::Any> {
194 Some(self)
195 }
196
197 fn create_instance(
202 &self,
203 target: &FerroObject,
204 target_property: Option<&'static FerroProperty>,
205 anchor: Option<&Ref<FerroObject>>,
206 ) -> Rc<dyn BindingExpressionBase> {
207 match self.try_create_instance(target, target_property, anchor) {
208 Ok(expression) => expression,
209 Err(e) => panic!("{e}"),
210 }
211 }
212}