1use crate::direct_property::DirectPropertyDyn;
2use crate::{DirectPropertyBase, FerroObject, FerroProperty, PropertyValue, TypeInfo};
3use std::cell::RefCell;
4use std::collections::HashMap;
5use std::rc::Rc;
6
7type TypeKey = *const TypeInfo;
8type PropertyList = Rc<Vec<&'static FerroProperty>>;
9
10#[derive(Default)]
11struct Inner {
12 properties: HashMap<u32, &'static FerroProperty>,
13 registered: HashMap<TypeKey, HashMap<u32, &'static FerroProperty>>,
14 attached: HashMap<TypeKey, HashMap<u32, &'static FerroProperty>>,
15 direct: HashMap<TypeKey, HashMap<u32, &'static dyn DirectPropertyDyn>>,
16 registered_cache: HashMap<TypeKey, PropertyList>,
17 attached_cache: HashMap<TypeKey, PropertyList>,
18 direct_cache: HashMap<TypeKey, PropertyList>,
19 inherited_cache: HashMap<TypeKey, PropertyList>,
20}
21
22#[derive(Default)]
24pub struct FerroPropertyRegistry {
25 inner: RefCell<Inner>,
26}
27
28fn key(type_: &'static TypeInfo) -> TypeKey {
29 type_ as TypeKey
30}
31
32fn collect(
33 map: &HashMap<TypeKey, HashMap<u32, &'static FerroProperty>>,
34 type_: &'static TypeInfo,
35 walk_bases: bool,
36 out: &mut Vec<&'static FerroProperty>,
37) {
38 let mut current = Some(type_);
39 while let Some(t) = current {
40 if let Some(props) = map.get(&key(t)) {
41 let start = out.len();
42 out.extend(props.values().copied());
43 out[start..].sort_by_key(|p| p.id());
45 }
46 if !walk_bases {
47 break;
48 }
49 current = t.base_type();
50 }
51}
52
53impl FerroPropertyRegistry {
54 pub fn instance() -> &'static FerroPropertyRegistry {
56 crate::ferro_thread_local! {
57 static INSTANCE: &'static FerroPropertyRegistry = Box::leak(Box::default());
58 }
59 INSTANCE.with(|instance| *instance)
60 }
61
62 pub(crate) fn properties(&self) -> Vec<&'static FerroProperty> {
64 self.inner.borrow().properties.values().copied().collect()
65 }
66
67 pub fn unregister_by_module(&self, types: impl IntoIterator<Item = &'static TypeInfo>) -> bool {
74 for type_ in types {
75 let properties: Vec<&'static FerroProperty> = {
79 let inner = self.inner.borrow();
80 let registered = inner.registered.values().flat_map(|properties| properties.values().copied());
81 let attached = inner.attached.values().flat_map(|properties| properties.values().copied());
82 let direct = inner.direct.values().flat_map(|properties| {
83 properties.values().map(|property| {
84 let property: &'static dyn DirectPropertyDyn = *property;
85 property.as_property()
86 })
87 });
88 let properties: Vec<&'static FerroProperty> = registered.chain(attached).chain(direct).collect();
89 properties
90 };
91 for property in properties {
92 property.unregister(type_);
93 }
94
95 let mut inner = self.inner.borrow_mut();
96 inner.registered_cache.remove(&key(type_));
97 inner.attached_cache.remove(&key(type_));
98 inner.direct_cache.remove(&key(type_));
99 inner.inherited_cache.remove(&key(type_));
100 }
101
102 true
103 }
104
105 pub fn get_registered(&self, type_: &'static TypeInfo) -> PropertyList {
107 if let Some(result) = self.inner.borrow().registered_cache.get(&key(type_)) {
108 return result.clone();
109 }
110 type_.ensure_class_init();
115 let mut inner = self.inner.borrow_mut();
116 let mut list = Vec::new();
117 collect(&inner.registered, type_, true, &mut list);
118 let result = Rc::new(list);
119 inner.registered_cache.insert(key(type_), result.clone());
120 result
121 }
122
123 pub fn get_registered_attached(&self, type_: &'static TypeInfo) -> PropertyList {
125 if let Some(result) = self.inner.borrow().attached_cache.get(&key(type_)) {
126 return result.clone();
127 }
128 type_.ensure_class_init();
129 let mut inner = self.inner.borrow_mut();
130 let mut list = Vec::new();
131 collect(&inner.attached, type_, true, &mut list);
132 let result = Rc::new(list);
133 inner.attached_cache.insert(key(type_), result.clone());
134 result
135 }
136
137 pub fn get_registered_direct(&self, type_: &'static TypeInfo) -> PropertyList {
139 if let Some(result) = self.inner.borrow().direct_cache.get(&key(type_)) {
140 return result.clone();
141 }
142 type_.ensure_class_init();
143 let mut inner = self.inner.borrow_mut();
144 let mut list: Vec<&'static FerroProperty> = Vec::new();
145 let mut current = Some(type_);
146 while let Some(t) = current {
147 if let Some(props) = inner.direct.get(&key(t)) {
148 for p in props.values() {
149 let p: &'static dyn DirectPropertyDyn = *p;
150 list.push(p.as_property());
151 }
152 }
153 current = t.base_type();
154 }
155 let result = Rc::new(list);
156 inner.direct_cache.insert(key(type_), result.clone());
157 result
158 }
159
160 pub fn get_registered_inherited(&self, type_: &'static TypeInfo) -> PropertyList {
163 if let Some(result) = self.inner.borrow().inherited_cache.get(&key(type_)) {
164 return result.clone();
165 }
166 let registered = self.get_registered(type_);
167 let attached = self.get_registered_attached(type_);
168 let mut list: Vec<&'static FerroProperty> = Vec::new();
169 for p in registered.iter().chain(attached.iter()) {
170 if p.inherits() && !list.iter().any(|x| x.id() == p.id()) {
171 list.push(p);
172 }
173 }
174 let result = Rc::new(list);
175 self.inner.borrow_mut().inherited_cache.insert(key(type_), result.clone());
176 result
177 }
178
179 pub fn get_declared(&self, type_: &'static TypeInfo) -> Vec<&'static FerroProperty> {
183 type_.ensure_class_init();
184 let mut list = Vec::new();
185 collect(&self.inner.borrow().registered, type_, false, &mut list);
186 list
187 }
188
189 pub fn get_registered_for(&self, o: &FerroObject) -> PropertyList {
191 self.get_registered(o.get_type())
192 }
193
194 pub fn get_registered_direct_for<T: PropertyValue>(
199 &self,
200 o: &FerroObject,
201 property: &'static DirectPropertyBase<T>,
202 ) -> &'static DirectPropertyBase<T> {
203 self.find_registered_direct(o, property).unwrap_or_else(|| {
204 panic!("Property '{}.{}' not registered on '{}'", property.owner(), property.name(), o.get_type())
205 })
206 }
207
208 pub fn find_registered_direct<T: PropertyValue>(
211 &self,
212 o: &FerroObject,
213 property: &'static DirectPropertyBase<T>,
214 ) -> Option<&'static DirectPropertyBase<T>> {
215 let object_type = o.get_type();
216 if property.owner().is_assignable_from(object_type) {
217 if std::ptr::eq(property.owner(), object_type) {
220 return Some(property);
221 }
222 }
223 let inner = self.inner.borrow();
224 let mut current = Some(object_type);
225 while let Some(t) = current {
226 if let Some(found) = inner.direct.get(&key(t)).and_then(|m| m.get(&property.id())) {
227 let found: &'static dyn DirectPropertyDyn = *found;
228 return found.as_any().downcast_ref::<DirectPropertyBase<T>>();
229 }
230 current = t.base_type();
231 }
232 None
233 }
234
235 pub fn find_registered(&self, type_: &'static TypeInfo, name: &str) -> Option<&'static FerroProperty> {
240 assert!(!name.contains('.'), "Attached properties not supported.");
241 self.get_registered(type_).iter().copied().find(|p| p.name() == name)
242 }
243
244 pub fn find_registered_for(&self, o: &FerroObject, name: &str) -> Option<&'static FerroProperty> {
246 self.find_registered(o.get_type(), name)
247 }
248
249 pub fn find_registered_by_id(&self, id: u32) -> Option<&'static FerroProperty> {
251 self.inner.borrow().properties.get(&id).copied()
252 }
253
254 pub fn is_registered(&self, type_: &'static TypeInfo, property: &FerroProperty) -> bool {
256 let has = |list: PropertyList| list.iter().any(|p| p.id() == property.id());
257 has(self.get_registered(type_)) || has(self.get_registered_attached(type_))
258 }
259
260 pub fn is_registered_for(&self, o: &FerroObject, property: &FerroProperty) -> bool {
262 self.is_registered(o.get_type(), property)
263 }
264
265 pub fn register<P: Registrable>(&self, type_: &'static TypeInfo, property: &'static P) {
270 self.register_core(type_, property.as_registered_property(), property.as_direct());
271 }
272
273 fn register_core(
276 &self,
277 type_: &'static TypeInfo,
278 base: &'static FerroProperty,
279 direct: Option<&'static dyn DirectPropertyDyn>,
280 ) {
281 let mut inner = self.inner.borrow_mut();
282 inner.registered.entry(key(type_)).or_default().entry(base.id()).or_insert(base);
283 if let Some(direct) = direct {
284 inner.direct.entry(key(type_)).or_default().entry(base.id()).or_insert(direct);
285 inner.direct_cache.clear();
286 }
287 inner.properties.entry(base.id()).or_insert(base);
288 inner.registered_cache.clear();
289 inner.inherited_cache.clear();
290 }
291
292 pub fn register_attached<P: Registrable>(&self, type_: &'static TypeInfo, property: &'static P) {
294 self.register_attached_core(type_, property.as_registered_property());
295 }
296
297 fn register_attached_core(&self, type_: &'static TypeInfo, base: &'static FerroProperty) {
300 assert!(base.is_attached(), "Cannot register a non-attached property as attached.");
301 let mut inner = self.inner.borrow_mut();
302 inner.attached.entry(key(type_)).or_default().entry(base.id()).or_insert(base);
303 inner.properties.entry(base.id()).or_insert(base);
304 inner.attached_cache.clear();
305 inner.inherited_cache.clear();
306 }
307}
308
309pub trait Registrable: 'static {
311 #[doc(hidden)]
312 fn as_registered_property(&'static self) -> &'static FerroProperty;
313 #[doc(hidden)]
314 #[allow(private_interfaces)]
315 fn as_direct(&'static self) -> Option<&'static dyn DirectPropertyDyn> {
316 None
317 }
318}
319
320impl<T: PropertyValue> Registrable for crate::StyledProperty<T> {
321 fn as_registered_property(&'static self) -> &'static FerroProperty {
322 self
323 }
324}
325
326impl<T: PropertyValue> Registrable for crate::AttachedProperty<T> {
327 fn as_registered_property(&'static self) -> &'static FerroProperty {
328 self
329 }
330}
331
332impl<TOwner: crate::ObjectType, T: PropertyValue> Registrable for crate::DirectProperty<TOwner, T> {
333 fn as_registered_property(&'static self) -> &'static FerroProperty {
334 self
335 }
336
337 #[allow(private_interfaces)]
338 fn as_direct(&'static self) -> Option<&'static dyn DirectPropertyDyn> {
339 let base: &'static DirectPropertyBase<T> = self;
340 Some(base)
341 }
342}