Skip to content

Commit c070c59

Browse files
committed
refactor(XMLDiff): enhance logging for element comparison and check-only mode
fix(XMLDiff): fix detection of changes in more then one attribute fix(XMLDiff): fix always usage remove for elements, replace detection is implemented, i.e. fixes #9 fix(XMLDiff): on adding to the end to `after` is used now
1 parent 6bcbf2f commit c070c59

File tree

1 file changed

+81
-21
lines changed

1 file changed

+81
-21
lines changed

XMLDiff/Program.cs

Lines changed: 81 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -416,10 +416,24 @@ private static bool CompareElements(
416416
{
417417
if (originalElem != null && modifiedElem != null)
418418
{
419+
string sel = GenerateXPath(originalElem, pathOptions);
420+
string selModified = GenerateXPath(modifiedElem, pathOptions);
421+
if (checkOnly)
422+
{
423+
Logger.Debug(
424+
$"Comparing elements '{GetElementInfo(originalElem)}'({sel}) vs '{GetElementInfo(modifiedElem)}'({selModified}). Check only: {checkOnly}"
425+
);
426+
}
427+
else
428+
{
429+
Logger.Info($"Comparing elements '{GetElementInfo(originalElem)}'({sel}) vs '{GetElementInfo(modifiedElem)}'({selModified}).");
430+
}
419431
if (originalElem.Name != modifiedElem.Name)
420432
{
421433
// Process can be there only in case of changes detection, not for the real diff generation
422-
Logger.Error($"Element names do not match: {originalElem.Name} vs {modifiedElem.Name}");
434+
Logger.Debug(
435+
$"Warning. Element names do not match: {originalElem.Name} vs {modifiedElem.Name}. Check only: {checkOnly}. Returning true."
436+
);
423437
return true;
424438
}
425439

@@ -430,11 +444,11 @@ private static bool CompareElements(
430444
Logger.Debug($"Comparing text in element '{originalElem.Name}': '{originalText}' vs '{modifiedText}'");
431445
if (originalText != modifiedText)
432446
{
433-
string sel = GenerateXPath(originalElem, pathOptions);
434447
if (!string.IsNullOrEmpty(modifiedText))
435448
{
436449
if (checkOnly)
437450
{
451+
Logger.Debug($"Text in element '{GetElementInfo(originalElem)}' does not match in check only mode. Returning true.");
438452
return true;
439453
}
440454
XElement replaceOp = new XElement("replace", new XAttribute("sel", sel), modifiedText);
@@ -445,6 +459,7 @@ private static bool CompareElements(
445459
{
446460
if (checkOnly)
447461
{
462+
Logger.Debug($"Text in element '{originalElem.Name}' removed in check only mode. Returning true.");
448463
return true;
449464
}
450465
XElement removeOp = new XElement("remove", new XAttribute("sel", $"{sel}/text()"));
@@ -458,7 +473,7 @@ private static bool CompareElements(
458473
modifiedElem = modifiedElem ?? modified.Root;
459474
if (originalElem == null || modifiedElem == null)
460475
{
461-
Logger.Error("Original or modified element is null.");
476+
Logger.Debug("Warning: Original or modified element is null.");
462477
return true;
463478
}
464479
// Compare children
@@ -467,6 +482,9 @@ private static bool CompareElements(
467482

468483
if (checkOnly && originalChildren.Count != modifiedChildren.Count)
469484
{
485+
Logger.Debug(
486+
$"Children count does not match for {GetElementInfo(originalElem)} and {GetElementInfo(modifiedElem)}: {originalChildren.Count} vs {modifiedChildren.Count} in check only mode. Returning true."
487+
);
470488
return true;
471489
}
472490

@@ -478,6 +496,18 @@ private static bool CompareElements(
478496
var modifiedChild = modifiedChildren[j];
479497

480498
bool matchedEnough = false;
499+
if (checkOnly)
500+
{
501+
Logger.Debug(
502+
$"Comparing child '{GetElementInfo(originalChild)}' of '{GetElementInfo(originalElem)}' vs '{GetElementInfo(modifiedChild)}' of '{GetElementInfo(modifiedElem)}'. Check only: {checkOnly}"
503+
);
504+
}
505+
else
506+
{
507+
Logger.Info(
508+
$"Comparing child '{GetElementInfo(originalChild)}' of '{GetElementInfo(originalElem)}' vs '{GetElementInfo(modifiedChild)}' of '{GetElementInfo(modifiedElem)}'"
509+
);
510+
}
481511
if (originalChild.Name == modifiedChild.Name)
482512
{
483513
var originalAttributes = originalChild.Attributes().ToDictionary(a => a.Name.LocalName, a => a.Value);
@@ -491,7 +521,7 @@ private static bool CompareElements(
491521

492522
foreach (var attr in modifiedAttributes)
493523
{
494-
Logger.Debug($"Checking attribute '{attr.Key}' in original element '{originalAttributes.Keys}'.");
524+
Logger.Debug($"Checking attribute '{attr.Key}' in original element attributes '{string.Join(", ", originalAttributes.Keys)}'.");
495525
if (!originalAttributes.ContainsKey(attr.Key))
496526
{
497527
Logger.Debug($"Original attributes does not contain key '{attr.Key}'.");
@@ -504,7 +534,6 @@ private static bool CompareElements(
504534
string sel = GenerateXPath(originalChild, pathOptions);
505535
savedOp = new XElement("add", new XAttribute("sel", sel), new XAttribute("type", $"@{attr.Key}"), attr.Value);
506536
Logger.Debug($"Found added attribute '{attr.Key}' with value '{attr.Value}' to element '{originalChild.Name}'.");
507-
break;
508537
}
509538
else if (originalAttributes[attr.Key] != attr.Value)
510539
{
@@ -524,7 +553,6 @@ private static bool CompareElements(
524553
Logger.Debug(
525554
$"Found replaced attribute '{attr.Key}' value from '{originalAttributes[attr.Key]}' to '{attr.Value}' in element '{originalChild.Name}'."
526555
);
527-
break;
528556
}
529557
}
530558
if (matchedEnough)
@@ -536,6 +564,9 @@ private static bool CompareElements(
536564
Logger.Debug($"Modified attributes does not contain key '{attr}'.");
537565
if (checkOnly)
538566
{
567+
Logger.Debug(
568+
$"Attribute '{attr}' from {GetElementInfo(originalChild)} not exists in {GetElementInfo(modifiedChild)} check only mode. Returning true."
569+
);
539570
return true;
540571
}
541572
matchedEnough = false;
@@ -547,6 +578,9 @@ private static bool CompareElements(
547578
{
548579
if (checkOnly)
549580
{
581+
Logger.Debug(
582+
$"At least one attribute does not match {GetElementInfo(originalChild)} vs {GetElementInfo(modifiedChild)} in check only mode. Returning true."
583+
);
550584
return true;
551585
}
552586
matchedEnough = false;
@@ -579,6 +613,9 @@ private static bool CompareElements(
579613
{
580614
if (checkOnly)
581615
{
616+
Logger.Debug(
617+
$"Children of {GetElementInfo(originalElem)} and {GetElementInfo(modifiedElem)} do not match in check only mode. Returning true."
618+
);
582619
return true;
583620
}
584621
}
@@ -590,9 +627,13 @@ private static bool CompareElements(
590627
{
591628
if (checkOnly)
592629
{
630+
Logger.Debug(
631+
$"Elements {GetElementInfo(originalElem)} and {GetElementInfo(modifiedElem)} do not match in check only mode. Returning true."
632+
);
593633
return true;
594634
}
595635
bool foundMatch = false;
636+
Logger.Debug($"Checking for match for '{GetElementInfo(originalChild)}' in the next child of '{GetElementInfo(modifiedElem)}'.");
596637
for (int k = j + 1; k < modifiedChildren.Count; k++)
597638
{
598639
var nextModifiedChild = modifiedChildren[k];
@@ -601,6 +642,7 @@ private static bool CompareElements(
601642
&& originalChild.Attributes().All(attr => nextModifiedChild.Attribute(attr.Name)?.Value == attr.Value)
602643
)
603644
{
645+
Logger.Debug($"Found match for '{GetElementInfo(originalChild)}' in the next child of '{GetElementInfo(modifiedElem)}'.");
604646
XElement addOp = new XElement(
605647
"add",
606648
new XAttribute("sel", GenerateXPath(originalChild, pathOptions)),
@@ -621,11 +663,33 @@ private static bool CompareElements(
621663

622664
if (!foundMatch)
623665
{
624-
string sel = GenerateXPath(originalChild, pathOptions);
625-
XElement removeOp = new XElement("remove", new XAttribute("sel", sel));
626-
diffRoot.Add(removeOp);
627-
Logger.Debug($"Removed element '{GetElementInfo(originalChild)}' from parent '{GetElementInfo(originalElem)}'.");
628-
i++;
666+
Logger.Debug($"Trying to identify - is it replace or remove operation.");
667+
if (
668+
(originalChildren[i].Name == modifiedChildren[j].Name)
669+
&& (
670+
(i + 1 == originalChildren.Count) && (j + 1 == modifiedChildren.Count)
671+
|| (i + 1 < originalChildren.Count)
672+
&& (j + 1 < modifiedChildren.Count)
673+
&& originalChildren[i + 1].Name == modifiedChildren[j + 1].Name
674+
&& originalChildren[i + 1].Attributes().All(attr => modifiedChildren[j + 1].Attribute(attr.Name)?.Value == attr.Value)
675+
)
676+
)
677+
{
678+
string sel = GenerateXPath(originalChild, pathOptions);
679+
XElement replaceOp = new XElement("replace", new XAttribute("sel", sel), modifiedChild);
680+
diffRoot.Add(replaceOp);
681+
Logger.Debug($"Replaced element '{GetElementInfo(originalChild)}' with '{GetElementInfo(modifiedChild)}'.");
682+
i++;
683+
j++;
684+
}
685+
else
686+
{
687+
string sel = GenerateXPath(originalChild, pathOptions);
688+
XElement removeOp = new XElement("remove", new XAttribute("sel", sel));
689+
diffRoot.Add(removeOp);
690+
Logger.Debug($"Removed element '{GetElementInfo(originalChild)}' from parent '{GetElementInfo(originalElem)}'.");
691+
i++;
692+
}
629693
}
630694
}
631695
}
@@ -642,16 +706,8 @@ private static bool CompareElements(
642706

643707
if (j + 1 <= modifiedChildren.Count)
644708
{
645-
XElement? originalLast = originalChildren.LastOrDefault();
646709
XElement addOp;
647-
if (originalLast != null)
648-
{
649-
addOp = new XElement("add", new XAttribute("sel", GenerateXPath(originalLast, pathOptions)), new XAttribute("pos", "after"));
650-
}
651-
else
652-
{
653-
addOp = new XElement("add", new XAttribute("sel", GenerateXPath(originalElem, pathOptions)));
654-
}
710+
addOp = new XElement("add", new XAttribute("sel", GenerateXPath(originalElem, pathOptions)));
655711
while (j < modifiedChildren.Count)
656712
{
657713
var addedChild = modifiedChildren[j];
@@ -661,6 +717,10 @@ private static bool CompareElements(
661717
}
662718
diffRoot.Add(addOp);
663719
}
720+
if (checkOnly)
721+
{
722+
Logger.Debug($"Matched elements of {GetElementInfo(originalElem)} and {GetElementInfo(modifiedElem)}. Check only: {checkOnly}");
723+
}
664724
return false;
665725
}
666726

@@ -846,7 +906,7 @@ private static string GetElementInfo(XElement? element)
846906
info += $"{element.Name}";
847907
if (element.HasAttributes)
848908
{
849-
info += $"{element.FirstAttribute?.Name}=\"{element.FirstAttribute?.Value}\"";
909+
info += $" {element.FirstAttribute?.Name}=\"{element.FirstAttribute?.Value}\"";
850910
if (element.Attributes().Count() > 1)
851911
{
852912
info += " ...";

0 commit comments

Comments
 (0)