1use std::collections::HashMap;
8
9use crate::node::{BranchNode, NodeRef};
10
11use super::edit_log::EditLog;
12
13#[derive(Debug, Clone)]
21pub struct MergeEntry {
22 node: Option<NodeRef>,
24 pub is_start: bool,
26 pub is_end: bool,
28 hangons: Vec<NodeRef>,
30 pub locked: bool,
32 pub moved: bool,
34 merge_partner: Option<NodeRef>,
36}
37
38impl MergeEntry {
39 pub fn new(node: Option<NodeRef>) -> Self {
41 MergeEntry {
42 node,
43 is_start: false,
44 is_end: false,
45 hangons: Vec::new(),
46 locked: false,
47 moved: false,
48 merge_partner: None,
49 }
50 }
51
52 pub fn start() -> Self {
54 MergeEntry {
55 node: None,
56 is_start: true,
57 is_end: false,
58 hangons: Vec::new(),
59 locked: false,
60 moved: false,
61 merge_partner: None,
62 }
63 }
64
65 pub fn end() -> Self {
67 MergeEntry {
68 node: None,
69 is_start: false,
70 is_end: true,
71 hangons: Vec::new(),
72 locked: false,
73 moved: false,
74 merge_partner: None,
75 }
76 }
77
78 pub fn node(&self) -> Option<NodeRef> {
80 self.node.clone()
81 }
82
83 pub fn hangons(&self) -> &[NodeRef] {
85 &self.hangons
86 }
87
88 pub fn add_hangon(&mut self, node: NodeRef) {
90 self.hangons.push(node);
91 }
92
93 pub fn hangon_count(&self) -> usize {
95 self.hangons.len()
96 }
97
98 pub fn set_merge_partner(&mut self, partner: Option<NodeRef>) {
100 self.merge_partner = partner;
101 }
102
103 pub fn merge_partner(&self) -> Option<&NodeRef> {
105 self.merge_partner.as_ref()
106 }
107
108 pub fn base_match(&self) -> Option<NodeRef> {
110 self.node.as_ref().and_then(BranchNode::base_match)
111 }
112}
113
114#[derive(Debug, Clone)]
122pub struct MergeList {
123 entries: Vec<MergeEntry>,
125 index: HashMap<u64, usize>,
127 entry_parent: Option<NodeRef>,
129 tail_pos: i32,
131}
132
133impl MergeList {
134 pub fn new(parent: Option<NodeRef>) -> Self {
136 MergeList {
137 entries: Vec::new(),
138 index: HashMap::new(),
139 entry_parent: parent,
140 tail_pos: -1,
141 }
142 }
143
144 pub fn from_branch(parent: &NodeRef, edit_log: &mut EditLog) -> Self {
146 let mut ml = MergeList::new(Some(parent.clone()));
147
148 let base_match = BranchNode::base_match(parent);
149
150 if base_match.is_none() {
151 ml.add_entry(MergeEntry::start());
153 let child_count = parent.borrow().child_count();
154 for i in 0..child_count {
155 if let Some(child) = parent.borrow().child(i).cloned() {
156 ml.add_hangon(child);
157 }
158 }
159 ml.lock_neighborhood(0, 1);
160 ml.add_entry(MergeEntry::end());
161 return ml;
162 }
163
164 let base_parent = base_match.unwrap();
165 let mut base_matches: HashMap<u64, Option<usize>> = HashMap::new();
166 let mut prev_child_pos: i32 = -1;
167
168 ml.add_entry(MergeEntry::start());
169
170 let child_count = parent.borrow().child_count();
171 for i in 0..child_count {
172 let current = match parent.borrow().child(i).cloned() {
173 Some(c) => c,
174 None => continue,
175 };
176
177 let current_base_match = BranchNode::base_match(¤t);
178
179 if let Some(base_node) = current_base_match {
180 let base_parent_match = {
182 let borrowed = base_node.borrow();
183 borrowed
184 .parent()
185 .upgrade()
186 .map(|p| p.borrow().id())
187 .unwrap_or(0)
188 };
189 let expected_parent_id = base_parent.borrow().id();
190
191 if base_parent_match != expected_parent_id {
192 ml.add_hangon(current);
194 ml.lock_neighborhood(0, 1);
195 } else {
196 let base_id = base_node.borrow().id();
197
198 if base_matches.contains_key(&base_id) {
199 ml.add_hangon(current);
201 if let Some(first_pos) = base_matches.get(&base_id).and_then(|p| *p) {
202 ml.lock_neighborhood_at(first_pos, 1, 1);
204 base_matches.insert(base_id, None);
206 }
207 ml.lock_neighborhood(0, 1);
208 } else {
209 ml.add_node(current.clone());
211 base_matches.insert(base_id, Some(ml.tail_pos as usize));
212
213 let child_pos = base_node.borrow().child_pos();
214 let child_pos_i32 = if child_pos < 0 { -2 } else { child_pos };
215
216 if (prev_child_pos + 1) != child_pos_i32 {
217 let mut moved = false;
219
220 if prev_child_pos != -2
221 && child_pos_i32 != -2
222 && prev_child_pos < child_pos_i32
223 {
224 for j in 0..child_count {
226 if let Some(sibling) = parent.borrow().child(j).cloned() {
227 if let Some(sibling_base) = BranchNode::base_match(&sibling)
228 {
229 let base_pos = sibling_base.borrow().child_pos();
230 if base_pos >= 0
231 && base_pos > prev_child_pos
232 && base_pos < child_pos_i32
233 {
234 moved = true;
235 break;
236 }
237 }
238 }
239 }
240 } else {
241 moved = true;
242 }
243
244 if moved {
245 ml.lock_neighborhood(1, 0);
246 ml.set_moved(true);
247 } else {
248 ml.set_moved(false);
249 }
250 }
251 prev_child_pos = child_pos_i32;
252 }
253 }
254 } else {
255 ml.add_hangon(current);
257 ml.lock_neighborhood(0, 1);
258 }
259 }
260
261 ml.add_entry(MergeEntry::end());
262
263 let base_child_count = base_parent.borrow().child_count() as i32;
265 if (prev_child_pos + 1) != base_child_count {
266 ml.lock_neighborhood(1, 0);
267 }
268
269 let _ = edit_log;
271
272 ml
273 }
274
275 pub fn add_entry(&mut self, mut entry: MergeEntry) {
277 self.tail_pos += 1;
278 let pos = self.tail_pos as usize;
279
280 while self.entries.len() <= pos {
282 self.entries.push(MergeEntry::new(None));
283 }
284
285 if let Some(existing) = self.entries.get(pos) {
287 entry.locked = entry.locked || existing.locked;
288 }
289
290 if let Some(base) = entry.base_match() {
292 let base_id = base.borrow().id();
293 self.index.insert(base_id, pos);
294 }
295
296 self.entries[pos] = entry;
297 }
298
299 pub fn add_node(&mut self, node: NodeRef) {
301 self.add_entry(MergeEntry::new(Some(node)));
302 }
303
304 pub fn add_hangon(&mut self, node: NodeRef) {
306 if self.tail_pos >= 0 {
307 let pos = self.tail_pos as usize;
308 if pos < self.entries.len() {
309 self.entries[pos].add_hangon(node);
310 }
311 }
312 }
313
314 pub fn set_moved(&mut self, moved: bool) {
316 if self.tail_pos >= 0 {
317 let pos = self.tail_pos as usize;
318 if pos < self.entries.len() {
319 self.entries[pos].moved = moved;
320 }
321 }
322 }
323
324 pub fn lock_neighborhood(&mut self, left: usize, right: usize) {
326 self.lock_neighborhood_at(self.tail_pos as usize, left, right);
327 }
328
329 pub fn lock_neighborhood_at(&mut self, pos: usize, left: usize, right: usize) {
331 let end_pos = pos + right + 1;
332
333 while self.entries.len() < end_pos {
335 self.entries.push(MergeEntry::new(None));
336 }
337
338 let start = pos.saturating_sub(left);
339 let end = std::cmp::min(pos + right + 1, self.entries.len());
340
341 for i in start..end {
342 self.entries[i].locked = true;
343 }
344 }
345
346 pub fn entry_count(&self) -> usize {
348 (self.tail_pos + 1) as usize
349 }
350
351 pub fn entry(&self, ix: usize) -> Option<&MergeEntry> {
353 self.entries.get(ix)
354 }
355
356 pub fn entry_mut(&mut self, ix: usize) -> Option<&mut MergeEntry> {
358 self.entries.get_mut(ix)
359 }
360
361 pub fn entries(&self) -> &[MergeEntry] {
363 &self.entries[..self.entry_count()]
364 }
365
366 pub fn remove_entry_at(&mut self, ix: usize) {
368 if ix < self.entries.len() {
369 self.entries.remove(ix);
370 self.tail_pos -= 1;
371
372 self.index.clear();
374 for (i, entry) in self.entries.iter().enumerate() {
375 if let Some(base) = entry.base_match() {
376 self.index.insert(base.borrow().id(), i);
377 }
378 }
379 }
380 }
381
382 pub fn move_hangons_to_predecessor(&mut self, ix: usize) {
384 if ix > 0 && ix < self.entries.len() {
385 let hangons: Vec<NodeRef> = self.entries[ix].hangons.drain(..).collect();
386 for hangon in hangons {
387 self.entries[ix - 1].add_hangon(hangon);
388 }
389 }
390 }
391
392 pub fn find_partner(&self, entry: &MergeEntry) -> Option<&MergeEntry> {
394 let ix = self.find_partner_index(Some(entry))?;
395 self.entries.get(ix)
396 }
397
398 pub fn find_partner_index(&self, entry: Option<&MergeEntry>) -> Option<usize> {
400 let entry = entry?;
401
402 if entry.is_start {
403 return Some(0);
404 }
405 if entry.is_end {
406 return Some(self.entry_count().saturating_sub(1));
407 }
408
409 let base = entry.base_match()?;
410 let base_id = base.borrow().id();
411 self.index.get(&base_id).copied()
412 }
413
414 pub fn match_in_list(&self, base: &NodeRef) -> Option<usize> {
416 let base_id = base.borrow().id();
417 self.index.get(&base_id).copied()
418 }
419
420 pub fn entry_parent(&self) -> Option<NodeRef> {
422 self.entry_parent.clone()
423 }
424
425 pub fn is_end(&self, ix: usize) -> bool {
427 self.entries.get(ix).is_none_or(|e| e.is_end)
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434 use crate::node::{new_branch_node, XmlContent, XmlElement};
435
436 fn make_element(name: &str) -> XmlContent {
437 XmlContent::Element(XmlElement::new(
438 name.to_string(),
439 std::collections::HashMap::new(),
440 ))
441 }
442
443 #[test]
444 fn test_merge_entry_basic() {
445 let entry = MergeEntry::new(None);
446 assert!(entry.node().is_none());
447 assert!(!entry.is_start);
448 assert!(!entry.is_end);
449 assert!(!entry.locked);
450 assert!(!entry.moved);
451 assert_eq!(entry.hangon_count(), 0);
452 }
453
454 #[test]
455 fn test_merge_entry_start_end() {
456 let start = MergeEntry::start();
457 assert!(start.is_start);
458 assert!(!start.is_end);
459
460 let end = MergeEntry::end();
461 assert!(!end.is_start);
462 assert!(end.is_end);
463 }
464
465 #[test]
466 fn test_merge_entry_hangons() {
467 let mut entry = MergeEntry::new(None);
468 let node = new_branch_node(Some(make_element("child")));
469
470 entry.add_hangon(node.clone());
471 assert_eq!(entry.hangon_count(), 1);
472 assert_eq!(entry.hangons()[0].borrow().id(), node.borrow().id());
473 }
474
475 #[test]
476 fn test_merge_list_empty() {
477 let ml = MergeList::new(None);
478 assert_eq!(ml.entry_count(), 0);
479 assert!(ml.entry_parent().is_none());
480 }
481
482 #[test]
483 fn test_merge_list_add_entries() {
484 let mut ml = MergeList::new(None);
485
486 ml.add_entry(MergeEntry::start());
487 assert_eq!(ml.entry_count(), 1);
488
489 ml.add_node(new_branch_node(Some(make_element("a"))));
490 assert_eq!(ml.entry_count(), 2);
491
492 ml.add_entry(MergeEntry::end());
493 assert_eq!(ml.entry_count(), 3);
494
495 assert!(ml.entry(0).unwrap().is_start);
496 assert!(ml.entry(2).unwrap().is_end);
497 }
498
499 #[test]
500 fn test_merge_list_locking() {
501 let mut ml = MergeList::new(None);
502
503 ml.add_entry(MergeEntry::start());
504 ml.add_node(new_branch_node(Some(make_element("a"))));
505 ml.add_node(new_branch_node(Some(make_element("b"))));
506 ml.add_entry(MergeEntry::end());
507
508 ml.lock_neighborhood_at(1, 1, 1);
509
510 assert!(ml.entry(0).unwrap().locked);
511 assert!(ml.entry(1).unwrap().locked);
512 assert!(ml.entry(2).unwrap().locked);
513 assert!(!ml.entry(3).unwrap().locked);
514 }
515
516 #[test]
517 fn test_merge_list_remove_entry() {
518 let mut ml = MergeList::new(None);
519
520 ml.add_entry(MergeEntry::start());
521 ml.add_node(new_branch_node(Some(make_element("a"))));
522 ml.add_node(new_branch_node(Some(make_element("b"))));
523 ml.add_entry(MergeEntry::end());
524
525 assert_eq!(ml.entry_count(), 4);
526
527 ml.remove_entry_at(2);
528 assert_eq!(ml.entry_count(), 3);
529 }
530}