configurationengine/source/plugins/symbian/ConeCRMLPlugin/CRMLPlugin/crml_comparator.py
changeset 0 2e8eeb919028
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/plugins/symbian/ConeCRMLPlugin/CRMLPlugin/crml_comparator.py	Thu Mar 11 17:04:37 2010 +0200
@@ -0,0 +1,324 @@
+#
+# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+#
+
+import logging
+from cone.public import plugin
+from crml_model import *
+
+class CrmlComparator(object):
+    def __init__(self, resource_ref, repo):
+        self.logger = logging.getLogger('cone.crml.comparator(%s)' % resource_ref)
+        self.resource_ref = resource_ref
+        self.repo = repo
+    
+    @classmethod
+    def get_flat_comparison_id(cls, repo):
+        """
+        Return the flat comparison ID for the given repository.
+        """
+        return cls._num_to_str(repo.uid_value)
+    
+    @classmethod
+    def get_flat_comparison_extra_data(cls, repo):
+        return {'repo': repo}
+    
+    @classmethod
+    def _num_to_str(cls, number):
+        if isinstance(number, basestring):
+            try:
+                number = long(number, 10)
+            except ValueError:
+                number = long(number, 16)
+        return "0x%08X" % number
+    
+    def flat_compare(self, target_resource_ref, target_repo):
+        """
+        Compare two CRML repository models.
+        @return: A plugin.FlatComparisonResult object.
+        """
+        source_repo = self.repo
+        
+        result = plugin.FlatComparisonResult()
+        
+        source_repo_uid = self._num_to_str(source_repo.uid_value)
+        target_repo_uid = self._num_to_str(target_repo.uid_value)
+        
+        if source_repo_uid != target_repo_uid:
+            raise RuntimeError("Comparing CRML implementations instances that don't have the same repository UID (%r vs. %r)" % (source_repo_uid, target_repo_uid))
+        
+        # Default field contents for new entries
+        default_field_content = {
+            'data'      : {}}
+        
+        Entry = plugin.FlatComparisonResultEntry
+        
+        if self.resource_ref != target_resource_ref:
+            result.modified.append(Entry(value_id       = 'file',
+                                         source_value   = self.resource_ref,
+                                         target_value   = target_resource_ref,
+                                         data           = {'source_repo': source_repo,
+                                                           'target_repo': target_repo}))
+        
+        # Compare repository attributes
+        # -----------------------------
+        repo_mods = self._flat_compare_object_attrs(
+            key_id      = None,
+            source_obj  = source_repo,
+            target_obj  = target_repo,
+            varnames    = ('uid_name', 'owner', 'backup', 'rfs', 'access'))
+        content = default_field_content.copy()
+        content['data'] = {'source_repo': source_repo,
+                           'target_repo': target_repo}
+        self._fill_in_entry_fields(repo_mods, content)
+        result.modified.extend(repo_mods)
+        
+        source_data = self._get_flat_comparison_data(source_repo)
+        target_data = self._get_flat_comparison_data(target_repo)
+        
+        # Find entries only in source
+        # ---------------------------
+        for id, crml_key in source_data.iteritems():
+            if id not in target_data:
+                data = {'repo': source_repo,
+                        'key':  crml_key}
+                result.only_in_source.append(Entry(sub_id=id, data=data))
+        
+        # Find entries only in target
+        # ---------------------------
+        for id, crml_key in target_data.iteritems():
+            if id not in source_data:
+                data = {'repo': source_repo,
+                        'key':  crml_key}
+                result.only_in_target.append(Entry(sub_id=id, data=data))
+        
+        # Find differing entries
+        # ----------------------
+        for id, source_key in source_data.iteritems():
+            if id not in target_data: continue
+            target_key = target_data[id]
+            if source_key == target_key: continue
+            
+            # Get the comparison result for the key
+            comp_result = self._get_flat_key_comparison_result(id, source_key, target_key)
+            
+            # Fill in the missing fields of the result entries
+            content = default_field_content.copy()
+            content['data'] = {'repo'   : source_repo,
+                               'key'    : source_key}
+            self._fill_in_entry_fields(comp_result.only_in_source, content)
+            
+            content = default_field_content.copy()
+            content['data'] = {'repo'   : target_repo,
+                               'key'    : target_key}
+            self._fill_in_entry_fields(comp_result.only_in_target,  content)
+            
+            content = default_field_content.copy()
+            content['data'] = {'source_repo'    : source_repo,
+                               'target_repo'    : target_repo,
+                               'source_key'     : source_key,
+                               'target_key'     : target_key}
+            self._fill_in_entry_fields(comp_result.modified, content)
+            
+            result.extend(comp_result)
+        
+        return result
+    
+    def _fill_in_entry_fields(self, entry_list, field_contents):
+        for entry in entry_list:
+            for varname, value in field_contents.iteritems():
+                if getattr(entry, varname) is None:
+                    setattr(entry, varname, value)
+    
+    def _get_flat_comparison_data(self, repository):
+        """
+        Return a dictionary containing all keys in the repository.
+        
+        The dictionary will have the CRML key UIDs as the dictionary keys and
+        the CRML key objects as the values.
+        """
+        data = {}
+        for key in repository.keys:
+            if isinstance(key, (CrmlSimpleKey, CrmlBitmaskKey)):
+                id = self._num_to_str(key.int)
+            elif isinstance(key, CrmlKeyRange):
+                id = self._num_to_str(key.first_int) + '-' + self._num_to_str(key.last_int)
+            data[id] = key
+        return data
+    
+    def _get_flat_key_comparison_result(self, key_id, source_key, target_key):
+        """
+        Return a flat comparison result for a source and target CRML key pair.
+        
+        @param key_id: The ID of the key, e.g. '0x00000001' for a simple key or
+            '0x00001000-0x00001FFF' for a key range.
+        @param source_key: The source key object.
+        @param target_key: The target key object.
+        @return: A plugin.FlatComparisonResult object.
+        """
+        result = plugin.FlatComparisonResult()
+        
+        if type(source_key) == type(target_key):
+            comp_funcs = {CrmlSimpleKey:   self._flat_compare_simple_keys,
+                          CrmlBitmaskKey:  self._flat_compare_bitmask_keys,
+                          CrmlKeyRange:    self._flat_compare_key_ranges}
+            func = comp_funcs[type(source_key)]
+            result.extend(func(key_id, source_key, target_key))
+        else:
+            # Perform base key comparison
+            result.modified.extend(self._flat_compare_base_keys(key_id, source_key, target_key))
+            
+            # Add an entry for key type change
+            type_ids = {CrmlSimpleKey:  'simple_key',
+                        CrmlBitmaskKey: 'bitmask_key',
+                        CrmlKeyRange:   'key_range'}
+            entry = plugin.FlatComparisonResultEntry(
+                sub_id       = key_id,
+                value_id     = 'key_type',
+                source_value = type_ids[type(source_key)],
+                target_value = type_ids[type(target_key)])
+            result.modified.append(entry)
+        
+        return result
+    
+    def _flat_compare_object_attrs(self, key_id, source_obj, target_obj, varnames):
+        result = []
+        for varname in varnames:
+            sval = getattr(source_obj, varname)
+            tval = getattr(target_obj, varname)
+            
+            if sval != tval:
+                if isinstance(sval, CrmlAccess):
+                    result.extend(self._flat_compare_object_attrs(
+                          key_id, sval, tval,
+                          ('cap_rd', 'cap_wr', 'sid_rd', 'sid_wr')))
+                else:
+                    entry = plugin.FlatComparisonResultEntry(
+                        sub_id       = key_id,
+                        value_id     = varname,
+                        source_value = sval,
+                        target_value = tval)
+                    result.append(entry)
+        return result
+    
+    def _flat_compare_base_keys(self, key_id, source_key, target_key, extra_varnames=[]):
+        varnames = ['name', 'backup', 'read_only', 'access']
+        varnames.extend(extra_varnames)
+        return self._flat_compare_object_attrs(key_id, source_key, target_key, varnames)
+    
+    def _flat_compare_simple_keys(self, key_id, source_key, target_key):
+        mod = self._flat_compare_base_keys(key_id, source_key, target_key,
+                                           extra_varnames=['ref', 'type'])
+        return plugin.FlatComparisonResult(modified=mod)
+    
+    def _flat_compare_bitmask_keys(self, key_id, source_key, target_key):
+        mod = self._flat_compare_base_keys(key_id, source_key, target_key,
+                                           extra_varnames=['type'])
+        only_in_source = []
+        only_in_target = []
+        
+        def get_bits_dict(key):
+            bits = {}
+            for bit in key.bits:
+                bits[bit.index] = bit
+            return bits
+        
+        src_bits = get_bits_dict(source_key)
+        tgt_bits = get_bits_dict(target_key)
+        
+        # Find bits only in source
+        # ------------------------
+        for index in src_bits.iterkeys():
+            if index not in tgt_bits:
+                entry = plugin.FlatComparisonResultEntry(
+                    sub_id = "%s (bit %s)" % (key_id, index))
+                only_in_source.append(entry)
+        
+        # Find bits only in target
+        # ------------------------
+        for index in tgt_bits.iterkeys():
+            if index not in src_bits:
+                entry = plugin.FlatComparisonResultEntry(
+                    sub_id = "%s (bit %s)" % (key_id, index))
+                only_in_target.append(entry)
+        
+        # Find modified bits
+        # ------------------
+        for index, src_bit in src_bits.iteritems():
+            if index not in tgt_bits: continue
+            tgt_bit = tgt_bits[index]
+            
+            mod.extend(self._flat_compare_object_attrs(
+                key_id      = "%s (bit %s)" % (key_id, index),
+                source_obj  = src_bit,
+                target_obj  = tgt_bit,
+                varnames    = ('ref', 'invert')))
+        
+        return plugin.FlatComparisonResult(modified         = mod,
+                                           only_in_source   = only_in_source,
+                                           only_in_target   = only_in_target)
+    
+    def _flat_compare_key_ranges(self, key_id, source_key, target_key):
+        mod = self._flat_compare_base_keys(key_id, source_key, target_key,
+                                           extra_varnames=['ref', 'index_bits', 'first_index'])
+        only_in_source = []
+        only_in_target = []
+        
+        # Use hexadecimal format for index bits
+        for entry in mod:
+            if entry.value_id == 'index_bits':
+                entry.source_value = self._num_to_str(entry.source_value)
+                entry.target_value = self._num_to_str(entry.target_value)
+        
+        def get_subkeys_dict(key):
+            subkeys = {}
+            for sk in key.subkeys:
+                subkeys[sk.int] = sk
+            return subkeys
+        
+        src_subkeys = get_subkeys_dict(source_key)
+        tgt_subkeys = get_subkeys_dict(target_key)
+        
+        # Find sub-keys only in source
+        # ----------------------------
+        for uid in src_subkeys.iterkeys():
+            if uid not in tgt_subkeys:
+                entry = plugin.FlatComparisonResultEntry(
+                    sub_id = "%s (sub-key %s)" % (key_id, self._num_to_str(uid)))
+                only_in_source.append(entry)
+        
+        # Find sub-keys only in target
+        # ----------------------------
+        for uid in tgt_subkeys.iterkeys():
+            if uid not in src_subkeys:
+                entry = plugin.FlatComparisonResultEntry(
+                    sub_id = "%s (sub-key %s)" % (key_id, self._num_to_str(uid)))
+                only_in_target.append(entry)
+        
+        # Find modified bits
+        # ------------------
+        for uid, src_subkey in src_subkeys.iteritems():
+            if uid not in tgt_subkeys: continue
+            tgt_subkey = tgt_subkeys[uid]
+            
+            mod.extend(self._flat_compare_object_attrs(
+                key_id      = "%s (sub-key %s)" % (key_id, self._num_to_str(uid)),
+                source_obj  = src_subkey,
+                target_obj  = tgt_subkey,
+                varnames    = ('ref', 'type', 'name')))
+        
+        return plugin.FlatComparisonResult(modified         = mod,
+                                           only_in_source   = only_in_source,
+                                           only_in_target   = only_in_target)