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::{MatchType, NodeRef, XmlContent, XmlElement};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
34pub enum Operation {
35 Nop,
37 MoveI,
39 MoveF,
41 Delete,
43}
44
45pub struct Merge {
47 matching: TriMatching,
49 pub conflict_log: ConflictLog,
51 pub edit_log: EditLog,
53 indent: usize,
55}
56
57impl Merge {
58 pub fn new(matching: TriMatching) -> Self {
60 Merge {
61 matching,
62 conflict_log: ConflictLog::new(),
63 edit_log: EditLog::new(),
64 indent: 0,
65 }
66 }
67
68 pub fn matching(&self) -> &TriMatching {
70 &self.matching
71 }
72
73 pub fn merge<W: Write>(&mut self, writer: &mut W) -> std::io::Result<()> {
77 writeln!(writer, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")?;
79
80 self.tree_merge(
82 Some(self.matching.left_root().clone()),
83 Some(self.matching.right_root().clone()),
84 writer,
85 )?;
86
87 Ok(())
88 }
89
90 fn tree_merge<W: Write>(
94 &mut self,
95 a: Option<NodeRef>,
96 b: Option<NodeRef>,
97 writer: &mut W,
98 ) -> std::io::Result<()> {
99 let mlist_a = a.as_ref().map(|node| self.make_merge_list(node));
101 let mlist_b = b.as_ref().map(|node| self.make_merge_list(node));
102
103 let merged = match (&mlist_a, &mlist_b) {
105 (Some(ma), Some(mb)) => self.make_merge_pair_list(ma.clone(), mb.clone()),
106 (Some(ma), None) => self.merge_list_to_pair_list(ma, None),
107 (None, Some(mb)) => self.merge_list_to_pair_list(mb, None),
108 (None, None) => MergePairList::new(),
109 };
110
111 for pair in merged.pairs() {
113 let merged_node = self.merge_node_content(pair);
114
115 if let Some(content) = &merged_node {
116 match content {
117 XmlContent::Text(text) => {
118 let text_str: String = text.text().iter().collect();
120 write!(
121 writer,
122 "{}{}",
123 Self::indent_str_for(self.indent),
124 escape_xml_text(&text_str)
125 )?;
126 }
127 XmlContent::Comment(comment) => {
128 let comment_text: String = comment.text().iter().collect();
130 writeln!(
131 writer,
132 "{}<!-- {} -->",
133 Self::indent_str_for(self.indent),
134 comment_text
135 )?;
136 }
137 XmlContent::Element(elem) => {
138 if elem.qname() == "$ROOT$" {
140 let partners = self.get_recursion_partners(pair);
141 self.tree_merge(partners.first, partners.second, writer)?;
142 } else {
143 self.write_start_element(writer, elem, self.indent)?;
145
146 self.indent += 1;
148
149 let partners = self.get_recursion_partners(pair);
151 self.tree_merge(partners.first, partners.second, writer)?;
152
153 self.indent -= 1;
155
156 writeln!(
158 writer,
159 "{}</{}>",
160 Self::indent_str_for(self.indent),
161 elem.qname()
162 )?;
163 }
164 }
165 }
166 }
167 }
168
169 Ok(())
170 }
171
172 fn make_merge_list(&mut self, parent: &NodeRef) -> MergeList {
174 MergeList::from_branch(parent, &mut self.edit_log)
175 }
176
177 fn make_merge_pair_list(&mut self, mlist_a: MergeList, mlist_b: MergeList) -> MergePairList {
179 let mut mlist_a = mlist_a;
180 let mut mlist_b = mlist_b;
181
182 self.remove_deleted_or_moved(&mut mlist_a, &mut mlist_b);
184
185 self.edit_log.checkpoint();
187
188 if mlist_a.entry_count() != mlist_b.entry_count() {
190 self.edit_log.rewind();
192 let is_left = mlist_a
193 .entry_parent()
194 .map(|p| BranchNode::is_left_tree(&p))
195 .unwrap_or(true);
196 return if is_left {
197 self.merge_list_to_pair_list(&mlist_a, Some(&mlist_b))
198 } else {
199 self.merge_list_to_pair_list(&mlist_b, Some(&mlist_a))
200 };
201 }
202
203 let mut merged = MergePairList::new();
204 let mut pos_a = 0usize;
205 let mut pos_b = 0usize;
206
207 loop {
208 let ea = mlist_a.entry(pos_a);
209 let eb = mlist_b.entry(pos_b);
210
211 if ea.is_none() || eb.is_none() {
212 break;
213 }
214
215 let ea = ea.unwrap();
216 let eb = eb.unwrap();
217
218 for hangon in ea.hangons() {
220 let partner = hangon.borrow().first_partner(MatchType::FULL);
221 merged.append(Some(hangon.clone()), partner);
222 self.log_hangon_struct_ops(hangon);
223 }
224
225 if !self.check_hangon_combine(ea, eb) {
227 for hangon in eb.hangons() {
228 let partner = hangon.borrow().first_partner(MatchType::FULL);
229 merged.append(Some(hangon.clone()), partner);
230 self.log_hangon_struct_ops(hangon);
231 }
232 }
233
234 let next_a: Option<usize>;
236 let next_b: Option<usize>;
237
238 let ea_locked = ea.locked && mlist_a.entry(pos_a + 1).is_some_and(|e| e.locked);
239 let eb_locked = eb.locked && mlist_b.entry(pos_b + 1).is_some_and(|e| e.locked);
240
241 if !ea_locked && !eb_locked {
242 next_a = Some(pos_a + 1);
244 next_b = Some(pos_b + 1);
245 } else if ea_locked && !eb_locked {
246 next_a = Some(pos_a + 1);
247 next_b = mlist_b.find_partner_index(mlist_a.entry(pos_a + 1));
248 } else if !ea_locked && eb_locked {
249 next_b = Some(pos_b + 1);
250 next_a = mlist_a.find_partner_index(mlist_b.entry(pos_b + 1));
251 } else {
252 let partner_b = mlist_b.find_partner_index(mlist_a.entry(pos_a + 1));
254 if partner_b != Some(pos_b + 1) {
255 self.conflict_log.add_list_conflict(
257 ConflictType::Move,
258 "Conflicting moves inside child list",
259 ea.node().and_then(|n| BranchNode::base_match(&n)),
260 ea.node(),
261 eb.node(),
262 );
263 self.edit_log.rewind();
264 let is_left = mlist_a
265 .entry_parent()
266 .map(|p| BranchNode::is_left_tree(&p))
267 .unwrap_or(true);
268 return if is_left {
269 self.merge_list_to_pair_list(&mlist_a, Some(&mlist_b))
270 } else {
271 self.merge_list_to_pair_list(&mlist_b, Some(&mlist_a))
272 };
273 }
274 next_a = Some(pos_a + 1);
275 next_b = partner_b;
276 }
277
278 pos_a = next_a.unwrap_or(pos_a + 1);
279 pos_b = next_b.unwrap_or(pos_b + 1);
280
281 if mlist_a.is_end(pos_a) || mlist_b.is_end(pos_b) {
283 break;
284 }
285
286 let ea = mlist_a.entry(pos_a);
288 let eb = mlist_b.entry(pos_b);
289 if let (Some(ea), Some(eb)) = (ea, eb) {
290 merged.append(ea.node(), eb.node());
291 self.log_entry_struct_ops(ea, eb);
292 }
293 }
294
295 self.edit_log.commit();
296 merged
297 }
298
299 fn merge_list_to_pair_list(
301 &mut self,
302 mlist_a: &MergeList,
303 mlist_b: Option<&MergeList>,
304 ) -> MergePairList {
305 let mut merged = MergePairList::new();
306
307 for (i, entry) in mlist_a.entries().iter().enumerate() {
308 if i > 0 && !mlist_a.is_end(i) {
310 let node = entry.node();
311 let partner = node
312 .as_ref()
313 .and_then(|n| n.borrow().first_partner(MatchType::FULL));
314 merged.append(node, partner);
315 if let Some(n) = entry.node() {
316 self.log_hangon_struct_ops(&n);
317 }
318 }
319
320 for hangon in entry.hangons() {
322 let partner = hangon.borrow().first_partner(MatchType::FULL);
323 merged.append(Some(hangon.clone()), partner);
324 self.log_hangon_struct_ops(hangon);
325 }
326
327 if let Some(mb) = mlist_b {
329 if let Some(partner_entry) = mb.find_partner(entry) {
330 if !self.check_hangon_combine(entry, partner_entry) {
331 for hangon in partner_entry.hangons() {
332 let partner = hangon.borrow().first_partner(MatchType::FULL);
333 merged.append(Some(hangon.clone()), partner);
334 self.log_hangon_struct_ops(hangon);
335 }
336 }
337 }
338 }
339 }
340
341 merged
342 }
343
344 fn remove_deleted_or_moved(&mut self, mlist_a: &mut MergeList, mlist_b: &mut MergeList) {
346 let base_parent = mlist_a
347 .entry_parent()
348 .and_then(|p| BranchNode::base_match(&p));
349
350 if base_parent.is_none() {
351 return;
352 }
353
354 let base_parent = base_parent.unwrap();
355 let child_count = base_parent.borrow().child_count();
356
357 for i in 0..child_count {
358 let bn = base_parent.borrow().child(i).cloned();
359 if bn.is_none() {
360 continue;
361 }
362 let bn = bn.unwrap();
363
364 let op1 = self.get_operation(&bn, mlist_a);
365 let op2 = self.get_operation(&bn, mlist_b);
366
367 if op1 <= op2 {
370 self.handle_operation_pair(op1, op2, &bn, mlist_a, mlist_b);
371 } else {
372 self.handle_operation_pair(op2, op1, &bn, mlist_b, mlist_a);
373 }
374 }
375 }
376
377 fn handle_operation_pair(
379 &mut self,
380 op1: Operation,
381 op2: Operation,
382 bn: &NodeRef,
383 ml1: &mut MergeList,
384 _ml2: &mut MergeList,
385 ) {
386 use Operation::*;
387
388 match (op1, op2) {
389 (Nop, Nop) | (Nop, MoveI) | (MoveI, MoveI) | (Delete, Delete) => {
390 }
392 (Nop, MoveF) | (Nop, Delete) => {
393 if let Some(ix) = ml1.match_in_list(bn) {
395 if op2 == Delete {
396 if let Some(entry) = ml1.entry(ix) {
398 if let Some(node) = entry.node() {
399 if self.is_deletia_modified(&node, ml1) {
400 self.conflict_log.add_list_warning(
401 ConflictType::Delete,
402 "Modifications in deleted subtree",
403 Some(bn.clone()),
404 entry.node(),
405 None,
406 );
407 }
408 }
409 }
410 self.edit_log.delete(Some(bn.clone()));
411 }
412
413 ml1.move_hangons_to_predecessor(ix);
415 ml1.remove_entry_at(ix);
416 }
417 }
418 (MoveI, MoveF) => {
419 if let Some(ix) = ml1.match_in_list(bn) {
421 let node = ml1.entry(ix).and_then(|e| e.node());
422 let partner = node
423 .as_ref()
424 .and_then(|n| n.borrow().first_partner(MatchType::FULL));
425 self.conflict_log.add_list_conflict(
426 ConflictType::Move,
427 "Node moved to different locations",
428 Some(bn.clone()),
429 node,
430 partner,
431 );
432 ml1.remove_entry_at(ix);
433 }
434 }
435 (MoveI, Delete) => {
436 if let Some(ix) = ml1.match_in_list(bn) {
438 let node = ml1.entry(ix).and_then(|e| e.node());
439 self.conflict_log.add_list_conflict(
440 ConflictType::Move,
441 "Node moved and deleted",
442 Some(bn.clone()),
443 node,
444 None,
445 );
446 self.edit_log.delete(Some(bn.clone()));
447 ml1.remove_entry_at(ix);
448 }
449 }
450 (MoveF, MoveF) => {
451 if self.is_movef_movef_conflict(bn) {
453 self.conflict_log.add_list_conflict(
454 ConflictType::Move,
455 "Node moved to different locations",
456 Some(bn.clone()),
457 BaseNode::left(bn).borrow().full_match(),
458 BaseNode::right(bn).borrow().full_match(),
459 );
460 }
461 }
462 (MoveF, Delete) => {
463 self.conflict_log.add_list_conflict(
465 ConflictType::Move,
466 "Node moved and deleted",
467 Some(bn.clone()),
468 BaseNode::left(bn).borrow().full_match(),
469 BaseNode::right(bn).borrow().full_match(),
470 );
471 }
472 _ => {}
473 }
474 }
475
476 fn get_operation(&self, bn: &NodeRef, ml: &MergeList) -> Operation {
478 if let Some(pos) = ml.match_in_list(bn) {
479 if ml.entry(pos).is_some_and(|e| e.moved) {
480 Operation::MoveI
481 } else {
482 Operation::Nop
483 }
484 } else {
485 let is_left = ml
487 .entry_parent()
488 .map(|p| BranchNode::is_left_tree(&p))
489 .unwrap_or(true);
490
491 let copies = if is_left {
492 BaseNode::left(bn)
493 } else {
494 BaseNode::right(bn)
495 };
496
497 if copies.borrow().match_count() == 0 {
498 Operation::Delete
499 } else {
500 Operation::MoveF
501 }
502 }
503 }
504
505 fn is_deletia_modified(&self, n: &NodeRef, ml: &MergeList) -> bool {
507 let base_match = BranchNode::base_match(n);
508 if base_match.is_none() {
509 return true; }
511
512 let base_match = base_match.unwrap();
513 if self.get_operation(&base_match, ml) != Operation::Nop {
514 return true; }
516
517 if BranchNode::base_match_type(n) != MatchType::FULL {
518 return true; }
520
521 if let Some(partners) = BranchNode::partners(n) {
523 if partners.borrow().match_count() == 0 {
524 if !self.matches(n, &base_match) {
525 return true; }
527 for i in 0..n.borrow().child_count() {
529 if let Some(child) = n.borrow().child(i).cloned() {
530 if BranchNode::base_match_type(&child) != MatchType::FULL {
532 return true;
533 }
534 }
535 }
536 }
537 }
538
539 false
540 }
541
542 fn is_movef_movef_conflict(&self, bn: &NodeRef) -> bool {
547 let left_node = BaseNode::left(bn).borrow().full_match();
549 let right_node = BaseNode::right(bn).borrow().full_match();
550
551 let (left_node, right_node) = match (left_node, right_node) {
552 (Some(l), Some(r)) => (l, r),
553 _ => return false,
554 };
555
556 let left_parent = BranchNode::parent(&left_node);
558 let right_parent = BranchNode::parent(&right_node);
559
560 match (left_parent, right_parent) {
561 (Some(lp), Some(rp)) => {
562 let lp_base = BranchNode::base_match(&lp);
563 let rp_base = BranchNode::base_match(&rp);
564 match (lp_base, rp_base) {
565 (Some(lb), Some(rb)) => lb.borrow().id() != rb.borrow().id(),
566 _ => true, }
568 }
569 (None, None) => false, _ => true, }
572 }
573
574 fn merge_node_content(&mut self, pair: &MergePair) -> Option<XmlContent> {
576 let n1 = pair.first.as_ref();
577 let n2 = pair.second.as_ref();
578
579 match (n1, n2) {
580 (None, None) => None,
581 (Some(n), None) | (None, Some(n)) => n.borrow().content().cloned(),
582 (Some(n1), Some(n2)) => {
583 let n1_content = n1.borrow().is_match(MatchType::CONTENT);
584 let n2_content = n2.borrow().is_match(MatchType::CONTENT);
585
586 if n1_content {
587 if !n2_content {
588 self.edit_log.update(Some(n2.clone()));
589 n2.borrow().content().cloned()
590 } else {
591 self.content_merge(n1, n2)
592 }
593 } else if n2_content {
594 self.edit_log.update(Some(n1.clone()));
595 n1.borrow().content().cloned()
596 } else {
597 self.content_merge(n1, n2)
599 }
600 }
601 }
602 }
603
604 fn content_merge(&mut self, a: &NodeRef, b: &NodeRef) -> Option<XmlContent> {
606 let base_a = BranchNode::base_match(a);
607 let base_b = BranchNode::base_match(b);
608
609 let a_updated = base_a.as_ref().is_none_or(|ba| !self.matches(a, ba));
610 let b_updated = base_b.as_ref().is_none_or(|bb| !self.matches(b, bb));
611
612 if a_updated && b_updated {
613 if self.matches(a, b) {
615 self.conflict_log.add_node_warning(
616 ConflictType::Update,
617 "Node updated in both branches, but updates are equal",
618 base_a.clone(),
619 Some(a.clone()),
620 Some(b.clone()),
621 );
622 self.edit_log.update(Some(a.clone()));
623 return a.borrow().content().cloned();
624 }
625
626 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
628 return Some(merged);
629 }
630
631 self.conflict_log.add_node_conflict(
633 ConflictType::Update,
634 "Node updated in both branches, using branch 1",
635 base_a,
636 Some(a.clone()),
637 Some(b.clone()),
638 );
639 self.edit_log.update(Some(a.clone()));
640 a.borrow().content().cloned()
641 } else if b_updated {
642 self.edit_log.update(Some(b.clone()));
643 b.borrow().content().cloned()
644 } else if a_updated {
645 self.edit_log.update(Some(a.clone()));
646 a.borrow().content().cloned()
647 } else {
648 a.borrow().content().cloned()
649 }
650 }
651
652 fn merge_elements(
654 &self,
655 base: Option<&NodeRef>,
656 a: &NodeRef,
657 b: &NodeRef,
658 ) -> Option<XmlContent> {
659 let base_content = base.and_then(|n| n.borrow().content().cloned());
660 let a_content = a.borrow().content().cloned();
661 let b_content = b.borrow().content().cloned();
662
663 match (&base_content, &a_content, &b_content) {
664 (
665 Some(XmlContent::Element(base_elem)),
666 Some(XmlContent::Element(a_elem)),
667 Some(XmlContent::Element(b_elem)),
668 ) => {
669 let tag_name = if base_elem.qname() == b_elem.qname() {
671 a_elem.qname().to_string()
672 } else if base_elem.qname() == a_elem.qname() {
673 b_elem.qname().to_string()
674 } else {
675 return None; };
677
678 self.merge_attributes(
680 base_elem.attributes(),
681 a_elem.attributes(),
682 b_elem.attributes(),
683 )
684 .map(|merged_attrs| XmlContent::Element(XmlElement::new(tag_name, merged_attrs)))
685 }
686 _ => None,
687 }
688 }
689
690 fn merge_attributes(
692 &self,
693 base: &std::collections::HashMap<String, String>,
694 a: &std::collections::HashMap<String, String>,
695 b: &std::collections::HashMap<String, String>,
696 ) -> Option<std::collections::HashMap<String, String>> {
697 let mut merged = std::collections::HashMap::new();
698 let mut deletia = std::collections::HashSet::new();
699
700 for (key, base_val) in base {
702 let in_a = a.get(key);
703 let in_b = b.get(key);
704
705 match (in_a, in_b) {
706 (None, Some(b_val)) if base_val != b_val => return None, (Some(a_val), None) if base_val != a_val => return None, (None, _) | (_, None) => {
709 deletia.insert(key.clone());
710 }
711 _ => {}
712 }
713 }
714
715 for (key, a_val) in a {
717 if deletia.contains(key) {
718 continue;
719 }
720
721 if let Some(b_val) = b.get(key) {
722 let base_val = base.get(key);
723 if base_val == Some(b_val) {
724 merged.insert(key.clone(), a_val.clone());
726 } else if base_val == Some(a_val) {
727 merged.insert(key.clone(), b_val.clone());
729 } else if a_val != b_val {
730 return None;
732 } else {
733 merged.insert(key.clone(), a_val.clone());
734 }
735 } else {
736 merged.insert(key.clone(), a_val.clone());
738 }
739 }
740
741 for (key, b_val) in b {
743 if !deletia.contains(key) && !a.contains_key(key) {
744 merged.insert(key.clone(), b_val.clone());
745 }
746 }
747
748 Some(merged)
749 }
750
751 fn get_recursion_partners(&self, pair: &MergePair) -> RecursionPartners {
753 let n1 = pair.first.as_ref();
754 let n2 = pair.second.as_ref();
755
756 match (n1, n2) {
757 (None, _) | (_, None) => RecursionPartners {
758 first: pair.first.clone(),
759 second: pair.second.clone(),
760 },
761 (Some(n1), Some(n2)) => {
762 let n1_children = n1.borrow().is_match(MatchType::CHILDREN);
763 let n2_children = n2.borrow().is_match(MatchType::CHILDREN);
764
765 if n1_children && n2_children {
766 RecursionPartners {
767 first: Some(n1.clone()),
768 second: Some(n2.clone()),
769 }
770 } else if n1_children && n2.borrow().is_match(MatchType::CONTENT) {
771 RecursionPartners {
772 first: None,
773 second: Some(n2.clone()),
774 }
775 } else if n1.borrow().is_match(MatchType::CONTENT) && n2_children {
776 RecursionPartners {
777 first: Some(n1.clone()),
778 second: None,
779 }
780 } else {
781 RecursionPartners {
782 first: Some(n1.clone()),
783 second: Some(n2.clone()),
784 }
785 }
786 }
787 }
788 }
789
790 fn matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
792 let a_borrowed = a.borrow();
793 let b_borrowed = b.borrow();
794 let a_content = a_borrowed.content();
795 let b_content = b_borrowed.content();
796
797 match (a_content, b_content) {
798 (Some(ac), Some(bc)) => ac.content_equals(bc),
799 _ => false,
800 }
801 }
802
803 fn log_hangon_struct_ops(&mut self, n: &NodeRef) {
805 if !BranchNode::has_base_match(n) {
806 self.edit_log.insert(Some(n.clone()));
807 } else {
808 let base = BranchNode::base_match(n).unwrap();
809 let is_left = BranchNode::is_left_tree(n);
810
811 let copies = if is_left {
812 BaseNode::left(&base)
813 } else {
814 BaseNode::right(&base)
815 };
816
817 if copies.borrow().match_count() > 1 {
818 self.edit_log.copy(Some(n.clone()));
819 } else {
820 self.edit_log.r#move(Some(n.clone()));
821 }
822 }
823 }
824
825 fn log_entry_struct_ops(&mut self, e1: &MergeEntry, e2: &MergeEntry) {
827 if e1.moved {
828 if let Some(n) = e1.node() {
829 self.edit_log.r#move(Some(n));
830 }
831 } else if e2.moved {
832 if let Some(n) = e2.node() {
833 self.edit_log.r#move(Some(n));
834 }
835 }
836 }
837
838 fn check_hangon_combine(&mut self, ea: &MergeEntry, eb: &MergeEntry) -> bool {
842 if ea.hangons().is_empty() {
844 return false;
845 }
846
847 if ea.hangons().len() != eb.hangons().len() {
848 if !ea.hangons().is_empty() && !eb.hangons().is_empty() {
849 self.conflict_log.add_list_warning(
850 ConflictType::Insert,
851 "Insertions/copies in both branches",
852 ea.node().and_then(|n| BranchNode::base_match(&n)),
853 ea.node(),
854 eb.node(),
855 );
856 }
857 return false;
858 }
859
860 let mut equal = true;
862 for (ha, hb) in ea.hangons().iter().zip(eb.hangons().iter()) {
863 if !self.tree_matches(ha, hb) {
864 equal = false;
865 break;
866 }
867 }
868
869 if equal {
870 self.conflict_log.add_list_warning(
871 ConflictType::Insert,
872 "Equal insertions/copies in both branches",
873 ea.node().and_then(|n| BranchNode::base_match(&n)),
874 ea.node(),
875 eb.node(),
876 );
877 } else if !ea.hangons().is_empty() {
878 self.conflict_log.add_list_warning(
879 ConflictType::Insert,
880 "Insertions/copies in both branches, sequencing",
881 ea.node().and_then(|n| BranchNode::base_match(&n)),
882 ea.node(),
883 eb.node(),
884 );
885 }
886
887 equal
888 }
889
890 fn tree_matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
892 if !self.matches(a, b) {
893 return false;
894 }
895
896 let a_count = a.borrow().child_count();
897 let b_count = b.borrow().child_count();
898
899 if a_count != b_count {
900 return false;
901 }
902
903 for i in 0..a_count {
904 let a_child = a.borrow().child(i).cloned();
905 let b_child = b.borrow().child(i).cloned();
906
907 match (a_child, b_child) {
908 (Some(ac), Some(bc)) => {
909 if !self.tree_matches(&ac, &bc) {
910 return false;
911 }
912 }
913 _ => return false,
914 }
915 }
916
917 true
918 }
919
920 fn write_start_element<W: Write>(
922 &self,
923 writer: &mut W,
924 elem: &XmlElement,
925 indent: usize,
926 ) -> std::io::Result<()> {
927 write!(writer, "{}<{}", Self::indent_str_for(indent), elem.qname())?;
928
929 let mut attrs: Vec<(&String, &String)> = elem.attributes().iter().collect();
931 attrs.sort_by(|a, b| a.0.cmp(b.0));
932
933 for (name, value) in attrs {
934 write!(writer, " {}=\"{}\"", name, escape_xml_attr(value))?;
935 }
936
937 writeln!(writer, ">")?;
938 Ok(())
939 }
940
941 fn indent_str_for(level: usize) -> String {
943 " ".repeat(level)
944 }
945}
946
947struct RecursionPartners {
949 first: Option<NodeRef>,
950 second: Option<NodeRef>,
951}
952
953fn escape_xml_text(s: &str) -> String {
955 s.replace('&', "&")
956 .replace('<', "<")
957 .replace('>', ">")
958}
959
960fn escape_xml_attr(s: &str) -> String {
962 s.replace('&', "&")
963 .replace('<', "<")
964 .replace('>', ">")
965 .replace('"', """)
966 .replace('\'', "'")
967}
968
969impl crate::node::NodeInner {
970 pub fn first_partner(&self, type_flags: MatchType) -> Option<NodeRef> {
972 if let crate::node::NodeKind::Branch {
973 match_type,
974 partners,
975 ..
976 } = self.kind()
977 {
978 if !match_type.intersects(type_flags) {
979 return None;
980 }
981
982 if let Some(partners) = partners {
983 let borrowed = partners.borrow();
984 for candidate in borrowed.matches() {
985 if BranchNode::base_match_type(candidate).intersects(type_flags) {
986 return Some(candidate.clone());
987 }
988 }
989 }
990 }
991 None
992 }
993
994 pub fn is_match(&self, type_flags: MatchType) -> bool {
996 if let crate::node::NodeKind::Branch { match_type, .. } = self.kind() {
997 match_type.intersects(type_flags)
998 } else {
999 false
1000 }
1001 }
1002}