Beispiel #1
0
        /// <summary>
        /// Parse a textual representation of patches and return a List of Patch objects.
        /// </summary>
        /// <param name="textline">Text representation of patches.</param>
        /// <returns>List of Patch objects.</returns>
        /// <exception cref="ArgumentException">If invalid input.</exception>
        public List <Patch> PatchFromText(string textline)
        {
            List <Patch> patches = new List <Patch>();

            if (textline.Length == 0)
            {
                return(patches);
            }
            string[] text        = textline.Split('\n');
            int      textPointer = 0;
            Patch    patch;
            Regex    patchHeader
                = new Regex("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$");
            Match  m;
            char   sign;
            string line;

            while (textPointer < text.Length)
            {
                m = patchHeader.Match(text[textPointer]);
                if (!m.Success)
                {
                    throw new ArgumentException("Invalid patch string: "
                                                + text[textPointer]);
                }
                patch = new Patch();
                patches.Add(patch);
                patch.start1 = Convert.ToInt32(m.Groups[1].Value);
                if (m.Groups[2].Length == 0)
                {
                    patch.start1--;
                    patch.length1 = 1;
                }
                else if (m.Groups[2].Value == "0")
                {
                    patch.length1 = 0;
                }
                else
                {
                    patch.start1--;
                    patch.length1 = Convert.ToInt32(m.Groups[2].Value);
                }

                patch.start2 = Convert.ToInt32(m.Groups[3].Value);
                if (m.Groups[4].Length == 0)
                {
                    patch.start2--;
                    patch.length2 = 1;
                }
                else if (m.Groups[4].Value == "0")
                {
                    patch.length2 = 0;
                }
                else
                {
                    patch.start2--;
                    patch.length2 = Convert.ToInt32(m.Groups[4].Value);
                }
                textPointer++;

                while (textPointer < text.Length)
                {
                    try
                    {
                        sign = text[textPointer][0];
                    }
                    catch (IndexOutOfRangeException)
                    {
                        // Blank line?  Whatever.
                        textPointer++;
                        continue;
                    }
                    line = text[textPointer].Substring(1);
                    line = line.Replace("+", "%2b");
                    line = System.Net.WebUtility.UrlDecode(line);
                    if (sign == '-')
                    {
                        // Deletion.
                        patch.diffs.Add(new Diff(Operation.DELETE, line));
                    }
                    else if (sign == '+')
                    {
                        // Insertion.
                        patch.diffs.Add(new Diff(Operation.INSERT, line));
                    }
                    else if (sign == ' ')
                    {
                        // Minor equality.
                        patch.diffs.Add(new Diff(Operation.EQUAL, line));
                    }
                    else if (sign == '@')
                    {
                        // Start of next patch.
                        break;
                    }
                    else
                    {
                        // WTF?
                        throw new ArgumentException(
                                  "Invalid patch mode '" + sign + "' in: " + line);
                    }
                    textPointer++;
                }
            }
            return(patches);
        }
Beispiel #2
0
        /// <summary>
        /// Add some padding on text start and end so that edges can match something.
        /// </summary>
        /// <remarks>Intended to be called only from within patch_apply.</remarks>
        /// <param name="patches">Array of Patch objects.</param>
        /// <returns>The padding string added to each side.</returns>
        public string PatchAddPadding(List <Patch> patches)
        {
            short  paddingLength = this.Patch_Margin;
            string nullPadding   = string.Empty;

            for (short x = 1; x <= paddingLength; x++)
            {
                nullPadding += (char)x;
            }

            // Bump all the patches forward.
            foreach (Patch aPatch in patches)
            {
                aPatch.start1 += paddingLength;
                aPatch.start2 += paddingLength;
            }

            // Add some padding on start of first diff.
            Patch       patch = patches.First();
            List <Diff> diffs = patch.diffs;

            if (diffs.Count == 0 || diffs.First().operation != Operation.EQUAL)
            {
                // Add nullPadding equality.
                diffs.Insert(0, new Diff(Operation.EQUAL, nullPadding));
                patch.start1  -= paddingLength; // Should be 0.
                patch.start2  -= paddingLength; // Should be 0.
                patch.length1 += paddingLength;
                patch.length2 += paddingLength;
            }
            else if (paddingLength > diffs.First().text.Length)
            {
                // Grow first equality.
                Diff firstDiff   = diffs.First();
                int  extraLength = paddingLength - firstDiff.text.Length;
                firstDiff.text = nullPadding.Substring(firstDiff.text.Length)
                                 + firstDiff.text;
                patch.start1  -= extraLength;
                patch.start2  -= extraLength;
                patch.length1 += extraLength;
                patch.length2 += extraLength;
            }

            // Add some padding on end of last diff.
            patch = patches.Last();
            diffs = patch.diffs;
            if (diffs.Count == 0 || diffs.Last().operation != Operation.EQUAL)
            {
                // Add nullPadding equality.
                diffs.Add(new Diff(Operation.EQUAL, nullPadding));
                patch.length1 += paddingLength;
                patch.length2 += paddingLength;
            }
            else if (paddingLength > diffs.Last().text.Length)
            {
                // Grow last equality.
                Diff lastDiff    = diffs.Last();
                int  extraLength = paddingLength - lastDiff.text.Length;
                lastDiff.text += nullPadding.Substring(0, extraLength);
                patch.length1 += extraLength;
                patch.length2 += extraLength;
            }

            return(nullPadding);
        }
