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 if let Some(entry) = ml1.entry(ix) {
391 if entry.locked {
392 self.conflict_log.add_list_conflict(
393 ConflictType::Delete,
394 "Moved or copied node deleted",
395 Some(bn.clone()),
396 entry.node(),
397 None,
398 );
399 }
400 }
401 }
402
403 ml1.move_hangons_to_predecessor(ix);
405 ml1.remove_entry_at(ix);
406 }
407 }
408 (MoveI, MoveF) => {
409 if let Some(ix) = ml1.match_in_list(bn) {
411 let node = ml1.entry(ix).and_then(|e| e.node());
412 let partner = node
413 .as_ref()
414 .and_then(|n| n.borrow().first_partner(MatchType::FULL));
415 self.conflict_log.add_list_conflict(
416 ConflictType::Move,
417 "Node moved to different locations",
418 Some(bn.clone()),
419 node,
420 partner,
421 );
422 ml1.remove_entry_at(ix);
423 }
424 }
425 (MoveI, Delete) => {
426 if let Some(ix) = ml1.match_in_list(bn) {
428 let node = ml1.entry(ix).and_then(|e| e.node());
429 self.conflict_log.add_list_conflict(
430 ConflictType::Move,
431 "Node moved and deleted",
432 Some(bn.clone()),
433 node,
434 None,
435 );
436 self.edit_log.delete(Some(bn.clone()));
437 ml1.remove_entry_at(ix);
438 }
439 }
440 (MoveF, MoveF) => {
441 if self.is_movef_movef_conflict(bn) {
443 self.conflict_log.add_list_conflict(
444 ConflictType::Move,
445 "Node moved to different locations",
446 Some(bn.clone()),
447 BaseNode::left(bn).borrow().full_match(),
448 BaseNode::right(bn).borrow().full_match(),
449 );
450 }
451 }
452 (MoveF, Delete) => {
453 self.conflict_log.add_list_conflict(
455 ConflictType::Move,
456 "Node moved and deleted",
457 Some(bn.clone()),
458 BaseNode::left(bn).borrow().full_match(),
459 BaseNode::right(bn).borrow().full_match(),
460 );
461 }
462 _ => {}
463 }
464 }
465
466 fn get_operation(&self, bn: &NodeRef, ml: &MergeList) -> Operation {
468 if let Some(pos) = ml.match_in_list(bn) {
469 if ml.entry(pos).is_some_and(|e| e.moved) {
470 Operation::MoveI
471 } else {
472 Operation::Nop
473 }
474 } else {
475 let is_left = ml
477 .entry_parent()
478 .map(|p| BranchNode::is_left_tree(&p))
479 .unwrap_or(true);
480
481 let copies = if is_left {
482 BaseNode::left(bn)
483 } else {
484 BaseNode::right(bn)
485 };
486
487 if copies.borrow().match_count() == 0 {
488 Operation::Delete
489 } else {
490 Operation::MoveF
491 }
492 }
493 }
494
495 fn is_deletia_modified(&mut self, n: &NodeRef, ml: &MergeList) -> bool {
500 let base_match = BranchNode::base_match(n);
501 if base_match.is_none() {
502 return true; }
504
505 let base_match = base_match.unwrap();
506 if self.get_operation(&base_match, ml) != Operation::Nop {
507 return true; }
509
510 if BranchNode::base_match_type(n) != MatchType::FULL {
511 return true; }
513
514 if let Some(partners) = BranchNode::partners(n) {
516 if partners.borrow().match_count() == 0 {
517 if !self.matches(n, &base_match) {
518 return true; }
520 let child_ml = self.make_merge_list(n);
522 for i in 0..n.borrow().child_count() {
523 if let Some(child) = n.borrow().child(i).cloned() {
524 if self.is_deletia_modified(&child, &child_ml) {
525 return true;
526 }
527 }
528 }
529 }
530 }
531
532 false
533 }
534
535 fn is_movef_movef_conflict(&self, bn: &NodeRef) -> bool {
540 let left_node = BaseNode::left(bn).borrow().full_match();
542 let right_node = BaseNode::right(bn).borrow().full_match();
543
544 let (left_node, right_node) = match (left_node, right_node) {
545 (Some(l), Some(r)) => (l, r),
546 _ => return false,
547 };
548
549 let left_parent = BranchNode::parent(&left_node);
551 let right_parent = BranchNode::parent(&right_node);
552
553 match (left_parent, right_parent) {
554 (Some(lp), Some(rp)) => {
555 let lp_base = BranchNode::base_match(&lp);
556 let rp_base = BranchNode::base_match(&rp);
557 match (lp_base, rp_base) {
558 (Some(lb), Some(rb)) => lb.borrow().id() != rb.borrow().id(),
559 _ => true, }
561 }
562 (None, None) => false, _ => true, }
565 }
566
567 fn merge_node_content(&mut self, pair: &MergePair) -> Option<XmlContent> {
569 let n1 = pair.first.as_ref();
570 let n2 = pair.second.as_ref();
571
572 match (n1, n2) {
573 (None, None) => None,
574 (Some(n), None) | (None, Some(n)) => n.borrow().content().cloned(),
575 (Some(n1), Some(n2)) => {
576 let n1_content = n1.borrow().is_match(MatchType::CONTENT);
577 let n2_content = n2.borrow().is_match(MatchType::CONTENT);
578
579 if n1_content {
580 if !n2_content {
581 self.edit_log.update(Some(n2.clone()));
582 n2.borrow().content().cloned()
583 } else {
584 self.content_merge(n1, n2)
585 }
586 } else if n2_content {
587 self.edit_log.update(Some(n1.clone()));
588 n1.borrow().content().cloned()
589 } else {
590 self.content_merge(n1, n2)
592 }
593 }
594 }
595 }
596
597 fn content_merge(&mut self, a: &NodeRef, b: &NodeRef) -> Option<XmlContent> {
599 let base_a = BranchNode::base_match(a);
600 let base_b = BranchNode::base_match(b);
601
602 let a_updated = base_a.as_ref().is_none_or(|ba| !self.matches(a, ba));
603 let b_updated = base_b.as_ref().is_none_or(|bb| !self.matches(b, bb));
604
605 if a_updated && b_updated {
606 if self.matches(a, b) {
608 self.conflict_log.add_node_warning(
609 ConflictType::Update,
610 "Node updated in both branches, but updates are equal",
611 base_a.clone(),
612 Some(a.clone()),
613 Some(b.clone()),
614 );
615 self.edit_log.update(Some(a.clone()));
616 return a.borrow().content().cloned();
617 }
618
619 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
621 return Some(merged);
622 }
623
624 self.conflict_log.add_node_conflict(
626 ConflictType::Update,
627 "Node updated in both branches, using branch 1",
628 base_a,
629 Some(a.clone()),
630 Some(b.clone()),
631 );
632 self.edit_log.update(Some(a.clone()));
633 a.borrow().content().cloned()
634 } else if b_updated {
635 if self.has_namespace_changes(a, base_a.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 2",
644 base_a,
645 Some(a.clone()),
646 Some(b.clone()),
647 );
648 }
649 self.edit_log.update(Some(b.clone()));
650 b.borrow().content().cloned()
651 } else if a_updated {
652 if self.has_namespace_changes(b, base_b.as_ref()) {
654 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
655 return Some(merged);
656 }
657 self.conflict_log.add_node_conflict(
659 ConflictType::Update,
660 "Namespace declaration conflict between branches, using branch 1",
661 base_a,
662 Some(a.clone()),
663 Some(b.clone()),
664 );
665 }
666 self.edit_log.update(Some(a.clone()));
667 a.borrow().content().cloned()
668 } else {
669 if self.has_namespace_changes(a, base_a.as_ref())
671 || self.has_namespace_changes(b, base_b.as_ref())
672 {
673 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
674 return Some(merged);
675 }
676 self.conflict_log.add_node_conflict(
678 ConflictType::Update,
679 "Namespace declaration conflict between branches, using branch 1",
680 base_a.clone(),
681 Some(a.clone()),
682 Some(b.clone()),
683 );
684 }
685 a.borrow().content().cloned()
686 }
687 }
688
689 fn merge_elements(
691 &self,
692 base: Option<&NodeRef>,
693 a: &NodeRef,
694 b: &NodeRef,
695 ) -> Option<XmlContent> {
696 let base_content = base.and_then(|n| n.borrow().content().cloned());
697 let a_content = a.borrow().content().cloned();
698 let b_content = b.borrow().content().cloned();
699
700 match (&base_content, &a_content, &b_content) {
701 (
702 Some(XmlContent::Element(base_elem)),
703 Some(XmlContent::Element(a_elem)),
704 Some(XmlContent::Element(b_elem)),
705 ) => {
706 let tag_name = if base_elem.names_match(b_elem) {
708 a_elem.qname().to_string()
709 } else if base_elem.names_match(a_elem) {
710 b_elem.qname().to_string()
711 } else {
712 return None; };
714
715 self.merge_attributes(
717 base_elem.attributes(),
718 a_elem.attributes(),
719 b_elem.attributes(),
720 )
721 .and_then(|merged_attrs| {
722 let merged_ns = self.merge_namespace_decls(
723 base_elem.namespace_decls(),
724 a_elem.namespace_decls(),
725 b_elem.namespace_decls(),
726 )?;
727 let expanded = if base_elem.names_match(b_elem) {
729 a_elem.expanded_name().cloned()
730 } else {
731 b_elem.expanded_name().cloned()
732 };
733 Some(XmlContent::Element(XmlElement::new_with_namespace(
734 tag_name,
735 expanded,
736 merged_ns,
737 merged_attrs,
738 )))
739 })
740 }
741 _ => None,
742 }
743 }
744
745 fn merge_attributes(
747 &self,
748 base: &std::collections::HashMap<String, String>,
749 a: &std::collections::HashMap<String, String>,
750 b: &std::collections::HashMap<String, String>,
751 ) -> Option<std::collections::HashMap<String, String>> {
752 let mut merged = std::collections::HashMap::new();
753 let mut deletia = std::collections::HashSet::new();
754
755 for (key, base_val) in base {
757 let in_a = a.get(key);
758 let in_b = b.get(key);
759
760 match (in_a, in_b) {
761 (None, Some(b_val)) if base_val != b_val => return None, (Some(a_val), None) if base_val != a_val => return None, (None, _) | (_, None) => {
764 deletia.insert(key.clone());
765 }
766 _ => {}
767 }
768 }
769
770 for (key, a_val) in a {
772 if deletia.contains(key) {
773 continue;
774 }
775
776 if let Some(b_val) = b.get(key) {
777 let base_val = base.get(key);
778 if base_val == Some(b_val) {
779 merged.insert(key.clone(), a_val.clone());
781 } else if base_val == Some(a_val) {
782 merged.insert(key.clone(), b_val.clone());
784 } else if a_val != b_val {
785 return None;
787 } else {
788 merged.insert(key.clone(), a_val.clone());
789 }
790 } else {
791 merged.insert(key.clone(), a_val.clone());
793 }
794 }
795
796 for (key, b_val) in b {
798 if !deletia.contains(key) && !a.contains_key(key) {
799 merged.insert(key.clone(), b_val.clone());
800 }
801 }
802
803 Some(merged)
804 }
805
806 fn merge_namespace_decls(
809 &self,
810 base: &std::collections::HashMap<String, String>,
811 a: &std::collections::HashMap<String, String>,
812 b: &std::collections::HashMap<String, String>,
813 ) -> Option<std::collections::HashMap<String, String>> {
814 let mut merged = std::collections::HashMap::new();
815 let mut deletia = std::collections::HashSet::new();
816
817 for (prefix, base_uri) in base {
819 let in_a = a.get(prefix);
820 let in_b = b.get(prefix);
821
822 match (in_a, in_b) {
823 (None, Some(b_uri)) if base_uri != b_uri => return None,
824 (Some(a_uri), None) if base_uri != a_uri => return None,
825 (None, _) | (_, None) => {
826 deletia.insert(prefix.clone());
827 }
828 _ => {}
829 }
830 }
831
832 for (prefix, a_uri) in a {
834 if deletia.contains(prefix) {
835 continue;
836 }
837 if let Some(b_uri) = b.get(prefix) {
838 let base_uri = base.get(prefix);
839 if base_uri == Some(b_uri) {
840 merged.insert(prefix.clone(), a_uri.clone());
841 } else if base_uri == Some(a_uri) {
842 merged.insert(prefix.clone(), b_uri.clone());
843 } else if a_uri != b_uri {
844 return None;
845 } else {
846 merged.insert(prefix.clone(), a_uri.clone());
847 }
848 } else {
849 merged.insert(prefix.clone(), a_uri.clone());
850 }
851 }
852
853 for (prefix, b_uri) in b {
855 if !deletia.contains(prefix) && !a.contains_key(prefix) {
856 merged.insert(prefix.clone(), b_uri.clone());
857 }
858 }
859
860 Some(merged)
861 }
862
863 fn get_recursion_partners(&self, pair: &MergePair) -> RecursionPartners {
865 let n1 = pair.first.as_ref();
866 let n2 = pair.second.as_ref();
867
868 match (n1, n2) {
869 (None, _) | (_, None) => RecursionPartners {
870 first: pair.first.clone(),
871 second: pair.second.clone(),
872 },
873 (Some(n1), Some(n2)) => {
874 let n1_children = n1.borrow().is_match(MatchType::CHILDREN);
875 let n2_children = n2.borrow().is_match(MatchType::CHILDREN);
876
877 if n1_children && n2_children {
878 RecursionPartners {
879 first: Some(n1.clone()),
880 second: Some(n2.clone()),
881 }
882 } else if n1_children && n2.borrow().is_match(MatchType::CONTENT) {
883 RecursionPartners {
884 first: None,
885 second: Some(n2.clone()),
886 }
887 } else if n1.borrow().is_match(MatchType::CONTENT) && n2_children {
888 RecursionPartners {
889 first: Some(n1.clone()),
890 second: None,
891 }
892 } else {
893 RecursionPartners {
894 first: Some(n1.clone()),
895 second: Some(n2.clone()),
896 }
897 }
898 }
899 }
900 }
901
902 fn has_namespace_changes(&self, node: &NodeRef, base: Option<&NodeRef>) -> bool {
904 let Some(base) = base else { return false };
905 let node_borrowed = node.borrow();
906 let base_borrowed = base.borrow();
907 match (node_borrowed.content(), base_borrowed.content()) {
908 (Some(XmlContent::Element(ne)), Some(XmlContent::Element(be))) => {
909 !ne.namespace_decls_equal(be)
910 }
911 _ => false,
912 }
913 }
914
915 fn matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
917 let a_borrowed = a.borrow();
918 let b_borrowed = b.borrow();
919 let a_content = a_borrowed.content();
920 let b_content = b_borrowed.content();
921
922 match (a_content, b_content) {
923 (Some(ac), Some(bc)) => ac.content_equals(bc),
924 _ => false,
925 }
926 }
927
928 fn log_hangon_struct_ops(&mut self, n: &NodeRef) {
930 if !BranchNode::has_base_match(n) {
931 self.edit_log.insert(Some(n.clone()));
932 } else {
933 let base = BranchNode::base_match(n).unwrap();
934 let is_left = BranchNode::is_left_tree(n);
935
936 let copies = if is_left {
937 BaseNode::left(&base)
938 } else {
939 BaseNode::right(&base)
940 };
941
942 if copies.borrow().match_count() > 1 {
943 self.edit_log.copy(Some(n.clone()));
944 } else {
945 self.edit_log.r#move(Some(n.clone()));
946 }
947 }
948 }
949
950 fn log_entry_struct_ops(&mut self, e1: &MergeEntry, e2: &MergeEntry) {
952 if e1.moved {
953 if let Some(n) = e1.node() {
954 self.edit_log.r#move(Some(n));
955 }
956 } else if e2.moved {
957 if let Some(n) = e2.node() {
958 self.edit_log.r#move(Some(n));
959 }
960 }
961 }
962
963 fn check_hangon_combine(&mut self, ea: &MergeEntry, eb: &MergeEntry) -> bool {
967 if ea.hangons().is_empty() {
969 return false;
970 }
971
972 if ea.hangons().len() != eb.hangons().len() {
973 if !ea.hangons().is_empty() && !eb.hangons().is_empty() {
974 self.conflict_log.add_list_warning(
975 ConflictType::Insert,
976 "Insertions/copies in both branches",
977 ea.node().and_then(|n| BranchNode::base_match(&n)),
978 ea.node(),
979 eb.node(),
980 );
981 }
982 return false;
983 }
984
985 let mut equal = true;
987 for (ha, hb) in ea.hangons().iter().zip(eb.hangons().iter()) {
988 if !self.tree_matches(ha, hb) {
989 equal = false;
990 break;
991 }
992 }
993
994 if equal {
995 self.conflict_log.add_list_warning(
996 ConflictType::Insert,
997 "Equal insertions/copies in both branches",
998 ea.node().and_then(|n| BranchNode::base_match(&n)),
999 ea.node(),
1000 eb.node(),
1001 );
1002 } else if !ea.hangons().is_empty() {
1003 self.conflict_log.add_list_warning(
1004 ConflictType::Insert,
1005 "Insertions/copies in both branches, sequencing",
1006 ea.node().and_then(|n| BranchNode::base_match(&n)),
1007 ea.node(),
1008 eb.node(),
1009 );
1010 }
1011
1012 equal
1013 }
1014
1015 fn tree_matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
1017 if !self.matches(a, b) {
1018 return false;
1019 }
1020
1021 let a_count = a.borrow().child_count();
1022 let b_count = b.borrow().child_count();
1023
1024 if a_count != b_count {
1025 return false;
1026 }
1027
1028 for i in 0..a_count {
1029 let a_child = a.borrow().child(i).cloned();
1030 let b_child = b.borrow().child(i).cloned();
1031
1032 match (a_child, b_child) {
1033 (Some(ac), Some(bc)) => {
1034 if !self.tree_matches(&ac, &bc) {
1035 return false;
1036 }
1037 }
1038 _ => return false,
1039 }
1040 }
1041
1042 true
1043 }
1044}
1045
1046struct RecursionPartners {
1048 first: Option<NodeRef>,
1049 second: Option<NodeRef>,
1050}
1051
1052impl crate::node::NodeInner {
1053 pub fn first_partner(&self, type_flags: MatchType) -> Option<NodeRef> {
1055 if let crate::node::NodeKind::Branch {
1056 match_type,
1057 partners,
1058 ..
1059 } = self.kind()
1060 {
1061 if !match_type.intersects(type_flags) {
1062 return None;
1063 }
1064
1065 if let Some(partners) = partners {
1066 let borrowed = partners.borrow();
1067 for candidate in borrowed.matches() {
1068 if BranchNode::base_match_type(candidate).intersects(type_flags) {
1069 return Some(candidate.clone());
1070 }
1071 }
1072 }
1073 }
1074 None
1075 }
1076
1077 pub fn is_match(&self, type_flags: MatchType) -> bool {
1079 if let crate::node::NodeKind::Branch { match_type, .. } = self.kind() {
1080 match_type.intersects(type_flags)
1081 } else {
1082 false
1083 }
1084 }
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089 use super::*;
1090 use crate::matching::TriMatching;
1091 use crate::xml::{BaseNodeFactory, BranchNodeFactory, XmlParser};
1092 use std::collections::HashMap;
1093
1094 fn merge_xml(base: &str, a: &str, b: &str) -> (String, usize) {
1096 let base_tree = XmlParser::new(BaseNodeFactory)
1097 .parse_str(base)
1098 .expect("parse base");
1099 let a_tree = XmlParser::new(BranchNodeFactory)
1100 .parse_str(a)
1101 .expect("parse a");
1102 let b_tree = XmlParser::new(BranchNodeFactory)
1103 .parse_str(b)
1104 .expect("parse b");
1105 let matching = TriMatching::new(a_tree, base_tree, b_tree);
1106 let mut merge = Merge::new(matching);
1107 let mut output = Vec::new();
1108 merge.merge(&mut output).expect("merge");
1109 let conflicts = merge.conflict_log.conflicts().len();
1110 (String::from_utf8(output).expect("utf8"), conflicts)
1111 }
1112
1113 #[test]
1114 fn test_merge_namespace_decls_deletion() {
1115 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com" xmlns:bar="http://bar.com"><child/></root>"#;
1117 let a = base;
1118 let b = r#"<?xml version="1.0"?><root xmlns:bar="http://bar.com"><child/></root>"#;
1119 let (output, conflicts) = merge_xml(base, a, b);
1120 assert!(!output.contains("xmlns:foo"), "xmlns:foo should be removed");
1121 assert!(output.contains("xmlns:bar"), "xmlns:bar should remain");
1122 assert_eq!(conflicts, 0);
1123 }
1124
1125 #[test]
1126 fn test_merge_namespace_decls_addition() {
1127 let base = r#"<?xml version="1.0"?><root xmlns:a="http://a.com"><child/></root>"#;
1129 let a = r#"<?xml version="1.0"?><root xmlns:a="http://a.com" xmlns:b="http://b.com"><child/></root>"#;
1130 let b = r#"<?xml version="1.0"?><root xmlns:a="http://a.com" xmlns:c="http://c.com"><child/></root>"#;
1131 let (output, conflicts) = merge_xml(base, a, b);
1132 assert!(output.contains("xmlns:b"), "xmlns:b should be present");
1133 assert!(output.contains("xmlns:c"), "xmlns:c should be present");
1134 assert_eq!(conflicts, 0);
1135 }
1136
1137 #[test]
1138 fn test_merge_namespace_decls_delete_vs_change_conflict() {
1139 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com" xmlns:bar="http://bar.com"><child/></root>"#;
1141 let a = r#"<?xml version="1.0"?><root xmlns:bar="http://bar.com"><child/></root>"#;
1142 let b = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-v2.com" xmlns:bar="http://bar.com"><child/></root>"#;
1143 let (_output, conflicts) = merge_xml(base, a, b);
1144 assert_eq!(conflicts, 1, "delete-vs-change should produce a conflict");
1145 }
1146
1147 #[test]
1148 fn test_merge_namespace_decls_both_change_same_prefix_conflict() {
1149 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com"><child/></root>"#;
1151 let a = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-a.com"><child/></root>"#;
1152 let b = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-b.com"><child/></root>"#;
1153 let (_output, conflicts) = merge_xml(base, a, b);
1154 assert_eq!(
1155 conflicts, 1,
1156 "both changing same prefix differently should conflict"
1157 );
1158 }
1159
1160 #[test]
1161 fn test_merge_namespace_decls_both_change_same_value() {
1162 let base = r#"<?xml version="1.0"?><root xmlns:foo="http://foo.com"><child/></root>"#;
1164 let a = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-v2.com"><child/></root>"#;
1165 let b = r#"<?xml version="1.0"?><root xmlns:foo="http://foo-v2.com"><child/></root>"#;
1166 let (output, conflicts) = merge_xml(base, a, b);
1167 assert!(
1168 output.contains("http://foo-v2.com"),
1169 "merged URI should be the new value"
1170 );
1171 assert_eq!(conflicts, 0);
1172 }
1173
1174 #[test]
1175 fn test_merge_namespace_decls_unit() {
1176 let matching = {
1178 let base = XmlParser::new(BaseNodeFactory)
1179 .parse_str("<root/>")
1180 .unwrap();
1181 let a = XmlParser::new(BranchNodeFactory)
1182 .parse_str("<root/>")
1183 .unwrap();
1184 let b = XmlParser::new(BranchNodeFactory)
1185 .parse_str("<root/>")
1186 .unwrap();
1187 TriMatching::new(a, base, b)
1188 };
1189 let merge = Merge::new(matching);
1190
1191 let mut base = HashMap::new();
1192 base.insert("foo".into(), "http://foo.com".into());
1193 base.insert("bar".into(), "http://bar.com".into());
1194
1195 let mut a = HashMap::new();
1197 a.insert("bar".into(), "http://bar.com".into());
1198 let b = base.clone();
1199
1200 let result = merge.merge_namespace_decls(&base, &a, &b).unwrap();
1201 assert!(!result.contains_key("foo"));
1202 assert!(result.contains_key("bar"));
1203
1204 let mut b_changed = HashMap::new();
1206 b_changed.insert("foo".into(), "http://foo-v2.com".into());
1207 b_changed.insert("bar".into(), "http://bar.com".into());
1208
1209 let result = merge.merge_namespace_decls(&base, &a, &b_changed);
1210 assert!(result.is_none(), "delete-vs-change should return None");
1211 }
1212}