|
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 |
|
18 |
|
19 |
|
20 import re |
|
21 import os |
|
22 import sys |
|
23 import logging |
|
24 import xml.parsers.expat |
|
25 import codecs |
|
26 from hcrplugin.hcr_exceptions import * |
|
27 from hcrplugin.hcrrepository import HcrRecord, HcrRepository |
|
28 from hcrplugin.hcr_writer import HcrWriter |
|
29 from hcrplugin.header_writer import * |
|
30 try: |
|
31 from cElementTree import ElementTree |
|
32 except ImportError: |
|
33 try: |
|
34 from elementtree import ElementTree |
|
35 except ImportError: |
|
36 try: |
|
37 from xml.etree import cElementTree as ElementTree |
|
38 except ImportError: |
|
39 from xml.etree import ElementTree |
|
40 |
|
41 import __init__ |
|
42 |
|
43 from cone.public import exceptions,plugin,utils,api |
|
44 |
|
45 class HcrmlImpl(plugin.ImplBase): |
|
46 """ |
|
47 <class description> |
|
48 """ |
|
49 |
|
50 IMPL_TYPE_ID = "hcrml" |
|
51 |
|
52 def __init__(self, resource_ref, configuration): |
|
53 plugin.ImplBase.__init__(self, resource_ref, configuration) |
|
54 self.logger = logging.getLogger('cone.hcrml(%s)' % resource_ref) |
|
55 self.configuration = configuration |
|
56 self.hcrml_file = resource_ref |
|
57 |
|
58 |
|
59 def generate(self, context=None): |
|
60 """ |
|
61 Generate the given implementation. |
|
62 @return: |
|
63 """ |
|
64 outputfile = self.__get_output_filename() |
|
65 if outputfile != None: |
|
66 # Create the path to the output file |
|
67 output_path = os.path.dirname(outputfile) |
|
68 if output_path != '' and not os.path.exists(output_path): |
|
69 os.makedirs(output_path) |
|
70 |
|
71 # For output type 'hcr', write the binary repository file |
|
72 if self.output_obj.type == 'hcr': |
|
73 self.logger.info("Generating binary repository to '%s'" % outputfile) |
|
74 writer = HcrWriter() |
|
75 repo = self.output_obj.get_hcr_repository() |
|
76 data = writer.get_repository_bindata(repo) |
|
77 f = open(outputfile,'wb') |
|
78 try: f.write(data) |
|
79 finally: f.close() |
|
80 elif self.output_obj.type == 'header': |
|
81 self.logger.info("Generating header file to '%s'" % outputfile) |
|
82 writer = HeaderWriter(outputfile, self.output_obj) |
|
83 writer.write() |
|
84 elif self.output_obj.type == None: |
|
85 # The HCRML file contains no <output> element, so no output should |
|
86 # be generated |
|
87 pass |
|
88 |
|
89 def get_refs(self): |
|
90 return self.refs |
|
91 |
|
92 def list_output_files(self): |
|
93 """ |
|
94 Return a list of output files as an array. |
|
95 """ |
|
96 fname = self.__get_output_filename() |
|
97 return [fname] if fname else [] |
|
98 |
|
99 def __get_output_filename(self): |
|
100 if self.output_obj.file != None: |
|
101 return os.path.normpath(os.path.join(self.output, self.output_obj.file)) |
|
102 else: |
|
103 return None |
|
104 |
|
105 |
|
106 class HcrmlReader(plugin.ReaderBase): |
|
107 NAMESPACE = 'http://www.symbianfoundation.org/xml/hcrml/1' |
|
108 FILE_EXTENSIONS = ['hcrml'] |
|
109 |
|
110 def __init__(self, resource_ref, configuration): |
|
111 self.configuration = configuration |
|
112 self.hcrml_file = resource_ref |
|
113 self.refs = [] |
|
114 self.namespaces = [self.NAMESPACE] |
|
115 self.doc = None |
|
116 |
|
117 @classmethod |
|
118 def read_impl(cls, resource_ref, configuration, etree): |
|
119 reader = HcrmlReader(resource_ref, configuration) |
|
120 reader.doc = etree |
|
121 |
|
122 impl = HcrmlImpl(resource_ref, configuration) |
|
123 impl.output_obj = reader.read_hcrml_output() |
|
124 impl.refs = reader.refs |
|
125 return impl |
|
126 |
|
127 def read_hcrml_output(self, ignore_includes=False): |
|
128 output = Output() |
|
129 |
|
130 # There should only be one <output> element, so use find() |
|
131 out_elem = self.doc.find("{%s}output" % self.namespaces[0]) |
|
132 if out_elem != None: |
|
133 version = out_elem.get('version') |
|
134 read_only = out_elem.get('readOnly') |
|
135 file = out_elem.get('file') |
|
136 type = out_elem.get('type') |
|
137 |
|
138 if type == None or type == '': |
|
139 raise NoTypeDefinedInOutPutTagError("Type attribute missing in hcrml file") |
|
140 |
|
141 if type not in ('hcr', 'header'): |
|
142 raise InvalidTypeDefinedInOutPutTagError("Type attribute invalid in hcrml file: %s" % type) |
|
143 |
|
144 output.version = version |
|
145 output.read_only = read_only |
|
146 output.file = file |
|
147 output.type = type |
|
148 |
|
149 # An <output> element may contain <include> elements for including other |
|
150 # HCRML files, so read and include categories from those |
|
151 if not ignore_includes: |
|
152 included_files = self.read_hcrml_includes(out_elem) |
|
153 read_categories = self.read_categories_from_hcrml_files(included_files) |
|
154 output.categories.extend(read_categories) |
|
155 |
|
156 |
|
157 """ output tag is not mandatory, but there should be some categories included """ |
|
158 for cat_elem in self.doc.getiterator("{%s}category" % self.namespaces[0]): |
|
159 category = self.read_hrcml_category(cat_elem) |
|
160 output.categories.append(category) |
|
161 return output |
|
162 |
|
163 def read_hcrml_includes(self, output_elem): |
|
164 """ |
|
165 Read all <include> elements under an <output> element. |
|
166 @return: List of other HCRML files to include. |
|
167 """ |
|
168 result = [] |
|
169 |
|
170 include_refs = [] |
|
171 for include_elem in output_elem.findall("{%s}include" % self.namespaces[0]): |
|
172 ref = include_elem.get('ref') |
|
173 if ref != None: include_refs.append(ref) |
|
174 |
|
175 if include_refs: |
|
176 # There are include refs, construct the list of files that should |
|
177 # be included |
|
178 all_files = self.configuration.list_resources() |
|
179 included_files = [] |
|
180 for ref in include_refs: |
|
181 files_by_ref = self.filter_file_list_by_include_ref(all_files, ref) |
|
182 result.extend(files_by_ref) |
|
183 |
|
184 # Make sure that no file is in the list more than once |
|
185 result = list(set(result)) |
|
186 return result |
|
187 |
|
188 def read_categories_from_hcrml_files(self, files): |
|
189 """ |
|
190 Read all categories from the list of the given HCRML files. |
|
191 """ |
|
192 categories = [] |
|
193 |
|
194 for file in files: |
|
195 # Skip the current file |
|
196 if os.path.normpath(file) == os.path.normpath(self.hcrml_file): |
|
197 continue |
|
198 |
|
199 # Read the <output> element and append its categories to the result list |
|
200 reader = HcrmlReader(file, self.configuration) |
|
201 reader.doc = self._read_xml_doc_from_resource(file, self.configuration) |
|
202 # Read the output element, but ignore includes, since we are |
|
203 # currently reading from inside an include |
|
204 output_obj = reader.read_hcrml_output(ignore_includes=True) |
|
205 categories.extend(output_obj.categories) |
|
206 |
|
207 return categories |
|
208 |
|
209 def read_hrcml_category(self,cat_elem): |
|
210 category_uid = cat_elem.get('uid') |
|
211 if category_uid == None or category_uid == '': |
|
212 raise NoCategoryUIDInHcrmlFileError("No category uid attribute implemented in hcrml file!") |
|
213 name = cat_elem.get('name') |
|
214 if name == None or name == '': |
|
215 raise NoCategoryNameInHcrmlFileError("No category name attribute implemented in hcrml file!") |
|
216 category = Category() |
|
217 category.name = name |
|
218 try: |
|
219 category.category_uid = long(category_uid) |
|
220 except ValueError: |
|
221 category.category_uid = long(category_uid, 16) |
|
222 category.xml_elem = cat_elem |
|
223 for setting_elem in cat_elem.getiterator("{%s}setting" % self.namespaces[0]): |
|
224 setting = self.read_hcrml_setting(setting_elem) |
|
225 category.settings.append(setting) |
|
226 return category |
|
227 |
|
228 |
|
229 def read_hcrml_setting(self,setting_elem): |
|
230 |
|
231 ref = setting_elem.get('ref') |
|
232 if ref == None or ref == '': |
|
233 raise NoRefInHcrmlFileError("No ref in setting tag attribute implemented in hcrml file!") |
|
234 else: |
|
235 self.refs.append(ref) |
|
236 type = setting_elem.get('type') |
|
237 if type == None or type == '': |
|
238 raise NoTypeAttributeInSettingHcrmlFileError("No type in setting tag attribute implemented in hcrml file ref: %s" % ref ) |
|
239 name = setting_elem.get('name') |
|
240 if name == None or name == '': |
|
241 raise NoNameAttributeInSettingHcrmlFileError("No type in setting tag attribute implemented in hcrml file ref: %s" % ref ) |
|
242 id = setting_elem.get('id') |
|
243 if id == None or id == '': |
|
244 raise NoIdAttributeInSettingHcrmlFileError("No id in setting tag attribute implemented in hcrml file ref: %s" % ref ) |
|
245 |
|
246 comment = setting_elem.get('comment') |
|
247 if comment == None: |
|
248 comment = '' |
|
249 |
|
250 |
|
251 setting = Setting(self.configuration) |
|
252 setting.comment = comment |
|
253 setting.name = name |
|
254 setting.ref = ref |
|
255 try: |
|
256 setting.id = long(id) |
|
257 except ValueError: |
|
258 setting.id = long(id, 16) |
|
259 setting.type = type |
|
260 setting.xml_elem = setting_elem |
|
261 for flag_elem in setting_elem.getiterator("{%s}flags" % self.namespaces[0]): |
|
262 flag = self.read_hrcml_flags(setting_elem) |
|
263 setting.flag = flag |
|
264 return setting |
|
265 |
|
266 def read_hrcml_flags(self,flag_elem): |
|
267 Uninitialised = flag_elem.get('Uninitialised') |
|
268 Modifiable = flag_elem.get('Modifiable') |
|
269 Persistent = flag_elem.get('Persistent') |
|
270 flag = Flag() |
|
271 flag.Uninitialised = Uninitialised |
|
272 flag.Modifiable = Modifiable |
|
273 flag.Persistent = Persistent |
|
274 return flag |
|
275 |
|
276 def filter_file_list_by_include_ref(self, files, ref): |
|
277 pattern = ref + '$' |
|
278 pattern = pattern.replace('.', r'\.') |
|
279 pattern = pattern.replace('*', '.*') |
|
280 pattern = '(^|.*/)' + pattern |
|
281 result = [] |
|
282 for file in files: |
|
283 if re.match(pattern, file.replace('\\', '/')) != None: |
|
284 result.append(file) |
|
285 return result |
|
286 |
|
287 |
|
288 class Flag(object): |
|
289 def __init__(self): |
|
290 self.Uninitialised = 0 |
|
291 self.Modifiable = 0 |
|
292 self.Persistent = 0 |
|
293 |
|
294 class Setting(object): |
|
295 def __init__(self,configuration): |
|
296 self.name = None |
|
297 self.ref = None |
|
298 self.type = None |
|
299 self.id = None |
|
300 self.flag = None |
|
301 self.comment = '' |
|
302 self.configuration = configuration |
|
303 |
|
304 @property |
|
305 def value(self): |
|
306 dview = self.configuration.get_default_view() |
|
307 feature = dview.get_feature(self.ref) |
|
308 value = feature.get_value() |
|
309 |
|
310 if self.type in (HcrRecord.VALTYPE_ARRAY_INT32, HcrRecord.VALTYPE_ARRAY_UINT32): |
|
311 # Convert string values to numbers |
|
312 value = map(lambda x: self.__str_to_long(x), value) |
|
313 elif self.type == HcrRecord.VALTYPE_BIN_DATA and feature.get_type() == 'string': |
|
314 value = self.__hex_to_bindata(value) |
|
315 return value |
|
316 |
|
317 def __str_to_long(self, str_value): |
|
318 try: |
|
319 return long(str_value) |
|
320 except ValueError: |
|
321 return long(str_value, 16) |
|
322 |
|
323 def __hex_to_bindata(self, hexdata): |
|
324 orig_hexdata = hexdata |
|
325 hexdata = hexdata.replace(' ', '').replace('\r', '').replace('\n', '').replace('\t', '') |
|
326 if len(hexdata) % 2 != 0: |
|
327 raise ValueError("Failed to convert %r into binary data: String length %d (whitespace stripped) is not divisible by 2", orig_hexdata, len(hexdata)) |
|
328 for c in hexdata: |
|
329 if c not in "0123456789abcdefABCDEF": |
|
330 raise ValueError("Failed to convert %r into binary data: Not a valid hex string", hexdata) |
|
331 |
|
332 temp = [] |
|
333 for i in xrange(len(hexdata) / 2): |
|
334 start = i * 2 |
|
335 end = start + 2 |
|
336 temp.append(chr(int(hexdata[start:end], 16))) |
|
337 return ''.join(temp) |
|
338 |
|
339 class Category(object): |
|
340 def __init__(self): |
|
341 self.name = None |
|
342 self.category_uid = None |
|
343 self.settings = [] |
|
344 |
|
345 def get_hcr_records(self): |
|
346 """ |
|
347 Return a list of HcrRecord objects created based on this category's settings. |
|
348 """ |
|
349 result = [] |
|
350 for setting in self.settings: |
|
351 record = HcrRecord(setting.type, setting.value, self.category_uid, setting.id) |
|
352 flag = setting.flag |
|
353 if flag: |
|
354 record.flags = 0 |
|
355 if flag.Uninitialised == '1': record.flags |= HcrRecord.FLAG_UNINITIALIZED |
|
356 if flag.Modifiable == '1': record.flags |= HcrRecord.FLAG_MODIFIABLE |
|
357 if flag.Persistent == '1': record.flags |= HcrRecord.FLAG_PERSISTENT |
|
358 result.append(record) |
|
359 return result |
|
360 |
|
361 |
|
362 class Output(object): |
|
363 def __init__(self): |
|
364 self.file = None |
|
365 self.type = None |
|
366 self.version = None |
|
367 self.read_only = None |
|
368 self.categories = [] |
|
369 |
|
370 def get_hcr_records(self): |
|
371 """ |
|
372 Return a list of HcrRecord objects created based on this output object's categories. |
|
373 """ |
|
374 result = [] |
|
375 for category in self.categories: |
|
376 result.extend(category.get_hcr_records()) |
|
377 return result |
|
378 |
|
379 def get_hcr_repository(self): |
|
380 """ |
|
381 Return a HcrRepository object created based on this output. |
|
382 |
|
383 The type of this Output object should be 'hcr', otherwise and an exception is raised. |
|
384 """ |
|
385 if self.type != 'hcr': |
|
386 raise RuntimeError("get_hcr_repository() called on an Output object with type '%s' (should be 'hcr')" % self.type) |
|
387 |
|
388 return HcrRepository(self.get_hcr_records()) |
|
389 |