gaphor/utils/compare.py
Arjan Molenaar 24a9cace51 Merged ports branch with trunk
git-svn-id: file:///Users/arjan/backup/gaphor/gaphor/trunk@2655 a8418922-720d-0410-834f-a69b97ada669
2009-01-23 23:22:10 +00:00

213 lines
6.7 KiB
Python

#!/usr/bin/env python
# vim:sw=4:et:
"""Report differences in two Gaphor models.
This can be called as:
python compare.py model1.gaphor model2.gaphor
This file is part of Gaphor.
"""
__all__ = ['Compare']
try:
import env
except ImportError:
pass
import gaphor.storage
import gaphor.storage.parser
import gaphor.UML
class Compare(object):
"""This class makes it possible to compare two files.
By default reports are printed to stdout in a diff-like syntax.
"""
def __init__(self, filename1, filename2):
self.filename1 = filename1
self.filename2 = filename2
self.elements1, self.factory1 = self.load(self.filename1)
self.elements2, self.factory2 = self.load(self.filename2)
self.show_id = True
def out(self, msg):
"""Print a message generated by report().
"""
print msg
def report(self, factory, element, name=None, value=None, isref=False):
"""Report an element that has differences.
The attribute show_id can be set to False to suppress element ids.
A fancy diff message is send to method out(msg).
"""
if factory is self.factory1:
msg = '-'
else:
msg = '+'
if isinstance(element, gaphor.storage.parser.canvas):
msg += ' <canvas>:'
else:
if self.show_id:
msg += ' %s' % element.id
n = element.get('name')
if n:
msg += ' (%s)' % n
if self.show_id or n:
msg += ':'
msg += ' %s' % (isinstance(element, gaphor.storage.parser.canvas) and 'Canvas' or element.type)
if name:
msg += '.%s' % name
if value:
if isref:
if self.show_id:
msg += ' = %s' % value
obj = factory.lookup(value)
if hasattr(obj, 'name'):
msg += ' (%s)' % obj.name
else:
msg += ' = %s' % value
self.out(msg)
def load(self, filename):
"""Load the model file and create a factory.
A tuple (elements, factory) is returned.
"""
elements = gaphor.storage.parser.parse(filename)
factory = gaphor.UML.ElementFactory()
try:
gaphor.storage.load_elements(elements, factory)
except Exception, e:
self.out('! File %s could not be loaded completely.' % filename)
self.out('! Trying to diff on parsed elements only.')
self.out(e)
return elements, factory
def elements_in_both_files(self):
"""Generator function that returns tuples (element1, element2) of
elements that exist in both files (they have the same id).
"""
vals = []
for key1, val1 in self.elements1.iteritems():
val2 = self.elements2.get(key1)
if val2:
yield (val1, val2)
def check_missing_elements(self):
"""Report elements that exist in one factory, but not in the other.
"""
keys1 = self.elements1.keys()
keys2 = self.elements2.keys()
for key in keys1:
if key not in keys2:
self.report(self.factory1, self.elements1[key])
for key in keys2:
if key not in keys1:
self.report(self.factory2, self.elements2[key])
def check_missing_references(self, element1, element2):
"""Report references to other elements that are present in one
element and not in the other one.
"""
keys1 = element1.references.keys()
keys2 = element2.references.keys()
for key in keys1:
if key not in keys2:
self.report(self.factory1, element1, key)
for key in keys2:
if key not in keys1:
self.report(self.factory2, element2, key)
def check_differences_references(self, element1, element2):
keys1 = element1.references.keys()
keys2 = element2.references.keys()
for key in keys1:
if key in keys2:
val1 = element1.references.get(key)
val2 = element2.references.get(key)
try:
for val in val1:
if val not in val2:
self.report(self.factory1, element1, key, val, True)
for val in val2:
if val not in val1:
self.report(self.factory2, element2, key, val, True)
except TypeError:
if val1 != val2:
self.report(self.factory1, element1, key, val1, True)
self.report(self.factory2, element2, key, val2, True)
def check_missing_values(self, element1, element2):
keys1 = element1.values.keys()
keys2 = element2.values.keys()
for key in keys1:
if key not in keys2:
self.report(self.factory1, element1, key)
for key in keys2:
if key not in keys1:
self.report(self.factory2, element2, key)
def check_differences_values(self, element1, element2):
keys1 = element1.values.keys()
keys2 = element2.values.keys()
for key in keys1:
if key in keys2:
val1 = element1.values.get(key)
val2 = element2.values.get(key)
if val1 != val2:
self.report(self.factory1, element1, key, val1)
self.report(self.factory2, element2, key, val2)
def compare(self):
"""Start the comparison of the files provided to the constructor.
"""
self.check_missing_elements()
for element1, element2 in self.elements_in_both_files():
self.check_missing_references(element1, element2)
self.check_differences_references(element1, element2)
self.check_missing_values(element1, element2)
self.check_differences_values(element1, element2)
if __name__ == '__main__':
import sys
usage = "usage: %s [-v][-h|--help] old_model new_model" % sys.argv[0]
files = []
show_id = False
# Parse command line arguments:
for arg in sys.argv[1:]:
if arg.startswith('-'):
if arg == '-v':
show_id = True
elif arg in ('-h', '--help'):
print usage
sys.exit(0)
else:
print '%s: invalid option "%s".' % (sys.argv[0], arg)
print usage
sys.exit(1)
else:
files.append(arg)
if len(files) != 2:
print usage
sys.exit(1)
c = Compare(files[0], files[1])
c.show_id = show_id
c.compare()