1mod conflict_log;
16mod edit_log;
17mod merge_list;
18mod merge_pair;
19
20pub use conflict_log::{ConflictLog, ConflictType};
21pub use edit_log::{EditLog, EditType};
22pub use merge_list::{MergeEntry, MergeList};
23pub use merge_pair::{MergePair, MergePairList};
24
25use std::io::Write;
26
27use crate::matching::TriMatching;
28use crate::node::base::BaseNode;
29use crate::node::branch::BranchNode;
30use crate::node::{new_node_ref, MatchType, NodeInner, NodeRef, XmlContent, XmlElement};
31use crate::xml::XmlPrinter;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
35pub enum Operation {
36 Nop,
38 MoveI,
40 MoveF,
42 Delete,
44}
45
46pub struct Merge {
48 matching: TriMatching,
50 pub conflict_log: ConflictLog,
52 pub edit_log: EditLog,
54}
55
56impl Merge {
57 pub fn new(matching: TriMatching) -> Self {
59 Merge {
60 matching,
61 conflict_log: ConflictLog::new(),
62 edit_log: EditLog::new(),
63 }
64 }
65
66 pub fn matching(&self) -> &TriMatching {
68 &self.matching
69 }
70
71 pub fn merge<W: Write>(&mut self, writer: &mut W) -> std::io::Result<()> {
75 let tree = self.merge_to_tree();
77
78 let mut printer = XmlPrinter::new(writer);
80 printer.print(&tree)
81 }
82
83 pub fn merge_to_tree(&mut self) -> NodeRef {
88 let root_content = XmlContent::Element(XmlElement::new(
90 "$ROOT$".to_string(),
91 std::collections::HashMap::new(),
92 ));
93 let root = new_node_ref(NodeInner::new_base(Some(root_content)));
94
95 self.tree_merge_to_node(
97 Some(self.matching.left_root().clone()),
98 Some(self.matching.right_root().clone()),
99 &root,
100 );
101
102 root
103 }
104
105 fn tree_merge_to_node(&mut self, a: Option<NodeRef>, b: Option<NodeRef>, parent: &NodeRef) {
107 let mlist_a = a.as_ref().map(|node| self.make_merge_list(node));
109 let mlist_b = b.as_ref().map(|node| self.make_merge_list(node));
110
111 let merged = match (&mlist_a, &mlist_b) {
113 (Some(ma), Some(mb)) => self.make_merge_pair_list(ma.clone(), mb.clone()),
114 (Some(ma), None) => self.merge_list_to_pair_list(ma, None),
115 (None, Some(mb)) => self.merge_list_to_pair_list(mb, None),
116 (None, None) => MergePairList::new(),
117 };
118
119 for pair in merged.pairs() {
121 let merged_content = self.merge_node_content(pair);
122
123 if let Some(content) = merged_content {
124 match &content {
125 XmlContent::Element(elem) if elem.qname() == "$ROOT$" => {
126 let partners = self.get_recursion_partners(pair);
128 self.tree_merge_to_node(partners.first, partners.second, parent);
129 }
130 _ => {
131 let child = new_node_ref(NodeInner::new_base(Some(content.clone())));
133
134 NodeInner::add_child_to_ref(parent, child.clone());
136
137 if let XmlContent::Element(_) = content {
139 let partners = self.get_recursion_partners(pair);
140 self.tree_merge_to_node(partners.first, partners.second, &child);
141 }
142 }
143 }
144 }
145 }
146 }
147
148 fn make_merge_list(&mut self, parent: &NodeRef) -> MergeList {
150 MergeList::from_branch(parent, &mut self.edit_log)
151 }
152
153 fn make_merge_pair_list(&mut self, mlist_a: MergeList, mlist_b: MergeList) -> MergePairList {
155 let mut mlist_a = mlist_a;
156 let mut mlist_b = mlist_b;
157
158 self.remove_deleted_or_moved(&mut mlist_a, &mut mlist_b);
160
161 self.edit_log.checkpoint();
163
164 if mlist_a.entry_count() != mlist_b.entry_count() {
166 self.edit_log.rewind();
168 let is_left = mlist_a
169 .entry_parent()
170 .map(|p| BranchNode::is_left_tree(&p))
171 .unwrap_or(true);
172 return if is_left {
173 self.merge_list_to_pair_list(&mlist_a, Some(&mlist_b))
174 } else {
175 self.merge_list_to_pair_list(&mlist_b, Some(&mlist_a))
176 };
177 }
178
179 let mut merged = MergePairList::new();
180 let mut pos_a = 0usize;
181 let mut pos_b = 0usize;
182
183 loop {
184 let ea = mlist_a.entry(pos_a);
185 let eb = mlist_b.entry(pos_b);
186
187 if ea.is_none() || eb.is_none() {
188 break;
189 }
190
191 let ea = ea.unwrap();
192 let eb = eb.unwrap();
193
194 for hangon in ea.hangons() {
196 let partner = hangon.borrow().first_partner(MatchType::FULL);
197 merged.append(Some(hangon.clone()), partner);
198 self.log_hangon_struct_ops(hangon);
199 }
200
201 if !self.check_hangon_combine(ea, eb) {
203 for hangon in eb.hangons() {
204 let partner = hangon.borrow().first_partner(MatchType::FULL);
205 merged.append(Some(hangon.clone()), partner);
206 self.log_hangon_struct_ops(hangon);
207 }
208 }
209
210 let next_a: Option<usize>;
212 let next_b: Option<usize>;
213
214 let ea_locked = ea.locked && mlist_a.entry(pos_a + 1).is_some_and(|e| e.locked);
215 let eb_locked = eb.locked && mlist_b.entry(pos_b + 1).is_some_and(|e| e.locked);
216
217 if !ea_locked && !eb_locked {
218 next_a = Some(pos_a + 1);
220 next_b = Some(pos_b + 1);
221 } else if ea_locked && !eb_locked {
222 next_a = Some(pos_a + 1);
223 next_b = mlist_b.find_partner_index(mlist_a.entry(pos_a + 1));
224 } else if !ea_locked && eb_locked {
225 next_b = Some(pos_b + 1);
226 next_a = mlist_a.find_partner_index(mlist_b.entry(pos_b + 1));
227 } else {
228 let partner_b = mlist_b.find_partner_index(mlist_a.entry(pos_a + 1));
230 if partner_b != Some(pos_b + 1) {
231 self.conflict_log.add_list_conflict(
233 ConflictType::Move,
234 "Conflicting moves inside child list",
235 ea.node().and_then(|n| BranchNode::base_match(&n)),
236 ea.node(),
237 eb.node(),
238 );
239 self.edit_log.rewind();
240 let is_left = mlist_a
241 .entry_parent()
242 .map(|p| BranchNode::is_left_tree(&p))
243 .unwrap_or(true);
244 return if is_left {
245 self.merge_list_to_pair_list(&mlist_a, Some(&mlist_b))
246 } else {
247 self.merge_list_to_pair_list(&mlist_b, Some(&mlist_a))
248 };
249 }
250 next_a = Some(pos_a + 1);
251 next_b = partner_b;
252 }
253
254 pos_a = next_a.unwrap_or(pos_a + 1);
255 pos_b = next_b.unwrap_or(pos_b + 1);
256
257 if mlist_a.is_end(pos_a) || mlist_b.is_end(pos_b) {
259 break;
260 }
261
262 let ea = mlist_a.entry(pos_a);
264 let eb = mlist_b.entry(pos_b);
265 if let (Some(ea), Some(eb)) = (ea, eb) {
266 merged.append(ea.node(), eb.node());
267 self.log_entry_struct_ops(ea, eb);
268 }
269 }
270
271 self.edit_log.commit();
272 merged
273 }
274
275 fn merge_list_to_pair_list(
277 &mut self,
278 mlist_a: &MergeList,
279 mlist_b: Option<&MergeList>,
280 ) -> MergePairList {
281 let mut merged = MergePairList::new();
282
283 for (i, entry) in mlist_a.entries().iter().enumerate() {
284 if i > 0 && !mlist_a.is_end(i) {
286 let node = entry.node();
287 let partner = node
288 .as_ref()
289 .and_then(|n| n.borrow().first_partner(MatchType::FULL));
290 merged.append(node, partner);
291 if let Some(n) = entry.node() {
292 self.log_hangon_struct_ops(&n);
293 }
294 }
295
296 for hangon in entry.hangons() {
298 let partner = hangon.borrow().first_partner(MatchType::FULL);
299 merged.append(Some(hangon.clone()), partner);
300 self.log_hangon_struct_ops(hangon);
301 }
302
303 if let Some(mb) = mlist_b {
305 if let Some(partner_entry) = mb.find_partner(entry) {
306 if !self.check_hangon_combine(entry, partner_entry) {
307 for hangon in partner_entry.hangons() {
308 let partner = hangon.borrow().first_partner(MatchType::FULL);
309 merged.append(Some(hangon.clone()), partner);
310 self.log_hangon_struct_ops(hangon);
311 }
312 }
313 }
314 }
315 }
316
317 merged
318 }
319
320 fn remove_deleted_or_moved(&mut self, mlist_a: &mut MergeList, mlist_b: &mut MergeList) {
322 let base_parent = mlist_a
323 .entry_parent()
324 .and_then(|p| BranchNode::base_match(&p));
325
326 if base_parent.is_none() {
327 return;
328 }
329
330 let base_parent = base_parent.unwrap();
331 let child_count = base_parent.borrow().child_count();
332
333 for i in 0..child_count {
334 let bn = base_parent.borrow().child(i).cloned();
335 if bn.is_none() {
336 continue;
337 }
338 let bn = bn.unwrap();
339
340 let op1 = self.get_operation(&bn, mlist_a);
341 let op2 = self.get_operation(&bn, mlist_b);
342
343 if op1 <= op2 {
346 self.handle_operation_pair(op1, op2, &bn, mlist_a, mlist_b);
347 } else {
348 self.handle_operation_pair(op2, op1, &bn, mlist_b, mlist_a);
349 }
350 }
351 }
352
353 fn handle_operation_pair(
355 &mut self,
356 op1: Operation,
357 op2: Operation,
358 bn: &NodeRef,
359 ml1: &mut MergeList,
360 _ml2: &mut MergeList,
361 ) {
362 use Operation::*;
363
364 match (op1, op2) {
365 (Nop, Nop) | (Nop, MoveI) | (MoveI, MoveI) | (Delete, Delete) => {
366 }
368 (Nop, MoveF) | (Nop, Delete) => {
369 if let Some(ix) = ml1.match_in_list(bn) {
371 if op2 == Delete {
372 if let Some(entry) = ml1.entry(ix) {
374 if let Some(node) = entry.node() {
375 if self.is_deletia_modified(&node, ml1) {
376 self.conflict_log.add_list_warning(
377 ConflictType::Delete,
378 "Modifications in deleted subtree",
379 Some(bn.clone()),
380 entry.node(),
381 None,
382 );
383 }
384 }
385 }
386 self.edit_log.delete(Some(bn.clone()));
387 }
388
389 ml1.move_hangons_to_predecessor(ix);
391 ml1.remove_entry_at(ix);
392 }
393 }
394 (MoveI, MoveF) => {
395 if let Some(ix) = ml1.match_in_list(bn) {
397 let node = ml1.entry(ix).and_then(|e| e.node());
398 let partner = node
399 .as_ref()
400 .and_then(|n| n.borrow().first_partner(MatchType::FULL));
401 self.conflict_log.add_list_conflict(
402 ConflictType::Move,
403 "Node moved to different locations",
404 Some(bn.clone()),
405 node,
406 partner,
407 );
408 ml1.remove_entry_at(ix);
409 }
410 }
411 (MoveI, Delete) => {
412 if let Some(ix) = ml1.match_in_list(bn) {
414 let node = ml1.entry(ix).and_then(|e| e.node());
415 self.conflict_log.add_list_conflict(
416 ConflictType::Move,
417 "Node moved and deleted",
418 Some(bn.clone()),
419 node,
420 None,
421 );
422 self.edit_log.delete(Some(bn.clone()));
423 ml1.remove_entry_at(ix);
424 }
425 }
426 (MoveF, MoveF) => {
427 if self.is_movef_movef_conflict(bn) {
429 self.conflict_log.add_list_conflict(
430 ConflictType::Move,
431 "Node moved to different locations",
432 Some(bn.clone()),
433 BaseNode::left(bn).borrow().full_match(),
434 BaseNode::right(bn).borrow().full_match(),
435 );
436 }
437 }
438 (MoveF, Delete) => {
439 self.conflict_log.add_list_conflict(
441 ConflictType::Move,
442 "Node moved and deleted",
443 Some(bn.clone()),
444 BaseNode::left(bn).borrow().full_match(),
445 BaseNode::right(bn).borrow().full_match(),
446 );
447 }
448 _ => {}
449 }
450 }
451
452 fn get_operation(&self, bn: &NodeRef, ml: &MergeList) -> Operation {
454 if let Some(pos) = ml.match_in_list(bn) {
455 if ml.entry(pos).is_some_and(|e| e.moved) {
456 Operation::MoveI
457 } else {
458 Operation::Nop
459 }
460 } else {
461 let is_left = ml
463 .entry_parent()
464 .map(|p| BranchNode::is_left_tree(&p))
465 .unwrap_or(true);
466
467 let copies = if is_left {
468 BaseNode::left(bn)
469 } else {
470 BaseNode::right(bn)
471 };
472
473 if copies.borrow().match_count() == 0 {
474 Operation::Delete
475 } else {
476 Operation::MoveF
477 }
478 }
479 }
480
481 fn is_deletia_modified(&self, n: &NodeRef, ml: &MergeList) -> bool {
483 let base_match = BranchNode::base_match(n);
484 if base_match.is_none() {
485 return true; }
487
488 let base_match = base_match.unwrap();
489 if self.get_operation(&base_match, ml) != Operation::Nop {
490 return true; }
492
493 if BranchNode::base_match_type(n) != MatchType::FULL {
494 return true; }
496
497 if let Some(partners) = BranchNode::partners(n) {
499 if partners.borrow().match_count() == 0 {
500 if !self.matches(n, &base_match) {
501 return true; }
503 for i in 0..n.borrow().child_count() {
505 if let Some(child) = n.borrow().child(i).cloned() {
506 if BranchNode::base_match_type(&child) != MatchType::FULL {
508 return true;
509 }
510 }
511 }
512 }
513 }
514
515 false
516 }
517
518 fn is_movef_movef_conflict(&self, bn: &NodeRef) -> bool {
523 let left_node = BaseNode::left(bn).borrow().full_match();
525 let right_node = BaseNode::right(bn).borrow().full_match();
526
527 let (left_node, right_node) = match (left_node, right_node) {
528 (Some(l), Some(r)) => (l, r),
529 _ => return false,
530 };
531
532 let left_parent = BranchNode::parent(&left_node);
534 let right_parent = BranchNode::parent(&right_node);
535
536 match (left_parent, right_parent) {
537 (Some(lp), Some(rp)) => {
538 let lp_base = BranchNode::base_match(&lp);
539 let rp_base = BranchNode::base_match(&rp);
540 match (lp_base, rp_base) {
541 (Some(lb), Some(rb)) => lb.borrow().id() != rb.borrow().id(),
542 _ => true, }
544 }
545 (None, None) => false, _ => true, }
548 }
549
550 fn merge_node_content(&mut self, pair: &MergePair) -> Option<XmlContent> {
552 let n1 = pair.first.as_ref();
553 let n2 = pair.second.as_ref();
554
555 match (n1, n2) {
556 (None, None) => None,
557 (Some(n), None) | (None, Some(n)) => n.borrow().content().cloned(),
558 (Some(n1), Some(n2)) => {
559 let n1_content = n1.borrow().is_match(MatchType::CONTENT);
560 let n2_content = n2.borrow().is_match(MatchType::CONTENT);
561
562 if n1_content {
563 if !n2_content {
564 self.edit_log.update(Some(n2.clone()));
565 n2.borrow().content().cloned()
566 } else {
567 self.content_merge(n1, n2)
568 }
569 } else if n2_content {
570 self.edit_log.update(Some(n1.clone()));
571 n1.borrow().content().cloned()
572 } else {
573 self.content_merge(n1, n2)
575 }
576 }
577 }
578 }
579
580 fn content_merge(&mut self, a: &NodeRef, b: &NodeRef) -> Option<XmlContent> {
582 let base_a = BranchNode::base_match(a);
583 let base_b = BranchNode::base_match(b);
584
585 let a_updated = base_a.as_ref().is_none_or(|ba| !self.matches(a, ba));
586 let b_updated = base_b.as_ref().is_none_or(|bb| !self.matches(b, bb));
587
588 if a_updated && b_updated {
589 if self.matches(a, b) {
591 self.conflict_log.add_node_warning(
592 ConflictType::Update,
593 "Node updated in both branches, but updates are equal",
594 base_a.clone(),
595 Some(a.clone()),
596 Some(b.clone()),
597 );
598 self.edit_log.update(Some(a.clone()));
599 return a.borrow().content().cloned();
600 }
601
602 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
604 return Some(merged);
605 }
606
607 self.conflict_log.add_node_conflict(
609 ConflictType::Update,
610 "Node updated in both branches, using branch 1",
611 base_a,
612 Some(a.clone()),
613 Some(b.clone()),
614 );
615 self.edit_log.update(Some(a.clone()));
616 a.borrow().content().cloned()
617 } else if b_updated {
618 if self.has_namespace_changes(a, base_a.as_ref()) {
620 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
621 return Some(merged);
622 }
623 self.conflict_log.add_node_conflict(
625 ConflictType::Update,
626 "Namespace declaration conflict between branches, using branch 2",
627 base_a,
628 Some(a.clone()),
629 Some(b.clone()),
630 );
631 }
632 self.edit_log.update(Some(b.clone()));
633 b.borrow().content().cloned()
634 } else if a_updated {
635 if self.has_namespace_changes(b, base_b.as_ref()) {
637 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
638 return Some(merged);
639 }
640 self.conflict_log.add_node_conflict(
642 ConflictType::Update,
643 "Namespace declaration conflict between branches, using branch 1",
644 base_a,
645 Some(a.clone()),
646 Some(b.clone()),
647 );
648 }
649 self.edit_log.update(Some(a.clone()));
650 a.borrow().content().cloned()
651 } else {
652 if self.has_namespace_changes(a, base_a.as_ref())
654 || self.has_namespace_changes(b, base_b.as_ref())
655 {
656 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
657 return Some(merged);
658 }
659 self.conflict_log.add_node_conflict(
661 ConflictType::Update,
662 "Namespace declaration conflict between branches, using branch 1",
663 base_a.clone(),
664 Some(a.clone()),
665 Some(b.clone()),
666 );
667 }
668 a.borrow().content().cloned()
669 }
670 }
671
672 fn merge_elements(
674 &self,
675 base: Option<&NodeRef>,
676 a: &NodeRef,
677 b: &NodeRef,
678 ) -> Option<XmlContent> {
679 let base_content = base.and_then(|n| n.borrow().content().cloned());
680 let a_content = a.borrow().content().cloned();
681 let b_content = b.borrow().content().cloned();
682
683 match (&base_content, &a_content, &b_content) {
684 (
685 Some(XmlContent::Element(base_elem)),
686 Some(XmlContent::Element(a_elem)),
687 Some(XmlContent::Element(b_elem)),
688 ) => {
689 let tag_name = if base_elem.names_match(b_elem) {
691 a_elem.qname().to_string()
692 } else if base_elem.names_match(a_elem) {
693 b_elem.qname().to_string()
694 } else {
695 return None; };
697
698 self.merge_attributes(
700 base_elem.attributes(),
701 a_elem.attributes(),
702 b_elem.attributes(),
703 )
704 .and_then(|merged_attrs| {
705 let merged_ns = self.merge_namespace_decls(
706 base_elem.namespace_decls(),
707 a_elem.namespace_decls(),
708 b_elem.namespace_decls(),
709 )?;
710 let expanded = if base_elem.names_match(b_elem) {
712 a_elem.expanded_name().cloned()
713 } else {
714 b_elem.expanded_name().cloned()
715 };
716 Some(XmlContent::Element(XmlElement::new_with_namespace(
717 tag_name,
718 expanded,
719 merged_ns,
720 merged_attrs,
721 )))
722 })
723 }
724 _ => None,
725 }
726 }
727
728 fn merge_attributes(
730 &self,
731 base: &std::collections::HashMap<String, String>,
732 a: &std::collections::HashMap<String, String>,
733 b: &std::collections::HashMap<String, String>,
734 ) -> Option<std::collections::HashMap<String, String>> {
735 let mut merged = std::collections::HashMap::new();
736 let mut deletia = std::collections::HashSet::new();
737
738 for (key, base_val) in base {
740 let in_a = a.get(key);
741 let in_b = b.get(key);
742
743 match (in_a, in_b) {
744 (None, Some(b_val)) if base_val != b_val => return None, (Some(a_val), None) if base_val != a_val => return None, (None, _) | (_, None) => {
747 deletia.insert(key.clone());
748 }
749 _ => {}
750 }
751 }
752
753 for (key, a_val) in a {
755 if deletia.contains(key) {
756 continue;
757 }
758
759 if let Some(b_val) = b.get(key) {
760 let base_val = base.get(key);
761 if base_val == Some(b_val) {
762 merged.insert(key.clone(), a_val.clone());
764 } else if base_val == Some(a_val) {
765 merged.insert(key.clone(), b_val.clone());
767 } else if a_val != b_val {
768 return None;
770 } else {
771 merged.insert(key.clone(), a_val.clone());
772 }
773 } else {
774 merged.insert(key.clone(), a_val.clone());
776 }
777 }
778
779 for (key, b_val) in b {
781 if !deletia.contains(key) && !a.contains_key(key) {
782 merged.insert(key.clone(), b_val.clone());
783 }
784 }
785
786 Some(merged)
787 }
788
789 fn merge_namespace_decls(
792 &self,
793 base: &std::collections::HashMap<String, String>,
794 a: &std::collections::HashMap<String, String>,
795 b: &std::collections::HashMap<String, String>,
796 ) -> Option<std::collections::HashMap<String, String>> {
797 let mut merged = std::collections::HashMap::new();
798 let mut deletia = std::collections::HashSet::new();
799
800 for (prefix, base_uri) in base {
802 let in_a = a.get(prefix);
803 let in_b = b.get(prefix);
804
805 match (in_a, in_b) {
806 (None, Some(b_uri)) if base_uri != b_uri => return None,
807 (Some(a_uri), None) if base_uri != a_uri => return None,
808 (None, _) | (_, None) => {
809 deletia.insert(prefix.clone());
810 }
811 _ => {}
812 }
813 }
814
815 for (prefix, a_uri) in a {
817 if deletia.contains(prefix) {
818 continue;
819 }
820 if let Some(b_uri) = b.get(prefix) {
821 let base_uri = base.get(prefix);
822 if base_uri == Some(b_uri) {
823 merged.insert(prefix.clone(), a_uri.clone());
824 } else if base_uri == Some(a_uri) {
825 merged.insert(prefix.clone(), b_uri.clone());
826 } else if a_uri != b_uri {
827 return None;
828 } else {
829 merged.insert(prefix.clone(), a_uri.clone());
830 }
831 } else {
832 merged.insert(prefix.clone(), a_uri.clone());
833 }
834 }
835
836 for (prefix, b_uri) in b {
838 if !deletia.contains(prefix) && !a.contains_key(prefix) {
839 merged.insert(prefix.clone(), b_uri.clone());
840 }
841 }
842
843 Some(merged)
844 }
845
846 fn get_recursion_partners(&self, pair: &MergePair) -> RecursionPartners {
848 let n1 = pair.first.as_ref();
849 let n2 = pair.second.as_ref();
850
851 match (n1, n2) {
852 (None, _) | (_, None) => RecursionPartners {
853 first: pair.first.clone(),
854 second: pair.second.clone(),
855 },
856 (Some(n1), Some(n2)) => {
857 let n1_children = n1.borrow().is_match(MatchType::CHILDREN);
858 let n2_children = n2.borrow().is_match(MatchType::CHILDREN);
859
860 if n1_children && n2_children {
861 RecursionPartners {
862 first: Some(n1.clone()),
863 second: Some(n2.clone()),
864 }
865 } else if n1_children && n2.borrow().is_match(MatchType::CONTENT) {
866 RecursionPartners {
867 first: None,
868 second: Some(n2.clone()),
869 }
870 } else if n1.borrow().is_match(MatchType::CONTENT) && n2_children {
871 RecursionPartners {
872 first: Some(n1.clone()),
873 second: None,
874 }
875 } else {
876 RecursionPartners {
877 first: Some(n1.clone()),
878 second: Some(n2.clone()),
879 }
880 }
881 }
882 }
883 }
884
885 fn has_namespace_changes(&self, node: &NodeRef, base: Option<&NodeRef>) -> bool {
887 let Some(base) = base else { return false };
888 let node_borrowed = node.borrow();
889 let base_borrowed = base.borrow();
890 match (node_borrowed.content(), base_borrowed.content()) {
891 (Some(XmlContent::Element(ne)), Some(XmlContent::Element(be))) => {
892 !ne.namespace_decls_equal(be)
893 }
894 _ => false,
895 }
896 }
897
898 fn matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
900 let a_borrowed = a.borrow();
901 let b_borrowed = b.borrow();
902 let a_content = a_borrowed.content();
903 let b_content = b_borrowed.content();
904
905 match (a_content, b_content) {
906 (Some(ac), Some(bc)) => ac.content_equals(bc),
907 _ => false,
908 }
909 }
910
911 fn log_hangon_struct_ops(&mut self, n: &NodeRef) {
913 if !BranchNode::has_base_match(n) {
914 self.edit_log.insert(Some(n.clone()));
915 } else {
916 let base = BranchNode::base_match(n).unwrap();
917 let is_left = BranchNode::is_left_tree(n);
918
919 let copies = if is_left {
920 BaseNode::left(&base)
921 } else {
922 BaseNode::right(&base)
923 };
924
925 if copies.borrow().match_count() > 1 {
926 self.edit_log.copy(Some(n.clone()));
927 } else {
928 self.edit_log.r#move(Some(n.clone()));
929 }
930 }
931 }
932
933 fn log_entry_struct_ops(&mut self, e1: &MergeEntry, e2: &MergeEntry) {
935 if e1.moved {
936 if let Some(n) = e1.node() {
937 self.edit_log.r#move(Some(n));
938 }
939 } else if e2.moved {
940 if let Some(n) = e2.node() {
941 self.edit_log.r#move(Some(n));
942 }
943 }
944 }
945
946 fn check_hangon_combine(&mut self, ea: &MergeEntry, eb: &MergeEntry) -> bool {
950 if ea.hangons().is_empty() {
952 return false;
953 }
954
955 if ea.hangons().len() != eb.hangons().len() {
956 if !ea.hangons().is_empty() && !eb.hangons().is_empty() {
957 self.conflict_log.add_list_warning(
958 ConflictType::Insert,
959 "Insertions/copies in both branches",
960 ea.node().and_then(|n| BranchNode::base_match(&n)),
961 ea.node(),
962 eb.node(),
963 );
964 }
965 return false;
966 }
967
968 let mut equal = true;
970 for (ha, hb) in ea.hangons().iter().zip(eb.hangons().iter()) {
971 if !self.tree_matches(ha, hb) {
972 equal = false;
973 break;
974 }
975 }
976
977 if equal {
978 self.conflict_log.add_list_warning(
979 ConflictType::Insert,
980 "Equal insertions/copies in both branches",
981 ea.node().and_then(|n| BranchNode::base_match(&n)),
982 ea.node(),
983 eb.node(),
984 );
985 } else if !ea.hangons().is_empty() {
986 self.conflict_log.add_list_warning(
987 ConflictType::Insert,
988 "Insertions/copies in both branches, sequencing",
989 ea.node().and_then(|n| BranchNode::base_match(&n)),
990 ea.node(),
991 eb.node(),
992 );
993 }
994
995 equal
996 }
997
998 fn tree_matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
1000 if !self.matches(a, b) {
1001 return false;
1002 }
1003
1004 let a_count = a.borrow().child_count();
1005 let b_count = b.borrow().child_count();
1006
1007 if a_count != b_count {
1008 return false;
1009 }
1010
1011 for i in 0..a_count {
1012 let a_child = a.borrow().child(i).cloned();
1013 let b_child = b.borrow().child(i).cloned();
1014
1015 match (a_child, b_child) {
1016 (Some(ac), Some(bc)) => {
1017 if !self.tree_matches(&ac, &bc) {
1018 return false;
1019 }
1020 }
1021 _ => return false,
1022 }
1023 }
1024
1025 true
1026 }
1027}
1028
1029struct RecursionPartners {
1031 first: Option<NodeRef>,
1032 second: Option<NodeRef>,
1033}
1034
1035impl crate::node::NodeInner {
1036 pub fn first_partner(&self, type_flags: MatchType) -> Option<NodeRef> {
1038 if let crate::node::NodeKind::Branch {
1039 match_type,
1040 partners,
1041 ..
1042 } = self.kind()
1043 {
1044 if !match_type.intersects(type_flags) {
1045 return None;
1046 }
1047
1048 if let Some(partners) = partners {
1049 let borrowed = partners.borrow();
1050 for candidate in borrowed.matches() {
1051 if BranchNode::base_match_type(candidate).intersects(type_flags) {
1052 return Some(candidate.clone());
1053 }
1054 }
1055 }
1056 }
1057 None
1058 }
1059
1060 pub fn is_match(&self, type_flags: MatchType) -> bool {
1062 if let crate::node::NodeKind::Branch { match_type, .. } = self.kind() {
1063 match_type.intersects(type_flags)
1064 } else {
1065 false
1066 }
1067 }
1068}
1069
1070#[cfg(test)]
1071mod tests {
1072 use super::*;
1073 use crate::matching::TriMatching;
1074 use crate::xml::{BaseNodeFactory, BranchNodeFactory, XmlParser};
1075 use std::collections::HashMap;
1076
1077 fn merge_xml(base: &str, a: &str, b: &str) -> (String, usize) {
1079 let base_tree = XmlParser::new(BaseNodeFactory)
1080 .parse_str(base)
1081 .expect("parse base");
1082 let a_tree = XmlParser::new(BranchNodeFactory)
1083 .parse_str(a)
1084 .expect("parse a");
1085 let b_tree = XmlParser::new(BranchNodeFactory)
1086 .parse_str(b)
1087 .expect("parse b");
1088 let matching = TriMatching::new(a_tree, base_tree, b_tree);
1089 let mut merge = Merge::new(matching);
1090 let mut output = Vec::new();
1091 merge.merge(&mut output).expect("merge");
1092 let conflicts = merge.conflict_log.conflicts().len();
1093 (String::from_utf8(output).expect("utf8"), conflicts)
1094 }
1095
1096 #[test]
1097 fn test_merge_namespace_decls_deletion() {
1098 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com" xmlns:bar="http://bar.com"><child/></root>"#;
1100 let a = base;
1101 let b = r#"<?xml version="1.0"?><root xmlns:bar="http://bar.com"><child/></root>"#;
1102 let (output, conflicts) = merge_xml(base, a, b);
1103 assert!(!output.contains("xmlns:foo"), "xmlns:foo should be removed");
1104 assert!(output.contains("xmlns:bar"), "xmlns:bar should remain");
1105 assert_eq!(conflicts, 0);
1106 }
1107
1108 #[test]
1109 fn test_merge_namespace_decls_addition() {
1110 let base = r#"<?xml version="1.0"?><root xmlns:a="http://a.com"><child/></root>"#;
1112 let a = r#"<?xml version="1.0"?><root xmlns:a="http://a.com" xmlns:b="http://b.com"><child/></root>"#;
1113 let b = r#"<?xml version="1.0"?><root xmlns:a="http://a.com" xmlns:c="http://c.com"><child/></root>"#;
1114 let (output, conflicts) = merge_xml(base, a, b);
1115 assert!(output.contains("xmlns:b"), "xmlns:b should be present");
1116 assert!(output.contains("xmlns:c"), "xmlns:c should be present");
1117 assert_eq!(conflicts, 0);
1118 }
1119
1120 #[test]
1121 fn test_merge_namespace_decls_delete_vs_change_conflict() {
1122 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com" xmlns:bar="http://bar.com"><child/></root>"#;
1124 let a = r#"<?xml version="1.0"?><root xmlns:bar="http://bar.com"><child/></root>"#;
1125 let b = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-v2.com" xmlns:bar="http://bar.com"><child/></root>"#;
1126 let (_output, conflicts) = merge_xml(base, a, b);
1127 assert_eq!(conflicts, 1, "delete-vs-change should produce a conflict");
1128 }
1129
1130 #[test]
1131 fn test_merge_namespace_decls_both_change_same_prefix_conflict() {
1132 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com"><child/></root>"#;
1134 let a = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-a.com"><child/></root>"#;
1135 let b = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-b.com"><child/></root>"#;
1136 let (_output, conflicts) = merge_xml(base, a, b);
1137 assert_eq!(
1138 conflicts, 1,
1139 "both changing same prefix differently should conflict"
1140 );
1141 }
1142
1143 #[test]
1144 fn test_merge_namespace_decls_both_change_same_value() {
1145 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com"><child/></root>"#;
1147 let a = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-v2.com"><child/></root>"#;
1148 let b = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-v2.com"><child/></root>"#;
1149 let (output, conflicts) = merge_xml(base, a, b);
1150 assert!(
1151 output.contains("http://foo-v2.com"),
1152 "merged URI should be the new value"
1153 );
1154 assert_eq!(conflicts, 0);
1155 }
1156
1157 #[test]
1158 fn test_merge_namespace_decls_unit() {
1159 let matching = {
1161 let base = XmlParser::new(BaseNodeFactory)
1162 .parse_str("<root/>")
1163 .unwrap();
1164 let a = XmlParser::new(BranchNodeFactory)
1165 .parse_str("<root/>")
1166 .unwrap();
1167 let b = XmlParser::new(BranchNodeFactory)
1168 .parse_str("<root/>")
1169 .unwrap();
1170 TriMatching::new(a, base, b)
1171 };
1172 let merge = Merge::new(matching);
1173
1174 let mut base = HashMap::new();
1175 base.insert("foo".into(), "http://foo.com".into());
1176 base.insert("bar".into(), "http://bar.com".into());
1177
1178 let mut a = HashMap::new();
1180 a.insert("bar".into(), "http://bar.com".into());
1181 let b = base.clone();
1182
1183 let result = merge.merge_namespace_decls(&base, &a, &b).unwrap();
1184 assert!(!result.contains_key("foo"));
1185 assert!(result.contains_key("bar"));
1186
1187 let mut b_changed = HashMap::new();
1189 b_changed.insert("foo".into(), "http://foo-v2.com".into());
1190 b_changed.insert("bar".into(), "http://bar.com".into());
1191
1192 let result = merge.merge_namespace_decls(&base, &a, &b_changed);
1193 assert!(result.is_none(), "delete-vs-change should return None");
1194 }
1195}