mirror of
				https://github.com/redmine/redmine.git
				synced 2025-11-03 20:06:24 +01:00 
			
		
		
		
	
		
			
	
	
		
			196 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
		
		
			
		
	
	
			196 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| 
								 | 
							
								# frozen_string_literal: true
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								=begin
							 | 
						||
| 
								 | 
							
								  = Diff
							 | 
						||
| 
								 | 
							
								  (({diff.rb})) - computes the differences between two arrays or
							 | 
						||
| 
								 | 
							
								  strings. Copyright (C) 2001 Lars Christensen
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  == Synopsis
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      diff = Diff.new(a, b)
							 | 
						||
| 
								 | 
							
								      b = a.patch(diff)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  == Class Diff
							 | 
						||
| 
								 | 
							
								  === Class Methods
							 | 
						||
| 
								 | 
							
								  --- Diff.new(a, b)
							 | 
						||
| 
								 | 
							
								  --- a.diff(b)
							 | 
						||
| 
								 | 
							
								        Creates a Diff object which represent the differences between
							 | 
						||
| 
								 | 
							
								        ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
							 | 
						||
| 
								 | 
							
								        of any objects, strings, or object of any class that include
							 | 
						||
| 
								 | 
							
								        module ((|Diffable|))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  == Module Diffable
							 | 
						||
| 
								 | 
							
								  The module ((|Diffable|)) is intended to be included in any class for
							 | 
						||
| 
								 | 
							
								  which differences are to be computed. Diffable is included into String
							 | 
						||
| 
								 | 
							
								  and Array when (({diff.rb})) is (({require}))'d.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Classes including Diffable should implement (({[]})) to get element at
							 | 
						||
| 
								 | 
							
								  integer indices, (({<<})) to append elements to the object and
							 | 
						||