Beispiel #3
0
        /// <summary>
        /// Look through the patches and break up any which are longer than the
        /// maximum limit of the match algorithm.
        /// </summary>
        /// <remarks>Intended to be called only from within patch_apply.</remarks>
        /// <param name="patches">List of Patch objects.</param>
        public void PatchSplitMax(List <Patch> patches)
        {
            short patch_size = Match.Match_MaxBits;

            for (int x = 0; x < patches.Count; x++)
            {
                if (patches[x].length1 <= patch_size)
                {
                    continue;
                }
                Patch bigpatch = patches[x];
                // Remove the big old patch.
                patches.Splice(x--, 1);
                int    start1     = bigpatch.start1;
                int    start2     = bigpatch.start2;
                string precontext = string.Empty;
                while (bigpatch.diffs.Count != 0)
                {
                    // Create one of several smaller patches.
                    Patch patch = new Patch();
                    bool  empty = true;
                    patch.start1 = start1 - precontext.Length;
                    patch.start2 = start2 - precontext.Length;
                    if (precontext.Length != 0)
                    {
                        patch.length1 = patch.length2 = precontext.Length;
                        patch.diffs.Add(new Diff(Operation.EQUAL, precontext));
                    }
                    while (bigpatch.diffs.Count != 0 &&
                           patch.length1 < patch_size - this.Patch_Margin)
                    {
                        Operation diff_type = bigpatch.diffs[0].operation;
                        string    diff_text = bigpatch.diffs[0].text;
                        if (diff_type == Operation.INSERT)
                        {
                            // Insertions are harmless.
                            patch.length2 += diff_text.Length;
                            start2        += diff_text.Length;
                            patch.diffs.Add(bigpatch.diffs.First());
                            bigpatch.diffs.RemoveAt(0);
                            empty = false;
                        }
                        else if (diff_type == Operation.DELETE && patch.diffs.Count == 1 &&
                                 patch.diffs.First().operation == Operation.EQUAL &&
                                 diff_text.Length > 2 * patch_size)
                        {
                            // This is a large deletion.  Let it pass in one chunk.
                            patch.length1 += diff_text.Length;
                            start1        += diff_text.Length;
                            empty          = false;
                            patch.diffs.Add(new Diff(diff_type, diff_text));
                            bigpatch.diffs.RemoveAt(0);
                        }
                        else
                        {
                            // Deletion or equality.  Only take as much as we can stomach.
                            diff_text = diff_text.Substring(0, Math.Min(diff_text.Length,
                                                                        patch_size - patch.length1 - Patch_Margin));
                            patch.length1 += diff_text.Length;
                            start1        += diff_text.Length;
                            if (diff_type == Operation.EQUAL)
                            {
                                patch.length2 += diff_text.Length;
                                start2        += diff_text.Length;
                            }
                            else
                            {
                                empty = false;
                            }
                            patch.diffs.Add(new Diff(diff_type, diff_text));
                            if (diff_text == bigpatch.diffs[0].text)
                            {
                                bigpatch.diffs.RemoveAt(0);
                            }
                            else
                            {
                                bigpatch.diffs[0].text =
                                    bigpatch.diffs[0].text.Substring(diff_text.Length);
                            }
                        }
                    }
                    // Compute the head context for the next patch.
                    precontext = DiffOps.GetText2(patch.diffs);
                    precontext = precontext.Substring(Math.Max(0,
                                                               precontext.Length - this.Patch_Margin));

                    string postcontext = null;
                    // Append the end context for this patch.
                    if (DiffOps.GetText1(bigpatch.diffs).Length > Patch_Margin)
                    {
                        postcontext = DiffOps.GetText1(bigpatch.diffs)
                                      .Substring(0, Patch_Margin);
                    }
                    else
                    {
                        postcontext = DiffOps.GetText1(bigpatch.diffs);
                    }

                    if (postcontext.Length != 0)
                    {
                        patch.length1 += postcontext.Length;
                        patch.length2 += postcontext.Length;
                        if (patch.diffs.Count != 0 &&
                            patch.diffs[patch.diffs.Count - 1].operation
                            == Operation.EQUAL)
                        {
                            patch.diffs[patch.diffs.Count - 1].text += postcontext;
                        }
                        else
                        {
                            patch.diffs.Add(new Diff(Operation.EQUAL, postcontext));
                        }
                    }
                    if (!empty)
                    {
                        patches.Splice(++x, 0, patch);
                    }
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// Compute a list of patches to turn text1 into text2.
        /// text2 is not provided, diffs are the delta between text1 and text2.
        /// </summary>
        /// <param name="text1">Old text.</param>
        /// <param name="diffs">Array of Diff objects for text1 to text2.</param>
        /// <returns>List of Patch objects.</returns>
        public List <Patch> PatchMake(string text1, List <Diff> diffs)
        {
            // Check for null inputs not needed since null can't be passed in C#.
            List <Patch> patches = new List <Patch>();

            if (diffs.Count == 0)
            {
                return(patches);  // Get rid of the null case.
            }
            Patch patch       = new Patch();
            int   char_count1 = 0; // Number of characters into the text1 string.
            int   char_count2 = 0; // Number of characters into the text2 string.
                                   // Start with text1 (prepatch_text) and apply the diffs until we arrive at
                                   // text2 (postpatch_text). We recreate the patches one by one to determine
                                   // context info.
            string prepatch_text  = text1;
            string postpatch_text = text1;

            foreach (Diff aDiff in diffs)
            {
                if (patch.diffs.Count == 0 && aDiff.operation != Operation.EQUAL)
                {
                    // A new patch starts here.
                    patch.start1 = char_count1;
                    patch.start2 = char_count2;
                }

                switch (aDiff.operation)
                {
                case Operation.INSERT:
                    patch.diffs.Add(aDiff);
                    patch.length2 += aDiff.text.Length;
                    postpatch_text = postpatch_text.Insert(char_count2, aDiff.text);
                    break;

                case Operation.DELETE:
                    patch.length1 += aDiff.text.Length;
                    patch.diffs.Add(aDiff);
                    postpatch_text = postpatch_text.Remove(char_count2,
                                                           aDiff.text.Length);
                    break;

                case Operation.EQUAL:
                    if (aDiff.text.Length <= 2 * Patch_Margin &&
                        patch.diffs.Count() != 0 && aDiff != diffs.Last())
                    {
                        // Small equality inside a patch.
                        patch.diffs.Add(aDiff);
                        patch.length1 += aDiff.text.Length;
                        patch.length2 += aDiff.text.Length;
                    }

                    if (aDiff.text.Length >= 2 * Patch_Margin)
                    {
                        // Time for a new patch.
                        if (patch.diffs.Count != 0)
                        {
                            PatchAaddContext(patch, prepatch_text);
                            patches.Add(patch);
                            patch = new Patch();
                            // Unlike Unidiff, our patch lists have a rolling context.
                            // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff
                            // Update prepatch text & pos to reflect the application of the
                            // just completed patch.
                            prepatch_text = postpatch_text;
                            char_count1   = char_count2;
                        }
                    }
                    break;
                }

                // Update the current character count.
                if (aDiff.operation != Operation.INSERT)
                {
                    char_count1 += aDiff.text.Length;
                }
                if (aDiff.operation != Operation.DELETE)
                {
                    char_count2 += aDiff.text.Length;
                }
            }
            // Pick up the leftover patch if not empty.
            if (patch.diffs.Count != 0)
            {
                PatchAaddContext(patch, prepatch_text);
                patches.Add(patch);
            }

            return(patches);
        }