Skip to main content

xfa_json/
import.rs

1//! Import JSON data into a FormTree.
2//!
3//! Merges field values from a `FormData` structure back into an existing
4//! FormTree, updating field values by matching SOM-style paths.
5
6use crate::types::{FieldValue, FormData};
7use indexmap::IndexMap;
8use xfa_layout_engine::form::{DrawContent, FormNodeId, FormNodeType, FormTree};
9
10/// Merge JSON field values into an existing FormTree.
11///
12/// Walks the tree starting at `root`, matching field paths from `data`
13/// and updating field values. Repeating sections are matched by array index.
14pub fn json_to_form_tree(data: &FormData, tree: &mut FormTree, root: FormNodeId) {
15    let node = tree.get(root);
16    let children: Vec<FormNodeId> = node.children.clone();
17
18    match &node.node_type {
19        FormNodeType::Root | FormNodeType::PageSet | FormNodeType::PageArea { .. } => {
20            for child_id in children {
21                merge_node(data, tree, child_id, "");
22            }
23        }
24        _ => {
25            merge_node(data, tree, root, "");
26        }
27    }
28}
29
30/// Recursively merge data into a subtree.
31fn merge_node(data: &FormData, tree: &mut FormTree, node_id: FormNodeId, parent_path: &str) {
32    let node = tree.get(node_id);
33    let name = node.name.clone();
34    let path = if parent_path.is_empty() {
35        name.clone()
36    } else {
37        format!("{parent_path}.{name}")
38    };
39
40    let node_type = node.node_type.clone();
41    let children: Vec<FormNodeId> = node.children.clone();
42    let is_repeating = node.occur.is_repeating();
43
44    match node_type {
45        FormNodeType::Field { .. } => {
46            if let Some(value) = data.fields.get(&path) {
47                let string_value = field_value_to_string(value);
48                tree.get_mut(node_id).node_type = FormNodeType::Field {
49                    value: string_value,
50                };
51            }
52        }
53        FormNodeType::Draw(..) => {
54            if let Some(value) = data.fields.get(&path) {
55                let string_value = field_value_to_string(value);
56                tree.get_mut(node_id).node_type =
57                    FormNodeType::Draw(DrawContent::Text(string_value));
58            }
59        }
60        FormNodeType::Image { .. } => {
61            // Images are static content - no data binding
62        }
63        // Area and ExclGroup behave like Subform for data import.
64        // SubformSet is transparent — recurse into children.
65        FormNodeType::Subform | FormNodeType::Area | FormNodeType::ExclGroup => {
66            if is_repeating {
67                // Repeating subforms are handled by the parent via
68                // merge_children_with_repeating_groups — skip here.
69            } else {
70                merge_children_with_repeating_groups(data, tree, &children, &path);
71            }
72        }
73        FormNodeType::SubformSet
74        | FormNodeType::Root
75        | FormNodeType::PageSet
76        | FormNodeType::PageArea { .. } => {
77            merge_children_with_repeating_groups(data, tree, &children, &path);
78        }
79    }
80}
81
82/// Merge children, grouping same-name repeating siblings and assigning
83/// array indices so each sibling gets the correct element from the JSON array.
84fn merge_children_with_repeating_groups(
85    data: &FormData,
86    tree: &mut FormTree,
87    children: &[FormNodeId],
88    parent_path: &str,
89) {
90    // Track how many times we've seen each repeating name, so we can assign indices.
91    let mut repeating_counts: IndexMap<String, usize> = IndexMap::new();
92
93    for &child_id in children {
94        let child = tree.get(child_id);
95        let is_repeating = child.occur.is_repeating();
96
97        if is_repeating {
98            let name = child.name.clone();
99            let index = repeating_counts.get(&name).copied().unwrap_or(0);
100            repeating_counts.insert(name.clone(), index + 1);
101
102            let path = if parent_path.is_empty() {
103                name
104            } else {
105                format!("{parent_path}.{name}")
106            };
107
108            // Look up the array and apply the correct element by index.
109            if let Some(FieldValue::Array(instances)) = data.fields.get(&path) {
110                if let Some(instance_data) = instances.get(index) {
111                    merge_instance(tree, child_id, instance_data);
112                }
113            }
114        } else {
115            merge_node(data, tree, child_id, parent_path);
116        }
117    }
118}
119
120/// Apply a single instance map (from a JSON array element) to a repeating
121/// subform node, recursively handling nested subforms and draw nodes.
122fn merge_instance(
123    tree: &mut FormTree,
124    node_id: FormNodeId,
125    instance_data: &IndexMap<String, FieldValue>,
126) {
127    let children: Vec<FormNodeId> = tree.get(node_id).children.clone();
128
129    for &child_id in &children {
130        let child = tree.get(child_id);
131        let child_name = child.name.clone();
132        let child_type = child.node_type.clone();
133
134        match child_type {
135            FormNodeType::Field { .. } => {
136                if let Some(value) = instance_data.get(&child_name) {
137                    let string_value = field_value_to_string(value);
138                    tree.get_mut(child_id).node_type = FormNodeType::Field {
139                        value: string_value,
140                    };
141                }
142            }
143            FormNodeType::Draw(..) => {
144                if let Some(value) = instance_data.get(&child_name) {
145                    let string_value = field_value_to_string(value);
146                    tree.get_mut(child_id).node_type =
147                        FormNodeType::Draw(DrawContent::Text(string_value));
148                }
149            }
150            FormNodeType::Subform => {
151                // Nested non-repeating subform: look up dotted keys (e.g., "Address.Street")
152                let nested_prefix = format!("{child_name}.");
153                let nested_data: IndexMap<String, FieldValue> = instance_data
154                    .iter()
155                    .filter_map(|(k, v)| {
156                        k.strip_prefix(&nested_prefix)
157                            .map(|rest| (rest.to_string(), v.clone()))
158                    })
159                    .collect();
160                if !nested_data.is_empty() {
161                    merge_instance(tree, child_id, &nested_data);
162                }
163            }
164            _ => {}
165        }
166    }
167}
168
169/// Convert a FieldValue back to a string for storage in FormTree.
170fn field_value_to_string(value: &FieldValue) -> String {
171    match value {
172        FieldValue::Text(s) => s.clone(),
173        FieldValue::Number(n) => {
174            if *n == n.trunc() && n.abs() < 1e15 {
175                format!("{}", *n as i64)
176            } else {
177                n.to_string()
178            }
179        }
180        FieldValue::Boolean(b) => if *b { "1" } else { "0" }.to_string(),
181        FieldValue::Null => String::new(),
182        FieldValue::Array(_) => String::new(),
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use crate::export::form_tree_to_json;
190    use indexmap::IndexMap;
191    use xfa_layout_engine::form::{FormNode, Occur};
192    use xfa_layout_engine::text::FontMetrics;
193    use xfa_layout_engine::types::{BoxModel, LayoutStrategy};
194
195    fn make_field(tree: &mut FormTree, name: &str, value: &str) -> FormNodeId {
196        tree.add_node(FormNode {
197            name: name.to_string(),
198            node_type: FormNodeType::Field {
199                value: value.to_string(),
200            },
201            box_model: BoxModel::default(),
202            layout: LayoutStrategy::Positioned,
203            children: vec![],
204            occur: Occur::once(),
205            font: FontMetrics::default(),
206            calculate: None,
207            validate: None,
208            column_widths: vec![],
209            col_span: 1,
210        })
211    }
212
213    fn make_subform(tree: &mut FormTree, name: &str, children: Vec<FormNodeId>) -> FormNodeId {
214        make_subform_with_occur(tree, name, children, Occur::once())
215    }
216
217    fn make_subform_with_occur(
218        tree: &mut FormTree,
219        name: &str,
220        children: Vec<FormNodeId>,
221        occur: Occur,
222    ) -> FormNodeId {
223        tree.add_node(FormNode {
224            name: name.to_string(),
225            node_type: FormNodeType::Subform,
226            box_model: BoxModel::default(),
227            layout: LayoutStrategy::TopToBottom,
228            children,
229            occur,
230            font: FontMetrics::default(),
231            calculate: None,
232            validate: None,
233            column_widths: vec![],
234            col_span: 1,
235        })
236    }
237
238    fn make_root(tree: &mut FormTree, children: Vec<FormNodeId>) -> FormNodeId {
239        tree.add_node(FormNode {
240            name: "Root".to_string(),
241            node_type: FormNodeType::Root,
242            box_model: BoxModel::default(),
243            layout: LayoutStrategy::TopToBottom,
244            children,
245            occur: Occur::once(),
246            font: FontMetrics::default(),
247            calculate: None,
248            validate: None,
249            column_widths: vec![],
250            col_span: 1,
251        })
252    }
253
254    #[test]
255    fn roundtrip_simple_form() {
256        let mut tree = FormTree::new();
257        let name = make_field(&mut tree, "Name", "Original");
258        let amount = make_field(&mut tree, "Amount", "100");
259        let form = make_subform(&mut tree, "form1", vec![name, amount]);
260        let root = make_root(&mut tree, vec![form]);
261
262        // Export → modify → import
263        let mut data = form_tree_to_json(&tree, root);
264        data.fields.insert(
265            "form1.Name".to_string(),
266            FieldValue::Text("Updated".to_string()),
267        );
268        data.fields
269            .insert("form1.Amount".to_string(), FieldValue::Number(200.0));
270
271        json_to_form_tree(&data, &mut tree, root);
272
273        // Verify updated values
274        let exported = form_tree_to_json(&tree, root);
275        assert_eq!(
276            exported.fields.get("form1.Name"),
277            Some(&FieldValue::Text("Updated".to_string()))
278        );
279        assert_eq!(
280            exported.fields.get("form1.Amount"),
281            Some(&FieldValue::Number(200.0))
282        );
283    }
284
285    #[test]
286    fn import_boolean_as_zero_one() {
287        let mut tree = FormTree::new();
288        let check = make_field(&mut tree, "Active", "0");
289        let form = make_subform(&mut tree, "form1", vec![check]);
290        let root = make_root(&mut tree, vec![form]);
291
292        let mut fields = IndexMap::new();
293        fields.insert("form1.Active".to_string(), FieldValue::Boolean(true));
294        let data = FormData { fields };
295
296        json_to_form_tree(&data, &mut tree, root);
297
298        // Boolean true → "1" in FormTree
299        match &tree.get(check).node_type {
300            FormNodeType::Field { value } => assert_eq!(value, "1"),
301            _ => panic!("Expected Field"),
302        }
303    }
304
305    #[test]
306    fn import_null_clears_field() {
307        let mut tree = FormTree::new();
308        let field = make_field(&mut tree, "Note", "something");
309        let form = make_subform(&mut tree, "form1", vec![field]);
310        let root = make_root(&mut tree, vec![form]);
311
312        let mut fields = IndexMap::new();
313        fields.insert("form1.Note".to_string(), FieldValue::Null);
314        let data = FormData { fields };
315
316        json_to_form_tree(&data, &mut tree, root);
317
318        match &tree.get(field).node_type {
319            FormNodeType::Field { value } => assert_eq!(value, ""),
320            _ => panic!("Expected Field"),
321        }
322    }
323
324    #[test]
325    fn field_value_to_string_formats() {
326        assert_eq!(field_value_to_string(&FieldValue::Number(42.0)), "42");
327        assert_eq!(field_value_to_string(&FieldValue::Number(3.14)), "3.14");
328        assert_eq!(field_value_to_string(&FieldValue::Boolean(true)), "1");
329        assert_eq!(field_value_to_string(&FieldValue::Boolean(false)), "0");
330        assert_eq!(field_value_to_string(&FieldValue::Null), "");
331        assert_eq!(
332            field_value_to_string(&FieldValue::Text("hi".to_string())),
333            "hi"
334        );
335    }
336
337    #[test]
338    fn import_repeating_by_index_not_always_first() {
339        // P1 regression: each repeating sibling must get the correct array element
340        let mut tree = FormTree::new();
341        let desc1 = make_field(&mut tree, "Desc", "old1");
342        let qty1 = make_field(&mut tree, "Qty", "0");
343        let item1 = make_subform_with_occur(
344            &mut tree,
345            "Item",
346            vec![desc1, qty1],
347            Occur::repeating(0, None, 2),
348        );
349
350        let desc2 = make_field(&mut tree, "Desc", "old2");
351        let qty2 = make_field(&mut tree, "Qty", "0");
352        let item2 = make_subform_with_occur(
353            &mut tree,
354            "Item",
355            vec![desc2, qty2],
356            Occur::repeating(0, None, 2),
357        );
358
359        let form = make_subform(&mut tree, "form1", vec![item1, item2]);
360        let root = make_root(&mut tree, vec![form]);
361
362        // Import: each array element should go to the correct sibling
363        let mut instance0 = IndexMap::new();
364        instance0.insert("Desc".to_string(), FieldValue::Text("Widget A".to_string()));
365        instance0.insert("Qty".to_string(), FieldValue::Number(10.0));
366        let mut instance1 = IndexMap::new();
367        instance1.insert("Desc".to_string(), FieldValue::Text("Widget B".to_string()));
368        instance1.insert("Qty".to_string(), FieldValue::Number(5.0));
369
370        let mut fields = IndexMap::new();
371        fields.insert(
372            "form1.Item".to_string(),
373            FieldValue::Array(vec![instance0, instance1]),
374        );
375        let data = FormData { fields };
376
377        json_to_form_tree(&data, &mut tree, root);
378
379        // Verify each sibling got the correct data
380        match &tree.get(desc1).node_type {
381            FormNodeType::Field { value } => assert_eq!(value, "Widget A"),
382            _ => panic!("Expected Field"),
383        }
384        match &tree.get(desc2).node_type {
385            FormNodeType::Field { value } => assert_eq!(value, "Widget B"),
386            _ => panic!("Expected Field"),
387        }
388        match &tree.get(qty1).node_type {
389            FormNodeType::Field { value } => assert_eq!(value, "10"),
390            _ => panic!("Expected Field"),
391        }
392        match &tree.get(qty2).node_type {
393            FormNodeType::Field { value } => assert_eq!(value, "5"),
394            _ => panic!("Expected Field"),
395        }
396    }
397
398    #[test]
399    fn import_nested_subform_inside_repeating() {
400        // P2 regression: nested structures inside repeated items must be applied
401        let mut tree = FormTree::new();
402
403        // Item[0] has a nested Address subform
404        let street1 = make_field(&mut tree, "Street", "old");
405        let addr1 = make_subform(&mut tree, "Address", vec![street1]);
406        let name1 = make_field(&mut tree, "Name", "old");
407        let item1 = make_subform_with_occur(
408            &mut tree,
409            "Item",
410            vec![name1, addr1],
411            Occur::repeating(0, None, 2),
412        );
413
414        let street2 = make_field(&mut tree, "Street", "old");
415        let addr2 = make_subform(&mut tree, "Address", vec![street2]);
416        let name2 = make_field(&mut tree, "Name", "old");
417        let item2 = make_subform_with_occur(
418            &mut tree,
419            "Item",
420            vec![name2, addr2],
421            Occur::repeating(0, None, 2),
422        );
423
424        let form = make_subform(&mut tree, "form1", vec![item1, item2]);
425        let root = make_root(&mut tree, vec![form]);
426
427        // Import with nested Address.Street keys
428        let mut inst0 = IndexMap::new();
429        inst0.insert("Name".to_string(), FieldValue::Text("Alice".to_string()));
430        inst0.insert(
431            "Address.Street".to_string(),
432            FieldValue::Text("123 Main St".to_string()),
433        );
434        let mut inst1 = IndexMap::new();
435        inst1.insert("Name".to_string(), FieldValue::Text("Bob".to_string()));
436        inst1.insert(
437            "Address.Street".to_string(),
438            FieldValue::Text("456 Oak Ave".to_string()),
439        );
440
441        let mut fields = IndexMap::new();
442        fields.insert(
443            "form1.Item".to_string(),
444            FieldValue::Array(vec![inst0, inst1]),
445        );
446        let data = FormData { fields };
447
448        json_to_form_tree(&data, &mut tree, root);
449
450        // Verify nested fields were applied
451        match &tree.get(name1).node_type {
452            FormNodeType::Field { value } => assert_eq!(value, "Alice"),
453            _ => panic!("Expected Field"),
454        }
455        match &tree.get(street1).node_type {
456            FormNodeType::Field { value } => assert_eq!(value, "123 Main St"),
457            _ => panic!("Expected Field"),
458        }
459        match &tree.get(name2).node_type {
460            FormNodeType::Field { value } => assert_eq!(value, "Bob"),
461            _ => panic!("Expected Field"),
462        }
463        match &tree.get(street2).node_type {
464            FormNodeType::Field { value } => assert_eq!(value, "456 Oak Ave"),
465            _ => panic!("Expected Field"),
466        }
467    }
468}