root/gaphor/tags/gaphor-0.3.0/utils/compare.py

Revision 219, 6.7 kB (checked in by arjanmol, 5 years ago)

*** empty log message ***

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/env python
2 # vim:sw=4:et:
3 """Report differences in two Gaphor models.
4
5 This can be called as:
6     python compare.py model1.gaphor model2.gaphor
7
8 This file is part of Gaphor.
9 """
10
11 __all__ = ['Compare']
12
13 from __future__ import generators
14
15 try:
16     import env
17 except ImportError:
18     pass
19
20 import gaphor.parser
21 import gaphor.storage
22 import gaphor.UML
23
24 class Compare(object):
25     """This class makes it possible to compare two files.
26     By default reports are printed to stdout in a diff-like syntax.
27     """
28
29     def __init__(self, filename1, filename2):
30         self.filename1 = filename1
31         self.filename2 = filename2
32
33         self.elements1, self.factory1 = self.load(self.filename1)
34         self.elements2, self.factory2 = self.load(self.filename2)
35
36         self.show_id = True
37
38     def out(self, msg):
39         """Print a message generated by report().
40         """
41         print msg
42    
43     def report(self, factory, element, name=None, value=None, isref=False):
44         """Report an element that has differences.
45         The attribute show_id can be set to False to suppress element ids.
46         A fancy diff message is send to method out(msg).
47         """
48         if factory is self.factory1:
49             msg = '-'
50         else:
51             msg = '+'
52        
53         if isinstance(element, gaphor.parser.canvas):
54             msg += ' <canvas>:'
55         else:
56             if self.show_id:
57                 msg += ' %s' % element.id
58             n = element.get('name')
59             if n:
60                 msg += ' (%s)' % n
61             if self.show_id or n:
62                 msg += ':'
63
64         msg += ' %s' % (isinstance(element, gaphor.parser.canvas) and 'Canvas' or element.type)
65
66         if name:
67             msg += '.%s' % name
68             if value:
69                 if isref:
70                     if self.show_id:
71                         msg += ' = %s' % value
72                     obj = factory.lookup(value)
73                     if hasattr(obj, 'name'):
74                         msg += ' (%s)' % obj.name
75                 else:
76                     msg += ' = %s' % value
77
78         self.out(msg)
79
80     def load(self, filename):
81         """Load the model file and create a factory.
82         A tuple (elements, factory) is returned.
83         """
84         elements = gaphor.parser.parse(filename)
85         factory = gaphor.UML.ElementFactory()
86         try:
87             gaphor.storage._load(elements, factory)
88         except Exception, e:
89             self.out('! File %s could not be loaded completely.' % filename)
90             self.out('! Trying to diff on parsed elements only.')
91         return elements, factory
92
93     def elements_in_both_files(self):
94         """Generator function that returns tuples (element1, element2) of
95         elements that exist in both files (they have the same id).
96         """
97         vals = []
98         for key1, val1 in self.elements1.iteritems():
99             val2 = self.elements2.get(key1)
100             if val2:
101                 yield (val1, val2)
102
103     def check_missing_elements(self):
104         """Report elements that exist in one factory, but not in the other.
105         """
106         keys1 = self.elements1.keys()
107         keys2 = self.elements2.keys()
108         for key in keys1:
109             if key not in keys2:
110                 self.report(self.factory1, self.elements1[key])
111
112         for key in keys2:
113             if key not in keys1:
114                 self.report(self.factory2, self.elements2[key])
115
116     def check_missing_references(self, element1, element2):
117         """Report references to other elements that are present in one
118         element and not in the other one.
119         """
120         keys1 = element1.references.keys()
121         keys2 = element2.references.keys()
122         for key in keys1:
123             if key not in keys2:
124                 self.report(self.factory1, element1, key)
125
126         for key in keys2:
127             if key not in keys1:
128                 self.report(self.factory2, element2, key)
129
130     def check_differences_references(self, element1, element2):
131         keys1 = element1.references.keys()
132         keys2 = element2.references.keys()
133         for key in keys1:
134             if key in keys2:
135                 val1 = element1.references.get(key)
136                 val2 = element2.references.get(key)
137                 try:
138                     for val in val1:
139                         if val not in val2:
140                             self.report(self.factory1, element1, key, val, True)
141
142                     for val in val2:
143                         if val not in val1:
144                             self.report(self.factory2, element2, key, val, True)
145                 except TypeError:
146                     if val1 != val2:
147                         self.report(self.factory1, element1, key, val1, True)
148                         self.report(self.factory2, element2, key, val2, True)
149
150     def check_missing_values(self, element1, element2):
151         keys1 = element1.values.keys()
152         keys2 = element2.values.keys()
153         for key in keys1:
154             if key not in keys2:
155                 self.report(self.factory1, element1, key)
156
157         for key in keys2:
158             if key not in keys1:
159                 self.report(self.factory2, element2, key)
160
161     def check_differences_values(self, element1, element2):
162         keys1 = element1.values.keys()
163         keys2 = element2.values.keys()
164         for key in keys1:
165             if key in keys2:
166                 val1 = element1.values.get(key)
167                 val2 = element2.values.get(key)
168                 if val1 != val2:
169                     self.report(self.factory1, element1, key, val1)
170                     self.report(self.factory2, element2, key, val2)
171
172
173     def compare(self):
174         """Start the comparison of the files provided to the constructor.
175         """
176         self.check_missing_elements()
177
178         for element1, element2 in self.elements_in_both_files():
179             self.check_missing_references(element1, element2)
180             self.check_differences_references(element1, element2)
181             self.check_missing_values(element1, element2)
182             self.check_differences_values(element1, element2)
183
184 if __name__ == '__main__':
185     import sys
186
187     usage = "usage: %s [-v][-h|--help] old_model new_model" % sys.argv[0]
188     files = []
189     show_id = False
190
191     # Parse command line arguments:
192     for arg in sys.argv[1:]:
193         if arg.startswith('-'):
194             if arg == '-v':
195                 show_id = True
196             elif arg in ('-h', '--help'):
197                 print usage
198                 sys.exit(0)
199             else:
200                 print '%s: invalid option "%s".' % (sys.argv[0], arg)
201                 print usage
202                 sys.exit(1)
203         else:
204             files.append(arg)
205
206     if len(files) != 2:
207         print usage
208         sys.exit(1)
209
210     c = Compare(files[0], files[1])
211     c.show_id = show_id
212     c.compare()
213
Note: See TracBrowser for help on using the browser.