| 
								 | 
							
								  (({ClassName#new})) should accept 0 arguments to create a new empty
							 | 
						||
| 
								 | 
							
								  object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  === Instance Methods
							 | 
						||
| 
								 | 
							
								  --- Diffable#patch(diff)
							 | 
						||
| 
								 | 
							
								        Applies the differences from ((|diff|)) to the object ((|obj|))
							 | 
						||
| 
								 | 
							
								        and return the result. ((|obj|)) is not changed. ((|obj|)) and
							 | 
						||
| 
								 | 
							
								        can be either an array or a string, but must match the object
							 | 
						||
| 
								 | 
							
								        from which the ((|diff|)) was created.
							 | 
						||
| 
								 | 
							
								=end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module Redmine
							 | 
						||
| 
								 | 
							
								  module StringArrayDiff
							 | 
						||
| 
								 | 
							
								    class Diff
							 | 
						||
| 
								 | 
							
								      VERSION = 0.3
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def Diff.lcs(a, b)
							 | 
						||
| 
								 | 
							
								        astart = 0
							 | 
						||
| 
								 | 
							
								        bstart = 0
							 | 
						||
| 
								 | 
							
								        afinish = a.length-1
							 | 
						||
| 
								 | 
							
								        bfinish = b.length-1
							 | 
						||
| 
								 | 
							
								        mvector = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # First we prune off any common elements at the beginning
							 | 
						||
| 
								 | 
							
								        while (astart <= afinish) && (bstart <= afinish) && (a[astart] == b[bstart])
							 | 
						||
| 
								 | 
							
								          mvector[astart] = bstart
							 | 
						||
| 
								 | 
							
								          astart += 1
							 | 
						||
| 
								 | 
							
								          bstart += 1
							 | 
						||
| 
								 | 
							
								        end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # now the end
							 | 
						||
| 
								 | 
							
								        while (astart <= afinish) && (bstart <= bfinish) && (a[afinish] == b[bfinish])
							 | 
						||
| 
								 | 
							
								          mvector[afinish] = bfinish
							 | 
						||
| 
								 | 
							
								          afinish -= 1
							 | 
						||
| 
								 | 
							
								          bfinish -= 1
							 | 
						||
| 
								 | 
							
								        end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        bmatches = b.reverse_hash(bstart..bfinish)
							 | 
						||
| 
								 | 
							
								        thresh = []
							 | 
						||
| 
								 | 
							
								        links = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        (astart..afinish).each { |aindex|
							 | 
						||
| 
								 | 
							
								          aelem = a[aindex]
							 | 
						||
| 
								 | 
							
								          next unless bmatches.has_key? aelem
							 | 
						||
| 
								 | 
							
								          k = nil
							 | 
						||
| 
								 | 
							
								          bmatches[aelem].reverse_each { |bindex|
							 | 
						||
| 
								 | 
							
								            if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
							 | 
						||
| 
								 | 
							
								              thresh[k] = bindex
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								              k = thresh.replacenextlarger(bindex, k)
							 | 
						||
| 
								 | 
							
								            end
							 | 
						||
| 
								 | 
							
								            links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if !thresh.empty?
							 | 
						||
| 
								 | 
							
								          link = links[thresh.length-1]
							 | 
						||
| 
								 | 
							
								          while link
							 | 
						||
| 
								 | 
							
								            mvector[link[1]] = link[2]
							 | 
						||
| 
								 | 
							
								            link = link[0]
							 | 
						||
| 
								 | 
							
								          end
							 | 
						||
| 
								 | 
							
								        end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return mvector
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def makediff(a, b)
							 | 
						||
| 
								 | 
							
								        mvector = Diff.lcs(a, b)
							 | 
						||
| 
								 | 
							
								        ai = bi = 0
							 | 
						||
| 
								 | 
							
								        while ai < mvector.length
							 | 
						||
| 
								 | 
							
								          bline = mvector[ai]
							 | 
						||
| 
								 | 
							
								          if bline
							 | 
						||
| 
								 | 
							
								            while bi < bline
							 | 
						||
| 
								 | 
							
								              discardb(bi, b[bi])
							 | 
						||
| 
								 | 
							
								              bi += 1
							 | 
						||
| 
								 | 
							
								            end
							 | 
						||
| 
								 | 
							
								            match(ai, bi)
							 | 
						||
| 
								 | 
							
								            bi += 1
							 | 
						||
| 
								 | 
							
								          else
							 | 
						||
| 
								 | 
							
								            discarda(ai, a[ai])
							 | 
						||
| 
								 | 
							
								          end
							 | 
						||
| 
								 | 
							
								          ai += 1
							 | 
						||
| 
								 | 
							
								        end
							 | 
						||
| 
								 | 
							
								        while ai < a.length
							 | 
						||
| 
								 | 
							
								          discarda(ai, a[ai])
							 | 
						||
| 
								 | 
							
								          ai += 1
							 | 
						||
| 
								 | 
							
								        end
							 | 
						||
| 
								 | 
							
								        while bi < b.length
							 | 
						||
| 
								 | 
							
								          discardb(bi, b[bi])
							 | 
						||
| 
								 | 
							
								          bi += 1
							 | 
						||
| 
								 | 
							
								        end
							 | 
						||
| 
								 | 
							
								        match(ai, bi)
							 | 
						||
| 
								 | 
							
								        1
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def compactdiffs
							 | 
						||
| 
								 | 
							
								        diffs = []
							 | 
						||
| 
								 | 
							
								        @diffs.each { |df|
							 | 
						||
| 
								 | 
							
								          i = 0
							 | 
						||
| 
								 | 
							
								          curdiff = []
							 | 
						||
| 
								 | 
							
								          while i < df.length
							 | 
						||
| 
								 | 
							
								            whot = df[i][0]
							 | 
						||
| 
								 | 
							
								            s = @isstring ? df[i][2].chr : [df[i][2]]
							 | 
						||
| 
								 | 
							
								            p = df[i][1]
							 | 
						||
| 
								 | 
							
								            last = df[i][1]
							 | 
						||
| 
								 | 
							
								            i += 1
							 | 
						||
| 
								 | 
							
								            while df[i] && df[i][0] == whot && df[i][1] == last+1
							 | 
						||
| 
								 | 
							
								              s << df[i][2]
							 | 
						||
| 
								 | 
							
								              last  = df[i][1]
							 | 
						||
| 
								 | 
							
								              i += 1
							 | 
						||
| 
								 | 
							
								            end
							 | 
						||
| 
								 | 
							
								            curdiff.push [whot, p, s]
							 | 
						||
| 
								 | 
							
								          end
							 | 
						||
| 
								 | 
							
								          diffs.push curdiff
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return diffs
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      attr_reader :diffs, :difftype
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def initialize(diffs_or_a, b = nil, isstring = nil)
							 | 
						||
| 
								 | 
							
								        if b.nil?
							 | 
						||
| 
								 | 
							
								          @diffs = diffs_or_a
							 | 
						||
| 
								 | 
							
								          @isstring = isstring
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								          @diffs = []
							 | 
						||
| 
								 | 
							
								          @curdiffs = []
							 | 
						||
| 
								 | 
							
								          makediff(diffs_or_a, b)
							 | 
						||
| 
								 | 
							
								          @difftype = diffs_or_a.class
							 | 
						||
| 
								 | 
							
								        end
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def match(ai, bi)
							 | 
						||
| 
								 | 
							
								        @diffs.push @curdiffs unless @curdiffs.empty?
							 | 
						||
| 
								 | 
							
								        @curdiffs = []
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def discarda(i, elem)
							 | 
						||
| 
								 | 
							
								        @curdiffs.push ['-', i, elem]
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def discardb(i, elem)
							 | 
						||
| 
								 | 
							
								        @curdiffs.push ['+', i, elem]
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def compact
							 | 
						||
| 
								 | 
							
								        return Diff.new(compactdiffs)
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def compact!
							 | 
						||
| 
								 | 
							
								        @diffs = compactdiffs
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      def inspect
							 | 
						||
| 
								 | 
							
								        @diffs.inspect
							 | 
						||
| 
								 | 
							
								      end
							 | 
						||
| 
								 | 
							
								    end
							 | 
						||
| 
								 | 
							
								  end
							 | 
						||
| 
								 | 
							
								end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Array
							 | 
						||
| 
								 | 
							
								  include Redmine::StringArrayDiff::Diffable
							 | 
						||
| 
								 | 
							
								end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class String
							 | 
						||
| 
								 | 
							
								  include Redmine::StringArrayDiff::Diffable
							 | 
						||
| 
								 | 
							
								end
							 |