annotate .svn/pristine/68/689ac1bb7ff5cc9966ab0877e8f6f03c5f72ee2b.svn-base @ 1519:afce8026aaeb redmine-2.4-integration

Merge from branch "live"
author Chris Cannam
date Tue, 09 Sep 2014 09:34:53 +0100
parents cbb26bc654de
children
rev   line source
Chris@909 1 module RedmineDiff
Chris@909 2 class Diff
Chris@909 3
Chris@909 4 VERSION = 0.3
Chris@909 5
Chris@909 6 def Diff.lcs(a, b)
Chris@909 7 astart = 0
Chris@909 8 bstart = 0
Chris@909 9 afinish = a.length-1
Chris@909 10 bfinish = b.length-1
Chris@909 11 mvector = []
Chris@909 12
Chris@909 13 # First we prune off any common elements at the beginning
Chris@909 14 while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
Chris@909 15 mvector[astart] = bstart
Chris@909 16 astart += 1
Chris@909 17 bstart += 1
Chris@909 18 end
Chris@909 19
Chris@909 20 # now the end
Chris@909 21 while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
Chris@909 22 mvector[afinish] = bfinish
Chris@909 23 afinish -= 1
Chris@909 24 bfinish -= 1
Chris@909 25 end
Chris@909 26
Chris@909 27 bmatches = b.reverse_hash(bstart..bfinish)
Chris@909 28 thresh = []
Chris@909 29 links = []
Chris@909 30
Chris@909 31 (astart..afinish).each { |aindex|
Chris@909 32 aelem = a[aindex]
Chris@909 33 next unless bmatches.has_key? aelem
Chris@909 34 k = nil
Chris@909 35 bmatches[aelem].reverse.each { |bindex|
Chris@909 36 if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
Chris@909 37 thresh[k] = bindex
Chris@909 38 else
Chris@909 39 k = thresh.replacenextlarger(bindex, k)
Chris@909 40 end
Chris@909 41 links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
Chris@909 42 }
Chris@909 43 }
Chris@909 44
Chris@909 45 if !thresh.empty?
Chris@909 46 link = links[thresh.length-1]
Chris@909 47 while link
Chris@909 48 mvector[link[1]] = link[2]
Chris@909 49 link = link[0]
Chris@909 50 end
Chris@909 51 end
Chris@909 52
Chris@909 53 return mvector
Chris@909 54 end
Chris@909 55
Chris@909 56 def makediff(a, b)
Chris@909 57 mvector = Diff.lcs(a, b)
Chris@909 58 ai = bi = 0
Chris@909 59 while ai < mvector.length
Chris@909 60 bline = mvector[ai]
Chris@909 61 if bline
Chris@909 62 while bi < bline
Chris@909 63 discardb(bi, b[bi])
Chris@909 64 bi += 1
Chris@909 65 end
Chris@909 66 match(ai, bi)
Chris@909 67 bi += 1
Chris@909 68 else
Chris@909 69 discarda(ai, a[ai])
Chris@909 70 end
Chris@909 71 ai += 1
Chris@909 72 end
Chris@909 73 while ai < a.length
Chris@909 74 discarda(ai, a[ai])
Chris@909 75 ai += 1
Chris@909 76 end
Chris@909 77 while bi < b.length
Chris@909 78 discardb(bi, b[bi])
Chris@909 79 bi += 1
Chris@909 80 end
Chris@909 81 match(ai, bi)
Chris@909 82 1
Chris@909 83 end
Chris@909 84
Chris@909 85 def compactdiffs
Chris@909 86 diffs = []
Chris@909 87 @diffs.each { |df|
Chris@909 88 i = 0
Chris@909 89 curdiff = []
Chris@909 90 while i < df.length
Chris@909 91 whot = df[i][0]
Chris@909 92 s = @isstring ? df[i][2].chr : [df[i][2]]
Chris@909 93 p = df[i][1]
Chris@909 94 last = df[i][1]
Chris@909 95 i += 1
Chris@909 96 while df[i] && df[i][0] == whot && df[i][1] == last+1
Chris@909 97 s << df[i][2]
Chris@909 98 last = df[i][1]
Chris@909 99 i += 1
Chris@909 100 end
Chris@909 101 curdiff.push [whot, p, s]
Chris@909 102 end
Chris@909 103 diffs.push curdiff
Chris@909 104 }
Chris@909 105 return diffs
Chris@909 106 end
Chris@909 107
Chris@909 108 attr_reader :diffs, :difftype
Chris@909 109
Chris@909 110 def initialize(diffs_or_a, b = nil, isstring = nil)
Chris@909 111 if b.nil?
Chris@909 112 @diffs = diffs_or_a
Chris@909 113 @isstring = isstring
Chris@909 114 else
Chris@909 115 @diffs = []
Chris@909 116 @curdiffs = []
Chris@909 117 makediff(diffs_or_a, b)
Chris@909 118 @difftype = diffs_or_a.class
Chris@909 119 end
Chris@909 120 end
Chris@909 121
Chris@909 122 def match(ai, bi)
Chris@909 123 @diffs.push @curdiffs unless @curdiffs.empty?
Chris@909 124 @curdiffs = []
Chris@909 125 end
Chris@909 126
Chris@909 127 def discarda(i, elem)
Chris@909 128 @curdiffs.push ['-', i, elem]
Chris@909 129 end
Chris@909 130
Chris@909 131 def discardb(i, elem)
Chris@909 132 @curdiffs.push ['+', i, elem]
Chris@909 133 end
Chris@909 134
Chris@909 135 def compact
Chris@909 136 return Diff.new(compactdiffs)
Chris@909 137 end
Chris@909 138
Chris@909 139 def compact!
Chris@909 140 @diffs = compactdiffs
Chris@909 141 end
Chris@909 142
Chris@909 143 def inspect
Chris@909 144 @diffs.inspect
Chris@909 145 end
Chris@909 146
Chris@909 147 end
Chris@909 148 end
Chris@909 149
Chris@909 150 module Diffable
Chris@909 151 def diff(b)
Chris@909 152 RedmineDiff::Diff.new(self, b)
Chris@909 153 end
Chris@909 154
Chris@909 155 # Create a hash that maps elements of the array to arrays of indices
Chris@909 156 # where the elements are found.
Chris@909 157
Chris@909 158 def reverse_hash(range = (0...self.length))
Chris@909 159 revmap = {}
Chris@909 160 range.each { |i|
Chris@909 161 elem = self[i]
Chris@909 162 if revmap.has_key? elem
Chris@909 163 revmap[elem].push i
Chris@909 164 else
Chris@909 165 revmap[elem] = [i]
Chris@909 166 end
Chris@909 167 }
Chris@909 168 return revmap
Chris@909 169 end
Chris@909 170
Chris@909 171 def replacenextlarger(value, high = nil)
Chris@909 172 high ||= self.length
Chris@909 173 if self.empty? || value > self[-1]
Chris@909 174 push value
Chris@909 175 return high
Chris@909 176 end
Chris@909 177 # binary search for replacement point
Chris@909 178 low = 0
Chris@909 179 while low < high
Chris@909 180 index = (high+low)/2
Chris@909 181 found = self[index]
Chris@909 182 return nil if value == found
Chris@909 183 if value > found
Chris@909 184 low = index + 1
Chris@909 185 else
Chris@909 186 high = index
Chris@909 187 end
Chris@909 188 end
Chris@909 189
Chris@909 190 self[low] = value
Chris@909 191 # $stderr << "replace #{value} : 0/#{low}/#{init_high} (#{steps} steps) (#{init_high-low} off )\n"
Chris@909 192 # $stderr.puts self.inspect
Chris@909 193 #gets
Chris@909 194 #p length - low
Chris@909 195 return low
Chris@909 196 end
Chris@909 197
Chris@909 198 def patch(diff)
Chris@909 199 newary = nil
Chris@909 200 if diff.difftype == String
Chris@909 201 newary = diff.difftype.new('')
Chris@909 202 else
Chris@909 203 newary = diff.difftype.new
Chris@909 204 end
Chris@909 205 ai = 0
Chris@909 206 bi = 0
Chris@909 207 diff.diffs.each { |d|
Chris@909 208 d.each { |mod|
Chris@909 209 case mod[0]
Chris@909 210 when '-'
Chris@909 211 while ai < mod[1]
Chris@909 212 newary << self[ai]
Chris@909 213 ai += 1
Chris@909 214 bi += 1
Chris@909 215 end
Chris@909 216 ai += 1
Chris@909 217 when '+'
Chris@909 218 while bi < mod[1]
Chris@909 219 newary << self[ai]
Chris@909 220 ai += 1
Chris@909 221 bi += 1
Chris@909 222 end
Chris@909 223 newary << mod[2]
Chris@909 224 bi += 1
Chris@909 225 else
Chris@909 226 raise "Unknown diff action"
Chris@909 227 end
Chris@909 228 }
Chris@909 229 }
Chris@909 230 while ai < self.length
Chris@909 231 newary << self[ai]
Chris@909 232 ai += 1
Chris@909 233 bi += 1
Chris@909 234 end
Chris@909 235 return newary
Chris@909 236 end
Chris@909 237 end
Chris@909 238
Chris@909 239 class Array
Chris@909 240 include Diffable
Chris@909 241 end
Chris@909 242
Chris@909 243 class String
Chris@909 244 include Diffable
Chris@909 245 end
Chris@909 246
Chris@909 247 =begin
Chris@909 248 = Diff
Chris@909 249 (({diff.rb})) - computes the differences between two arrays or
Chris@909 250 strings. Copyright (C) 2001 Lars Christensen
Chris@909 251
Chris@909 252 == Synopsis
Chris@909 253
Chris@909 254 diff = Diff.new(a, b)
Chris@909 255 b = a.patch(diff)
Chris@909 256
Chris@909 257 == Class Diff
Chris@909 258 === Class Methods
Chris@909 259 --- Diff.new(a, b)
Chris@909 260 --- a.diff(b)
Chris@909 261 Creates a Diff object which represent the differences between
Chris@909 262 ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
Chris@909 263 of any objects, strings, or object of any class that include
Chris@909 264 module ((|Diffable|))
Chris@909 265
Chris@909 266 == Module Diffable
Chris@909 267 The module ((|Diffable|)) is intended to be included in any class for
Chris@909 268 which differences are to be computed. Diffable is included into String
Chris@909 269 and Array when (({diff.rb})) is (({require}))'d.
Chris@909 270
Chris@909 271 Classes including Diffable should implement (({[]})) to get element at
Chris@909 272 integer indices, (({<<})) to append elements to the object and
Chris@909 273 (({ClassName#new})) should accept 0 arguments to create a new empty
Chris@909 274 object.
Chris@909 275
Chris@909 276 === Instance Methods
Chris@909 277 --- Diffable#patch(diff)
Chris@909 278 Applies the differences from ((|diff|)) to the object ((|obj|))
Chris@909 279 and return the result. ((|obj|)) is not changed. ((|obj|)) and
Chris@909 280 can be either an array or a string, but must match the object
Chris@909 281 from which the ((|diff|)) was created.
Chris@909 282 =end