|
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 # @author Teemu Rytkonen |
|
18 import re |
|
19 import logging |
|
20 import simplejson |
|
21 |
|
22 """ cone specific imports """ |
|
23 from cone.public import persistence, exceptions, api, utils, container |
|
24 from cone.carbon import model |
|
25 |
|
26 MODEL = model |
|
27 |
|
28 def dumps(obj, indent=True): |
|
29 """ Make sure that the object is mapped to an object in this model """ |
|
30 mobj = obj._get_mapper('carbon').map_object(obj) |
|
31 writer = get_writer_for_class(mobj.__class__.__name__) |
|
32 dict = writer.dumps(mobj) |
|
33 # Return the data as dict, as it is urlencoded by client |
|
34 return dict |
|
35 |
|
36 def loads(jsonstr): |
|
37 return CarbonReader().loads(jsonstr) |
|
38 |
|
39 |
|
40 class CarbonResourceMapper(object): |
|
41 def __init__(self): |
|
42 self.CARBON_RESOURCE_TYPE_MAP = {'configurationroot' : self.map_carbon_configurationroot, |
|
43 'configurationlayer' : self.map_carbon_configurationlayer, |
|
44 'featurelist' : self.map_carbon_featurelist} |
|
45 self.CONFML_RESOURCE_TYPE_MAP = {'configurationroot' : self.map_confml_configurationroot, |
|
46 'configurationlayer' : self.map_confml_configurationlayer, |
|
47 'featurelist' : self.map_confml_featurelist} |
|
48 |
|
49 def map_carbon_resource(self, resourcepath): |
|
50 for resourceext in self.CARBON_RESOURCE_TYPE_MAP: |
|
51 if resourcepath.endswith(resourceext): |
|
52 return self.CARBON_RESOURCE_TYPE_MAP[resourceext](resourcepath) |
|
53 return resourcepath |
|
54 |
|
55 def map_confml_resource(self, resourcetype, resourcepath): |
|
56 return self.CONFML_RESOURCE_TYPE_MAP[resourcetype](resourcepath) |
|
57 |
|
58 def map_carbon_configurationroot(self, resourcepath): |
|
59 return resourcepath.replace('.configurationroot', '.confml') |
|
60 |
|
61 def map_carbon_configurationlayer(self, resourcepath): |
|
62 return resourcepath.replace('.configurationlayer', '/root.confml') |
|
63 |
|
64 def map_carbon_featurelist(self, resourcepath): |
|
65 return "featurelists/%s" % resourcepath.replace('.featurelist', '.confml') |
|
66 |
|
67 def map_confml_configurationroot(self, resourcepath): |
|
68 return resourcepath.replace('.confml', '.configurationroot') |
|
69 |
|
70 def map_confml_configurationlayer(self, resourcepath): |
|
71 return resourcepath.replace('/root.confml', '.configurationlayer') |
|
72 |
|
73 def map_confml_featurelist(self, resourcepath): |
|
74 path = resourcepath.replace('featurelists/','').replace('.confml', '') |
|
75 version_identifier = 'WORKING' |
|
76 m = re.match('^(.*) \((.*)\)', path) |
|
77 # if the resourcepath does not have version information |
|
78 # use default WORKING |
|
79 if m: |
|
80 path = m.group(1) |
|
81 version_identifier = m.group(2) |
|
82 return '%s (%s).featurelist' % (path, version_identifier) |
|
83 |
|
84 class ResourceListReader(persistence.ConeReader): |
|
85 """ |
|
86 """ |
|
87 def loads(self, jsonstr): |
|
88 """ |
|
89 @param jsonstr: The json string to read. |
|
90 """ |
|
91 reslist = model.ResourceList() |
|
92 datadict = simplejson.loads(jsonstr) |
|
93 for configuration in datadict.get('configurations', []): |
|
94 reslist.add_resource(model.ConfigurationResource(**configuration)) |
|
95 for featurelist in datadict.get('featurelists', []): |
|
96 reslist.add_resource(model.FeatureListResource(**featurelist)) |
|
97 return reslist |
|
98 |
|
99 class HasResourceReader(persistence.ConeReader): |
|
100 """ |
|
101 """ |
|
102 def loads(self, jsonstr): |
|
103 """ |
|
104 @param jsonstr: The json string to read. |
|
105 """ |
|
106 try: |
|
107 datadict = simplejson.loads(jsonstr) |
|
108 return datadict.get('has_resource',False) |
|
109 except ValueError,e: |
|
110 logging.getLogger('cone').error("Failed to parser json from %s" % jsonstr) |
|
111 raise e |
|
112 |
|
113 |
|
114 class CarbonWriter(persistence.ConeWriter): |
|
115 """ |
|
116 """ |
|
117 def dumps(self, obj): |
|
118 """ |
|
119 @param obj: The object |
|
120 """ |
|
121 """ Make sure that the object is mapped to an object in this model """ |
|
122 mobj = obj._get_mapper('carbon').map_object(obj) |
|
123 writer = get_writer_for_class(mobj.__class__.__name__) |
|
124 return writer.dumps(obj) |
|
125 |
|
126 |
|
127 class CarbonReader(persistence.ConeReader): |
|
128 """ |
|
129 """ |
|
130 def loads(self, jsonstr): |
|
131 """ |
|
132 @param xml: The xml which to read. reads only the first object. |
|
133 """ |
|
134 try: |
|
135 datadict = simplejson.loads(jsonstr) |
|
136 for key in datadict: |
|
137 reader = get_reader_for_elem(key) |
|
138 return reader.loads(datadict[key]) |
|
139 except (SyntaxError, ValueError),e: |
|
140 utils.log_exception(logging.getLogger('cone'), "Json string parse raised exception: %s!" % (e)) |
|
141 raise exceptions.ParseError("Json string %s parse raised exception: %s!" % (jsonstr,e)) |
|
142 |
|
143 class ConfigurationCreateWriter(CarbonWriter): |
|
144 @classmethod |
|
145 def supported_class(cls, classname): |
|
146 """ |
|
147 Class method to determine if this CarbonWriter supports writing |
|
148 of the given class name |
|
149 """ |
|
150 return False |
|
151 |
|
152 def dumps(self, obj): |
|
153 """ |
|
154 @param obj: The Configuration object |
|
155 """ |
|
156 featurelists = [] |
|
157 included = [] |
|
158 # Remove the featurelists and configurations from the creation phase |
|
159 # resmapper = CarbonResourceMapper() |
|
160 # for confpath in obj.list_configurations(): |
|
161 # config = obj.get_configuration(confpath) |
|
162 # if config.meta and config.meta.get('type') == 'featurelist': |
|
163 # featurelists.append(resmapper.map_confml_resource('featurelist',confpath)) |
|
164 # elif config.meta and config.meta.get('type'): |
|
165 # included.append(resmapper.map_confml_resource(config.meta.get('type'),confpath)) |
|
166 # else: |
|
167 # # ignore configs that are not carbon configs |
|
168 # pass |
|
169 |
|
170 configuration_dict = {'name' : obj.name, |
|
171 'parent_path' : '', |
|
172 'included' : included, |
|
173 'description' : obj.desc or 'Needs description', |
|
174 'configuration_type' : 'carbon', |
|
175 'resource_type' : 'configuration', |
|
176 'feature_lists' : featurelists, |
|
177 } |
|
178 |
|
179 return configuration_dict |
|
180 |
|
181 class ConfigurationWriter(CarbonWriter): |
|
182 @classmethod |
|
183 def supported_class(cls, classname): |
|
184 """ |
|
185 Class method to determine if this CarbonWriter supports writing |
|
186 of the given class name |
|
187 """ |
|
188 if classname=="CarbonConfiguration": |
|
189 return True |
|
190 else: |
|
191 return False |
|
192 |
|
193 def dumps(self, obj): |
|
194 if obj.meta: |
|
195 if obj.meta.get('type') == 'configurationroot': |
|
196 return self.dumps_root(obj) |
|
197 elif obj.meta.get('type') == 'configurationlayer': |
|
198 return self.dumps_layer(obj) |
|
199 raise Exception("Not supported CarbonConfigruration, %s" % obj) |
|
200 |
|
201 def dumps_root(self, obj): |
|
202 """ |
|
203 @param obj: The Configuration object |
|
204 """ |
|
205 featurelists = [] |
|
206 included = [] |
|
207 resmapper = CarbonResourceMapper() |
|
208 for confpath in obj.list_configurations(): |
|
209 config = obj.get_configuration(confpath) |
|
210 if config.meta: |
|
211 if config.meta.get('type') == 'featurelist': |
|
212 featurelists.append(resmapper.map_confml_resource('featurelist',confpath)) |
|
213 else: |
|
214 included.append(resmapper.map_confml_resource(config.meta.get('type'),confpath)) |
|
215 else: |
|
216 # This default case could also be identified as error |
|
217 included.append(confpath) |
|
218 |
|
219 configuration_dict = {'feature_lists': featurelists, |
|
220 'parent_config': None, |
|
221 'configuration_name': obj.name, |
|
222 'version_identifier': obj.version_identifier, |
|
223 'included': included, |
|
224 'ref': obj.ref} |
|
225 |
|
226 return configuration_dict |
|
227 |
|
228 def dumps_layer(self, obj): |
|
229 """ |
|
230 @param obj: The Configuration object |
|
231 """ |
|
232 configuration_dict = {'version_identifier': obj.version_identifier} |
|
233 |
|
234 datawriter = DataWriter() |
|
235 data = datawriter.dumps(obj) |
|
236 configuration_dict['data'] = data |
|
237 |
|
238 return configuration_dict |
|
239 |
|
240 class ConfigurationRootReader(CarbonReader): |
|
241 """ |
|
242 """ |
|
243 @classmethod |
|
244 def supported_elem(cls, elemname, parent=None): |
|
245 """ |
|
246 Class method to determine if this ConfmlWriter supports writing |
|
247 of the given elem name |
|
248 """ |
|
249 if elemname=="configurationroot": |
|
250 return True |
|
251 else: |
|
252 return False |
|
253 |
|
254 def __init__(self): |
|
255 pass |
|
256 |
|
257 def loads(self, dict): |
|
258 """ |
|
259 @param obj: The Configuration object |
|
260 """ |
|
261 name = dict.get('configuration_name') |
|
262 path = name + ".confml" |
|
263 conf = model.CarbonConfiguration(dict.get('ref'), path=path, type='configurationroot') |
|
264 conf.name = name |
|
265 conf.version = dict.get('version_identifier') |
|
266 resmapper = CarbonResourceMapper() |
|
267 |
|
268 """ Read the featurelists as included configurations """ |
|
269 for fealist in dict.get('feature_lists',[]): |
|
270 conf.include_configuration(resmapper.map_carbon_resource(fealist)) |
|
271 """ Read the included configurations """ |
|
272 for includedconfig in dict.get('included',[]): |
|
273 conf.include_configuration(resmapper.map_carbon_resource(includedconfig)) |
|
274 return conf |
|
275 |
|
276 class ConfigurationLayerReader(CarbonReader): |
|
277 """ |
|
278 """ |
|
279 @classmethod |
|
280 def supported_elem(cls, elemname, parent=None): |
|
281 """ |
|
282 Class method to determine if this ConfmlWriter supports writing |
|
283 of the given elem name |
|
284 """ |
|
285 if elemname=="configurationlayer": |
|
286 return True |
|
287 else: |
|
288 return False |
|
289 |
|
290 def __init__(self): |
|
291 pass |
|
292 |
|
293 def loads(self, dict): |
|
294 """ |
|
295 @param obj: The Configuration object |
|
296 """ |
|
297 name = dict.get('configuration_name') |
|
298 path = name + ".confml" |
|
299 conf = model.CarbonConfiguration(dict.get('ref'), path=path, type='configurationlayer') |
|
300 conf.name = name |
|
301 |
|
302 conf.version = dict.get('version_identifier') |
|
303 |
|
304 """ Last read the data of this configuration and add it as a configuration """ |
|
305 data_reader = DataReader() |
|
306 datacont = data_reader.loads(dict.get('data', {})) |
|
307 proxy = api.ConfigurationProxy(datacont.path) |
|
308 conf.add_configuration(proxy) |
|
309 proxy._set_obj(datacont) |
|
310 |
|
311 return conf |
|
312 |
|
313 class FeatureListCreateWriter(CarbonWriter): |
|
314 @classmethod |
|
315 def supported_class(cls, classname): |
|
316 """ |
|
317 Feature list create writer is supported only explicitly |
|
318 """ |
|
319 return False |
|
320 |
|
321 def dumps(self, obj): |
|
322 """ |
|
323 @param obj: The FeatureList object |
|
324 """ |
|
325 """ Make sure that the object is mapped to an object in this model """ |
|
326 mobj = obj._get_mapper('carbon').map_object(obj) |
|
327 featurelist_dict = {'type' : 'featurelist', |
|
328 'flv_description' : mobj.desc or 'Needs description', |
|
329 'version_identifier' : mobj.version_identifier |
|
330 } |
|
331 if hasattr(mobj, 'responsible'): |
|
332 featurelist_dict['responsible'] = mobj.responsible |
|
333 return featurelist_dict |
|
334 |
|
335 class FeatureListWriter(CarbonWriter): |
|
336 @classmethod |
|
337 def supported_class(cls, classname): |
|
338 """ |
|
339 Feature list create writer is supported only explicitly |
|
340 """ |
|
341 if classname == 'FeatureList': |
|
342 return True |
|
343 else: |
|
344 return False |
|
345 |
|
346 def dumps(self, obj): |
|
347 """ |
|
348 @param obj: The FeatureList object |
|
349 """ |
|
350 |
|
351 featurelist_dict = { |
|
352 'type' : 'featurelist', |
|
353 'name' : obj.name, |
|
354 'flv_description' : obj.desc or 'Needs description', |
|
355 'path' : obj.path, |
|
356 'features' : [] |
|
357 } |
|
358 if obj.meta.get('version_identifier'): |
|
359 featurelist_dict['version_identifier'] = obj.meta.get('version_identifier') |
|
360 # add all features of the featurelist |
|
361 for fearef in obj.list_features(): |
|
362 feature = obj.get_feature(fearef) |
|
363 writer = FeatureWriter() |
|
364 feadict = writer.dumps(feature) |
|
365 featurelist_dict['features'].append(feadict) |
|
366 |
|
367 return featurelist_dict |
|
368 |
|
369 class FeatureListReader(CarbonReader): |
|
370 """ |
|
371 """ |
|
372 @classmethod |
|
373 def supported_elem(cls, elemname, parent=None): |
|
374 """ |
|
375 Class method to determine if this ConfmlWriter supports writing |
|
376 of the given elem name |
|
377 """ |
|
378 if elemname=="featurelist": |
|
379 return True |
|
380 else: |
|
381 return False |
|
382 |
|
383 def __init__(self): |
|
384 pass |
|
385 |
|
386 def loads(self, dict): |
|
387 """ |
|
388 @param obj: The Configuration object |
|
389 """ |
|
390 fealist_expanded = dict.get('expanded') |
|
391 fealist_version = dict.get('version_identifier') |
|
392 fealist_is_latest_version = dict.get('is_latest_version') |
|
393 fealist_list_id = dict.get('list_id') |
|
394 fealist_path = dict.get('path') |
|
395 fealist_version_title = dict.get('version_title') |
|
396 fealist_can_be_released = dict.get('can_be_released') |
|
397 fealist_type = dict.get('type') |
|
398 fealist_has_external_relations = dict.get('is_latest_version') |
|
399 |
|
400 # Create a configuration object from the featurelist |
|
401 conf = model.FeatureList(path='featurelists/'+fealist_version_title+'.confml') |
|
402 conf.meta.add('version_identifier', fealist_version) |
|
403 |
|
404 for feature in dict.get('features'): |
|
405 reader = FeatureReader() |
|
406 fea = reader.loads(feature) |
|
407 if fea != None: |
|
408 conf.add_feature(fea) |
|
409 |
|
410 for feafqr in conf.list_all_features(): |
|
411 # Add empty data object to featurelist configuration |
|
412 conf.add_data(api.Data(fqr=feafqr)) |
|
413 |
|
414 return conf |
|
415 |
|
416 |
|
417 class FeatureWriter(CarbonWriter): |
|
418 CONFML_TO_CARBON_TYPE = { |
|
419 'boolean' : 'BOOLEAN', |
|
420 'int' : 'INTEGER', |
|
421 'selection' : 'SELECTION', |
|
422 'string' : 'STRING', |
|
423 None : None, |
|
424 '' : '' |
|
425 } |
|
426 @classmethod |
|
427 def supported_class(cls, classname): |
|
428 """ |
|
429 Class method to determine if this ConfmlWriter supports writing |
|
430 of the given class name |
|
431 """ |
|
432 if classname=="Feature" or\ |
|
433 classname=="CarbonBooleanSetting" or\ |
|
434 classname=="CarbonIntSetting" or\ |
|
435 classname=="CarbonStringSetting" or\ |
|
436 classname=="CarbonSelectSetting"or\ |
|
437 classname=="CarbonSetting": |
|
438 return True |
|
439 else: |
|
440 return False |
|
441 |
|
442 def dumps(self, obj): |
|
443 """ |
|
444 @param obj: The Feature object |
|
445 """ |
|
446 """ Make sure that the object is mapped to an object in this model """ |
|
447 mobj = obj._get_mapper('carbon').map_object(obj) |
|
448 |
|
449 featuredict = {'type' : 'feature', |
|
450 'status' : 'APPROVED', |
|
451 'title' : mobj.name, |
|
452 'ref' : mobj.ref, |
|
453 'description' : mobj.desc or 'Needs description', |
|
454 'responsible' : None, |
|
455 'value_type' : self.CONFML_TO_CARBON_TYPE[mobj.type], |
|
456 'children' : []} |
|
457 if featuredict['value_type'] != None: |
|
458 featuredict['type_object'] = 'carbon_feature_type_normal' |
|
459 if mobj.type == 'selection': |
|
460 featuredict['options'] = mobj.options.keys() |
|
461 |
|
462 writer = FeatureWriter() |
|
463 for fearef in mobj.list_features(): |
|
464 feaobj = obj.get_feature(fearef) |
|
465 childdict = writer.dumps(feaobj) |
|
466 featuredict['children'].append(childdict) |
|
467 return featuredict |
|
468 |
|
469 |
|
470 class FeatureReader(CarbonReader): |
|
471 """ |
|
472 """ |
|
473 @classmethod |
|
474 def supported_elem(cls, elemname, parent=None): |
|
475 """ |
|
476 Class method to determine if this ConfmlWriter supports writing |
|
477 of the given elem name |
|
478 """ |
|
479 if elemname=="features": |
|
480 return True |
|
481 else: |
|
482 return False |
|
483 |
|
484 def __init__(self): |
|
485 pass |
|
486 |
|
487 def loads(self, dict): |
|
488 """ |
|
489 @param obj: The Configuration object |
|
490 """ |
|
491 id = dict.get('id') |
|
492 name = dict.get('title') |
|
493 ref = dict.get('ref') |
|
494 ref = utils.resourceref.to_objref(ref) |
|
495 status = dict.get('status') |
|
496 value_type = dict.get('value_type') |
|
497 description = dict.get('description') |
|
498 |
|
499 if value_type == 'boolean': |
|
500 fea = model.CarbonBooleanSetting(ref, type=value_type) |
|
501 elif value_type == 'integer': |
|
502 fea = model.CarbonIntSetting(ref, type=value_type) |
|
503 elif value_type == 'string': |
|
504 fea = model.CarbonStringSetting(ref, type=value_type) |
|
505 elif value_type == 'selection': |
|
506 fea = model.CarbonSelectionSetting(ref, type=value_type) |
|
507 for option in dict.get('options'): |
|
508 fea.add_option(option,option) |
|
509 elif value_type == '': |
|
510 fea = model.CarbonFeature(ref, type=value_type) |
|
511 else: |
|
512 fea = model.CarbonFeature(ref) |
|
513 |
|
514 |
|
515 fea.name = name |
|
516 fea.status = status |
|
517 fea.desc = description |
|
518 |
|
519 for childdict in dict.get('children',[]): |
|
520 reader = FeatureReader() |
|
521 subfea = reader.loads(childdict) |
|
522 if subfea != None: |
|
523 fea.add_feature(subfea) |
|
524 return fea |
|
525 |
|
526 class DataWriter(CarbonWriter): |
|
527 @classmethod |
|
528 def supported_class(cls, classname): |
|
529 """ |
|
530 Class method to determine if this ConfmlWriter supports writing |
|
531 of the given class name |
|
532 """ |
|
533 if classname=="Data" or \ |
|
534 classname=="DataContainer": |
|
535 return True |
|
536 else: |
|
537 return False |
|
538 |
|
539 def dumps(self, obj): |
|
540 """ |
|
541 @param obj: The DataContainer object |
|
542 """ |
|
543 datadict = {} |
|
544 for dataelem in obj._traverse(type=api.Data): |
|
545 if dataelem.get_value() != None: |
|
546 datadict[dataelem.get_fearef()] = map_confml2carbon_value(dataelem.get_value()) |
|
547 return datadict |
|
548 |
|
549 |
|
550 class DataReader(CarbonReader): |
|
551 """ |
|
552 """ |
|
553 @classmethod |
|
554 def supported_elem(cls, elemname, parent=None): |
|
555 """ |
|
556 Class method to determine if this ConfmlWriter supports writing |
|
557 of the given elem name |
|
558 """ |
|
559 if elemname=="data": |
|
560 return True |
|
561 else: |
|
562 return False |
|
563 |
|
564 def __init__(self): |
|
565 pass |
|
566 |
|
567 def loads(self, dict): |
|
568 """ |
|
569 @param obj: The Configuration object |
|
570 """ |
|
571 datacont = api.Configuration('confml/data.confml') |
|
572 for dataref in dict.keys(): |
|
573 # Ignore null values |
|
574 if dict[dataref]: |
|
575 refs = [] |
|
576 for elem in dataref.split('.'): |
|
577 refs.append(utils.resourceref.to_objref(elem)) |
|
578 newref = '.'.join(refs) |
|
579 dataelem = api.Data(fqr=newref, value=map_carbon2confml_value(dict[dataref])) |
|
580 datacont.add_data(dataelem) |
|
581 return datacont |
|
582 |
|
583 def map_carbon2confml_value(value): |
|
584 if value == 'DEFINED': |
|
585 return 'true' |
|
586 elif value == 'UNDEFINED': |
|
587 return 'false' |
|
588 else: |
|
589 return value |
|
590 |
|
591 def map_confml2carbon_value(value): |
|
592 if value == 'true': |
|
593 return 'DEFINED' |
|
594 elif value == 'false': |
|
595 return 'UNDEFINED' |
|
596 else: |
|
597 return value |
|
598 |
|
599 def get_reader_for_elem(elemname, parent=None): |
|
600 for reader in CarbonReader.__subclasses__(): |
|
601 if reader.supported_elem(elemname,parent): |
|
602 return reader() |
|
603 raise exceptions.ConePersistenceError("No reader for given elem %s under %s found!" % (elemname, parent)) |
|
604 |
|
605 def get_writer_for_class(classname): |
|
606 for writer in CarbonWriter.__subclasses__(): |
|
607 if writer.supported_class(classname): |
|
608 return writer () |
|
609 raise exceptions.ConePersistenceError("No writer for given class found! %s" % classname) |