private XElement DoChangeElement(XElement target, bool throwOnError, XDiffOptions options) { var e = Find(target, BasisElement, options); if (null == e) { if (throwOnError) { throw new Exception("cannot find target for " + BasisElement); } return(target); } if (NewValue.Contains("<") && NewValue.Contains(">")) { e.Nodes().ToArray().Remove(); var x = XElement.Parse("<a>" + NewValue + "</a>"); foreach (var n in x.Nodes()) { e.Add(n); } } else { e.Value = NewValue; } return(target); }
private XElement DoCreateAttribute(XElement target, bool throwOnError, XDiffOptions options) { var e = Find(target, BasisElement, options); if (null == e) { if (throwOnError) { throw new Exception("cannot find target for " + BasisElement); } return(target); } if (null == NewestAttribute) { foreach (var a in NewestElement.Attributes()) { e.SetAttributeValue(a.Name, a.Value); } } else { e.SetAttributeValue(NewestAttribute.Name.LocalName, NewestAttribute.Value); } return(target); }
public XDiffKey(XDiffOptions opts, XElement e) { _options = opts; Name = e.Name.LocalName; Id = e.Attr("id"); Code = e.Attr("code"); }
/// <summary> /// Применяет разницу к исходному элементу /// </summary> public XElement Apply(XElement target, bool throwOnError = true, XDiffOptions options = null) { options = options ?? new XDiffOptions(); switch (Action) { case XDiffAction.CreateElement: return(DoCreateElement(target, throwOnError, options)); case XDiffAction.DeleteElement: return(DoDeleteElement(target, throwOnError, options)); case XDiffAction.ChangeElement: return(DoChangeElement(target, throwOnError, options)); case XDiffAction.RenameElement: return(DoRenameElement(target, throwOnError, options)); case XDiffAction.CreateAttribute: return(DoCreateAttribute(target, throwOnError, options)); case XDiffAction.DeleteAttribute: return(DoDeleteAttribute(target, throwOnError, options)); case XDiffAction.ChangeAttribute: return(DoChangeAttribute(target, throwOnError, options)); case XDiffAction.ChangeHierarchyPosition: return(DoChangeHierarchyPosition(target, throwOnError, options)); } return(target); }
private XElement DoCreateElement(XElement target, bool throwOnError, XDiffOptions options) { var t = target; var parent = NewestElement.Attribute("__parent"); if (null != parent) { t = FindByParent(target, new ParentCondition(parent.Value)); } if (null == t) { throw new Exception("cannot find target parent elment " + parent); } if (options.IsHierarchy) //мы должны порождать элементы без дочек во избежание дублирования, дочки накатятся сами { var header = new XElement(NewestElement); header.Elements().Remove(); var p = header.Attribute("__parent"); if (null != p) { p.Remove(); } t.Add(header); } else { t.Add(new XElement(NewestElement)); } return(target); }
XElement Find(XElement target, string finder, XDiffOptions options) { var finds = finder.Split('-'); var name = ""; var attrname = ""; var val = ""; if (finds.Length == 3) { name = finds[0]; attrname = finds[1]; val = finds[2]; } else { attrname = finds[0]; val = finds[1]; } Func <XElement, bool> ismatch = e => { if (name != "" && name != e.Name.LocalName) { return(false); } var a = e.Attribute(attrname); if (null == a) { return(false); } return(a.Value == val); }; XElement[] result; if (options.IsHierarchy) { result = target.Descendants().Where(ismatch).ToArray(); } else { result = target.Elements().Where(ismatch).ToArray(); } if (0 == result.Length) { return(null); } if (1 == result.Length) { return(result[0]); } throw new Exception("double match"); }
private XElement DoRenameElement(XElement target, bool throwOnError, XDiffOptions options) { var e = Find(target, BasisElement, options); if (null == e) { if (throwOnError) { throw new Exception("cannot find target for " + BasisElement); } return(target); } e.Name = NewValue; return(target); }
private XElement DoDeleteElement(XElement target, bool throwOnError, XDiffOptions options) { var e = Find(target, BasisElement, options); if (null == e) { if (throwOnError) { throw new Exception("cannot find target for " + BasisElement); } return(target); } e.Remove(); return(target); }
/// <summary> /// /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="__options"></param> /// <returns></returns> public static bool IsJoined(XElement a, XElement b, XDiffOptions __options) { //тут не на кодах, id используется как первичный ключ var aid = __options.ChangeIds? null : a.Attribute("id"); var haid = aid != null; var bid = __options.ChangeIds ? null : b.Attribute("id"); var hbid = bid != null; var acode = a.Attribute("code"); var hacode = acode != null; var bcode = b.Attribute("code"); var hbcode = bcode != null; if (!haid && !hacode) { return(false); //no identity on a } if (!hbid && !hbcode) { return(false); //no idenityt on a } var testvala = ""; var testvalb = ""; if (haid && hbid) // both on ids { testvala = aid.Value; testvalb = bid.Value; } else if (hacode && hbcode) { testvala = acode.Value; testvalb = bcode.Value; } else { return(false); //some incompability checks } if (testvala != testvalb) { return(false); } if (__options.IsNameIndepended) { return(true); } return(a.Name.LocalName == b.Name.LocalName); }
private XElement DoDeleteAttribute(XElement target, bool throwOnError, XDiffOptions options) { var e = Find(target, BasisElement, options); if (null == e) { if (throwOnError) { throw new Exception("cannot find target for " + BasisElement); } return(target); } var ex = e.Attribute(BasisAttribute.Name.LocalName); if (null != ex) { ex.Remove(); } return(target); }
XElement Find(XElement target, XElement search, XDiffOptions options) { XElement[] result; if (options.IsHierarchy) { result = target.Descendants().Where(_ => XDiffExtensions.IsJoined(_, search, options)).ToArray(); } else { result = target.Elements().Where(_ => XDiffExtensions.IsJoined(_, search, options)).ToArray(); } if (0 == result.Length) { return(null); } if (1 == result.Length) { return(result[0]); } throw new Exception("double match"); }
private static string GetResult(XElement b, XElement n, XDiffOptions opts = null){ var result = new XDiffGenerator(opts).GetDiff(b, n).LogToString().Replace("\"", "'").Trim(); Console.WriteLine(result); return result; }
public void CanAppplyPatchHierarchcallyWithNameChanges() { var b = XElement.Parse("<a><z code='2' name='y'/><z id='3' code='4'><x id='5' name='x'/></z></a>"); var n = XElement.Parse("<a><u code='2' name='y'><y id='5' name='x'/></u><z id='3' code='4'></z></a>"); var opts = new XDiffOptions{IsHierarchy = true, IsNameIndepended = true}; GetResult(b, n, opts); Assert.True(new XDiffGenerator(opts).IsDiff(b, n)); var result = new XDiffGenerator(opts).GetDiff(b, n).Apply(b,opts); Console.WriteLine(result.ToString()); Assert.False(new XDiffGenerator(opts).IsDiff(b, n)); }
public void CanMergeNonSeparateDeleteAsChangesAttributes() { var b = XElement.Parse("<a><z id='1' name='2' a='3' b='4' c='5' x='aaa6'/></a>"); var n = XElement.Parse("<a><z id='1' name='2' a='3' b='3' /></a>"); var opts = new XDiffOptions { MergeAttributeChanges = true, TreatDeleteAttributesAsChanges = true }; var diff = new XDiffGenerator(opts).GetDiff(b, n).ToArray(); var log = diff.LogToString(); Console.WriteLine(log); Assert.AreEqual(1, diff.Length); Assert.AreEqual(@"ChangeAttribute n0 BasisElement name=z id=1 NewestElement : (<update b=""3"" c=""0"" x="""" />) ".LfOnly().Trim(), log.LfOnly().Trim()); diff.Apply(b, opts); Console.WriteLine(b); Assert.AreEqual(@"<a> <z id=""1"" name=""2"" a=""3"" b=""3"" c=""0"" x="""" /> </a>".Trim().LfOnly(),b.ToString().Trim().LfOnly()); }
public void CanMergeNonSeparateNewAndUpdatedAttributes() { var b = XElement.Parse("<a><z id='1' name='2' a='3' b='4' c='5'/></a>"); var n = XElement.Parse("<a><z id='1' name='2' a='6' b='4' c='7' d='8' e='9'/></a>"); var opts = new XDiffOptions { MergeAttributeChanges = true,TreatNewAttributesAsChanges = true}; var diff = new XDiffGenerator(opts).GetDiff(b, n).ToArray(); var log = diff.LogToString(); Console.WriteLine(log); Assert.AreEqual(1, diff.Length); Assert.AreEqual(@"ChangeAttribute n0 BasisElement name=z id=1 NewestElement : (<update a=""6"" c=""7"" d=""8"" e=""9"" />) ", log); diff.Apply(b, opts); Assert.False(new XDiffGenerator().IsDiff(b, n)); }
public void CanPreventActions() { var b = XElement.Parse("<a><z id='1' code='2'/><z id='3' code='4'/></a>"); var n = XElement.Parse("<a><z id='3' code='2'/><z id='1' code='4'/><z id='5'/></a>"); var opts = new XDiffOptions(); opts.ErrorActions = opts.ErrorActions | XDiffAction.CreateElement; Assert.Throws<Exception>(()=> GetResult(b, n, opts)); }
public void CanFilterActions() { var b = XElement.Parse("<a><z id='1' code='2'/><z id='3' code='4'/></a>"); var n = XElement.Parse("<a><z id='3' set-code='2'/><z id='1' set-code='4'/><z id='5'/></a>"); var result = GetResult(b, n); Assert.AreEqual(@"CreateElement n0 NewestElement : (<z id='5' />) ChangeAttribute n1 BasisElement name=z id=1 NewestAttribute code :4 ChangeAttribute n2 BasisElement name=z id=3 NewestAttribute code :2".Length, result.Length); Console.WriteLine(); var opts = new XDiffOptions(); opts.IncludeActions = opts.IncludeActions & ~XDiffAction.CreateElement; result = GetResult(b, n, opts); Assert.AreEqual(@"ChangeAttribute n0 BasisElement name=z id=1 NewestAttribute code :4 ChangeAttribute n1 BasisElement name=z id=3 NewestAttribute code :2".Length, result.Length); Console.WriteLine(); opts = new XDiffOptions(); opts.IncludeActions = opts.IncludeActions & ~XDiffAction.ChangeAttribute; result = GetResult(b, n, opts); Assert.AreEqual(@"CreateElement n0 NewestElement : (<z id='5' />)".Length, result.Length); }
private XElement DoChangeHierarchyPosition(XElement target, bool throwOnError, XDiffOptions options) { var e = Find(target, BasisElement, options); if (null == e) { if (throwOnError) { throw new Exception("cannot find target for " + BasisElement); } return(target); } var __parent = e.Attribute("__parent"); var pval = NewValue; __parent.Remove(); e.Remove(); if (string.IsNullOrWhiteSpace(pval)) { target.Add(e); } else { var newtarg = Find(target, pval, options); if (null == newtarg) { if (throwOnError) { throw new Exception("cannot find target for " + pval); } return(target); } newtarg.Add(e); } return(target); }
/// <summary> /// /// </summary> /// <param name="diffs"></param> /// <param name="target"></param> /// <param name="options"></param> public static XElement Apply(this IEnumerable <XDiffItem> diffs, XElement target, XDiffOptions options = null) { options = options ?? new XDiffOptions(); IDictionary <string, string> movereplaces = new Dictionary <string, string>(); var orderedDiffs = diffs. OrderBy(_ => _.Action) .ThenByDescending(_ => null == _.NewestElement ? 0 : _.NewestElement.Descendants().Count()) .ToArray(); foreach (var diff in orderedDiffs) { if (diff.CanChangeHierarchyTarget) { FillDictionary(movereplaces, diff); } else if (diff.CanChangeHierarchyTarget) { while (movereplaces.ContainsKey(diff.NewValue)) { diff.NewValue = movereplaces[diff.NewValue]; } } diff.Apply(target, true, options); } return(target); }
/// <summary> /// </summary> public XDiffGenerator(XDiffOptions options = null) { _options = options ?? new XDiffOptions(); }