|
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 GenConfml plugin for ConE |
|
18 ''' |
|
19 |
|
20 import re |
|
21 import os |
|
22 import sys |
|
23 import logging |
|
24 import xml.parsers.expat |
|
25 import confflattener |
|
26 import xslttransformer |
|
27 import codecs |
|
28 import tempfile |
|
29 import tempfile |
|
30 |
|
31 try: |
|
32 from cElementTree import ElementTree |
|
33 except ImportError: |
|
34 try: |
|
35 from elementtree import ElementTree |
|
36 except ImportError: |
|
37 try: |
|
38 from xml.etree import cElementTree as ElementTree |
|
39 except ImportError: |
|
40 from xml.etree import ElementTree |
|
41 |
|
42 import __init__ |
|
43 |
|
44 from cone.public import exceptions,plugin,utils,api |
|
45 from cone.confml import persistentconfml |
|
46 |
|
47 class GenconfmlImpl(plugin.ImplBase): |
|
48 """ |
|
49 GenConfml plugin implementation |
|
50 """ |
|
51 |
|
52 IMPL_TYPE_ID = "gcfml" |
|
53 |
|
54 |
|
55 def __init__(self,ref,configuration, output='output', linesep=os.linesep, reader=None): |
|
56 """ |
|
57 Overloading the default constructor |
|
58 """ |
|
59 plugin.ImplBase.__init__(self,ref,configuration) |
|
60 self.logger = logging.getLogger('cone.gcfml(%s)' % self.ref) |
|
61 self.errors = False |
|
62 self.xstl_etree = None |
|
63 self.xslt_temp_file_name = os.path.join(tempfile.gettempdir(), "genconfml_temp_%i.xslt" % os.getpid()) |
|
64 self.set_output_root(output) |
|
65 self.linesep = linesep |
|
66 self._flatconfig = None |
|
67 self.temp_confml_file = os.path.join(tempfile.gettempdir(),'temp_flatted_%i.confml' % os.getpid()) |
|
68 self.reader = reader |
|
69 |
|
70 def generate(self, context=None): |
|
71 """ |
|
72 Generate the given implementation. |
|
73 """ |
|
74 self.create_output() |
|
75 return |
|
76 |
|
77 def get_refs(self): |
|
78 result = [] |
|
79 for ref in self.reader.settings: |
|
80 # Process the reference, so that it will work with has_ref(). |
|
81 # E.g. 'MyFeature/MySetting' -> 'MyFeature.MySetting' |
|
82 # 'MyFeature/* -> 'MyFeature' |
|
83 ref = ref.replace('/', '.') |
|
84 if ref.endswith('.*'): |
|
85 ref = ref[:-2] |
|
86 result.append(ref) |
|
87 return result |
|
88 |
|
89 def list_output_files(self): |
|
90 """ Return a list of output files as an array. """ |
|
91 return [self.get_output_filename()] |
|
92 |
|
93 def get_output_filename(self): |
|
94 """ Return a output file name. """ |
|
95 |
|
96 name = self.reader.name |
|
97 if name == None: name = "" |
|
98 target = self.reader.target |
|
99 if target == None: target = "" |
|
100 output = self.output |
|
101 if self.output == None: output = "" |
|
102 |
|
103 # Make sure that target file is generated under output |
|
104 target = utils.resourceref.remove_begin_slash(utils.resourceref.norm(target)) |
|
105 subdir = self.reader.subdir |
|
106 if subdir == None: |
|
107 self.output_subdir = subdir |
|
108 output_file = os.path.normpath(os.path.join(output, target, name)) |
|
109 |
|
110 return output_file |
|
111 |
|
112 def create_output(self, layers=None): |
|
113 """ Generate all output """ |
|
114 resource = self.configuration.get_resource(self.ref) |
|
115 write_element_enc(self.reader.stylesheet_elem, self.xslt_temp_file_name, self.reader.stylesheet_output_enc) |
|
116 gen = Generator() |
|
117 |
|
118 target = self.reader.target |
|
119 if target == None: target = "" |
|
120 |
|
121 output_file = self.get_output_filename() |
|
122 # Don't create the dirs here, since the output file may be filtered out |
|
123 # if it is empty |
|
124 #if not os.path.exists(os.path.dirname(output_file)): |
|
125 # os.makedirs(os.path.dirname(output_file)) |
|
126 |
|
127 self.logger.info('Generating %s' % output_file) |
|
128 |
|
129 flatted_conf_as_element = persistentconfml.ConfmlWriter().dumps(self.flatconfig) |
|
130 postprocessed_element = self.post_process_flattening(flatted_conf_as_element) |
|
131 write_element_enc(postprocessed_element, self.temp_confml_file, self.reader.stylesheet_output_enc) |
|
132 gen.generate(self.configuration, resource, output_file, self.xslt_temp_file_name, self.reader.settings, self.reader.stylesheet_output_enc) |
|
133 |
|
134 def post_process_flattening(self, element): |
|
135 """ |
|
136 Pick just data element and build document out of it |
|
137 """ |
|
138 |
|
139 data_element = element.find("data") |
|
140 if data_element == None: |
|
141 self.logger.warning('No data to generate!!') |
|
142 new_doc = "<?xml version=\"1.0\"?><configuration>" + "</configuration>" |
|
143 else: |
|
144 new_doc = "<?xml version=\"1.0\"?><configuration>" + ElementTree.tostring(data_element) + "</configuration>" |
|
145 return ElementTree.fromstring(new_doc) |
|
146 |
|
147 @property |
|
148 def flatconfig(self): |
|
149 """ |
|
150 Create a flat configuration from the current configuration with the given setting refs. |
|
151 Take the last configuration element, which will contain the data elements |
|
152 """ |
|
153 if not self._flatconfig: |
|
154 try: |
|
155 cf = confflattener.ConfigurationFlattener() |
|
156 self._flatconfig = api.Configuration() |
|
157 cf.flat(self.configuration, self.reader.settings, self._flatconfig) |
|
158 except (exceptions.ConeException, TypeError, Exception), e: |
|
159 utils.log_exception(self.logger, 'Failed to flat configuration with settings %s. Exception: %s' % (self.reader.settings, e)) |
|
160 raise exceptions.ConeException('Failed to flat configuration. Exception: %s' % e) |
|
161 return self._flatconfig |
|
162 |
|
163 |
|
164 def write_element(element, output, linesep=os.linesep): |
|
165 """ |
|
166 """ |
|
167 if element != None and ElementTree.iselement(element): |
|
168 enc = None |
|
169 |
|
170 |
|
171 try: |
|
172 out_file = open(output, 'w') |
|
173 out_string = ElementTree.tostring(element) |
|
174 out_string = out_string.replace('\r\n', linesep) |
|
175 out_string = out_string.replace('\n', linesep) |
|
176 out_file.write(out_string) |
|
177 out_file.close() |
|
178 except Exception, e: |
|
179 raise exceptions.ConeException('Cannot write Element to file (%s). Exception: %s' % (output, e)) |
|
180 else: |
|
181 raise exceptions.ConeException('Cannot write element to file, because None element passed or not Element passed.') |
|
182 |
|
183 def remove_namespace(doc, namespace): |
|
184 """Remove namespace in the passed document in place.""" |
|
185 ns = u'{%s}' % namespace |
|
186 nsl = len(ns) |
|
187 for elem in doc.getiterator(): |
|
188 if elem.tag.startswith(ns): |
|
189 elem.tag = elem.tag[nsl:] |
|
190 |
|
191 def write_element_enc(element, output, enc, linesep=os.linesep): |
|
192 """ |
|
193 Writes element to file |
|
194 """ |
|
195 if element != None and ElementTree.iselement(element): |
|
196 enc = None |
|
197 |
|
198 |
|
199 try: |
|
200 remove_namespace(element, 'http://www.s60.com/xml/genconfml/1') |
|
201 |
|
202 |
|
203 out_file = codecs.open(output, 'w', enc) |
|
204 output_string = ElementTree.tostring(element) |
|
205 output_string = output_string.replace('\r\n', linesep) |
|
206 output_string = output_string.replace('\n', linesep) |
|
207 out_file.write(output_string) |
|
208 out_file.close() |
|
209 except Exception, e: |
|
210 raise exceptions.ConeException('Cannot write Element to file (%s). Exception: %s' % (output, e)) |
|
211 else: |
|
212 raise exceptions.ConeException('Cannot write element to file, because None element passed or not Element passed.') |
|
213 |
|
214 |
|
215 def write_element_tempfile(element, tempfile): |
|
216 """ |
|
217 Writes element to temp file |
|
218 """ |
|
219 if element != None and ElementTree.iselement(element): |
|
220 |
|
221 try: |
|
222 tempfile.write(ElementTree.tostring(element)) |
|
223 except Exception, e: |
|
224 raise exceptions.ConeException('Cannot write Element to file (%s). Exception: %s' % (output, e)) |
|
225 else: |
|
226 raise exceptions.ConeException('Cannot write element to file, because None element passed or not Element passed.') |
|
227 |
|
228 class GenconfmlImplReader(plugin.ReaderBase): |
|
229 """ |
|
230 Parses a single gcfml file |
|
231 """ |
|
232 NAMESPACE = 'http://www.s60.com/xml/genconfml/1' |
|
233 IGNORED_NAMESPACES = ['http://www.w3.org/1999/XSL/Transform', |
|
234 'http://www.w3.org/2001/xinclude'] |
|
235 FILE_EXTENSIONS = ['gcfml'] |
|
236 |
|
237 def __init__(self): |
|
238 self.stylesheet = None |
|
239 self.namespaces = self.IGNORED_NAMESPACES + [self.NAMESPACE] |
|
240 self.settings = None |
|
241 self.name = None |
|
242 self.subdir = None |
|
243 self.target = None |
|
244 self.stylesheet_elem = None |
|
245 self.stylesheet_output_enc = None |
|
246 self.nss = None |
|
247 |
|
248 @classmethod |
|
249 def read_impl(cls, resource_ref, configuration, etree): |
|
250 |
|
251 reader = GenconfmlImplReader() |
|
252 reader.from_etree(etree) |
|
253 return GenconfmlImpl(resource_ref, configuration, reader=reader) |
|
254 |
|
255 def from_etree(self, etree): |
|
256 self.stylesheet = self.parse_stylesheet(etree) |
|
257 self.settings = self.parse_settings(etree) |
|
258 self.name = self.parse_name(etree) |
|
259 self.subdir = self.parse_subdir(etree) |
|
260 self.target = self.parse_target(etree) |
|
261 self.stylesheet_elem = self.parse_stylesheet_elem(etree) |
|
262 self.stylesheet_output_enc = self.parse_stylesheet_output_enc(etree) |
|
263 self.nss = self.parse_stylesheet_nss(etree) |
|
264 |
|
265 return |
|
266 |
|
267 def parse_target(self, etree): |
|
268 """ |
|
269 Parses target from etree |
|
270 """ |
|
271 |
|
272 target = "" |
|
273 for elem in etree.getiterator("{%s}file" % self.namespaces[2]): |
|
274 if elem != None: |
|
275 target = elem.get('target') |
|
276 |
|
277 return target |
|
278 |
|
279 def parse_name(self, etree): |
|
280 """ |
|
281 Parses name from etree |
|
282 """ |
|
283 |
|
284 name = "" |
|
285 for elem in etree.getiterator("{%s}file" % self.namespaces[2]): |
|
286 if elem != None: |
|
287 name = elem.get('name') |
|
288 |
|
289 return name |
|
290 |
|
291 def parse_subdir(self, etree): |
|
292 """ |
|
293 Parses subdir from etree |
|
294 """ |
|
295 |
|
296 subdir = "" |
|
297 for elem in etree.getiterator("{%s}file" % self.namespaces[2]): |
|
298 if elem != None: |
|
299 subdir = elem.get('subdir') |
|
300 if subdir == None: |
|
301 subdir = "" |
|
302 |
|
303 return subdir |
|
304 |
|
305 |
|
306 def parse_stylesheet(self,etree): |
|
307 """ |
|
308 Parses stylesheet from getree |
|
309 """ |
|
310 |
|
311 stylesheet = "" |
|
312 stylesheet_elem = etree.find("{%s}stylesheet" % self.namespaces[0]) |
|
313 if stylesheet_elem != None: |
|
314 stylesheet = ElementTree.tostring(stylesheet_elem) |
|
315 return stylesheet |
|
316 |
|
317 def parse_stylesheet_output_enc(self, etree): |
|
318 enc = "" |
|
319 ss_elem = etree.find("{%s}stylesheet" % self.namespaces[0]) |
|
320 if ss_elem != None: |
|
321 children = ss_elem.getchildren() |
|
322 for child in children: |
|
323 if child.tag == '{%s}output' % self.namespaces[0]: |
|
324 enc = child.attrib.get('encoding') |
|
325 return enc |
|
326 |
|
327 def parse_stylesheet_nss(self, etree): |
|
328 nss = None |
|
329 |
|
330 for elem in etree.getiterator(): |
|
331 name = elem.tag |
|
332 if name[0] == "{": |
|
333 uri, tag = name[1:].split("}") |
|
334 if tag == "stylesheet": |
|
335 nss = uri |
|
336 return nss |
|
337 |
|
338 def parse_stylesheet_elem(self,etree): |
|
339 """ |
|
340 Parses stylesheet element from getree |
|
341 """ |
|
342 |
|
343 return etree.find("{%s}stylesheet" % self.namespaces[0]) |
|
344 |
|
345 def parse_settings(self,etree): |
|
346 """ |
|
347 Parses settings from etree |
|
348 """ |
|
349 |
|
350 settings = [] |
|
351 |
|
352 for elem in etree.getiterator("{%s}file" % self.namespaces[2]): |
|
353 if elem != None: |
|
354 setting_elems = elem.findall("{%s}setting" % self.namespaces[2]) |
|
355 for setting_elem in setting_elems: |
|
356 if setting_elem != None: |
|
357 settings.append(setting_elem.get('ref')) |
|
358 |
|
359 return settings |
|
360 |
|
361 class Generator(object): |
|
362 """ |
|
363 Genconfml generator |
|
364 """ |
|
365 def __init__(self): |
|
366 self.temp_confml_file = os.path.join(tempfile.gettempdir(),'temp_flatted_%i.confml' % os.getpid()) |
|
367 pass |
|
368 |
|
369 def post_process_flattening(self, element): |
|
370 """ |
|
371 Pick just data element and build document out of it |
|
372 """ |
|
373 |
|
374 data_element = element.find("data") |
|
375 if data_element == None: |
|
376 self.logger.warning('No data to generate!!') |
|
377 new_doc = "<?xml version=\"1.0\"?><configuration>" + "</configuration>" |
|
378 else: |
|
379 new_doc = "<?xml version=\"1.0\"?><configuration>" + ElementTree.tostring(data_element) + "</configuration>" |
|
380 return ElementTree.fromstring(new_doc) |
|
381 |
|
382 |
|
383 def generate(self, configuration, input, output, xslt, settings, enc=sys.getdefaultencoding()): |
|
384 """ |
|
385 Generates output |
|
386 """ |
|
387 self.logger = logging.getLogger('cone.gcfml{%s}' % input.path) |
|
388 |
|
389 |
|
390 try: |
|
391 tf = xslttransformer.XsltTransformer() |
|
392 tf.transform_lxml(os.path.abspath(self.temp_confml_file), os.path.abspath(xslt), output, enc) |
|
393 #tf.transform_4s(os.path.abspath(self.temp_confml_file), os.path.abspath(xslt), output, enc) |
|
394 except (exceptions.ConeException, TypeError, Exception), e: |
|
395 logging.getLogger('cone.gcfml').warning('Failed to do XSLT tranformation. Exception: %s' % e) |
|
396 raise exceptions.ConeException('Failed to do XSLT tranformation. Exception: %s' % e) |
|
397 |
|
398 """ Removes template files """ |
|
399 if not logging.getLogger('cone').getEffectiveLevel() != 10: |
|
400 os.remove(os.path.abspath(self.temp_confml_file)) |
|
401 os.remove(os.path.abspath(xslt)) |