00001 from silme.core.object import Blob, L10nObject, Comment
00002 from silme.core.entity import Entity
00003 from entitylist import EntityListDiff
00004 from entity import EntityDiff
00005 from difflib import *
00006
00007 def compare_lists(list1, list2):
00008 resultlist = []
00009 s = SequenceMatcher(None, list1, list2)
00010 for tag, i1, i2, j1, j2 in s.get_opcodes():
00011 resultlist.append((tag, i1, i2, j1, j2))
00012 return resultlist
00013
00014 def compare_structures (l10nobject1, l10nobject2):
00015 structure1 = []
00016 structure2 = []
00017 for i in l10nobject1:
00018 if isinstance(i, Entity):
00019 structure1.append(u'E'+i.id)
00020 elif isinstance(i, unicode):
00021 structure1.append(u'U')
00022 elif isinstance(i, Comment):
00023 structure1.append(u'C')
00024 for i in l10nobject2:
00025 if isinstance(i, Entity):
00026 structure2.append(u'E'+i.id)
00027 elif isinstance(i, unicode):
00028 structure2.append(u'U')
00029 elif isinstance(i, Comment):
00030 structure2.append(u'C')
00031 difftype = compare_lists(structure1, structure2)
00032 return difftype
00033
00034 class L10nObjectDiff(list):
00035 def __init__(self):
00036 list.__init__(self)
00037 self.id = None
00038 self.uri = None
00039
00040 def empty(self):
00041 return not bool(len(self))
00042
00043 def get_entitylistdiff(self):
00044 entitylistdiff = EntityListDiff()
00045 entitylistdiff.id = self.id
00046 entitylistdiff.uri = self.uri
00047 entities = self.get_entities(type='all')
00048 for entity in entities.values():
00049 flags = entity.params['diff_flags']
00050 if 'modified' in flags:
00051 entitylistdiff.add('modified', entity=entity, id=entity.id)
00052 elif 'added' in flags:
00053 entitylistdiff.add('added', entity=entity, id=entity.id)
00054 elif 'removed' in flags:
00055 entitylistdiff.add('removed', entity=entity, id=entity.id)
00056 return entitylistdiff
00057
00058 def get_entity(self, id):
00059 entityList = self.get_entitylistdiff()
00060 try:
00061 return entityList.get_entity(id)
00062 except KeyError:
00063 raise KeyError('No such id: '+id)
00064
00065 def get_entities(self, type='all'):
00066 (added, removed, modified) = {},{},{}
00067
00068 def _get_entities_of_type(chunks, type):
00069 current = added if type=='added' else removed
00070 opposite = added if type=='removed' else removed
00071 for elem in chunks:
00072 if isinstance(elem, Entity):
00073 if opposite.has_key(elem.id):
00074 entitydiff = elem.diff(opposite[elem.id])
00075 if not entitydiff.empty():
00076 entitydiff.params['diff_flags'] = ['modified']
00077 modified[elem.id] = entitydiff
00078 del opposite[elem.id]
00079 else:
00080 elem.params['diff_flags'] = type
00081 current[elem.id] = elem
00082
00083 for i in self:
00084 if 'modified' in i['flags'] and isinstance(i['elements'][1], EntityDiff):
00085 if i['elements'][1].has_key('value'):
00086 modified[i['elements'][0]] = i['elements'][1]
00087 modified[i['elements'][0]].params['diff_flags'] = i['flags']
00088 elif 'added' in i['flags']:
00089 _get_entities_of_type(i['elements'], 'added')
00090 elif 'removed' in i['flags']:
00091 _get_entities_of_type(i['elements'], 'removed')
00092 elif 'replaced' in i['flags']:
00093 _get_entities_of_type(i['elements'], 'removed')
00094 _get_entities_of_type(i['elements2'], 'added')
00095
00096 entities = {}
00097 if type == 'all' or 'modified' in type:
00098 entities.update(modified)
00099 if type == 'all' or 'added' in type:
00100 entities.update(added)
00101 if type == 'all' or 'removed' in type:
00102 entities.update(removed)
00103 return entities
00104
00105 def get_entities2(self, type='all'):
00106 entities = {}
00107 for i in self:
00108 if 'modified' in i['flags'] and (type=='all' or type=='modified') and isinstance(i['elements'][1], EntityDiff):
00109 if i['elements'][1].has_key('value'):
00110 entities[i['elements'][0]] = i['elements'][1]
00111 elif 'added' in i['flags'] and (type=='all' or type=='added'):
00112 for elem in i['elements']:
00113 if isinstance(elem, Entity):
00114 if entities.has_key(elem.id) and \
00115 isinstance(entities[elem.id], Entity) and \
00116 'removed' in entities[elem.id].params['diff_flags']:
00117 entitydiff = elem.diff(entities[elem.id])
00118 if entitydiff.empty():
00119 del entities[elem.id]
00120 else:
00121 entities[elem.id] = entitydiff
00122 else:
00123 elem.params['diff_flags'] = i['flags']
00124 entities[elem.id] = elem
00125 elif 'removed' in i['flags'] and (type=='all' or type=='removed'):
00126 for elem in i['elements']:
00127 if isinstance(elem, Entity):
00128 if entities.has_key(elem.id) and \
00129 isinstance(entities[elem.id], Entity) and \
00130 'added' in entities[elem.id].params['diff_flags']:
00131 entitydiff = elem.diff(entities[elem.id])
00132 if entitydiff.empty():
00133 del entities[elem.id]
00134 else:
00135 entities[elem.id] = entitydiff
00136 else:
00137 elem.params['diff_flags'] = i['flags']
00138 entities[elem.id] = elem
00139 elif 'replaced' in i['flags']:
00140 if (type=='all' or type=='removed'):
00141 for elem in i['elements']:
00142 if isinstance(elem, Entity):
00143 if entities.has_key(elem.id) and \
00144 isinstance(entities[elem.id], Entity) and \
00145 'added' in entities[elem.id].params['diff_flags']:
00146 entitydiff = elem.diff(entities[elem.id])
00147 if entitydiff.empty():
00148 del entities[elem.id]
00149 else:
00150 entities[elem.id] = entitydiff
00151 else:
00152 elem.params['diff_flags'] = ['removed']
00153 entities[elem.id] = elem
00154 if (type=='all' or type=='added'):
00155 for elem in i['elements2']:
00156 if isinstance(elem, Entity):
00157 if entities.has_key(elem.id) and \
00158 isinstance(entities[elem.id], Entity) and \
00159 'removed' in entities[elem.id].params['diff_flags']:
00160 entitydiff = elem.diff(entities[elem.id])
00161 if entitydiff.empty():
00162 del entities[elem.id]
00163 else:
00164 entities[elem.id] = entitydiff
00165 else:
00166 elem.params['diff_flags'] = ['added']
00167 entities[elem.id] = elem
00168 return entities
00169
00170 def add(self, type, element):
00171 chunk = {'flags':[type]}
00172 chunk['elements'] = element
00173 self.append(chunk)
00174
00175 def get_prev_elements (self, pos):
00176 prev_elements = []
00177 while len(prev_elements) < 3:
00178 if pos > 0:
00179 pos = pos-1
00180 prev_elements.append(self[pos])
00181 else:
00182 prev_elements.append('BEGIN')
00183 break
00184 return prev_elements
00185
00186 L10nObject.get_prev_elements = get_prev_elements
00187
00188 def get_next_elements (self, pos):
00189 next_elements = []
00190 while len(next_elements) < 3:
00191 if pos < len(self):
00192 next_elements.append(self[pos])
00193 pos = pos+1
00194 else:
00195 next_elements.append('END')
00196 break
00197 return next_elements
00198
00199 L10nObject.get_next_elements = get_next_elements
00200
00201
00202 def locate_position (self, prev_elements, next_elements, chunk_size=None, by='any', mode=''):
00203 pos = None
00204 if by == 'any':
00205 pos = self.locate_position(prev_elements, next_elements, chunk_size=chunk_size, by='entity', mode=mode)
00206 if pos == None:
00207 pos = self.locate_position(prev_elements, next_elements, chunk_size=chunk_size, by= 'marker', mode=mode)
00208 return pos
00209
00210 for p, elem in enumerate(prev_elements):
00211 if by == 'entity' and isinstance(elem, Entity):
00212 pos = self.locate_entity(elem)
00213 if pos != None:
00214 pos = pos+p+1
00215 break
00216 elif by == 'marker' and elem == 'BEGIN':
00217 pos = p
00218 break
00219 if pos == None and chunk_size != None:
00220 for p, elem in enumerate(next_elements):
00221 if by == 'entity' and isinstance(elem, Entity):
00222 pos = self.locate_entity(elem)
00223 if pos != None:
00224 pos = pos-p
00225 break
00226 elif by == 'marker' and elem == 'END':
00227 pos = len(self)-p
00228 break
00229 if pos != None and mode == 'added':
00230 pos = pos + 1
00231 if pos != None:
00232 pos = pos - chunkSize + 1
00233 return pos
00234
00235 L10nObject.locate_position = locate_position
00236
00237
00238 def locate_entity (self, entity):
00239 pos = self.get_entity_position(entity.id)
00240 return pos
00241
00242 L10nObject.locate_entity = locate_entity
00243
00244 def locate_comment (cls, l10nobject, comment):
00245 pos = None
00246 for p, i in enumerate(l10nobject.structure):
00247 if isinstance(i, Comment):
00248 pos = p
00249 break
00250 return pos
00251
00252 L10nObject.locate_comment = locate_comment
00253
00254 def guess_spacing (cls, l10nobject, pos, spc_num=3, ext='dtd', entity=None):
00255 spacing = 1
00256 return spacing
00257
00258 L10nObject.guess_spacing = guess_spacing
00259
00260 def l10nobject_diff (self, l10nobject, flags=None, values=True):
00261 if flags == None:
00262 flags = ['added','removed','modified','replaced']
00263 l10nobject_diff = L10nObjectDiff()
00264 if self.id is not None:
00265 l10nobject_diff.id = self.id
00266 l10nobject_diff.uri = (self.uri, l10nobject.uri)
00267 structure_diff = compare_structures(self, l10nobject)
00268 for pos, elem in enumerate(structure_diff):
00269 if elem[0] == 'insert' and 'added' in flags:
00270 chunk = {'flags':['added']}
00271 chunk['elements'] = l10nobject[elem[3]:elem[4]]
00272 chunk['prev'] = l10nobject.get_prev_elements(elem[3])
00273 chunk['next'] = self.get_next_elements(elem[2])
00274 l10nobject_diff.append(chunk)
00275 elif elem[0] == 'equal' and ('modified' in flags or 'unmodified' in flags):
00276 for i in range(0, elem[2]-elem[1]):
00277 key = (elem[1]+i, elem[3]+i)
00278 rec = (self[key[0]], l10nobject[key[1]])
00279 if isinstance(rec[0], unicode):
00280 if values is False or rec[0]==rec[1]:
00281 if 'unmodified' in flags:
00282 chunk = {'flags': ['unmodified'], 'elements': rec[0]}
00283 else:
00284 continue
00285 else:
00286 if 'modified' in flags:
00287 chunk = {'flags': ['modified'], 'elements': (rec[0], rec[1])}
00288 else:
00289 continue
00290 chunk['prev'] = l10nobject.get_prev_elements(key[1])
00291 chunk['next'] = self.get_next_elements(key[0]+1)
00292 elif isinstance(rec[0], Entity):
00293 if values is True:
00294 entity_diff = rec[0].diff(rec[1], struct=True)
00295 if entity_diff.empty():
00296 if 'unmodified' in flags:
00297 chunk = {'flags': ['unmodified'], 'elements': rec[0]}
00298 else:
00299 continue
00300 else:
00301 if 'modified' in flags:
00302 chunk = {'flags': ['modified'], 'elements': (rec[0].id, entity_diff)}
00303 else:
00304 continue
00305 else:
00306 if 'unmodified' in flags:
00307 chunk = {'flags': ['unmodified'], 'elements': rec[0]}
00308 else:
00309 continue
00310 chunk['prev'] = l10nobject.get_prev_elements(key[1])
00311 chunk['next'] = self.get_next_elements(key[0]+1)
00312 elif isinstance(rec[0], Comment):
00313 comment_diff = rec[0].diff(rec[1], flags=flags, values=values)
00314 if comment_diff.empty():
00315 if 'unmodified' in flags:
00316 chunk = {'flags': ['unmodified'], 'elements': rec[0]}
00317 else:
00318 continue
00319 else:
00320 if 'modified' in flags:
00321 chunk = {'flags': ['modified'], 'elements': ('comment', comment_diff)}
00322 else:
00323 continue
00324 chunk['prev'] = l10nobject.get_prev_elements(key[1])
00325 chunk['next'] = self.get_next_elements(key[0]+1)
00326 l10nobject_diff.append(chunk)
00327 elif elem[0] == 'replace' and 'replaced' in flags:
00328 chunk = {'flags': ['replaced']}
00329 chunk['elements'] = []
00330 chunk['elements2'] = []
00331 chunk['prev'] = l10nobject.get_prev_elements(elem[3])
00332 chunk['next'] = self.get_next_elements(elem[2])
00333 for i in range(0, elem[2]-elem[1]):
00334 chunk['elements'].append(self[elem[1]+i])
00335 for i in range(0, elem[4]-elem[3]):
00336 chunk['elements2'].append(l10nobject[elem[3]+i])
00337 l10nobject_diff.append(chunk)
00338 elif elem[0] == 'delete' and 'removed' in flags:
00339 chunk = {'flags': ['removed']}
00340 chunk['elements'] = self[elem[1]:elem[2]]
00341 chunk['prev'] = l10nobject.get_prev_elements(elem[3])
00342 chunk['next'] = self.get_next_elements(elem[2])
00343 l10nobject_diff.append(chunk)
00344 return l10nobject_diff
00345
00346 L10nObject.diff = l10nobject_diff
00347
00348 def apply_entitylist_diff(self, entitylist_diff):
00349 for i in entitylist_diff:
00350 if 'modified' in entitylist_diff[i]['flags']:
00351 for el in self:
00352 if isinstance(el, Entity) and el.id == i:
00353 el.set_value(entitylist_diff[i]['elem'].get_value()[1])
00354 elif 'added' in entitylist_diff[i]['flags']:
00355 self.addEntity(entitylist_diff[i]['elem'])
00356 elif 'removed' in entitylist_diff[i]['type']:
00357 self.removeEntity(i)
00358
00359 L10nObject.apply_entitylist_diff = apply_entitylist_diff
00360
00361 def apply_l10nobject_diff (self, l10nobject_diff):
00362 if isinstance(l10nobject_diff, EntityListDiff):
00363 self.apply_entitylist_diff(l10nobject_diff)
00364 return
00365 for i in l10nobject_diff:
00366 if 'added' in i["flags"]:
00367 pos = self.locate_position(i['prev'], i['next'], chunk_size=len(i['elements']), mode='added')
00368 if pos != None:
00369 self[pos:pos] = i['elements']
00370 if 'modified' in i["flags"]:
00371 element = i['elements']
00372 if isinstance(element[1], EntityDiff):
00373 pos = self.get_entity_position(element[0])
00374 self[pos].apply_diff(element[1])
00375 elif isinstance(element[0], Comment):
00376 pos = self.locate_position(i['prev'], i['next'], chunk_size=len(i['elements']))
00377 self[pos] = element[1]
00378 elif isinstance(element[0], unicode):
00379 pos = self.locate_position(i['prev'], i['next'], chunk_size=len(i['elements']))
00380 if self[pos] == element[0]:
00381 self[pos] = element[1]
00382 if 'replaced' in i["flags"]:
00383 pos = self.locate_position(i['prev'], i['next'], chunk_size=len(i['elements']))
00384 if pos != None:
00385 self[pos:pos+1] = i['elements2']
00386 if 'removed' in i['flags']:
00387 pos = self.locate_position(i['prev'], i['next'], chunk_size=len(i['elements']))
00388 self[pos:pos+len(i['elements'])] = []
00389
00390 L10nObject.apply_diff = apply_l10nobject_diff