annotate lib/diff.rb @ 8:0c83d98252d9 yuya

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