static void PrintDiff(HashDiff diff) { StringBuilder sb = new StringBuilder(); diff.Write(sb); System.Diagnostics.Debug.WriteLine(sb.ToString()); }
/// <summary> /// Merges two HashDifferences to a unified difference. /// </summary> public static HashDiff Merge(HashDiff left, HashDiff right) { // Find the combined key list. List <object> keys = left.Operations.Keys.ToList <object>(); foreach (var kvp in right.Operations) { if (!left.Operations.ContainsKey(kvp.Key)) { keys.Add(kvp.Key); } } // Merge the operations for each key. HashDiff h = new HashDiff(); foreach (object key in keys) { if (left.Operations.ContainsKey(key) && right.Operations.ContainsKey(key)) { h.Operations[key] = DiffOperation.Merge(left.Operations[key], right.Operations[key]); } else if (left.Operations.ContainsKey(key)) { h.Operations[key] = left.Operations[key]; } else if (right.Operations.ContainsKey(key)) { h.Operations[key] = right.Operations[key]; } } return(h); }
/// <summary> /// Finds the difference between two Json arrays based on element IDs. /// </summary> public static HashDiff IdDiff(ArrayList a, ArrayList b) { HashDiff diff = new HashDiff(); for (int i = 0; i < a.Count; ++i) { object id = Id.GetId(a[i]); DiffOperation op = DiffValue(a[i], Id.FindObjectWithId(b, id)); if (op != null) { diff.Operations[id] = op; } } for (int i = 0; i < b.Count; ++i) { object id = Id.GetId(b[i]); object a_object = Id.FindObjectWithId(a, id); if (a_object == null) { diff.Operations[id] = new ChangeOperation(b[i]); } } return(diff); }
/// <summary> /// Finds the difference between two Json hashtables. /// </summary> public static HashDiff Diff(Hashtable first, Hashtable second) { HashDiff diff = new HashDiff(); foreach (DictionaryEntry de in first) { string key = (de.Key as string); object first_value = de.Value; object second_value = second.ContainsKey(key) ? second[key] : null; DiffOperation op = DiffValue(first_value, second_value); if (op != null) { diff.Operations[key] = op; } } foreach (DictionaryEntry de in second) { string key = de.Key as string; if (!first.ContainsKey(key)) { diff.Operations[key] = new ChangeOperation(de.Value); } } return(diff); }
static void TestValidDiff(Hashtable a, Hashtable b, HashDiff diff) { diff.Apply(a); HashDiff newdiff = JsonDiff.Diff(a, b); System.Diagnostics.Debug.Assert(newdiff.Empty()); }
static void TestDiff(string s_a, string s_b) { Hashtable a = Sjson(s_a); Hashtable b = Sjson(s_b); HashDiff diff = JsonDiff.Diff(a, b); PrintDiff(diff); TestValidDiff(a, b, diff); }
/// <summary> /// Computes the three-way-merge of Json hashtables. /// </summary> public static Hashtable Merge(Hashtable parent, Hashtable left, Hashtable right) { HashDiff left_diff = JsonDiff.Diff(parent, left); HashDiff right_diff = JsonDiff.Diff(parent, right); HashDiff diff = HashDiff.Merge(left_diff, right_diff); Hashtable res = parent.DeepClone(); diff.Apply(res); return(res); }
/// <summary> /// Computes the three-way-merge of Json hashtables and returns the intermediate diff results. /// </summary> public static Hashtable MergeDetailed(Hashtable parent, Hashtable left, Hashtable right, out HashDiff left_diff, out HashDiff right_diff, out HashDiff merged_diff) { left_diff = JsonDiff.Diff(parent, left); right_diff = JsonDiff.Diff(parent, right); merged_diff = HashDiff.Merge(left_diff, right_diff); Hashtable res = parent.DeepClone(); merged_diff.Apply(res); return(res); }
/// <summary> /// Finds the difference operation between two Json value items. /// </summary> private static DiffOperation DiffValue(object first, object second) { if (first == null && second == null) { return(null); } else if (second == null) { return(new RemoveOperation()); } else if (first == null) { return(new ChangeOperation(second)); } else if (first.GetType() != second.GetType()) { return(new ChangeOperation(second)); } else if (first is double) { return((double)first == (double)second ? null : new ChangeOperation(second)); } else if (first is bool) { return((bool)first == (bool)second ? null : new ChangeOperation(second)); } else if (first is string) { return((string)first == (string)second ? null : new ChangeOperation(second)); } else if (first is Hashtable) { HashDiff subdiff = Diff(first as Hashtable, second as Hashtable); return(subdiff.Empty() ? null : new ChangeObjectOperation(subdiff)); } else if (first is ArrayList) { if (AreIdArrays(first as ArrayList, second as ArrayList)) { HashDiff subdiff = IdDiff(first as ArrayList, second as ArrayList); return(subdiff.Empty() ? null : new ChangeIdArrayOperation(subdiff)); } else { PositionArrayDiff subdiff = PositionDiff(first as ArrayList, second as ArrayList); return(subdiff.Empty() ? null : new ChangePositionArrayOperation(subdiff)); } } else { throw new System.Exception("error"); } }
// Shows the difference between two hash tables. private void DisplayDiff(Hashtable a, Hashtable b, HashDiff diff, int indent) { HashSet <string> keys = new HashSet <string>(); foreach (string key in a.Keys) { keys.Add(key); } foreach (string key in b.Keys) { keys.Add(key); } foreach (string key in keys.OrderBy(i => i)) { if (diff.Operations.ContainsKey(key)) { DiffOperation dop = diff.Operations[key]; if (dop is RemoveOperation) { RemovedText(_af.ObjectField(a, key, indent), ""); } else if (dop is ChangeOperation) { ChangedText(_af.ObjectField(a, key, indent), _bf.ObjectField(b, key, indent)); } else if (dop is ChangeObjectOperation) { SameText(_af.ObjectStart(key, indent), _bf.ObjectStart(key, indent)); DisplayDiff(a[key] as Hashtable, b[key] as Hashtable, (dop as ChangeObjectOperation).Diff, indent + 1); SameText(_af.ObjectEnd(indent), _bf.ObjectEnd(indent)); } else if (dop is ChangePositionArrayOperation) { SameText(_af.ArrayStart(key, indent), _bf.ArrayStart(key, indent)); DisplayDiff(a[key] as ArrayList, b[key] as ArrayList, (dop as ChangePositionArrayOperation).Diff, indent + 1); SameText(_af.ArrayEnd(indent), _bf.ArrayEnd(indent)); } else if (dop is ChangeIdArrayOperation) { SameText(_af.ArrayStart(key, indent), _bf.ArrayStart(key, indent)); DisplayDiff(a[key] as ArrayList, b[key] as ArrayList, (dop as ChangeIdArrayOperation).Diff, indent + 1); SameText(_af.ArrayEnd(indent), _bf.ArrayEnd(indent)); } } else { SameText(_af.ObjectField(a, key, indent), _bf.ObjectField(a, key, indent)); } } }
// Shows the difference between two arrays that use the id-merge method. private void DisplayDiff(ArrayList a, ArrayList b, HashDiff diff, int indent) { HashSet <object> keys = new HashSet <object>(); foreach (object h in a) { keys.Add(Id.GetId(h)); } foreach (object h in b) { keys.Add(Id.GetId(h)); } foreach (object key in keys.OrderBy(i => i)) { object ao = Id.FindObjectWithId(a, key); object bo = Id.FindObjectWithId(b, key); DisplayArrayDiff(ao, bo, diff.Operations.GetValueOrDefault(key, null), indent); } }
/// <summary> /// Shows the difference between the two JSON objects a and b. /// Diff is the computed difference between the objects. /// If json is set to true, the output is showed in JSON format, /// otherwise SJSON format is used. /// </summary> public DiffVisualForm(Hashtable a, Hashtable b, HashDiff diff, bool json) { InitializeComponent(); _json = json; if (_json) { _af = new JsonFormatter(); _bf = new JsonFormatter(); } else { _af = new SjsonFormatter(); _bf = new SjsonFormatter(); } _diff_lines.Add(0); // Enforce object creation (you can get a highlighting bug otherwise). IntPtr ah = aTextBox.Handle; IntPtr bh = bTextBox.Handle; if (_json) { SameText("{", "{"); DisplayDiff(a, b, diff, 1); SameText("\n}", "\n}"); } else { DisplayDiff(a, b, diff, 0); } _diff_lines.Add(aTextBox.Lines.Count()); aTextBox.SelectionStart = 0; aTextBox.SelectionLength = 0; bTextBox.SelectionStart = 0; bTextBox.SelectionLength = 0; }
// Shows the difference between a and b in the text box. private void DisplayDiff(RichTextBox rtb, IFormatter f, Hashtable a, Hashtable b, HashDiff diff, int indent, string path) { HashSet <string> keys = new HashSet <string>(); foreach (string key in a.Keys) { keys.Add(key); } foreach (string key in b.Keys) { keys.Add(key); } foreach (string key in keys.OrderBy(i => i)) { string subpath = path + "." + key; CheckLineNumber(rtb, subpath); if (diff.Operations.ContainsKey(key)) { DiffOperation dop = diff.Operations[key]; if (dop is RemoveOperation) { RemovedText(rtb, f.ObjectField(a, key, indent)); } else if (dop is ChangeOperation) { ChangedText(rtb, f.ObjectField(a, key, indent), f.ObjectField(b, key, indent)); } else if (dop is ChangeObjectOperation) { SameText(rtb, f.ObjectStart(key, indent)); DisplayDiff(rtb, f, a[key] as Hashtable, b[key] as Hashtable, (dop as ChangeObjectOperation).Diff, indent + 1, subpath); SameText(rtb, f.ObjectEnd(indent)); } else if (dop is ChangePositionArrayOperation) { SameText(rtb, f.ArrayStart(key, indent)); DisplayDiff(rtb, f, a[key] as ArrayList, b[key] as ArrayList, (dop as ChangePositionArrayOperation).Diff, indent + 1, subpath); SameText(rtb, f.ArrayEnd(indent)); } else if (dop is ChangeIdArrayOperation) { SameText(rtb, f.ArrayStart(key, indent)); DisplayDiff(rtb, f, a[key] as ArrayList, b[key] as ArrayList, (dop as ChangeIdArrayOperation).Diff, indent + 1, subpath); SameText(rtb, f.ArrayEnd(indent)); } } else { SameText(rtb, f.ObjectField(b, key, indent)); } } }
public static ChangeIdArrayOperation Merge(ChangeIdArrayOperation left, ChangeIdArrayOperation right) { return(new ChangeIdArrayOperation(HashDiff.Merge(left.Diff, right.Diff))); }
// Shows the difference between a and b in the text box. private void DisplayDiff(RichTextBox rtb, IFormatter f, ArrayList a, ArrayList b, HashDiff diff, int indent, string path) { HashSet <object> keys = new HashSet <object>(); foreach (object h in a) { keys.Add(Id.GetId(h)); } foreach (object h in b) { keys.Add(Id.GetId(h)); } foreach (object key in keys.OrderBy(i => i)) { object ao = Id.FindObjectWithId(a, key); object bo = Id.FindObjectWithId(b, key); string subpath = string.Format("{0}.{1}", path, key); DisplayArrayDiff(rtb, f, ao, bo, diff.Operations.GetValueOrDefault(key, null), indent, subpath); } }
public MergeVisualForm(Hashtable parent, Hashtable a, HashDiff adiff, Hashtable b, HashDiff bdiff, Hashtable c, HashDiff cdiff, bool json) { InitializeComponent(); _json = json; if (_json) { _af = new JsonFormatter(); _bf = new JsonFormatter(); _cf = new JsonFormatter(); } else { _af = new SjsonFormatter(); _bf = new SjsonFormatter(); _cf = new SjsonFormatter(); } // Enforce object creation (you can get a highlighting bug otherwise). IntPtr ah = aTextBox.Handle; IntPtr bh = bTextBox.Handle; IntPtr ch = cTextBox.Handle; // This while loop is a rather ugly thing. // // We use the _line_number Dictionary to store the highest line number where // a particular key path (e.g. items.size.x) have been displayed in any of the // views. When a view wants to display a particular key, it pads the text with // newlines so that the key appears at the maximum line number where it has // appeared before. If the current line number is higher than the previous // maximum, the _line_number Dictionary is updated with the new maximum and // _line_numbers_changed is true. // // By looping here and reformatting the text until _line_number is no longer // changing we ensure that all keys are displayed at the same line in all three // views, so that we can scroll them simultaneously. // // In theory, this could take quite some time for the worst-case scenario and // it would be better to rewrite the code so that we are formatting the three views // simultaneously, padding with newlines as we go along. However, that is rather // hairy to write and this seems to work well for now. do { _diff_lines_set.Clear(); aTextBox.Text = ""; bTextBox.Text = ""; cTextBox.Text = ""; _line_numbers_changed = false; int indent = 0; if (_json) { SameText(aTextBox, "{"); SameText(bTextBox, "{"); SameText(cTextBox, "{"); indent = 1; } DisplayDiff(aTextBox, _af, parent, a, adiff, indent, ""); DisplayDiff(bTextBox, _bf, parent, b, bdiff, indent, ""); DisplayDiff(cTextBox, _cf, parent, c, cdiff, indent, ""); if (_json) { SameText(aTextBox, "\n}"); SameText(bTextBox, "\n}"); SameText(cTextBox, "\n}"); } } while (_line_numbers_changed); _diff_lines_set.Add(0); _diff_lines_set.Add(aTextBox.Lines.Count()); _diff_lines = _diff_lines_set.OrderBy(i => i).ToList(); _current_diff = 0; aTextBox.SelectionStart = 0; aTextBox.SelectionLength = 0; bTextBox.SelectionStart = 0; bTextBox.SelectionLength = 0; cTextBox.SelectionStart = 0; cTextBox.SelectionLength = 0; }
static void Main(string[] args) { // We need to set this to prevent parse errors in JSON/SJSON parsing. Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; // Print usage information if there where no arguments. if (args.Length == 0) { PrintUsage(); } // -hold can be used as a debugging tool, to give a debugger the chance // to attach to the process when it is just starting up. bool escape_hold = false; int i = 0; while (i < args.Length) { if (args[i] == "-json") { _use_json = true; ++i; } else if (args[i] == "-window") { _window = true; ++i; } else if (args[i] == "-diff") { if (i + 2 >= args.Length) { Error("Not enough arguments for -diff"); } Hashtable a = Load(args[i + 1]); Hashtable b = Load(args[i + 2]); HashDiff diff = JsonDiff.Diff(a, b); if (_window) { DiffVisualForm form = new DiffVisualForm(a, b, diff, _use_json); form.ShowDialog(); } else { StringBuilder sb = new StringBuilder(); diff.Write(sb); Show(sb.ToString(), "Diff"); } i += 3; } else if (args[i] == "-merge") { if (i + 4 >= args.Length) { Error("Not enough arguments for -merge"); } Hashtable parent = Load(args[i + 1]); Hashtable theirs = Load(args[i + 2]); Hashtable mine = Load(args[i + 3]); if (_window) { HashDiff theirs_diff, mine_diff, merged_diff; Hashtable result = JsonDiff.MergeDetailed(parent, theirs, mine, out theirs_diff, out mine_diff, out merged_diff); MergeVisualForm form = new MergeVisualForm(parent, theirs, theirs_diff, mine, mine_diff, result, merged_diff, _use_json); form.ShowDialog(); Save(result, args[i + 4]); } else { Hashtable result = JsonDiff.Merge(parent, theirs, mine); Save(result, args[i + 4]); } // Remove source files (should their be an option for this?) File.Delete(args[i + 1]); File.Delete(args[i + 2]); File.Delete(args[i + 3]); i += 5; } else if (args[i] == "-help") { PrintUsage(); ++i; } else if (args[i] == "-hold") { while (!escape_hold) { ; } ++i; } else if (args[i] == "-test") { Test.TestAll(); ++i; } } }
public ChangeObjectOperation(HashDiff diff) { Diff = diff; }
public ChangeIdArrayOperation(HashDiff diff) { Diff = diff; }