@@ -13,16 +13,20 @@ class XMLPatch
1313 {
1414 private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
1515
16+ private Options ? ParsedOptions { get ; set ; }
17+
1618 static void Main ( string [ ] args )
1719 {
20+ var program = new XMLPatch ( ) ;
1821 Parser
1922 . Default . ParseArguments < Options > ( args )
20- . WithParsed < Options > ( opts => RunOptionsAndReturnExitCode ( opts ) )
23+ . WithParsed < Options > ( opts => program . RunOptionsAndReturnExitCode ( opts ) )
2124 . WithNotParsed < Options > ( ( errs ) => HandleParseError ( errs ) ) ;
2225 }
2326
24- private static void RunOptionsAndReturnExitCode ( Options opts )
27+ private void RunOptionsAndReturnExitCode ( Options opts )
2528 {
29+ ParsedOptions = opts ;
2630 ConfigureLogging ( opts . LogToFile , opts . AppendToLog ) ;
2731
2832 Assembly assembly = Assembly . GetExecutingAssembly ( ) ;
@@ -91,6 +95,7 @@ public class Options
9195 private string ? xsd ;
9296 private string ? logToFile ;
9397 private bool appendToLog ;
98+ private bool allowDoubles ;
9499
95100 [ Option ( 'o' , "original_xml" , Required = true , HelpText = "Path to the original XML file or directory." ) ]
96101 public string ? OriginalXml
@@ -142,6 +147,19 @@ public bool AppendToLog
142147 get => appendToLog ;
143148 set => appendToLog = value ;
144149 }
150+
151+ [ Option (
152+ ' ' ,
153+ "allow-doubles" ,
154+ Required = false ,
155+ HelpText = "Allow doubles in the diff XML. Useful for scripts patching." ,
156+ Default = false
157+ ) ]
158+ public bool AllowDoubles
159+ {
160+ get => allowDoubles ;
161+ set => allowDoubles = value ;
162+ }
145163 }
146164
147165 #endregion
@@ -188,12 +206,7 @@ private static void ConfigureLogging(string? logToFile, bool appendToLog = false
188206
189207 #region Processing
190208
191- private static void ProcessSingleFile (
192- string originalXmlPath ,
193- string diffXmlPath ,
194- string outputXmlPath ,
195- XmlReaderSettings ? diffReaderSettings
196- )
209+ private void ProcessSingleFile ( string originalXmlPath , string diffXmlPath , string outputXmlPath , XmlReaderSettings ? diffReaderSettings )
197210 {
198211 Logger . Info ( $ "Patching the original XML file '{ originalXmlPath } ' with the diff XML file '{ diffXmlPath } ' to '{ outputXmlPath } '.") ;
199212 if ( ! File . Exists ( originalXmlPath ) )
@@ -325,7 +338,7 @@ private static void ProcessSingleFile(
325338 }
326339 }
327340
328- private static void ProcessDirectories ( string originalDir , string diffDir , string outputDir , XmlReaderSettings ? diffReaderSettings )
341+ private void ProcessDirectories ( string originalDir , string diffDir , string outputDir , XmlReaderSettings ? diffReaderSettings )
329342 {
330343 var diffFiles = Directory . EnumerateFiles ( diffDir , "*.xml" , SearchOption . AllDirectories ) ;
331344 foreach ( var diffFilePath in diffFiles )
@@ -376,7 +389,7 @@ private static int DetectIndentation(string xmlPath)
376389
377390 #region Apply Operations
378391
379- private static void ApplyAdd ( XElement addElement , XElement originalRoot )
392+ private void ApplyAdd ( XElement addElement , XElement originalRoot )
380393 {
381394 string ? sel = addElement . Attribute ( "sel" ) ? . Value ;
382395 Logger . Debug ( $ "Applying add operation: '{ sel } '") ;
@@ -421,9 +434,14 @@ private static void ApplyAdd(XElement addElement, XElement originalRoot)
421434 if ( pos == "before" )
422435 {
423436 if (
424- targetElement
437+ ( ParsedOptions == null || ! ParsedOptions . AllowDoubles )
438+ && targetElement
425439 . Parent ! . Elements ( )
426- . Any ( e => e . Name == cloned . Name && e . Attributes ( ) . All ( a => cloned . Attribute ( a . Name ) ? . Value == a . Value ) )
440+ . Any ( e =>
441+ e . Name == cloned . Name
442+ && e . Attributes ( ) . All ( a => cloned . Attribute ( a . Name ) ? . Value == a . Value )
443+ && cloned . Attributes ( ) . All ( a => e . Attribute ( a . Name ) ? . Value == a . Value )
444+ )
427445 )
428446 {
429447 Logger . Warn ( $ "Element '{ clonedInfo } ' already exists in '{ parentInfo } '. Skipping.") ;
@@ -435,9 +453,14 @@ private static void ApplyAdd(XElement addElement, XElement originalRoot)
435453 else if ( pos == "after" )
436454 {
437455 if (
438- targetElement
456+ ( ParsedOptions == null || ! ParsedOptions . AllowDoubles )
457+ && targetElement
439458 . Parent ! . Elements ( )
440- . Any ( e => e . Name == cloned . Name && e . Attributes ( ) . All ( a => cloned . Attribute ( a . Name ) ? . Value == a . Value ) )
459+ . Any ( e =>
460+ e . Name == cloned . Name
461+ && e . Attributes ( ) . All ( a => cloned . Attribute ( a . Name ) ? . Value == a . Value )
462+ && cloned . Attributes ( ) . All ( a => e . Attribute ( a . Name ) ? . Value == a . Value )
463+ )
441464 )
442465 {
443466 Logger . Warn ( $ "Element '{ clonedInfo } ' already exists in '{ parentInfo } '. Skipping.") ;
@@ -449,10 +472,13 @@ private static void ApplyAdd(XElement addElement, XElement originalRoot)
449472 else if ( pos == "prepend" )
450473 {
451474 if (
452- targetElement
475+ ( ParsedOptions == null || ! ParsedOptions . AllowDoubles )
476+ && targetElement
453477 . Elements ( )
454478 . Any ( e =>
455- e . Name == cloned . Name && e . Attributes ( ) . All ( a => a . Name == "_source" || cloned . Attribute ( a . Name ) ? . Value == a . Value )
479+ e . Name == cloned . Name
480+ && e . Attributes ( ) . All ( a => cloned . Attribute ( a . Name ) ? . Value == a . Value )
481+ && cloned . Attributes ( ) . All ( a => e . Attribute ( a . Name ) ? . Value == a . Value )
456482 )
457483 )
458484 {
@@ -465,10 +491,13 @@ private static void ApplyAdd(XElement addElement, XElement originalRoot)
465491 else if ( pos == "append" )
466492 {
467493 if (
468- targetElement
494+ ( ParsedOptions == null || ! ParsedOptions . AllowDoubles )
495+ && targetElement
469496 . Elements ( )
470497 . Any ( e =>
471- e . Name == cloned . Name && e . Attributes ( ) . All ( a => a . Name == "_source" || cloned . Attribute ( a . Name ) ? . Value == a . Value )
498+ e . Name == cloned . Name
499+ && e . Attributes ( ) . All ( a => cloned . Attribute ( a . Name ) ? . Value == a . Value )
500+ && cloned . Attributes ( ) . All ( a => e . Attribute ( a . Name ) ? . Value == a . Value )
472501 )
473502 )
474503 {
@@ -500,7 +529,7 @@ private static void ApplyAdd(XElement addElement, XElement originalRoot)
500529 }
501530 }
502531
503- private static void ApplyReplace ( XElement replaceElement , XElement originalRoot )
532+ private void ApplyReplace ( XElement replaceElement , XElement originalRoot )
504533 {
505534 string ? sel = replaceElement . Attribute ( "sel" ) ? . Value ;
506535 Logger . Info ( $ "Applying replace operation: '{ sel } '") ;
@@ -553,7 +582,7 @@ private static void ApplyReplace(XElement replaceElement, XElement originalRoot)
553582 }
554583 }
555584
556- private static void ApplyRemove ( XElement removeElement , XElement originalRoot )
585+ private void ApplyRemove ( XElement removeElement , XElement originalRoot )
557586 {
558587 string ? sel = removeElement . Attribute ( "sel" ) ? . Value ;
559588 Logger . Info ( $ "Applying remove operation: '{ sel } '") ;
@@ -635,7 +664,7 @@ private static string GetElementInfo(XElement? element)
635664 info += $ "{ element . Name } ";
636665 if ( element . HasAttributes )
637666 {
638- info += $ "{ element . FirstAttribute ? . Name } =\" { element . FirstAttribute ? . Value } \" ";
667+ info += $ " { element . FirstAttribute ? . Name } =\" { element . FirstAttribute ? . Value } \" ";
639668 if ( element . Attributes ( ) . Count ( ) > 1 )
640669 {
641670 info += " ..." ;
0 commit comments