configurationengine/source/plugins/symbian/ConeHCRPlugin/hcrplugin/hcr_writer.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/plugins/symbian/ConeHCRPlugin/hcrplugin/hcr_writer.py	Thu Mar 11 17:04:37 2010 +0200
@@ -0,0 +1,217 @@
+#
+# 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
+import __init__
+  
+
+from struct import pack, unpack
+
+from cone.public import exceptions,plugin,utils,api
+from hcrplugin.hcrrepository import HcrRecord
+from hcrplugin.hcr_exceptions import *
+from hcrplugin.hcr_header import HcrHeader
+
+# Dictionary for mapping the HCRML value types to their
+# implementation values
+VALUE_TYPE_MAP = {
+    HcrRecord.VALTYPE_INT32         : 0x00000001,
+    HcrRecord.VALTYPE_INT16         : 0x00000002,
+    HcrRecord.VALTYPE_INT8          : 0x00000004,
+    HcrRecord.VALTYPE_BOOL          : 0x00000008,
+    HcrRecord.VALTYPE_UINT32        : 0x00000010,
+    HcrRecord.VALTYPE_UINT16        : 0x00000020,
+    HcrRecord.VALTYPE_UINT8         : 0x00000040,
+    HcrRecord.VALTYPE_LIN_ADDR      : 0x00000100,
+    HcrRecord.VALTYPE_BIN_DATA      : 0x00010000,
+    HcrRecord.VALTYPE_TEXT8         : 0x00020000,
+    HcrRecord.VALTYPE_ARRAY_INT32   : 0x00040000,
+    HcrRecord.VALTYPE_ARRAY_UINT32  : 0x00080000,
+    HcrRecord.VALTYPE_INT64         : 0x01000000,
+    HcrRecord.VALTYPE_UINT64        : 0x02000000,
+    }
+
+
+VALUE_TYPES_WITH_LSD = [
+    HcrRecord.VALTYPE_BIN_DATA,
+    HcrRecord.VALTYPE_TEXT8,
+    HcrRecord.VALTYPE_ARRAY_INT32,
+    HcrRecord.VALTYPE_ARRAY_UINT32,
+    HcrRecord.VALTYPE_INT64,
+    HcrRecord.VALTYPE_UINT64
+    ]
+
+VALUE_TYPES_UNSIGNED_INT = [
+    HcrRecord.VALTYPE_UINT32,
+    HcrRecord.VALTYPE_UINT16,
+    HcrRecord.VALTYPE_UINT8,
+    HcrRecord.VALTYPE_LIN_ADDR,
+    HcrRecord.VALTYPE_UINT64,
+    ]
+
+NUMERIC_VALUE_RANGES = {
+    HcrRecord.VALTYPE_INT32         : (-2**31,  2**31-1),
+    HcrRecord.VALTYPE_INT16         : (-2**15,  2**15-1),
+    HcrRecord.VALTYPE_INT8          : (-2**7,   2**7-1),
+    HcrRecord.VALTYPE_UINT32        : (0,       2**32-1),
+    HcrRecord.VALTYPE_UINT16        : (0,       2**16-1),
+    HcrRecord.VALTYPE_UINT8         : (0,       2**8-1),
+    HcrRecord.VALTYPE_LIN_ADDR      : (0,       2**32-1),
+    HcrRecord.VALTYPE_INT64         : (-2**63,  2**63-1),
+    HcrRecord.VALTYPE_UINT64        : (0,       2**64-1),
+    HcrRecord.VALTYPE_ARRAY_INT32   : (-2**31,  2**31-1),
+    HcrRecord.VALTYPE_ARRAY_UINT32  : (0,       2**32-1),
+}
+
+
+
+class HcrWriter(object):
+    
+    # Maximum size of LSD section data for one record
+    LSD_MAX_SIZE_PER_RECORD = 512
+    
+    def get_record_setting_id(self, record):
+        """
+        Return the setting ID value used for sorting the records in the repository.
+        """
+        return (record.category_id, record.element_id)
+    
+    def get_repository_bindata(self, repository):
+        """
+        @return: The binary data to write into a file for the given repository.
+        """
+        dup_ids = repository.get_duplicate_record_ids()
+        if len(dup_ids) > 0:
+            raise DuplicateRecordError("The repository contains the following duplicate records (category ID, element ID): %r" % dup_ids)
+        
+        header_data   = None
+        header_size   = 32
+        record_data   = []
+        records_size  = 0
+        lsd_data      = []
+        lsd_size      = 0
+        lsd_offset    = None
+        
+        # Generate the record and LSD section data
+        records = sorted(repository.records, key=self.get_record_setting_id)
+        for record in records:
+            lsd_pos = None
+            lsd = self.get_record_lsd_bindata(record)
+            if lsd != None:
+                # Store the position before adding the padding,
+                # because it shouldn't include the padding bytes
+                lsd_pos = (lsd_size, len(lsd))
+                lsd += self._get_padding(len(lsd))
+                lsd_data.append(lsd)
+                lsd_size += len(lsd)
+            
+            rdata = self.get_record_bindata(record, lsd_pos)
+            record_data.append(rdata)
+            records_size += len(rdata)
+        
+        lsd_offset = header_size + records_size
+        
+        header = HcrHeader()
+        header.version = repository.version
+        header.flags = repository.flags
+        header.nrecords = len(repository.records)
+        header.lsd_offset = lsd_offset
+        header.lsd_size = lsd_size
+        
+        header_data = header.dumps()
+        if len(header_data) != header_size:
+            raise RuntimeError("Internal logic error! Header size is %d and not %d as it should be!" % (len(header_data), header_size))
+        
+        output = []
+        output.append(header_data)
+        output.extend(record_data)
+        output.extend(lsd_data)
+        output = ''.join(output)
+        if len(output) % 4 != 0:
+            raise RuntimeError("Internal logic error! Output size is not divisible by 4 (%d)" % (len(output)))
+        
+        return output
+    
+    def _get_padding(self, size, padding_char='\x00'):
+        if size % 4 == 0:   amount = 0
+        else:               amount = 4 - (size % 4)
+        return amount * padding_char
+    
+    
+    def get_record_bindata(self, record, lsd_pos=None):
+        """
+        @param lsd_pos: The position of the record's data in the Large
+            Setting Data section. Should be a tuple: (offset, size).
+        @return: The binary data to write for the given record object.
+        """
+        self._check_value_range(record)
+        
+        if record.type in VALUE_TYPES_WITH_LSD:
+            RECORD_FMT = "<IIIHHI"
+            return pack(RECORD_FMT,record.category_id,record.element_id,VALUE_TYPE_MAP[record.type],record.flags,lsd_pos[1],lsd_pos[0])
+        else:
+            if record.type in VALUE_TYPES_UNSIGNED_INT:
+                RECORD_FMT = "<IIIHHI"
+            else:
+                RECORD_FMT = "<IIIHHi"
+            return pack(RECORD_FMT,record.category_id,record.element_id,VALUE_TYPE_MAP[record.type],record.flags,0,record.value)
+        
+    
+    def get_record_lsd_bindata(self, record):
+        """
+        @return: The binary data to write into the Large Setting Data
+            section for the given setting, or None if an entry in the
+            LSD section is not necessary.
+        """
+        result = None
+        
+        if record.type == HcrRecord.VALTYPE_TEXT8:
+            result = record.value.encode("utf-8")
+        
+        elif record.type == HcrRecord.VALTYPE_BIN_DATA:
+            result = record.value 
+
+        elif record.type == HcrRecord.VALTYPE_ARRAY_INT32:
+            result = pack("<%di"%len(record.value),*record.value)
+
+        elif record.type == HcrRecord.VALTYPE_ARRAY_UINT32:
+            result = pack("<%dI"%len(record.value),*record.value)
+
+        elif record.type == HcrRecord.VALTYPE_INT64:
+            result = pack("<q",record.value)
+
+        elif record.type == HcrRecord.VALTYPE_UINT64:
+            result = pack("<Q",record.value)
+        
+        if result != None:
+            if len(result) > self.LSD_MAX_SIZE_PER_RECORD:
+                msg = "Data size for value in record (category=%d, element=%d) is too large: size %d bytes, but maximum is %d bytes" \
+                    % (record.category_id, record.element_id, len(result), self.LSD_MAX_SIZE_PER_RECORD)
+                raise TooLargeLsdDataError(msg)
+        return result
+    
+    def _check_value_range(self, record):
+        if record.type in NUMERIC_VALUE_RANGES:
+            range = NUMERIC_VALUE_RANGES[record.type]
+            
+            values = record.value
+            if not isinstance(values, list): values = [values]
+            
+            for val in values:
+                if val < range[0] or val > range[1]:
+                    msg = "Value in record (category=%d, element=%d) is invalid for its type ('%s'): %d is not in the range %d-%d" \
+                        % (record.category_id, record.element_id, record.type, val, range[0], range[1])
+                    raise ValueNotInRangeError(msg)