fbxcel_dom/v7400/object/
cache.rs

1//! Objects cache.
2
3use std::collections::HashMap;
4
5use fbxcel::tree::v7400::{NodeHandle, Tree};
6use log::{debug, trace};
7use string_interner::{DefaultBackend, StringInterner};
8
9use crate::v7400::{
10    error::{
11        load::{LoadError, StructureError},
12        object::ObjectMetaError,
13    },
14    object::{ObjectClassSym, ObjectId, ObjectMeta, ObjectNodeId},
15};
16
17/// Objects cache.
18#[derive(Debug, Clone)]
19pub(crate) struct ObjectsCache {
20    /// A map from object ID to node ID.
21    obj_id_to_node_id: HashMap<ObjectId, ObjectNodeId>,
22    /// Object metadata store.
23    meta: HashMap<ObjectNodeId, ObjectMeta>,
24    /// Interned object classes and subclasses.
25    class_strings: StringInterner<DefaultBackend<ObjectClassSym>>,
26    /// `Document` nodes.
27    document_nodes: Vec<ObjectNodeId>,
28}
29
30impl ObjectsCache {
31    /// Returns object node ID corresponding to the given object ID.
32    pub(crate) fn node_id(&self, obj_id: ObjectId) -> Option<ObjectNodeId> {
33        self.obj_id_to_node_id.get(&obj_id).cloned()
34    }
35
36    /// Returns a reference to the object metadata.
37    pub(crate) fn meta_from_node_id(&self, node_id: ObjectNodeId) -> Option<&ObjectMeta> {
38        self.meta.get(&node_id)
39    }
40
41    /// Creates a new `ObjectsCache` from the given FBX data tree.
42    pub(crate) fn from_tree(tree: &Tree) -> Result<Self, LoadError> {
43        debug!("Loading objects cache");
44        let objects_cache = ObjectsCacheBuilder::default().load(tree)?;
45        debug!(
46            "Loaded objects cache successfully: {} objects",
47            objects_cache.obj_id_to_node_id.len()
48        );
49        Ok(objects_cache)
50    }
51
52    /// Resolves object class and subclass to string.
53    ///
54    /// # Panics
55    ///
56    /// Panics if the given symbol is not registered in the internal table.
57    pub(crate) fn resolve_class_string(&self, sym: ObjectClassSym) -> &str {
58        self.class_strings
59            .resolve(sym)
60            .unwrap_or_else(|| panic!("Unresolvable class name symbol: sym={:?}", sym))
61    }
62
63    /// Returns document node IDs.
64    pub(crate) fn document_nodes(&self) -> &[ObjectNodeId] {
65        &self.document_nodes
66    }
67
68    /// Returns an iterator of object IDs.
69    pub(crate) fn object_node_ids(&self) -> impl Iterator<Item = ObjectNodeId> + '_ {
70        self.meta.keys().cloned()
71    }
72}
73
74/// Objects cache builder.
75#[derive(Debug)]
76struct ObjectsCacheBuilder {
77    /// A map from object ID to node ID.
78    obj_id_to_node_id: HashMap<ObjectId, ObjectNodeId>,
79    /// Object metadata store.
80    meta: HashMap<ObjectNodeId, ObjectMeta>,
81    /// Interned object classes and subclasses.
82    class_strings: StringInterner<DefaultBackend<ObjectClassSym>>,
83    /// `Document` nodes.
84    document_nodes: Vec<ObjectNodeId>,
85}
86
87impl ObjectsCacheBuilder {
88    /// Loads the data from the tree.
89    fn load(mut self, tree: &Tree) -> Result<ObjectsCache, LoadError> {
90        self.load_objects(tree)?;
91        self.load_documents(tree)?;
92        Ok(self.build())
93    }
94
95    /// Creates an `ObjectsCache` from the builder.
96    fn build(self) -> ObjectsCache {
97        ObjectsCache {
98            obj_id_to_node_id: self.obj_id_to_node_id,
99            meta: self.meta,
100            class_strings: self.class_strings,
101            document_nodes: self.document_nodes,
102        }
103    }
104
105    /// Loads object nodes under toplevel `Objects` node.
106    fn load_objects(&mut self, tree: &Tree) -> Result<(), LoadError> {
107        let objects_node = tree
108            .root()
109            .children_by_name("Objects")
110            .next()
111            .ok_or(StructureError::MissingObjectsNode)?;
112        for object_node in objects_node.children() {
113            self.load_object(object_node)?;
114        }
115
116        Ok(())
117    }
118
119    /// Loads `Document` object nodes under toplevel `Documents` node.
120    fn load_documents(&mut self, tree: &Tree) -> Result<(), LoadError> {
121        let documents_node = tree
122            .root()
123            .children_by_name("Documents")
124            .next()
125            .ok_or(StructureError::MissingDocumentsNode)?;
126        for object_node in documents_node.children_by_name("Document") {
127            let obj_node_id = self.load_object(object_node)?;
128            self.document_nodes.push(obj_node_id);
129        }
130
131        Ok(())
132    }
133
134    /// Loads an object from the node handle and caches it.
135    fn load_object(&mut self, node: NodeHandle<'_>) -> Result<ObjectNodeId, ObjectMetaError> {
136        trace!("Loading object metadata, node_id={:?}", node.node_id());
137        assert!(
138            !self.meta.contains_key(&ObjectNodeId::new(node.node_id())),
139            "The node is already loaded: node_id={:?}",
140            node.node_id()
141        );
142
143        let obj_id = self.load_object_id(node)?;
144        let (name, class_sym) = self.load_name_class(node, obj_id)?;
145        let subclass_sym = self.load_subclass(node, obj_id)?;
146
147        let obj_node_id = ObjectNodeId::new(node.node_id());
148        let meta = ObjectMeta::new(obj_id, name, class_sym, subclass_sym);
149        trace!(
150            "Successfully loaded object metadata: node={:?}, metadata={:?}",
151            obj_node_id,
152            meta,
153        );
154
155        self.obj_id_to_node_id.insert(obj_id, obj_node_id);
156        self.meta.insert(obj_node_id, meta);
157
158        Ok(obj_node_id)
159    }
160
161    /// Loads an object ID from the given object node.
162    fn load_object_id(&self, node: NodeHandle<'_>) -> Result<ObjectId, ObjectMetaError> {
163        let attrs = node.attributes();
164        let obj_id = attrs
165            .get(0)
166            .ok_or_else(|| ObjectMetaError::MissingId(node.node_id()))?
167            .get_i64_or_type()
168            .map(ObjectId::new)
169            .map_err(|ty| ObjectMetaError::InvalidIdType(node.node_id(), ty))?;
170        trace!("Got object id: {:?}", obj_id);
171        if let Some(&alt_node_id) = self.obj_id_to_node_id.get(&obj_id) {
172            return Err(ObjectMetaError::DuplicateObjectId(
173                obj_id,
174                node.node_id(),
175                alt_node_id.into(),
176            ));
177        }
178        Ok(obj_id)
179    }
180
181    /// Loads name and class from the given object node.
182    fn load_name_class(
183        &mut self,
184        node: NodeHandle<'_>,
185        obj_id: ObjectId,
186    ) -> Result<(Option<String>, ObjectClassSym), ObjectMetaError> {
187        let attrs = node.attributes();
188        let (name, class) = attrs
189            .get(1)
190            .ok_or_else(|| ObjectMetaError::MissingNameClass(node.node_id(), obj_id))?
191            .get_string_or_type()
192            .map(|name_class| {
193                name_class.find("\u{0}\u{1}").map_or_else(
194                    || (None, ""),
195                    |sep_pos| {
196                        (
197                            Some(name_class[0..sep_pos].to_owned()),
198                            &name_class[sep_pos + 2..],
199                        )
200                    },
201                )
202            })
203            .map_err(|ty| ObjectMetaError::InvalidNameClassType(node.node_id(), obj_id, ty))?;
204        let class_sym = self.class_strings.get_or_intern(class);
205        trace!(
206            "Got name and class: object_id={:?}, name={:?}, class={:?}, class_sym={:?}",
207            obj_id,
208            name,
209            class,
210            class_sym
211        );
212
213        Ok((name, class_sym))
214    }
215
216    /// Loads subclass from the given object node.
217    fn load_subclass(
218        &mut self,
219        node: NodeHandle<'_>,
220        obj_id: ObjectId,
221    ) -> Result<ObjectClassSym, ObjectMetaError> {
222        let attrs = node.attributes();
223        let subclass = attrs
224            .get(2)
225            .ok_or_else(|| ObjectMetaError::MissingSubclass(node.node_id(), obj_id))?
226            .get_string_or_type()
227            .map_err(|ty| ObjectMetaError::InvalidSubclassType(node.node_id(), obj_id, ty))?;
228        let subclass_sym = self.class_strings.get_or_intern(subclass);
229        trace!(
230            "Got subclass: object_id={:?}, subclass={:?}, subclass_sym={:?}",
231            obj_id,
232            subclass,
233            subclass_sym
234        );
235
236        Ok(subclass_sym)
237    }
238}
239
240impl Default for ObjectsCacheBuilder {
241    fn default() -> Self {
242        Self {
243            obj_id_to_node_id: Default::default(),
244            meta: Default::default(),
245            class_strings: StringInterner::new(),
246            document_nodes: Default::default(),
247        }
248    }
249}