public void changes() { while (true) { if (openDocument != null) { List <Patch> patches; Dictionary <int, List <Patch> > returnedPatches; Object[] temp; while (true) { try { if (hasLock == true) { Thread.Sleep(1000); //Copies the document Textbox // TextRange range = new TextRange(DocumentText.Document.ContentStart, DocumentText.Document.ContentEnd); string myText2 = currentTextboxText; patches = diffMatch.patch_make(diffMatch.diff_main(openDocument.FileContents, myText2)); temp = diffMatch.patch_apply(patches, openDocument.FileContents); openDocument.FileContents = temp[0].ToString(); network.sendDocChanges(openDocument.FileName, patches, myInfo); openDocument.SaveID = network.getLastPatchID(openDocument.FileName) + 1; } else { Thread.Sleep(1000); returnedPatches = network.getDocChanges(openDocument.FileName, myInfo, openDocument.SaveID); if (returnedPatches.Count > 0) { foreach (List <Patch> item in returnedPatches.Values) { temp = diffMatch.patch_apply(item, openDocument.FileContents); openDocument.FileContents = temp[0].ToString(); this.Dispatcher.Invoke(new Action(() => { DocumentText.Document.Blocks.Clear(); DocumentText.AppendText(openDocument.FileContents); })); } openDocument.SaveID = returnedPatches.Last().Key + 1; } } } catch (Exception) { //MessageBoxResult result = System.Windows.MessageBox.Show("Error: Connection to the server was lost. Would you like to reconnect?", "Error", MessageBoxButton.YesNo); //if (result == MessageBoxResult.No) //{ // this.Close(); // Environment.Exit(1); //} } } } } }
private static string ApplyPatches(diff_match_patch googleDiff, string testInput, List <Patch> patches) { object[] results = googleDiff.patch_apply(patches, testInput, out List <Patch> effectivePatches); string patched = (string)results[0]; bool[] applied = (bool[])results[1]; diff_match_patch.PatchResult[] resultCodes = (diff_match_patch.PatchResult[])results[2]; for (int i = 0; i < applied.Length; i++) { if (!applied[i]) { throw new Exception($"failed to apply {effectivePatches[i]}: {resultCodes[i]}"); } switch (resultCodes[i]) { case diff_match_patch.PatchResult.UNKNOWN: throw new Exception($"invalid result code from application of {effectivePatches[i]}"); case diff_match_patch.PatchResult.APPLIED_PERFECT: break; case diff_match_patch.PatchResult.APPLIED_IMPERFECT: Debug.WriteLine("WARNING: patch imperfectly matched input, but was applied"); break; default: throw new Exception( $"patch should not have returned success with result code {resultCodes[i]}"); } } return(patched); }
private bool[] GooglePatchApply(string source, List <Patch> patches, out string patched, out List <Patch> effectivePatches, out diff_match_patch.PatchResult[] resultCodes) { object[] results = _googleDiff.patch_apply(patches, source, out effectivePatches); if (results.Length != 3) { // something horrible has happened with out third party library call throw new Exception( $"patch_apply in GoogleDiffMatchPatch returned incorrect number of results: {results.Length}; broken installation"); } patched = (string)results[0]; bool[] applied = (bool[])results[1]; resultCodes = (diff_match_patch.PatchResult[])results[2]; if (applied.Length != resultCodes.Length) { // something horrible has happened with out third party library call throw new Exception( $"patch_apply in GoogleDiffMatchPatch returned unmatched result arrays; broken installation"); } if (applied.Length != effectivePatches.Count) { // something horrible has happened with out third party library call throw new Exception( $"patch_apply in GoogleDiffMatchPatch returned incorrect number of results {applied.Length} for {effectivePatches.Count} patches; broken installation"); } return(applied); }
private void GeneratePatch(PatchResult f) { var newPath = MakeAbsolutePath(f.DestinationFile.Replace("\\destination\\", "\\patchedSource\\")); var d = Path.GetDirectoryName(newPath); if (!Directory.Exists(d)) { Directory.CreateDirectory(d); } if (f.PatchResultType == PatchResultType.CopyFile) { File.Copy(MakeAbsolutePath(f.DestinationFile), newPath, true); return; } var content = File.ReadAllText(MakeAbsolutePath(f.SourceFile)); var dmp = new diff_match_patch(); var r = dmp.patch_apply(f.PatchList, content); var newContent = r[0]; File.WriteAllText(newPath, newContent.ToString()); }
public void Edit(JObject editData) { var reader = new JsonSerializer(); var jsonPatches = editData["Content"]["Patches"]; var patches = jsonPatches.Select(p=> reader.Deserialize<Patch>(new JTokenReader(p))).ToList(); var patcher = new diff_match_patch(); _content = patcher.patch_apply(patches.ToList(), Content)[0] as string; }
public void ShouldApplyPatch() { var json = "[{\"diffs\":[[0,\"Welcome to Orchard!\"],[1,\"kk\"],[0,\"Welcome to Orchard!\"]],\"start1\":0,\"start2\":0,\"length1\":38,\"length2\":40}]"; var patches = JsonConvert.DeserializeObject <List <PatchModel> >(json); var patchModelToPatchMapper = new PatchModelToPatchMapper(); var patchesTranslated = patches.Select(o => patchModelToPatchMapper.Map(o)).ToList(); var sample = _diffMatchPatch.patch_make("Welcome to Orchard!", "Welcome to Orchard!l"); Object[] results = _diffMatchPatch.patch_apply(sample, "Welcome to Orchard!"); var patchesApplied = _diffMatchPatch.patch_apply(patchesTranslated, "Welcome to Orchard!"); //Assert.That(patchesApplied); }
public static Result Merge(this diff_match_patch dmp, string originalText, string editedText_1, string editedText_2) { var res = new Result(); var patches = dmp.patch_make(originalText, editedText_1); res.rawResult = dmp.patch_apply(patches, editedText_2); res.mergeResult = res.rawResult[0] as string; var patchResults = (res.rawResult[1] as IEnumerable <bool>); res.patches = patches.ToDictionary(patchResults); return(res); }
public string GetPreviousVersion(int versionNumber) { var current = Current; var enumerator = PreviousVersions.GetEnumerator(); for (int i = 0; i < versionNumber; i++) { enumerator.MoveNext(); var patcher = new diff_match_patch(); current = patcher.patch_apply(patcher.patch_fromText(enumerator.Current.Patch), current).Item1; } return current; }
//The patches are applied to the file contents and then written to file. public FailureType saveDocument() { for (int i = SaveID; i <= patches.Last().Key; i++) { //Applied the patches and gets the contents. Object[] temp = diffMatch.patch_apply(this.patches.ElementAt(i).Value, this.fileContents); fileContents = temp[0].ToString(); } //Sets save id so the server and clients know when the last time the server saved was. this.SaveID = this.patches.Last().Key + 1; //Writes the Document to file. writeToFile(); return(FailureType.Good); }
public void Diff() { var t1 = "ahoj jedna"; var t2 = "ahoj jedna dva"; var t3 = "ahoj tri dva"; var dmp = new diff_match_patch(); var patchT2toT1 = dmp.patch_make(t2, t1); var patchT2toT1text = dmp.patch_toText(patchT2toT1);//persist List <string> patches = new List <string>(); patches.Add(patchT2toT1text); var patchT3toT2 = dmp.patch_make(t3, t2); var patchT3toT2text = dmp.patch_toText(patchT3toT2);//persist patches.Add(patchT3toT2text); //restore var altT3ToT2Patch = dmp.patch_fromText(patchT3toT2text); var altT2 = dmp.patch_apply(altT3ToT2Patch, t3)[0].ToString(); Assert.Equal(t2, altT2); var altT2ToT1Patch = dmp.patch_fromText(patchT2toT1text); var altT1 = dmp.patch_apply(altT2ToT1Patch, altT2)[0].ToString(); Assert.Equal(t1, altT1); string restore = (string)t3.Clone(); for (int i = patches.Count - 1; i >= 0; i--) { var restorePatch = dmp.patch_fromText(patches[i]); restore = dmp.patch_apply(restorePatch, restore)[0].ToString(); } Assert.Equal(t1, restore); }
private void ProcessAureliaPatch(PatchResult f, int templateId, string sourcePath) { var dRoot = Path.Combine(ContentRootPath, $"Content\\{templateId}\\destination"); var sRel = $"\\content\\{templateId}\\source\\"; var dRel = $"\\content\\{templateId}\\destination\\"; var fromPath = CLICFileUtils.GetTemplatePath(ContentRootPath, templateId.ToString(), TemplatePathType.Destination) + f.DestinationFile.Replace(dRel, ""); var mappedSourceFile = tfm.MapSourceFile(f.SourceFile); var toPath = Path.Combine(sourcePath, mappedSourceFile.Replace(sRel, ""));//need to map the sourcepath file (which can be of a different name) to the destination (patched) file: Eg/ MyProject1.sln > Site.sln var d = Path.GetDirectoryName(toPath); if (!Directory.Exists(d)) { Directory.CreateDirectory(d); } if (f.PatchResultType == PatchResultType.CopyFile && (!File.Exists(toPath))) { File.Copy(fromPath, toPath, true); Console.WriteLine($"Copying: {toPath}"); return; } if (f.PatchResultType == PatchResultType.Identical) { return; } if (f.PatchList == null) { return; } var content = File.ReadAllText(toPath); var dmp = new diff_match_patch(); var r = dmp.patch_apply(f.PatchList, content); //Apply any text transforms var newContent = r[0]; File.WriteAllText(toPath, newContent.ToString()); Console.WriteLine($"Patched: {toPath}"); }
public string GenerateEditedContent() { if(RevisionsConflict && RevisionChosenForResolution == null) throw new ConflictException(_patchResult); if (RevisionChosenForResolution != null) return RevisionChosenForResolution.GenerateEditedContent(); var dmp = new diff_match_patch { Match_Distance = 200, Match_Threshold = 0.2f }; var patches = dmp.patch_make(PreviousRevisionAppliedTo.GenerateEditedContent(), RevisionsToMerge.ElementAt(0).GenerateEditedContent()); var patchResult = dmp.patch_apply(patches, RevisionsToMerge.ElementAt(1).GenerateEditedContent()); return patchResult[0] as string; }
public bool TryUpdateModel <TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) where TModel : class { if (!prefix.Equals(_prefix)) { return(false); } var currentValue = model.GetType().GetProperty(_elementWithoutPrefix).GetValue(model, null) as string; var diffMatchPatch = new diff_match_patch(); var obj = diffMatchPatch.patch_apply(_patches, currentValue); model.GetType().GetProperty(_elementWithoutPrefix).SetValue(model, obj[0], null); return(true); }
/// <summary> /// Merges the text editor content based on: /// - the content at the time of last sync between Google Docs (save or content download) /// - the current editor content /// - the content that's coming from Google Docs /// </summary> /// <param name="editorLastSync">Editor content at the time of last sync.</param> /// <param name="editorNow">Editor content now.</param> /// <param name="contentComingFromGoogleDocs">New content coming from Google Docs</param> /// <returns>Merged content.</returns> public static string MergeText(string editorLastSync, string editorNow, string contentComingFromGoogleDocs) { var dmp = new diff_match_patch { Match_Distance = 1000, Match_Threshold = 0.5f, Patch_DeleteThreshold = 0.5f }; // let's create patches based on editorContent on last sync and the new text from Google Docs var patches = dmp.patch_make(editorLastSync, contentComingFromGoogleDocs); Debug.WriteLine("SyncContentUpdated > Patches: \t\t\t\t\t" + dmp.patch_toText(patches)); // let's apply those patches to the current editor text var results = dmp.patch_apply(patches, editorNow); // and return results return(results[0].ToString()); }
private static void TestBrokenImperfectMatch() { // this is a test that fails on the master version of this library // and is fixed in my fork (reported to master repo) string referenceInput = "diff matching patching"; string referenceOutput = "diff match patch"; string imperfectInput = "diff matching pthing"; diff_match_patch googleDiff = new diff_match_patch(); List <Diff> diffs = googleDiff.diff_main(referenceInput, referenceOutput); googleDiff.diff_cleanupSemantic(diffs); List <Patch> patches = googleDiff.patch_make(diffs); Debug.WriteLine(googleDiff.patch_toText(patches)); string patched = (string)googleDiff.patch_apply(patches, imperfectInput, out List <Patch> effectivePatches)[0]; Debug.WriteLine(effectivePatches.Count); Debug.WriteLine(patched); Debug.Assert(patched == "diff match pth"); }
private void CheckForConflicts() { var dmp = new diff_match_patch {Match_Distance = 200, Match_Threshold = 0.2f}; var patches = dmp.patch_make(RevisionsToMerge[0].PreviousRevisionAppliedTo.GenerateEditedContent(), RevisionsToMerge[0].GenerateEditedContent()); _patchResult = dmp.patch_apply(patches, RevisionsToMerge.ElementAt(1).GenerateEditedContent()); var patchSucessIndicators = (bool[])_patchResult[1]; if (patchSucessIndicators.Any(sucess => !sucess)) RevisionsConflict = true; }
/// <summary> /// Updates the application. /// </summary> public void Update(Action <UpdateStatus, object> callback) { callback(UpdateStatus.Starting, null); diff_match_patch dmf = new diff_match_patch(); WebClient client = new WebClient(); callback(UpdateStatus.RetrievingFileList, null); string state = client.DownloadString(this.m_UpdateUri + "/initial"); string[] lines = state.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); callback(UpdateStatus.RetrievedFileList, lines.Length); int opers = 0; foreach (string line in lines) { Hash hash = Hash.FromString(line.Split(new char[] { ' ' }, 2)[0]); string file = line.Split(new char[] { ' ' }, 2)[1]; callback(UpdateStatus.DeterminingOperation, file); // See if file already exists. if (File.Exists(Path.Combine(this.m_LocalPath, file)) || hash == Hash.Empty) { if (hash == Hash.Empty) { // Delete the local file if it exists. if (File.Exists(Path.Combine(this.m_LocalPath, file))) { callback(UpdateStatus.DeletionStart, file); File.Delete(Path.Combine(this.m_LocalPath, file)); callback(UpdateStatus.DeletionFinish, file); opers++; } } else { // Compare hashes, ignore if same. Hash current = Hash.FromFile(Path.Combine(this.m_LocalPath, file)); if (current == hash) { continue; } // Download patch. string patchstream; try { patchstream = client.DownloadString(this.m_UpdateUri + "/patch/" + current + "/" + hash + "/" + file); callback(UpdateStatus.PatchStart, file); int a = 1; using (StringReader reader = new StringReader(patchstream)) { while (true) { string header = reader.ReadLine(); if (header == "--- END OF PATCHES ---") { break; } else if (header.StartsWith("--- NEXT PATCH (")) { int count = Convert.ToInt32(header.Substring("--- NEXT PATCH (".Length, header.Length - "--- NEXT PATCH () ---".Length)); char[] data = new char[count]; reader.ReadBlock(data, 0, count); List <Patch> patches = dmf.patch_fromText(new string(data)); string newText; using (StreamReader stream = new StreamReader(Path.Combine(this.m_LocalPath, file))) { newText = (string)dmf.patch_apply(patches, stream.ReadToEnd())[0]; } using (StreamWriter stream = new StreamWriter(Path.Combine(this.m_LocalPath, file))) { stream.Write(newText); } callback(UpdateStatus.PatchApplied, a); a++; // Read the empty terminator line. reader.ReadLine(); } else { throw new DataMisalignedException(); } } } callback(UpdateStatus.PatchFinish, file); opers++; } catch (WebException) { // There's no patch list for this file, redownload. string[] coms = file.Split('/'); DirectoryInfo di = new DirectoryInfo(this.m_LocalPath); for (int i = 0; i < coms.Length - 1; i++) { DirectoryInfo[] dis = di.GetDirectories(coms[i]); if (dis.Length == 0) { di = di.CreateSubdirectory(coms[i]); } else { di = dis[0]; } } callback(UpdateStatus.DownloadFreshStart, file); client.DownloadFile(this.m_UpdateUri + "/file/" + file, Path.Combine(this.m_LocalPath, file)); callback(UpdateStatus.DownloadFreshFinish, file); opers++; } } } else { // File does not exist, download fresh. string[] coms = file.Split('/'); DirectoryInfo di = new DirectoryInfo(this.m_LocalPath); for (int i = 0; i < coms.Length - 1; i++) { DirectoryInfo[] dis = di.GetDirectories(coms[i]); if (dis.Length == 0) { di = di.CreateSubdirectory(coms[i]); } else { di = dis[0]; } } callback(UpdateStatus.DownloadNewStart, file); client.DownloadFile(this.m_UpdateUri + "/file/" + file, Path.Combine(this.m_LocalPath, file)); callback(UpdateStatus.DownloadNewFinish, file); opers++; } } callback(UpdateStatus.Complete, opers); return; }
/// <summary> /// Merges the text editor content based on: /// - the content at the time of last sync between Google Docs (save or content download) /// - the current editor content /// - the content that's coming from Google Docs /// </summary> /// <param name="editorLastSync">Editor content at the time of last sync.</param> /// <param name="editorNow">Editor content now.</param> /// <param name="contentComingFromGoogleDocs">New content coming from Google Docs</param> /// <returns>Merged content.</returns> public static string MergeText(string editorLastSync, string editorNow, string contentComingFromGoogleDocs) { var dmp = new diff_match_patch { Match_Distance = 1000, Match_Threshold = 0.5f, Patch_DeleteThreshold = 0.5f }; // let's create patches based on editorContent on last sync and the new text from Google Docs var patches = dmp.patch_make(editorLastSync, contentComingFromGoogleDocs); Debug.WriteLine("SyncContentUpdated > Patches: \t\t\t\t\t" + dmp.patch_toText(patches)); // let's apply those patches to the current editor text var results = dmp.patch_apply(patches, editorNow); // and return results return results[0].ToString(); }
private void CreatePatchForUpdate(Document document, UpdateDto updateDto) { var currentRevision = document.GetCurrentRevision(); var lastUpdate = currentRevision.UpdateDto; //non existing revision - used as null object var secondLastUpdate = new UpdateDto { NewRevisionId = -1 }; if (document.CurrentRevisionId > FIRST_VALID_REVISON_ID) { secondLastUpdate = document.GetRevision(document.CurrentRevisionId - 1).UpdateDto; } bool creationSucessfull = false; //update is either based on current version //or on previous version where // - the member which initialised the previous version was the owner itself // - or a member with a lower member name (and thus the given update will be applied afterwards) //) if (IsFirstPreviousOfSecond(lastUpdate, updateDto) || (IsFirstPreviousOfSecond(secondLastUpdate, updateDto) && MemberOfFirstUpdateIsOwnerOrLowerMember(lastUpdate, updateDto) ) ) { var result = _diffMatchPatch.patch_apply(updateDto.Patch, document.Content); if (result.Item2.All(x => x)) { document.Content = result.Item1; creationSucessfull = true; } else { HandleErrorOnUpdate(updateDto); } } else { var revision = document.GetRevision(updateDto.PreviousRevisionId); if (revision.Id + SUPPORTED_NUM_OF_REACTIVE_UPDATES >= currentRevision.Id) { var nextRevision = document.GetRevision(revision.Id + 1); //move to next revision as long as while ( nextRevision.UpdateDto.PreviousHash.SequenceEqual(updateDto.PreviousHash) && MemberOfFirstUpdateIsNotOwnerAndHigherMember(updateDto, nextRevision.UpdateDto) && nextRevision.Id < currentRevision.Id) { revision = nextRevision; nextRevision = document.GetRevision(nextRevision.Id + 1); } var content = revision.Content; var tmpRevision = revision; var patch = updateDto.Patch; //apply all patches on top of the found revision while (tmpRevision.Id <= currentRevision.Id) { var result = _diffMatchPatch.patch_apply(patch, content); if (result.Item2.All(x => x)) { content = result.Item1; if (tmpRevision.Id == currentRevision.Id) { break; } tmpRevision = document.GetRevision(tmpRevision.Id + 1); patch = tmpRevision.UpdateDto.Patch; } else { HandleErrorOnUpdate(updateDto); } } document.Content = content; updateDto.Patch = _diffMatchPatch.patch_make(currentRevision.Content, content); creationSucessfull = true; } } if (creationSucessfull) { document.CurrentRevisionId = currentRevision.Id + 1; document.CurrentHash = GetHash(document.Content); updateDto.NewRevisionId = document.CurrentRevisionId; updateDto.NewHash = document.CurrentHash; document.AddRevision(new Revision { Id = document.CurrentRevisionId, Content = document.Content, UpdateDto = updateDto }); if (IsNotOwnUpdate(updateDto)) { _editor.UpdateText(updateDto.DocumentId, document.Content); var acknowledgeDto = new AcknowledgeDto { PreviousRevisionId = updateDto.PreviousRevisionId, PreviousHash = updateDto.PreviousHash, NewRevisionId = updateDto.NewRevisionId, NewHash = updateDto.NewHash, DocumentId = document.Id }; _communication.AckRequest(updateDto.MemberHost, acknowledgeDto); } var newUpdateDto = new UpdateDto { DocumentId = document.Id, MemberName = updateDto.MemberName, MemberHost = _serverHost, PreviousRevisionId = updateDto.PreviousRevisionId, PreviousHash = updateDto.PreviousHash, NewRevisionId = updateDto.NewRevisionId, NewHash = updateDto.NewHash, Patch = updateDto.Patch, EditorCount = document.EditorCount }; foreach (var editorHost in document.Editors().Values) { if (updateDto.MemberHost != editorHost) { try { _communication.UpdateRequest(editorHost, newUpdateDto); } catch (EndpointNotFoundException) { document.Editors().Remove(editorHost); } } } } else if (IsNotOwnUpdate(updateDto)) { HandleErrorOnUpdate(updateDto); } }
public string Patch(string sourceConfigPath, string destConfigPath) { string patchedFile = null; if (!(File.Exists(sourceConfigPath) && File.Exists(destConfigPath))) { Console.WriteLine("Config Patch error - could not find source or destination files"); return(patchedFile); } string sourceData = File.ReadAllText(sourceConfigPath); string destData = File.ReadAllText(destConfigPath); List <string> luaConfigs = new List <string>(); int openBraces = 0; foreach (string l in File.ReadAllLines(destConfigPath)) { if (l.Contains('{')) { ++openBraces; } if (l.Contains('}')) { --openBraces; } if (l.Contains('=') && openBraces == 0) { string def = l.Substring(0, l.IndexOf('=')); def = def.Trim().Trim('\t').Trim('\r').Trim('\n'); luaConfigs.Add(def); } } diff_match_patch dmp = new diff_match_patch(); List <Diff> diff = dmp.diff_main(sourceData, destData); dmp.diff_cleanupSemantic(diff); List <Patch> patches = dmp.patch_make(sourceData, diff); List <Patch> filteredPatches = new List <Patch>(); foreach (Patch p in patches) { bool foundPatch = false; foreach (Diff d in p.diffs) { if (d.operation == Operation.INSERT || d.operation == Operation.DELETE) { foreach (string f in luaConfigs) { if (d.text.IndexOf(f) >= 0) { filteredPatches.Add(p); foundPatch = true; break; } } } if (foundPatch) { break; } } } Console.WriteLine("Applying the following patches: "); foreach (Patch p in filteredPatches) { Console.WriteLine(p); } object[] v = dmp.patch_apply(filteredPatches, sourceData); return(Convert.ToString(v[0])); }
/// <summary> /// Unpatch a JSON object /// </summary> /// <param name="right">Patched JSON object</param> /// <param name="patch">JSON Patch Document</param> /// <returns>Unpatched JSON object</returns> /// <exception cref="System.IO.InvalidDataException">Thrown if the patch document is invalid</exception> public JToken Unpatch(JToken right, JToken patch) { if (patch == null) { return(right); } if (patch.Type == JTokenType.Object) { var patchObj = (JObject)patch; JProperty arrayDiffCanary = patchObj.Property("_t"); if (right != null && right.Type == JTokenType.Array && arrayDiffCanary != null && arrayDiffCanary.Value.Type == JTokenType.String && arrayDiffCanary.Value.ToObject <string>() == "a") { return(ArrayUnpatch((JArray)right, patchObj)); } return(ObjectUnpatch(right as JObject, patchObj)); } if (patch.Type == JTokenType.Array) { var patchArray = (JArray)patch; if (patchArray.Count == 1) // Add (we need to remove the property) { return(null); } if (patchArray.Count == 2) // Replace { return(patchArray[0]); } if (patchArray.Count == 3) // Delete, Move or TextDiff { if (patchArray[2].Type != JTokenType.Integer) { throw new InvalidDataException("Invalid patch object"); } int op = patchArray[2].Value <int>(); if (op == 0) { return(patchArray[0]); } if (op == 2) { if (right.Type != JTokenType.String) { throw new InvalidDataException("Invalid patch object"); } var dmp = new diff_match_patch(); List <Patch> patches = dmp.patch_fromText(patchArray[0].ToObject <string>()); if (!patches.Any()) { throw new InvalidDataException("Invalid textline"); } var unpatches = new List <Patch>(); for (int i = patches.Count - 1; i >= 0; --i) { Patch p = patches[i]; var u = new Patch { length1 = p.length1, length2 = p.length2, start1 = p.start1, start2 = p.start2 }; foreach (Diff d in p.diffs) { if (d.operation == Operation.DELETE) { u.diffs.Add(new Diff(Operation.INSERT, d.text)); } else if (d.operation == Operation.INSERT) { u.diffs.Add(new Diff(Operation.DELETE, d.text)); } else { u.diffs.Add(d); } } unpatches.Add(u); } object[] result = dmp.patch_apply(unpatches, right.Value <string>()); var unpatchResults = (bool[])result[1]; if (unpatchResults.Any(x => !x)) { throw new InvalidDataException("Text patch failed"); } string left = (string)result[0]; return(left); } throw new InvalidDataException("Invalid patch object"); } throw new InvalidDataException("Invalid patch object"); } return(null); }
/// <summary> /// Patch a JSON object /// </summary> /// <param name="left">Unpatched JSON object</param> /// <param name="patch">JSON Patch Document</param> /// <returns>Patched JSON object</returns> /// <exception cref="System.IO.InvalidDataException">Thrown if the patch document is invalid</exception> public JToken Patch(JToken left, JToken patch) { if (patch == null) { return(left); } if (patch.Type == JTokenType.Object) { var patchObj = (JObject)patch; JProperty arrayDiffCanary = patchObj.Property("_t"); if (left != null && left.Type == JTokenType.Array && arrayDiffCanary != null && arrayDiffCanary.Value.Type == JTokenType.String && arrayDiffCanary.Value.ToObject <string>() == "a") { return(ArrayPatch((JArray)left, patchObj)); } return(ObjectPatch(left as JObject, patchObj)); } if (patch.Type == JTokenType.Array) { var patchArray = (JArray)patch; if (patchArray.Count == 1) // Add { return(patchArray[0]); } if (patchArray.Count == 2) // Replace { return(patchArray[1]); } if (patchArray.Count == 3) // Delete, Move or TextDiff { if (patchArray[2].Type != JTokenType.Integer) { throw new InvalidDataException("Invalid patch object"); } int op = patchArray[2].Value <int>(); if (op == 0) { return(null); } if (op == 2) { if (left.Type != JTokenType.String) { throw new InvalidDataException("Invalid patch object"); } var dmp = new diff_match_patch(); List <Patch> patches = dmp.patch_fromText(patchArray[0].ToObject <string>()); if (!patches.Any()) { throw new InvalidDataException("Invalid textline"); } object[] result = dmp.patch_apply(patches, left.Value <string>()); var patchResults = (bool[])result[1]; if (patchResults.Any(x => !x)) { throw new InvalidDataException("Text patch failed"); } string right = (string)result[0]; return(right); } throw new InvalidDataException("Invalid patch object"); } throw new InvalidDataException("Invalid patch object"); } return(null); }
/// <summary> /// Updates the application. /// </summary> public void Update(Action<UpdateStatus, object> callback) { callback(UpdateStatus.Starting, null); diff_match_patch dmf = new diff_match_patch(); WebClient client = new WebClient(); callback(UpdateStatus.RetrievingFileList, null); string state = client.DownloadString(this.m_UpdateUri + "/initial"); string[] lines = state.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); callback(UpdateStatus.RetrievedFileList, lines.Length); int opers = 0; foreach (string line in lines) { Hash hash = Hash.FromString(line.Split(new char[] { ' ' }, 2)[0]); string file = line.Split(new char[] { ' ' }, 2)[1]; callback(UpdateStatus.DeterminingOperation, file); // See if file already exists. if (File.Exists(Path.Combine(this.m_LocalPath, file)) || hash == Hash.Empty) { if (hash == Hash.Empty) { // Delete the local file if it exists. if (File.Exists(Path.Combine(this.m_LocalPath, file))) { callback(UpdateStatus.DeletionStart, file); File.Delete(Path.Combine(this.m_LocalPath, file)); callback(UpdateStatus.DeletionFinish, file); opers++; } } else { // Compare hashes, ignore if same. Hash current = Hash.FromFile(Path.Combine(this.m_LocalPath, file)); if (current == hash) continue; // Download patch. string patchstream; try { patchstream = client.DownloadString(this.m_UpdateUri + "/patch/" + current + "/" + hash + "/" + file); callback(UpdateStatus.PatchStart, file); int a = 1; using (StringReader reader = new StringReader(patchstream)) { while (true) { string header = reader.ReadLine(); if (header == "--- END OF PATCHES ---") break; else if (header.StartsWith("--- NEXT PATCH (")) { int count = Convert.ToInt32(header.Substring("--- NEXT PATCH (".Length, header.Length - "--- NEXT PATCH () ---".Length)); char[] data = new char[count]; reader.ReadBlock(data, 0, count); List<Patch> patches = dmf.patch_fromText(new string(data)); string newText; using (StreamReader stream = new StreamReader(Path.Combine(this.m_LocalPath, file))) { newText = (string)dmf.patch_apply(patches, stream.ReadToEnd())[0]; } using (StreamWriter stream = new StreamWriter(Path.Combine(this.m_LocalPath, file))) { stream.Write(newText); } callback(UpdateStatus.PatchApplied, a); a++; // Read the empty terminator line. reader.ReadLine(); } else throw new DataMisalignedException(); } } callback(UpdateStatus.PatchFinish, file); opers++; } catch (WebException) { // There's no patch list for this file, redownload. string[] coms = file.Split('/'); DirectoryInfo di = new DirectoryInfo(this.m_LocalPath); for (int i = 0; i < coms.Length - 1; i++) { DirectoryInfo[] dis = di.GetDirectories(coms[i]); if (dis.Length == 0) di = di.CreateSubdirectory(coms[i]); else di = dis[0]; } callback(UpdateStatus.DownloadFreshStart, file); client.DownloadFile(this.m_UpdateUri + "/file/" + file, Path.Combine(this.m_LocalPath, file)); callback(UpdateStatus.DownloadFreshFinish, file); opers++; } } } else { // File does not exist, download fresh. string[] coms = file.Split('/'); DirectoryInfo di = new DirectoryInfo(this.m_LocalPath); for (int i = 0; i < coms.Length - 1; i++) { DirectoryInfo[] dis = di.GetDirectories(coms[i]); if (dis.Length == 0) di = di.CreateSubdirectory(coms[i]); else di = dis[0]; } callback(UpdateStatus.DownloadNewStart, file); client.DownloadFile(this.m_UpdateUri + "/file/" + file, Path.Combine(this.m_LocalPath, file)); callback(UpdateStatus.DownloadNewFinish, file); opers++; } } callback(UpdateStatus.Complete, opers); return; }