# -*- ruby encoding: utf-8 -*-
# Represents a simplistic (non-contextual) change. Represents the removal or
# addition of an element from either the old or the new sequenced
# enumerable.
class Diff::LCS::Change
# The only actions valid for changes are '+' (add), '-' (delete), '='
# (no change), '!' (changed), '<' (tail changes from first sequence), or
# '>' (tail changes from second sequence). The last two ('<>') are only
# found with Diff::LCS::diff and Diff::LCS::sdiff.
VALID_ACTIONS = %W(+ - = ! > <)
def self.valid_action?(action)
VALID_ACTIONS.include? action
end
# Returns the action this Change represents.
attr_reader :action
# Returns the position of the Change.
attr_reader :position
# Returns the sequence element of the Change.
attr_reader :element
def initialize(*args)
@action, @position, @element = *args
unless Diff::LCS::Change.valid_action?(@action)
raise "Invalid Change Action '#{@action}'"
end
raise "Invalid Position Type" unless @position.kind_of? Fixnum
end
def inspect
to_a.inspect
end
def to_a
[ @action, @position, @element ]
end
def self.from_a(arr)
arr = arr.flatten(1)
case arr.size
when 5
Diff::LCS::ContextChange.new(*(arr[0...5]))
when 3
Diff::LCS::Change.new(*(arr[0...3]))
else
raise "Invalid change array format provided."
end
end
include Comparable
def ==(other)
(self.action == other.action) and
(self.position == other.position) and
(self.element == other.element)
end
def <=>(other)
r = self.action <=> other.action
r = self.position <=> other.position if r.zero?
r = self.element <=> other.element if r.zero?
r
end
def adding?
@action == '+'
end
def deleting?
@action == '-'
end
def unchanged?
@action == '='
end
def changed?
@action == '!'
end
def finished_a?
@action == '>'
end
def finished_b?
@action == '<'
end
end
# Represents a contextual change. Contains the position and values of the
# elements in the old and the new sequenced enumerables as well as the action
# taken.
class Diff::LCS::ContextChange < Diff::LCS::Change
# We don't need these two values.
undef :position
undef :element
# Returns the old position being changed.
attr_reader :old_position
# Returns the new position being changed.
attr_reader :new_position
# Returns the old element being changed.
attr_reader :old_element
# Returns the new element being changed.
attr_reader :new_element
def initialize(*args)
@action, @old_position, @old_element, @new_position, @new_element = *args
unless Diff::LCS::Change.valid_action?(@action)
raise "Invalid Change Action '#{@action}'"
end
unless @old_position.nil? or @old_position.kind_of? Fixnum
raise "Invalid (Old) Position Type"
end
unless @new_position.nil? or @new_position.kind_of? Fixnum
raise "Invalid (New) Position Type"
end
end
def to_a
[ @action,
[ @old_position, @old_element ],
[ @new_position, @new_element ]
]
end
def inspect(*args)
to_a.inspect
end
def self.from_a(arr)
Diff::LCS::Change.from_a(arr)
end
# Simplifies a context change for use in some diff callbacks. '<' actions
# are converted to '-' and '>' actions are converted to '+'.
def self.simplify(event)
ea = event.to_a
case ea[0]
when '-'
ea[2][1] = nil
when '<'
ea[0] = '-'
ea[2][1] = nil
when '+'
ea[1][1] = nil
when '>'
ea[0] = '+'
ea[1][1] = nil
end
Diff::LCS::ContextChange.from_a(ea)
end
def ==(other)
(@action == other.action) and
(@old_position == other.old_position) and
(@new_position == other.new_position) and
(@old_element == other.old_element) and
(@new_element == other.new_element)
end
def <=>(other)
r = @action <=> other.action
r = @old_position <=> other.old_position if r.zero?
r = @new_position <=> other.new_position if r.zero?
r = @old_element <=> other.old_element if r.zero?
r = @new_element <=> other.new_element if r.zero?
r
end
end