public static int GetLevel(string Source, string Destination) { Differ DiffMaker = new Differ(); SideBySideDiffBuilder SideBySideDiffer = new SideBySideDiffBuilder(DiffMaker); SideBySideDiffModel SideBySideDiffResult = SideBySideDiffer.BuildDiffModel(Source, Destination); return GetLevel(SideBySideDiffResult, Source, Destination); }
public static void Diff(this string expected, string actual, string message = null) { var d = new Differ(); var inlineBuilder = new InlineDiffBuilder(d); var result = inlineBuilder.BuildDiffModel(expected, actual); var hasChanges = result.Lines.Any(l => l.Type != ChangeType.Unchanged); if (!hasChanges) return; var diff = result.Lines.Aggregate(new StringBuilder().AppendLine(message), (sb, line) => { if (line.Type == ChangeType.Inserted) sb.Append("+ "); else if (line.Type == ChangeType.Deleted) sb.Append("- "); else sb.Append(" "); sb.AppendLine(line.Text); return sb; }, sb => sb.ToString()); diff += "\r\n C# approximation of actual: \r\n new "; var approx = Regex.Replace(actual, @"^(?=.*:.*)[^:]+:", (s) => s .Value.Replace("\"", "") .Replace(":", " =") , RegexOptions.Multiline) .Replace(" = {", " = new {") .Replace(" = [", " = new [] {") ; approx = Regex.Replace(approx, @"^\s*\],?.*$", s => s.Value.Replace("]", "}"), RegexOptions.Multiline); diff += approx + ";"; throw new Exception(diff); }
/// <summary> /// measure differential between two files /// </summary> public static DiffCounter Count( string originalFile, string modifiedFile) { var original = File.ReadAllText(@originalFile); var modified = File.ReadAllText(@modifiedFile); var d = new Differ(); var counter = new DiffCounter(); var side = new SideBySideDiffBuilder(d); var result = side.BuildDiffModel(original, modified); // added, modified, and unmodified at modified file foreach (var line in result.NewText.Lines) { if (line.Type == ChangeType.Inserted) { counter.AddedCount++; } if (line.Type == ChangeType.Modified) { counter.ModifiedCount++; } if (line.Type == ChangeType.Unchanged) { counter.EqualCount++; } } // deleted from original file foreach (var line in result.OldText.Lines) { if (line.Type == ChangeType.Deleted) { counter.DeletedCount++; } } return counter; }
public void DisplayDiff(string filename, string fullpath, string author, string old, string updated) { grid.RowDefinitions.Clear(); var rd = new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }; grid.RowDefinitions.Add(rd); // Remove the line number blocks and such. actionsThem is the last item in the XAML int x = grid.Children.IndexOf(message); grid.Children.RemoveRange(x + 1, grid.Children.Count - x); message.Visibility = Visibility.Collapsed; if (filename.EndsWith(".docx") || filename.EndsWith(".doc")) { // Word document. So let the user open the doc in Word message.Visibility = Visibility.Visible; message.Text = "This is a Word document. Please save it to view its contents."; message.Inlines.Add(" You can also "); var fakeUri = new Uri("http://asdf.com"); var link = new Hyperlink(new Run("view the changes")) { NavigateUri = fakeUri }; link.RequestNavigate += (s, e) => CompareInWord(old, updated, filename, Path.GetDirectoryName(fullpath), author); message.Inlines.Add(link); message.Inlines.Add(" in Word."); } else if (SentenceFilter.IsBinary(old) || SentenceFilter.IsBinary(updated)) { message.Visibility = Visibility.Visible; message.Text = "This file is in binary. Please save it to view its contents."; } else { // Compute the diff, break it into blocks if (old == null) old = ""; if (updated == null) updated = ""; var d = new Differ(); var dr = d.CreateLineDiffs(old, updated, false); int curBlock = 0, numBlocks = dr.DiffBlocks.Count; List<LineBlock> lineBlocks = new List<LineBlock>(); for (int i = 0; i <= dr.PiecesOld.Length; i++) { while (curBlock < numBlocks && dr.DiffBlocks[curBlock].DeleteStartA < i) { curBlock++; } if (curBlock < numBlocks && dr.DiffBlocks[curBlock].DeleteStartA == i) { var db = dr.DiffBlocks[curBlock]; if (db.DeleteCountA > 0) { lineBlocks.Add(new LineBlock(Util.ArraySlice(dr.PiecesOld, db.DeleteStartA, db.DeleteCountA), BlockType.ChangeDelete)); } if (db.InsertCountB > 0) { lineBlocks.Add(new LineBlock(Util.ArraySlice(dr.PiecesNew, db.InsertStartB, db.InsertCountB), BlockType.ChangeAdd)); } i += db.DeleteCountA; curBlock++; } if (i < dr.PiecesOld.Length) { lineBlocks.Add(new LineBlock(Util.ArraySlice(dr.PiecesOld, i, 1))); } } // Draw the actual blocks. DrawLineBlocks(lineBlocks); } }
public static string GetFormattedDiff(string oldText, string newText) { var differ = new Differ(); var diff = GetDiff(differ, oldText, newText); return diff.Lines.Any(x => x.Type != ChangeType.Unchanged) ? FormatDiff(diff) : string.Empty; }
/// <summary> /// Make a diff between two strings /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> public static DiffResult Diff(string a, string b) { var differ = new Differ(); //var result = differ.CreateCharacterDiffs(a, b, false, true); var result = differ.CreateWordDiffs(a, b, true, true, new char[]{',',' ','\''}); return result; }
internal static string[] DoSideBySideDiff(string SourceText, string DestinationText) { string[] Result = new string[3]; Differ DiffMaker = new Differ(); SideBySideDiffBuilder SideBySideDiffer = new SideBySideDiffBuilder(DiffMaker); SideBySideDiffModel SideBySideDiffResult = SideBySideDiffer.BuildDiffModel(SourceText, DestinationText); int DiffLevel = IronDiffer.GetLevel(SideBySideDiffResult, SourceText, DestinationText); Result[0] = FullDiff(SideBySideDiffResult.OldText.Lines); Result[1] = FullDiff(SideBySideDiffResult.NewText.Lines); Result[2] = DiffLevel.ToString(); return Result; }
public static void Do(string upc1, string upc2) { Product product1 = DataAccess.GetProduct("794552240051"); Product product2 = DataAccess.GetProduct("794552240358"); Differ differ = new Differ(); ISideBySideDiffBuilder diffBuilder = new SideBySideDiffBuilder2(differ); PrintToConsole(product1, diffBuilder, product2); //PrintToHtml(product1, diffBuilder, product2); }
private void DetectChunkChanges(byte[] previousChunkData, byte[] chunkData, string name, string label, string display) { bool debugChunkFiles = true; DiffType InvestigatePluginPresetFileFormatDiffType = DiffType.Binary; // do a binary comparison between what changed before and after this method was called // if the chunk is not empty, try to detect what has changed if (chunkData != null && chunkData.Length > 0) { int chunkLength = chunkData.Length; // binary comparison to find out where the chunk has changed if (previousChunkData != null && previousChunkData.Length > 0) { if (debugChunkFiles) { BinaryFile.ByteArrayToFile("Preset Chunk Data - previousChunkData.dat", previousChunkData); BinaryFile.ByteArrayToFile("Preset Chunk Data - chunkData.dat", chunkData); } if (InvestigatePluginPresetFileFormatDiffType == DiffType.Binary) { SimpleBinaryDiff.Diff diff = SimpleBinaryDiff.GetDiff(previousChunkData, chunkData); if (diff != null) { System.Diagnostics.Debug.WriteLine("BinDiff: {0}", diff); // store each of the chunk differences in a list foreach (SimpleBinaryDiff.DiffPoint point in diff.Points) { this.InvestigatedPluginPresetFileFormatList.Add( new InvestigatedPluginPresetFileFormat(point.Index, point.NewValue, name, label, display)); } } } else if (InvestigatePluginPresetFileFormatDiffType == DiffType.Text) { // assume we are dealing with text and not binary data var d = new Differ(); string OldText = BinaryFile.ByteArrayToString(previousChunkData); string NewText = BinaryFile.ByteArrayToString(chunkData); DiffResult res = d.CreateWordDiffs(OldText, NewText, true, true, new[] {' ', '\r', '\n'}); //DiffResult res = d.CreateCharacterDiffs(OldText, NewText, true, true); List<UnidiffEntry> diffList = UnidiffSeqFormater.GenerateWithLineNumbers(res); var queryTextDiffs = from dl in diffList where dl.Type == UnidiffType.Insert select dl; foreach (var e in queryTextDiffs) { string text = e.Text; text = text.Replace("\n", ""); if (text != "") { System.Diagnostics.Debug.WriteLine(String.Format("TextDiff: {0} {1}", e.Index, text)); this.InvestigatedPluginPresetFileFormatList.Add( new InvestigatedPluginPresetFileFormat(e.Index, 0, name, label, display, text)); } } } } } }
public async Task<ActionResult> CompareBlobs(Errorable<BlobID.Partial> epida, Errorable<BlobID.Partial> epidb) { if (epida.HasErrors || epidb.HasErrors) return Json(new { errors = (epida.Errors + epidb.Errors).ToJSON() }, JsonRequestBehavior.AllowGet); // Resolve the partial IDs: var eids = await cms.blrepo.ResolvePartialIDs(epida.Value, epidb.Value); if (eids[0].HasErrors || eids[1].HasErrors) return Json(new { errors = (eids[0].Errors + eids[1].Errors).ToJSON() }, JsonRequestBehavior.AllowGet); BlobID idA = eids[0].Value; BlobID idB = eids[1].Value; // Get the Blobs: var ebls = await cms.blrepo.GetBlobs(idA, idB); if (ebls[0].HasErrors || ebls[1].HasErrors) return Json(new { errors = (ebls[0].Errors + ebls[1].Errors).ToJSON() }, JsonRequestBehavior.AllowGet); IStreamedBlob blA = ebls[0].Value, blB = ebls[1].Value; // Stream in both blobs' contents to string values: var etextA = await blA.ReadStreamAsync<string>(async st => { using (var sr = new StreamReader(st, Encoding.UTF8)) return (Errorable<string>)await sr.ReadToEndAsync(); }); if (etextA.HasErrors) return ErrorJson(etextA); var etextB = await blB.ReadStreamAsync<string>(async st => { using (var sr = new StreamReader(st, Encoding.UTF8)) return (Errorable<string>)await sr.ReadToEndAsync(); }); if (etextB.HasErrors) return ErrorJson(etextB); // TODO: update to a better diff engine that supports merging... // Create a diff engine: IDiffer differ = new Differ(); ISideBySideDiffBuilder builder = new SideBySideDiffBuilder(differ); // Run the diff engine to get the output model: // NOTE: I would prefer it to read in via a Stream but perhaps that is not possible given the algorithm implemented. var sxs = builder.BuildDiffModel(etextA.Value, etextB.Value); // Return the result as JSON: return Json(sxs.ToJSON(), JsonRequestBehavior.AllowGet); }
private void ProcessBlockDiff(LineBlock oldLineBlock, LineBlock newLineBlock, bool modifyOld = true) { // Do block-by-block diffs inside LineBlocks. var oldBlocks = new List<Block>(); var newBlocks = new List<Block>(); foreach (var line in oldLineBlock.lines) { oldBlocks.AddRange(line.blocks); } foreach (var line in newLineBlock.lines) { newBlocks.AddRange(line.blocks); } var d = new Differ(); DiffResult diff = d.CreateLineDiffs(String.Join("\n", oldBlocks.Select(x => x.ToString()).ToArray()), String.Join("\n", newBlocks.Select(x => x.ToString()).ToArray()), false); foreach (DiffBlock dblock in diff.DiffBlocks) { if (modifyOld) { for (int i = 0; i < dblock.DeleteCountA && dblock.DeleteStartA + i < oldBlocks.Count; i++) { oldBlocks[i + dblock.DeleteStartA].type = BlockType.ChangeDelete; } } for (int i = 0; i < dblock.InsertCountB && dblock.InsertStartB + i < newBlocks.Count; i++) { newBlocks[i + dblock.InsertStartB].type = BlockType.ChangeAdd; } } }
public static SideBySideDiffModel GetDiff(string Source, string Destination) { Differ DiffMaker = new Differ(); SideBySideDiffBuilder SideBySideDiffer = new SideBySideDiffBuilder(DiffMaker); return SideBySideDiffer.BuildDiffModel(Source, Destination); }
/// <inheritdoc /> public void SetParameterAutomated(int index, float value) { RaisePluginCalled("SetParameterAutomated(" + index + ", " + value + ")"); // This chunk of code handles investigation of a preset format in order to be able to // reverse engineer where the different changes to the parameters are stored within the preset file. string name = PluginContext.PluginCommandStub.GetParameterName(index); string label = PluginContext.PluginCommandStub.GetParameterLabel(index); string display = PluginContext.PluginCommandStub.GetParameterDisplay(index); if ("".Equals(display)) display = "" + value; Debug.WriteLine(string.Format("SetParameterAutomated. Name: {0}, Label: {1}, Value: {2}", name, label, display)); if (DoInvestigatePluginPresetFileFormat) { // read in the preset chunk and // do a binary comparison between what changed before and after this method was called byte[] chunkData = PluginContext.PluginCommandStub.GetChunk(true); // if we are tracking a specific number of bytes from the chunk, store those if (DoTrackPluginPresetFileFormat) { if (TrackPluginPresetFilePosition > -1 && TrackPluginPresetFileNumberOfBytes > 0) { var trackedChunkData = new byte[TrackPluginPresetFileNumberOfBytes]; Array.Copy(chunkData, TrackPluginPresetFilePosition, trackedChunkData, 0, TrackPluginPresetFileNumberOfBytes); TrackPluginPresetFileBytes = trackedChunkData; } } // if the chunk is not empty, try to detect what has changed if (chunkData != null && chunkData.Length > 0) { int chunkLength = chunkData.Length; // TODO: DELETE THIS /* string wavesPluginName = WavesPreset.GetPluginName(chunkData); if (wavesPluginName != null) { switch (wavesPluginName) { case "SSLChannel": WavesSSLChannel sslChannel = new WavesSSLChannel(); sslChannel.ReadChunkData(chunkData); sslChannel.Write("sslchannel-output.txt"); break; case "SSLComp": WavesSSLComp sslComp = new WavesSSLComp(); sslComp.ReadChunkData(chunkData); sslComp.Write("sslcomp-output.txt"); break; case "PuigChild": break; } } */ // binary comparison to find out where the chunk has changed if (previousChunkData != null && previousChunkData.Length > 0) { if (debugChunkFiles) { BinaryFile.ByteArrayToFile("Preset Chunk Data - previousChunkData.dat", previousChunkData); BinaryFile.ByteArrayToFile("Preset Chunk Data - chunkData.dat", chunkData); } if (InvestigatePluginPresetFileFormatDiffType == DiffType.Binary) { SimpleBinaryDiff.Diff diff = SimpleBinaryDiff.GetDiff(previousChunkData, chunkData); if (diff != null) { Debug.WriteLine(string.Format("BinDiff: {0}", diff)); // store each of the chunk differences in a list foreach (SimpleBinaryDiff.DiffPoint point in diff.Points) { this.InvestigatedPluginPresetFileFormatList.Add( new InvestigatedPluginPresetFileFormat(point.Index, point.NewValue, name, label, display)); } } } else if (InvestigatePluginPresetFileFormatDiffType == DiffType.Text) { // assume we are dealing with text and not binary data var d = new Differ(); string OldText = BinaryFile.ByteArrayToString(previousChunkData); string NewText = BinaryFile.ByteArrayToString(chunkData); DiffResult res = d.CreateWordDiffs(OldText, NewText, true, true, new[] {' ', '\r', '\n'}); //DiffResult res = d.CreateCharacterDiffs(OldText, NewText, true, true); List<UnidiffEntry> diffList = UnidiffSeqFormater.GenerateWithLineNumbers(res); var queryTextDiffs = from dl in diffList where dl.Type == UnidiffType.Insert select dl; foreach (var e in queryTextDiffs) { string text = e.Text; text = text.Replace("\n", ""); if (text != "") { System.Diagnostics.Debug.WriteLine(String.Format("TextDiff: {0} {1}", e.Index, text)); this.InvestigatedPluginPresetFileFormatList.Add( new InvestigatedPluginPresetFileFormat(e.Index, 0, name, label, display, text)); } } } } previousChunkData = chunkData; } } }
/// <summary> /// Lê o csv e retorna uma lista com as rodadas /// </summary> /// <param name="instanceFile"></param> /// <param name="biblioteca"></param> /// <param name="dirGa"></param> private static List<RodadaMapper> RecuperarRodadasDoGaNoCsv(FileInfo instanceFile, DirectoryInfo biblioteca, DirectoryInfo dirGa) { if (!File.Exists(biblioteca.GetFiles().First().FullName)) { Console.WriteLine("Deveria existir o arquivo original da biblioteca aqui. | {0}", dirGa.FullName); Environment.Exit(-1); } //Ler as rodadas var rodadas = new List<RodadaMapper>(); using (var csv = new TextFieldParser(instanceFile.FullName)) { csv.ReadLine(); csv.ReadLine(); csv.TextFieldType = FieldType.Delimited; csv.SetDelimiters(","); csv.HasFieldsEnclosedInQuotes = true; //Conta das linhas do original var totalLinhas = ContarLinhas(biblioteca.GetFiles().First().FullName); //Conta Tokens do Original var totalchars = GetNumOfCharsInFile(biblioteca.GetFiles().First().FullName); //Texto do Original var textoOriginal = File.ReadAllText(biblioteca.GetFiles().First().FullName); while (!csv.EndOfData) { string[] currentRow = csv.ReadFields(); string bestFile = Path.GetFileName(currentRow[1]); int totalLinhasBest = 0; int totalcharsBest = 0; string tempoOriginal = "00:00:00,000"; string fitOrignal = "00000"; string tempoFinalComUnload = ""; string fitFinal = ""; DiffPaneModel resultadoComparacao = null; //Tempo e fit originais tempoOriginal = RecuperarTempoMedioeFitOriginal(biblioteca, dirGa, currentRow[0], out fitOrignal); var fileList = dirGa.GetFiles(bestFile, SearchOption.AllDirectories); //Se o arquivo melhorado existe, conta as linhas e os caracteres do mesmo if (fileList.Any()) { totalLinhasBest = ContarLinhas(fileList.First().FullName); totalcharsBest = GetNumOfCharsInFile(fileList.First().FullName); tempoFinalComUnload = currentRow[4]; fitFinal = currentRow[3]; var textoMelhor = File.ReadAllText(fileList.First().FullName); var d = new Differ(); var builder = new InlineDiffBuilder(d); resultadoComparacao = builder.BuildDiffModel(textoOriginal, textoMelhor); } else { //Não houve melhor totalLinhasBest = totalLinhas; totalcharsBest = totalchars; tempoFinalComUnload = tempoOriginal; fitFinal = fitOrignal; } rodadas.Add(new RodadaMapper() { Algoritmo = dirGa.Name, Biblioteca = biblioteca.Name, Rodada = currentRow[0], Individuo = currentRow[1], Operacao = currentRow[2], Fitness = fitOrignal, FitnessFinal = fitFinal, TempoOriginalComUnload = tempoOriginal, TempoFinalComUnload = tempoFinalComUnload, Testes = currentRow[5], LocOriginal = totalLinhas, LocFinal = totalLinhasBest, CaracteresOriginal = totalchars, CaracteresFinal = totalcharsBest, Diferencas = resultadoComparacao }); } } return rodadas; }
private void MakeDiffPangoMarkup() { var d = new Differ (); var differ = new SideBySideFullDiffBuilder(d); var diffRes = differ.BuildDiffModel(OldValue, NewValue); OldPangoText = PangoRender.RenderDiffLines (diffRes.OldText); NewPangoText = PangoRender.RenderDiffLines (diffRes.NewText); isPangoMade = true; }
// 创建展示两个 OPAC 记录差异的 HTML 字符串 // parameters: // strNewText 如果为 "",表示内容全部被删除,依然会输出对照格式;如果为 null,表示只输出左边的部分 // return: // -1 出错 // 0 成功。两边相等 // 1 两边不相等 public static int DiffOpacHtml( string strOldText, string strNewText, out string strHtml, out string strError) { strError = ""; strHtml = ""; if (string.IsNullOrEmpty(strOldText) == true && string.IsNullOrEmpty(strNewText) == true) return 0; var marc_differ = new Differ(); var marc_builder = new SideBySideDiffBuilder(marc_differ); var marc_diff_result = marc_builder.BuildDiffModel(strOldText, strNewText == null? "" : strNewText); Debug.Assert(marc_diff_result.OldText.Lines.Count == marc_diff_result.NewText.Lines.Count, ""); /* public enum ChangeType { Unchanged, Deleted, Inserted, Imaginary, Modified } * */ bool bChanged = false; StringBuilder strResult = new StringBuilder("\r\n<table class='marc'>", 4096); for (int index = 0; index < marc_diff_result.NewText.Lines.Count; index++) { var newline = marc_diff_result.NewText.Lines[index]; var oldline = marc_diff_result.OldText.Lines[index]; if (string.IsNullOrEmpty(newline.Text) == true && string.IsNullOrEmpty(oldline.Text) == true) continue; if (oldline.Type != ChangeType.Unchanged || newline.Type != ChangeType.Unchanged) bChanged = true; string strLineClass = "datafield"; if (strNewText == null) { strResult.Append("\r\n<tr class='" + strLineClass + "'>"); // 创建一个字段的 HTML 局部 三个 <td> strResult.Append(BuildOpacFieldHtml(ChangeType.Unchanged, oldline.Text)); strResult.Append("\r\n</tr>"); continue; } strResult.Append("\r\n<tr class='" + strLineClass + "'>"); // 创建一个字段的 HTML 局部 三个 <td> strResult.Append(BuildOpacFieldHtml(oldline.Type, oldline.Text)); strResult.Append(SEP); strResult.Append(BuildOpacFieldHtml(newline.Type, newline.Text)); strResult.Append("\r\n</tr>"); } strResult.Append("</table>"); strHtml = strResult.ToString(); if (bChanged == false) return 0; return 1; }
static string DiffXml( int nLevel, string strOldFragmentXml, XmlNodeType old_nodetype, string strNewFragmentXml, XmlNodeType new_nodetype) { if (string.IsNullOrEmpty(strOldFragmentXml) == true && string.IsNullOrEmpty(strNewFragmentXml) == true) return ""; if (old_nodetype != XmlNodeType.Element || new_nodetype != XmlNodeType.Element) { if (old_nodetype == XmlNodeType.Element) strOldFragmentXml = GetIndentXml(strOldFragmentXml); if (new_nodetype == XmlNodeType.Element) strNewFragmentXml = GetIndentXml(strNewFragmentXml); return GetPlanTextDiffHtml( nLevel, strOldFragmentXml, strNewFragmentXml); } string strOldChildren = ""; string strOldBegin = ""; string strOldEnd = ""; List<XmlNode> old_childnodes = null; string strOldElementName = ""; GetComparableXmlString(strOldFragmentXml, out strOldChildren, out old_childnodes, out strOldElementName, out strOldBegin, out strOldEnd); string strNewChildren = ""; string strNewBegin = ""; string strNewEnd = ""; List<XmlNode> new_childnodes = null; string strNewElementName = ""; GetComparableXmlString(strNewFragmentXml, out strNewChildren, out new_childnodes, out strNewElementName, out strNewBegin, out strNewEnd); bool bSpecialCompare = false; // 是否属于特殊情况: 根元素名不相同,但仍需要比较下级 if (strOldElementName != strNewElementName && nLevel == 0) bSpecialCompare = true; if (strOldElementName != strNewElementName && bSpecialCompare == false) { // 元素名不一样了并且不是根级别,就没有必要做细节比较了 // 意思是说如果是根级别,即便根元素名不一样,也要比较其下级 if (old_nodetype == XmlNodeType.Element) strOldFragmentXml = GetIndentXml(strOldFragmentXml); if (new_nodetype == XmlNodeType.Element) strNewFragmentXml = GetIndentXml(strNewFragmentXml); return GetPlanTextDiffHtml( nLevel, strOldFragmentXml, strNewFragmentXml); } string strLineClass = "datafield"; StringBuilder strResult = new StringBuilder(4096); if (nLevel > 0 || bSpecialCompare == true) { ChangeType begin_type = ChangeType.Unchanged; if (strOldBegin != strNewBegin) begin_type = ChangeType.Modified; // Begin strResult.Append("\r\n<tr class='" + strLineClass + "'>"); strResult.Append(BuildFragmentFieldHtml(nLevel, begin_type, strOldBegin)); strResult.Append(SEP); strResult.Append(BuildFragmentFieldHtml(nLevel, begin_type, strNewBegin)); strResult.Append("\r\n</tr>"); } // return "\r\n<td class='content' colspan='3'></td>"; if (string.IsNullOrEmpty(strOldChildren) == false || string.IsNullOrEmpty(strNewChildren) == false) { var xml_differ = new Differ(); var xml_builder = new SideBySideDiffBuilder(xml_differ); var xml_diff_result = xml_builder.BuildDiffModel(strOldChildren, strNewChildren); Debug.Assert(xml_diff_result.OldText.Lines.Count == xml_diff_result.NewText.Lines.Count, ""); int old_index = 0; int new_index = 0; for (int index = 0; index < xml_diff_result.NewText.Lines.Count; index++) { var newline = xml_diff_result.NewText.Lines[index]; var oldline = xml_diff_result.OldText.Lines[index]; XmlNode new_node = null; if (newline.Type != ChangeType.Imaginary) new_node = new_childnodes[new_index++]; XmlNode old_node = null; if (oldline.Type != ChangeType.Imaginary) old_node = old_childnodes[old_index++]; if (newline.Type == ChangeType.Modified) { strResult.Append(DiffXml(nLevel + 1, oldline.Text, old_node.NodeType, newline.Text, new_node.NodeType)); continue; } string strOldText = ""; if (old_node != null && old_node.NodeType == XmlNodeType.Element) strOldText = GetIndentXml(oldline.Text); else strOldText = oldline.Text; string strNewText = ""; if (new_node != null && new_node.NodeType == XmlNodeType.Element) strNewText = GetIndentXml(newline.Text); else strNewText = newline.Text; strResult.Append("\r\n<tr class='" + strLineClass + "'>"); // 创建一个 XML 字段的 HTML 局部 三个 <td> strResult.Append(BuildFragmentFieldHtml(nLevel + 1, oldline.Type, strOldText)); strResult.Append(SEP); strResult.Append(BuildFragmentFieldHtml(nLevel + 1, newline.Type, strNewText)); strResult.Append("\r\n</tr>"); } } if (nLevel > 0 || bSpecialCompare == true) { ChangeType end_type = ChangeType.Unchanged; if (strOldEnd != strNewEnd) end_type = ChangeType.Modified; // End strResult.Append("\r\n<tr class='" + strLineClass + "'>"); strResult.Append(BuildFragmentFieldHtml(nLevel, end_type, strOldEnd)); strResult.Append(SEP); strResult.Append(BuildFragmentFieldHtml(nLevel, end_type, strNewEnd)); strResult.Append("\r\n</tr>"); } return strResult.ToString(); }
private void ProcessDiff(string original, string myVersion, string newVersion) { // Do a line-by-line diff, marking changed line groups. // Also, find intersecting diffs and mark them as conflicted changes for resolution. var d = new Differ(); var diffs = new DiffResult[2] { d.CreateLineDiffs(original, myVersion ?? "", false), d.CreateLineDiffs(original, newVersion ?? "", false) }; string[][] newPieces = new string[2][]; newPieces[0] = diffs[0].PiecesNew; newPieces[1] = diffs[1].PiecesNew; if (myVersion == "") newPieces[0] = new string[] { "" }; if (newVersion == "") newPieces[1] = new string[] { "" }; var dblocks = new List<Tuple<DiffBlock, int>>(); for (int side = 0; side < 2; side++) { foreach (var block in diffs[side].DiffBlocks) { DiffBlock actualBlock = block; if (diffs[side].PiecesNew.Length == 0 && block.InsertCountB == 0 && block.InsertStartB == 0) { actualBlock = new DiffBlock(block.DeleteStartA, block.DeleteCountA, 0, 1); } dblocks.Add(new Tuple<DiffBlock, int>(actualBlock, side)); } } dblocks.Sort((a, b) => a.Item1.DeleteStartA.CompareTo(b.Item1.DeleteStartA)); content = new List<LineBlock>[2]; for (int i = 0; i < 2; i++) { content[i] = new List<LineBlock>(); } conflictOrigBlocks = new List<LineBlock>(); string[] origLines = diffs[0].PiecesOld; int nextOriginalLine = 0; for (int i = 0; i < dblocks.Count; ) { DiffBlock block = dblocks[i].Item1; int owner = dblocks[i].Item2; // Add unchanged (original) lines. if (block.DeleteStartA > nextOriginalLine) { foreach (var lineBlocks in content) { lineBlocks.Add(new LineBlock(Util.ArraySlice(origLines, nextOriginalLine, block.DeleteStartA - nextOriginalLine))); } nextOriginalLine = block.DeleteStartA; } int rangeStart = block.DeleteStartA; int rangeEnd = rangeStart + block.DeleteCountA; int j = i; // If this change intersects any other changes, then merge them together to form a block. while (j < dblocks.Count && dblocks[j].Item1.DeleteStartA <= rangeEnd) { rangeEnd = Math.Max(rangeEnd, dblocks[j].Item1.DeleteStartA + dblocks[j].Item1.DeleteCountA); j++; } if (j == i + 1) { // A regular change. var oldBlock = new LineBlock(Util.ArraySlice(diffs[owner].PiecesOld, block.DeleteStartA, block.DeleteCountA), BlockType.ChangeDelete); var newBlock = new LineBlock(Util.ArraySlice(newPieces[owner], block.InsertStartB, block.InsertCountB), BlockType.ChangeAdd); if (block.DeleteCountA != 0 && block.InsertCountB != 0) { oldBlock.type = BlockType.Conflict; newBlock.type = BlockType.Conflict; } else if (block.DeleteCountA == 0) { oldBlock.type = BlockType.Blank; } else if (block.InsertCountB == 0) { newBlock.type = BlockType.Blank; } // can't both be empty! ProcessBlockDiff(oldBlock, newBlock); content[owner].Add(newBlock); content[1 - owner].Add(oldBlock); conflictOrigBlocks.Add(owner == 0 ? newBlock : oldBlock); } else { // Create a change block. for (int side = 0; side < 2; side++) { int curOriginalLine = rangeStart; var conflictBlock = new LineBlock(); var origBlock = new LineBlock(); conflictBlock.type = BlockType.Conflict; for (int k = i; k < j; k++) { DiffBlock subBlock = dblocks[k].Item1; if (dblocks[k].Item2 != side) continue; if (subBlock.DeleteStartA > curOriginalLine) { conflictBlock.AddLines(Util.ArraySlice(origLines, curOriginalLine, subBlock.DeleteStartA - curOriginalLine)); } curOriginalLine = subBlock.DeleteStartA + subBlock.DeleteCountA; var oldBlock = new LineBlock(Util.ArraySlice(diffs[side].PiecesOld, subBlock.DeleteStartA, subBlock.DeleteCountA), BlockType.ChangeDelete); var newBlock = new LineBlock(Util.ArraySlice(newPieces[side], subBlock.InsertStartB, subBlock.InsertCountB), BlockType.ChangeAdd); ProcessBlockDiff(oldBlock, newBlock, false); origBlock.Append(oldBlock); conflictBlock.Append(newBlock); } if (rangeEnd > curOriginalLine) { conflictBlock.AddLines(Util.ArraySlice(origLines, curOriginalLine, rangeEnd - curOriginalLine)); } if (conflictBlock.lines.Count == 0) { conflictBlock.type = BlockType.ChangeDelete; } content[side].Add(conflictBlock); if (side == 0) { conflictOrigBlocks.Add(origBlock); } } int last = content[0].Count - 1; if (content[0][last].ToString() == content[1][last].ToString()) { // Not actually a conflict if they're both the same change. if (content[0][last].lines.Count == 0) { // If both are deleted, just show nothing. content[0].RemoveAt(last); content[1].RemoveAt(last); } else { // Make them normal blocks. content[0][last] = new LineBlock(content[0][last].lines.Select(x => x.ToString()).ToArray()); content[1][last] = new LineBlock(content[1][last].lines.Select(x => x.ToString()).ToArray()); } conflictOrigBlocks.RemoveAt(conflictOrigBlocks.Count - 1); } } nextOriginalLine = rangeEnd; i = j; } // Add any remaining unchanged lines. if (origLines.Length > nextOriginalLine) { foreach (var lineBlocks in content) { lineBlocks.Add(new LineBlock(Util.ArraySlice(origLines, nextOriginalLine, origLines.Length - nextOriginalLine))); } } origBlocks = new LineBlock[2][]; origBlocks[0] = new LineBlock[content[0].Count]; origBlocks[1] = new LineBlock[content[1].Count]; content[0].CopyTo(origBlocks[0]); content[1].CopyTo(origBlocks[1]); }
// 创建展示两个 MARC 记录差异的 HTML 字符串 // return: // -1 出错 // 0 成功 public static int DiffHtml( string strOldTitle, string strOldMarc, string strOldFragmentXml, string strOldImageFragment, string strNewTitle, string strNewMarc, string strNewFragmentXml, string strNewImageFragment, out string strHtml, out string strError) { strError = ""; strHtml = ""; // int nRet = 0; if (string.IsNullOrEmpty(strOldMarc) == true && string.IsNullOrEmpty(strNewMarc) == true) return 0; string strOldHeader = ""; string strNewHeader = ""; string strOldBody = ""; string strNewBody = ""; SplitMarc(strOldMarc, out strOldHeader, out strOldBody); SplitMarc(strNewMarc, out strNewHeader, out strNewBody); if (strOldHeader.Length < 24) strOldHeader = strOldHeader.PadRight(24, '?'); if (strNewHeader.Length < 24) strNewHeader = strNewHeader.PadRight(24, '?'); var marc_differ = new MarcDiffer(); var marc_builder = new MarcDiffBuilder(marc_differ); var marc_diff_result = marc_builder.BuildDiffModel(strOldBody, strNewBody); Debug.Assert(marc_diff_result.OldText.Lines.Count == marc_diff_result.NewText.Lines.Count, ""); /* public enum ChangeType { Unchanged, Deleted, Inserted, Imaginary, Modified } * */ StringBuilder strResult = new StringBuilder("\r\n<table class='marc'>", 4096); if (string.IsNullOrEmpty(strOldTitle) == false || string.IsNullOrEmpty(strNewTitle) == false) { string strLineClass = "header"; strResult.Append("\r\n<tr class='" + strLineClass + "'>"); strResult.Append(BuildHeaderHtml(false, strOldTitle)); strResult.Append(SEP); strResult.Append(BuildHeaderHtml(false, strNewTitle)); strResult.Append("\r\n</tr>"); } { string strLineClass = "header"; bool bModified = strOldHeader != strNewHeader; strResult.Append("\r\n<tr class='" + strLineClass + "'>"); strResult.Append(BuildHeaderHtml(bModified, strOldHeader)); strResult.Append(SEP); strResult.Append(BuildHeaderHtml(bModified, strNewHeader)); strResult.Append("\r\n</tr>"); } if (string.IsNullOrEmpty(strOldImageFragment) == false || string.IsNullOrEmpty(strNewImageFragment) == false) { string strLineClass = "header"; strResult.Append("\r\n<tr class='" + strLineClass + "'>"); strResult.Append(GetImageHtml(strOldImageFragment)); strResult.Append(SEP); strResult.Append(GetImageHtml(strNewImageFragment)); strResult.Append("\r\n</tr>"); } for (int index = 0; index < marc_diff_result.NewText.Lines.Count; index++) { var newline = marc_diff_result.NewText.Lines[index]; var oldline = marc_diff_result.OldText.Lines[index]; if (string.IsNullOrEmpty(newline.Text) == true && string.IsNullOrEmpty(oldline.Text) == true) continue; string strLineClass = "datafield"; strResult.Append("\r\n<tr class='" + strLineClass + "'>"); // 创建一个字段的 HTML 局部 三个 <td> strResult.Append(BuildFieldHtml( oldline.Type, oldline.Text)); strResult.Append(SEP); strResult.Append(BuildFieldHtml(newline.Type, newline.Text)); strResult.Append("\r\n</tr>"); #if NO if (newline.Type == ChangeType.Unchanged) { } else if (newline.Type == ChangeType.Inserted) { } else if (newline.Type == ChangeType.Modified) { } else if (oldline.Type == ChangeType.Deleted) { } #endif } #if NO if (string.IsNullOrEmpty(strOldFragmentXml) == false || string.IsNullOrEmpty(strNewFragmentXml) == false) { var xml_differ = new Differ(); var xml_builder = new SideBySideDiffBuilder(xml_differ); var xml_diff_result = xml_builder.BuildDiffModel(GetComparableXmlString(strOldFragmentXml), GetComparableXmlString(strNewFragmentXml)); Debug.Assert(xml_diff_result.OldText.Lines.Count == xml_diff_result.NewText.Lines.Count, ""); for (int index = 0; index < xml_diff_result.NewText.Lines.Count; index++) { var newline = xml_diff_result.NewText.Lines[index]; var oldline = xml_diff_result.OldText.Lines[index]; string strLineClass = "datafield"; if (newline.Type == ChangeType.Modified) { } strResult.Append("\r\n<tr class='" + strLineClass + "'>"); // 创建一个 XML 字段的 HTML 局部 三个 <td> strResult.Append(BuildFragmentFieldHtml(oldline.Type, oldline.Text)); strResult.Append(SEP); strResult.Append(BuildFragmentFieldHtml(newline.Type, newline.Text)); strResult.Append("\r\n</tr>"); } } #endif if (string.IsNullOrEmpty(strOldFragmentXml) == false || string.IsNullOrEmpty(strNewFragmentXml) == false) { string strLineClass = "sepline"; strResult.Append("\r\n<tr class='" + strLineClass + "'>"); strResult.Append("<td class='sepline' colspan='3'> </td>"); strResult.Append("<td class='cross'> </td>"); strResult.Append("<td class='sepline' colspan='3'> </td>"); strResult.Append("\r\n</tr>"); } strResult.Append(DiffXml( 0, strOldFragmentXml, XmlNodeType.Element, strNewFragmentXml, XmlNodeType.Element)); strResult.Append("</table>"); strHtml = strResult.ToString(); return 0; }