configurationengine/source/plugins/symbian/ConeHCRPlugin/hcrplugin/hcr_writer.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
equal deleted inserted replaced
-1:000000000000 0:2e8eeb919028
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description: 
       
    15 #
       
    16 
       
    17 import logging
       
    18 import __init__
       
    19   
       
    20 
       
    21 from struct import pack, unpack
       
    22 
       
    23 from cone.public import exceptions,plugin,utils,api
       
    24 from hcrplugin.hcrrepository import HcrRecord
       
    25 from hcrplugin.hcr_exceptions import *
       
    26 from hcrplugin.hcr_header import HcrHeader
       
    27 
       
    28 # Dictionary for mapping the HCRML value types to their
       
    29 # implementation values
       
    30 VALUE_TYPE_MAP = {
       
    31     HcrRecord.VALTYPE_INT32         : 0x00000001,
       
    32     HcrRecord.VALTYPE_INT16         : 0x00000002,
       
    33     HcrRecord.VALTYPE_INT8          : 0x00000004,
       
    34     HcrRecord.VALTYPE_BOOL          : 0x00000008,
       
    35     HcrRecord.VALTYPE_UINT32        : 0x00000010,
       
    36     HcrRecord.VALTYPE_UINT16        : 0x00000020,
       
    37     HcrRecord.VALTYPE_UINT8         : 0x00000040,
       
    38     HcrRecord.VALTYPE_LIN_ADDR      : 0x00000100,
       
    39     HcrRecord.VALTYPE_BIN_DATA      : 0x00010000,
       
    40     HcrRecord.VALTYPE_TEXT8         : 0x00020000,
       
    41     HcrRecord.VALTYPE_ARRAY_INT32   : 0x00040000,
       
    42     HcrRecord.VALTYPE_ARRAY_UINT32  : 0x00080000,
       
    43     HcrRecord.VALTYPE_INT64         : 0x01000000,
       
    44     HcrRecord.VALTYPE_UINT64        : 0x02000000,
       
    45     }
       
    46 
       
    47 
       
    48 VALUE_TYPES_WITH_LSD = [
       
    49     HcrRecord.VALTYPE_BIN_DATA,
       
    50     HcrRecord.VALTYPE_TEXT8,
       
    51     HcrRecord.VALTYPE_ARRAY_INT32,
       
    52     HcrRecord.VALTYPE_ARRAY_UINT32,
       
    53     HcrRecord.VALTYPE_INT64,
       
    54     HcrRecord.VALTYPE_UINT64
       
    55     ]
       
    56 
       
    57 VALUE_TYPES_UNSIGNED_INT = [
       
    58     HcrRecord.VALTYPE_UINT32,
       
    59     HcrRecord.VALTYPE_UINT16,
       
    60     HcrRecord.VALTYPE_UINT8,
       
    61     HcrRecord.VALTYPE_LIN_ADDR,
       
    62     HcrRecord.VALTYPE_UINT64,
       
    63     ]
       
    64 
       
    65 NUMERIC_VALUE_RANGES = {
       
    66     HcrRecord.VALTYPE_INT32         : (-2**31,  2**31-1),
       
    67     HcrRecord.VALTYPE_INT16         : (-2**15,  2**15-1),
       
    68     HcrRecord.VALTYPE_INT8          : (-2**7,   2**7-1),
       
    69     HcrRecord.VALTYPE_UINT32        : (0,       2**32-1),
       
    70     HcrRecord.VALTYPE_UINT16        : (0,       2**16-1),
       
    71     HcrRecord.VALTYPE_UINT8         : (0,       2**8-1),
       
    72     HcrRecord.VALTYPE_LIN_ADDR      : (0,       2**32-1),
       
    73     HcrRecord.VALTYPE_INT64         : (-2**63,  2**63-1),
       
    74     HcrRecord.VALTYPE_UINT64        : (0,       2**64-1),
       
    75     HcrRecord.VALTYPE_ARRAY_INT32   : (-2**31,  2**31-1),
       
    76     HcrRecord.VALTYPE_ARRAY_UINT32  : (0,       2**32-1),
       
    77 }
       
    78 
       
    79 
       
    80 
       
    81 class HcrWriter(object):
       
    82     
       
    83     # Maximum size of LSD section data for one record
       
    84     LSD_MAX_SIZE_PER_RECORD = 512
       
    85     
       
    86     def get_record_setting_id(self, record):
       
    87         """
       
    88         Return the setting ID value used for sorting the records in the repository.
       
    89         """
       
    90         return (record.category_id, record.element_id)
       
    91     
       
    92     def get_repository_bindata(self, repository):
       
    93         """
       
    94         @return: The binary data to write into a file for the given repository.
       
    95         """
       
    96         dup_ids = repository.get_duplicate_record_ids()
       
    97         if len(dup_ids) > 0:
       
    98             raise DuplicateRecordError("The repository contains the following duplicate records (category ID, element ID): %r" % dup_ids)
       
    99         
       
   100         header_data   = None
       
   101         header_size   = 32
       
   102         record_data   = []
       
   103         records_size  = 0
       
   104         lsd_data      = []
       
   105         lsd_size      = 0
       
   106         lsd_offset    = None
       
   107         
       
   108         # Generate the record and LSD section data
       
   109         records = sorted(repository.records, key=self.get_record_setting_id)
       
   110         for record in records:
       
   111             lsd_pos = None
       
   112             lsd = self.get_record_lsd_bindata(record)
       
   113             if lsd != None:
       
   114                 # Store the position before adding the padding,
       
   115                 # because it shouldn't include the padding bytes
       
   116                 lsd_pos = (lsd_size, len(lsd))
       
   117                 lsd += self._get_padding(len(lsd))
       
   118                 lsd_data.append(lsd)
       
   119                 lsd_size += len(lsd)
       
   120             
       
   121             rdata = self.get_record_bindata(record, lsd_pos)
       
   122             record_data.append(rdata)
       
   123             records_size += len(rdata)
       
   124         
       
   125         lsd_offset = header_size + records_size
       
   126         
       
   127         header = HcrHeader()
       
   128         header.version = repository.version
       
   129         header.flags = repository.flags
       
   130         header.nrecords = len(repository.records)
       
   131         header.lsd_offset = lsd_offset
       
   132         header.lsd_size = lsd_size
       
   133         
       
   134         header_data = header.dumps()
       
   135         if len(header_data) != header_size:
       
   136             raise RuntimeError("Internal logic error! Header size is %d and not %d as it should be!" % (len(header_data), header_size))
       
   137         
       
   138         output = []
       
   139         output.append(header_data)
       
   140         output.extend(record_data)
       
   141         output.extend(lsd_data)
       
   142         output = ''.join(output)
       
   143         if len(output) % 4 != 0:
       
   144             raise RuntimeError("Internal logic error! Output size is not divisible by 4 (%d)" % (len(output)))
       
   145         
       
   146         return output
       
   147     
       
   148     def _get_padding(self, size, padding_char='\x00'):
       
   149         if size % 4 == 0:   amount = 0
       
   150         else:               amount = 4 - (size % 4)
       
   151         return amount * padding_char
       
   152     
       
   153     
       
   154     def get_record_bindata(self, record, lsd_pos=None):
       
   155         """
       
   156         @param lsd_pos: The position of the record's data in the Large
       
   157             Setting Data section. Should be a tuple: (offset, size).
       
   158         @return: The binary data to write for the given record object.
       
   159         """
       
   160         self._check_value_range(record)
       
   161         
       
   162         if record.type in VALUE_TYPES_WITH_LSD:
       
   163             RECORD_FMT = "<IIIHHI"
       
   164             return pack(RECORD_FMT,record.category_id,record.element_id,VALUE_TYPE_MAP[record.type],record.flags,lsd_pos[1],lsd_pos[0])
       
   165         else:
       
   166             if record.type in VALUE_TYPES_UNSIGNED_INT:
       
   167                 RECORD_FMT = "<IIIHHI"
       
   168             else:
       
   169                 RECORD_FMT = "<IIIHHi"
       
   170             return pack(RECORD_FMT,record.category_id,record.element_id,VALUE_TYPE_MAP[record.type],record.flags,0,record.value)
       
   171         
       
   172     
       
   173     def get_record_lsd_bindata(self, record):
       
   174         """
       
   175         @return: The binary data to write into the Large Setting Data
       
   176             section for the given setting, or None if an entry in the
       
   177             LSD section is not necessary.
       
   178         """
       
   179         result = None
       
   180         
       
   181         if record.type == HcrRecord.VALTYPE_TEXT8:
       
   182             result = record.value.encode("utf-8")
       
   183         
       
   184         elif record.type == HcrRecord.VALTYPE_BIN_DATA:
       
   185             result = record.value 
       
   186 
       
   187         elif record.type == HcrRecord.VALTYPE_ARRAY_INT32:
       
   188             result = pack("<%di"%len(record.value),*record.value)
       
   189 
       
   190         elif record.type == HcrRecord.VALTYPE_ARRAY_UINT32:
       
   191             result = pack("<%dI"%len(record.value),*record.value)
       
   192 
       
   193         elif record.type == HcrRecord.VALTYPE_INT64:
       
   194             result = pack("<q",record.value)
       
   195 
       
   196         elif record.type == HcrRecord.VALTYPE_UINT64:
       
   197             result = pack("<Q",record.value)
       
   198         
       
   199         if result != None:
       
   200             if len(result) > self.LSD_MAX_SIZE_PER_RECORD:
       
   201                 msg = "Data size for value in record (category=%d, element=%d) is too large: size %d bytes, but maximum is %d bytes" \
       
   202                     % (record.category_id, record.element_id, len(result), self.LSD_MAX_SIZE_PER_RECORD)
       
   203                 raise TooLargeLsdDataError(msg)
       
   204         return result
       
   205     
       
   206     def _check_value_range(self, record):
       
   207         if record.type in NUMERIC_VALUE_RANGES:
       
   208             range = NUMERIC_VALUE_RANGES[record.type]
       
   209             
       
   210             values = record.value
       
   211             if not isinstance(values, list): values = [values]
       
   212             
       
   213             for val in values:
       
   214                 if val < range[0] or val > range[1]:
       
   215                     msg = "Value in record (category=%d, element=%d) is invalid for its type ('%s'): %d is not in the range %d-%d" \
       
   216                         % (record.category_id, record.element_id, record.type, val, range[0], range[1])
       
   217                     raise ValueNotInRangeError(msg)