1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
require 'andand'
module RGen
module Util
class ModelComparatorBase
CompareSpec = Struct.new(:identifier, :recurse, :filter, :ignore_features, :display_name, :sort)
INDENT = " "
class << self
attr_reader :compareSpecs
def compare_spec(clazz, hash)
@compareSpecs ||= {}
raise "Compare spec already defined for #{clazz}" if @compareSpecs[clazz]
spec = CompareSpec.new
hash.each_pair do |k,v|
spec.send("#{k}=",v)
end
@compareSpecs[clazz] = spec
end
end
# compares two sets of elements
def compare(as, bs, recursive=true)
result = []
aById = as.select{|e| useElement?(e)}.inject({}){|r, e| r[elementIdentifier(e)] = e; r}
bById = bs.select{|e| useElement?(e)}.inject({}){|r, e| r[elementIdentifier(e)] = e; r}
onlyA = sortElements((aById.keys - bById.keys).collect{|id| aById[id]})
onlyB = sortElements((bById.keys - aById.keys).collect{|id| bById[id]})
aAndB = sortElementPairs((aById.keys & bById.keys).collect{|id| [aById[id], bById[id]]})
onlyA.each do |e|
result << "- #{elementDisplayName(e)}"
end
onlyB.each do |e|
result << "+ #{elementDisplayName(e)}"
end
if recursive
aAndB.each do |ab|
a, b = *ab
r = compareElements(a, b)
if r.size > 0
result << "#{elementDisplayName(a)}"
result += r.collect{|l| INDENT+l}
end
end
end
result
end
def sortElementPairs(pairs)
pairs.sort do |x,y|
a, b = x[0], y[0]
r = a.class.name <=> b.class.name
r = compareSpec(a).sort.call(a,b) if r == 0 && compareSpec(a) && compareSpec(a).sort
r
end
end
def sortElements(elements)
elements.sort do |a,b|
r = a.class.name <=> b.class.name
r = compareSpec(a).sort.call(a,b) if r == 0 && compareSpec(a) && compareSpec(a).sort
r
end
end
def elementDisplayName(e)
if compareSpec(e) && compareSpec(e).display_name
compareSpec(e).display_name.call(e)
else
elementIdentifier(e)
end
end
def compareElements(a, b)
result = []
if a.class != b.class
result << "Class: #{a.class} -> #{b.class}"
else
a.class.ecore.eAllStructuralFeatures.reject{|f| f.derived || compareSpec(a).andand.ignore_features.andand.include?(f.name.to_sym)}.each do |f|
va, vb = a.getGeneric(f.name), b.getGeneric(f.name)
if f.is_a?(RGen::ECore::EAttribute)
r = compareValues(f.name, va, vb)
result << r if r
else
va, vb = [va].compact, [vb].compact unless f.many
r = compare(va, vb, f.containment || compareSpec(a).andand.recurse.andand.include?(f.name.to_sym))
if r.size > 0
result << "[#{f.name}]"
result += r.collect{|l| INDENT+l}
end
end
end
end
result
end
def compareValues(name, val1, val2)
result = nil
result = "[#{name}] #{val1} -> #{val2}" if val1 != val2
result
end
def elementIdentifier(element)
cs = compareSpec(element)
if cs && cs.identifier
if cs.identifier.is_a?(Proc)
cs.identifier.call(element)
else
cs.identifier
end
else
if element.respond_to?(:name)
element.name
else
element.object_id
end
end
end
def useElement?(element)
cs = compareSpec(element)
!(cs && cs.filter) || cs.filter.call(element)
end
def compareSpec(element)
@compareSpec ||= {}
return @compareSpec[element.class] if @compareSpec[element.class]
return nil unless self.class.compareSpecs
key = self.class.compareSpecs.keys.find{|k| element.is_a?(k)}
@compareSpec[element.class] = self.class.compareSpecs[key]
end
end
end
end
|