|
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 ''' |
|
19 A plugin implementation for content selection from ConfigurationLayers. |
|
20 ''' |
|
21 |
|
22 |
|
23 import re |
|
24 import os |
|
25 import sys |
|
26 import logging |
|
27 import shutil |
|
28 import copy |
|
29 import xml.parsers.expat |
|
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 |
|
46 class ContentOutput(object): |
|
47 def __init__(self, **kwargs): |
|
48 self._dir = kwargs.get('dir',None) |
|
49 self._file = kwargs.get('file',None) |
|
50 self.flatten = kwargs.get('flatten',False) |
|
51 self.inputs = kwargs.get('inputs', []) |
|
52 self.configuration = None |
|
53 |
|
54 def set_configuration(self, configuration): |
|
55 self.configuration = configuration |
|
56 for input in self.inputs: |
|
57 input.configuration = self.configuration |
|
58 |
|
59 def get_configuration(self, configuration): |
|
60 self.configuration = configuration |
|
61 |
|
62 def path_convert(self, path): |
|
63 (drive, tail) = os.path.splitdrive(path) |
|
64 return tail.lstrip('\\/') |
|
65 |
|
66 def get_dir(self): |
|
67 if self.configuration and ConfmlRefs.is_confml_ref(self._dir): |
|
68 parts = self._dir.split(ConfmlRefs.ref_separator) |
|
69 for (index, part) in enumerate(parts): |
|
70 if ConfmlRefs.is_confml_ref(part): |
|
71 ref = ConfmlRefs.get_confml_ref(part) |
|
72 parts[index] = self.configuration.get_default_view().get_feature(ref).value |
|
73 parts[index] = self.path_convert(parts[index]) |
|
74 else: |
|
75 parts[index] = part |
|
76 return (os.sep).join(parts) |
|
77 else: |
|
78 return self.path_convert(self._dir) |
|
79 |
|
80 def set_dir(self, dir): |
|
81 self._dir = dir |
|
82 |
|
83 def get_file(self): |
|
84 if self.configuration and self._file != None and ConfmlRefs.is_confml_ref(self._file): |
|
85 parts = self._file.split(ConfmlRefs.ref_separator) |
|
86 for (index, part) in enumerate(parts): |
|
87 if ConfmlRefs.is_confml_ref(part): |
|
88 ref = ConfmlRefs.get_confml_ref(part) |
|
89 parts[index] = self.configuration.get_default_view().get_feature(ref).value |
|
90 parts[index] = self.path_convert(parts[index]) |
|
91 else: |
|
92 parts[index] = part |
|
93 return (os.sep).join(parts) |
|
94 else: |
|
95 return self._file |
|
96 |
|
97 def set_file(self, file): |
|
98 self._file = file |
|
99 |
|
100 dir = property(get_dir, set_dir) |
|
101 file = property(get_file, set_file) |
|
102 |
|
103 def get_refs(self): |
|
104 refs = [] |
|
105 for input in self.inputs: |
|
106 refs.extend(input.get_refs()) |
|
107 return refs |
|
108 |
|
109 |
|
110 class ContentInput(object): |
|
111 def __init__(self, **kwargs): |
|
112 self._dir = kwargs.get('dir',None) |
|
113 self._file = kwargs.get('file',None) |
|
114 self._include = kwargs.get('include', {}) |
|
115 self._exclude = kwargs.get('exclude', {}) |
|
116 self.configuration = None |
|
117 |
|
118 @property |
|
119 def dir(self): |
|
120 |
|
121 if self.configuration and ConfmlRefs.is_confml_ref(self._dir): |
|
122 cref = ConfmlRefs.get_confml_ref(self._dir) |
|
123 return self.configuration.get_default_view().get_feature(cref).value |
|
124 else: |
|
125 return self._dir |
|
126 |
|
127 @property |
|
128 def file(self): |
|
129 |
|
130 if self._file and self.configuration and ConfmlRefs.is_confml_ref(self._file): |
|
131 cref = ConfmlRefs.get_confml_ref(self._file) |
|
132 return self.configuration.get_default_view().get_feature(cref).value |
|
133 else: |
|
134 return self._file |
|
135 |
|
136 |
|
137 def _dereference_dict(self, data): |
|
138 if self.configuration: |
|
139 # Make a deep copy of the data, or otherwise get_refs() will |
|
140 # return the correct refs only on the first call, as the |
|
141 # references are replaced here |
|
142 data = copy.deepcopy(data) |
|
143 |
|
144 dview = self.configuration.get_default_view() |
|
145 for key in data: |
|
146 key_list = data.get(key) |
|
147 for (index,elem) in enumerate(key_list): |
|
148 if ConfmlRefs.is_confml_ref(elem): |
|
149 cref = ConfmlRefs.get_confml_ref(elem) |
|
150 try: |
|
151 # change None value to empty string |
|
152 cvalue = dview.get_feature(cref).value or '' |
|
153 if utils.is_list(cvalue): |
|
154 cvalue = ", ".join(cvalue) |
|
155 key_list[index] = cvalue |
|
156 except exceptions.NotFound: |
|
157 logging.getLogger('cone.content').error("Feature ref '%s' in include key '%s' not found." % (cref,key)) |
|
158 return data |
|
159 |
|
160 @property |
|
161 def include(self): |
|
162 return self._dereference_dict(self._include) |
|
163 |
|
164 @property |
|
165 def exclude(self): |
|
166 return self._dereference_dict(self._exclude) |
|
167 |
|
168 def get_filelist(self): |
|
169 filelist = [] |
|
170 if self.file: |
|
171 filelist.append(self.file) |
|
172 for elem in self.include.get('files',[]): |
|
173 elem = elem.lower().split(',') |
|
174 filelist += [selem.strip() for selem in elem] |
|
175 return filelist |
|
176 |
|
177 def get_include_pattern(self): |
|
178 return self.include.get('pattern',[''])[0] |
|
179 |
|
180 def get_exclude_pattern(self): |
|
181 return self.exclude.get('pattern',[''])[0] |
|
182 |
|
183 def get_refs(self): |
|
184 refs = [] |
|
185 if self._dir is not None: |
|
186 refs.extend(utils.extract_delimited_tokens(self._dir)) |
|
187 if self._file is not None: |
|
188 refs.extend(utils.extract_delimited_tokens(self._file)) |
|
189 for value_list in self._include.itervalues(): |
|
190 for value in value_list: |
|
191 refs.extend(utils.extract_delimited_tokens(value)) |
|
192 for value_list in self._exclude.itervalues(): |
|
193 for value in value_list: |
|
194 refs.extend(utils.extract_delimited_tokens(value)) |
|
195 return refs |
|
196 |
|
197 |
|
198 class ExternalContentInput(ContentInput): |
|
199 def __init__(self, **kwargs): |
|
200 super(ExternalContentInput,self).__init__(**kwargs) |
|
201 |
|
202 class ContentParserBase(object): |
|
203 """ |
|
204 Parses a single content implml file |
|
205 """ |
|
206 NAMESPACES = ['http://www.s60.com/xml/content/1'] |
|
207 INCLUDE_ATTR = ['pattern'] |
|
208 EXCLUDE_ATTR = ['pattern'] |
|
209 def __init__(self): |
|
210 self.namespaces = self.NAMESPACES |
|
211 |
|
212 def parse_phase(self,etree): |
|
213 phase = "" |
|
214 phase = etree.get('phase') |
|
215 return phase |
|
216 |
|
217 def parse_desc(self,etree): |
|
218 desc = "" |
|
219 desc_elem = etree.find("{%s}desc" % self.namespaces[0]) |
|
220 if desc_elem != None: |
|
221 desc = desc_elem.text |
|
222 return desc |
|
223 |
|
224 def parse_tags(self,etree): |
|
225 tags = {} |
|
226 for tag in etree.getiterator("{%s}tag" % self.namespaces[0]): |
|
227 tagname = tag.get('name','') |
|
228 tagvalue = tag.get('value') |
|
229 values = tags.get(tagname,[]) |
|
230 values.append(tagvalue) |
|
231 tags[tagname] = values |
|
232 return tags |
|
233 |
|
234 def parse_input_include(self,etree): |
|
235 include_elem = etree.getiterator("{%s}include" % self.namespaces[0]) |
|
236 include = {} |
|
237 for f in include_elem: |
|
238 for key in f.keys(): |
|
239 # Add the attribute if it is found to include dict |
|
240 include[key] = [] |
|
241 include[key].append(f.get(key)) |
|
242 return include |
|
243 |
|
244 def parse_input_exclude(self,etree): |
|
245 elem = etree.getiterator("{%s}exclude" % self.namespaces[0]) |
|
246 exclude = {} |
|
247 for f in elem: |
|
248 for key in f.keys(): |
|
249 # Add the attribute if it is found |
|
250 exclude[key] = [] |
|
251 exclude[key].append(f.get(key)) |
|
252 return exclude |
|
253 |
|
254 class Content1Parser(ContentParserBase): |
|
255 """ |
|
256 Parses a single content implml file |
|
257 """ |
|
258 NAMESPACES = ['http://www.s60.com/xml/content/1'] |
|
259 def __init__(self): |
|
260 super(ContentParserBase,self).__init__() |
|
261 self.namespaces = self.NAMESPACES |
|
262 |
|
263 def parse_input(self,etree): |
|
264 input_elem = etree.find("{%s}input" % self.namespaces[0]) |
|
265 input_dir = "" |
|
266 input_file = "" |
|
267 if input_elem != None: |
|
268 if input_elem.get('dir'): |
|
269 input_dir = input_elem.get('dir') |
|
270 if input_elem.get('file'): |
|
271 input_dir = input_elem.get('file') |
|
272 includes = self.parse_input_include(etree) |
|
273 excludes = self.parse_input_exclude(etree) |
|
274 return ContentInput(dir=input_dir, include=includes, exclude=excludes, file=input_file) |
|
275 return None |
|
276 |
|
277 def parse_outputs(self,etree): |
|
278 output_elem = etree.find("{%s}output" % self.namespaces[0]) |
|
279 output_dir = "" |
|
280 output_flatten = False |
|
281 inputs = [] |
|
282 if output_elem != None: |
|
283 output_dir = output_elem.get('dir','') |
|
284 output_flatten = output_elem.get('flatten','') == "true" |
|
285 input = self.parse_input(etree) |
|
286 if input: |
|
287 inputs.append(input) |
|
288 return [ContentOutput(dir=output_dir, flatten=output_flatten, inputs=inputs)] |
|
289 |
|
290 class Content2Parser(ContentParserBase): |
|
291 """ |
|
292 Parses a single content implml file |
|
293 """ |
|
294 NAMESPACES = ['http://www.s60.com/xml/content/2'] |
|
295 def __init__(self): |
|
296 super(ContentParserBase,self).__init__() |
|
297 self.namespaces = self.NAMESPACES |
|
298 |
|
299 |
|
300 def parse_input(self,input_elem): |
|
301 input = None |
|
302 input_dir = '' |
|
303 input_file = '' |
|
304 if input_elem != None: |
|
305 if input_elem.get('dir'): |
|
306 input_dir = input_elem.get('dir') |
|
307 if input_elem.get('file'): |
|
308 input_file= input_elem.get('file') |
|
309 includes = self.parse_input_include(input_elem) |
|
310 excludes = self.parse_input_exclude(input_elem) |
|
311 input = ContentInput(dir=input_dir, include=includes, exclude=excludes, file=input_file) |
|
312 return input |
|
313 |
|
314 def parse_external_input(self,input_elem): |
|
315 input = None |
|
316 input_dir = '' |
|
317 if input_elem != None: |
|
318 if input_elem.get('dir'): |
|
319 input_dir = input_elem.get('dir') |
|
320 includes = self.parse_input_include(input_elem) |
|
321 excludes = self.parse_input_exclude(input_elem) |
|
322 input = ExternalContentInput(dir=input_dir, include=includes, exclude=excludes) |
|
323 return input |
|
324 |
|
325 def parse_outputs(self,etree): |
|
326 outputs = [] |
|
327 for output_elem in etree.getiterator("{%s}output" % self.namespaces[0]): |
|
328 inputs = [] |
|
329 output_dir = output_elem.get('dir','') |
|
330 output_file = output_elem.get('file','') |
|
331 output_flatten = output_elem.get('flatten','') == "true" |
|
332 for input_elem in output_elem.getiterator("{%s}input" % self.namespaces[0]): |
|
333 inputs.append(self.parse_input(input_elem)) |
|
334 for input_elem in output_elem.getiterator("{%s}externalinput" % self.namespaces[0]): |
|
335 inputs.append(self.parse_external_input(input_elem)) |
|
336 outputs.append(ContentOutput(dir=output_dir, flatten=output_flatten, inputs=inputs, file=output_file)) |
|
337 return outputs |
|
338 |
|
339 |
|
340 class ContentImplReader(object): |
|
341 """ |
|
342 Parses a single content implml file |
|
343 """ |
|
344 PARSERS = {'http://www.s60.com/xml/content/1' : Content1Parser, |
|
345 'http://www.s60.com/xml/content/2' : Content2Parser} |
|
346 def __init__(self): |
|
347 self.desc = None |
|
348 self.outputs = None |
|
349 self.phase = None |
|
350 |
|
351 def fromstring(self, xml_as_string): |
|
352 etree = ElementTree.fromstring(xml_as_string) |
|
353 # Loop through parsers and try to find a match |
|
354 (namespace,elemname) = get_elemname(etree.tag) |
|
355 pclass = self.PARSERS.get(namespace, None) |
|
356 self.parser = pclass() |
|
357 self.desc = self.parser.parse_desc(etree) |
|
358 self.outputs = self.parser.parse_outputs(etree) |
|
359 self.phase = self.parser.parse_phase(etree) |
|
360 self.tags = self.parser.parse_tags(etree) |
|
361 return |
|
362 |
|
363 namespace_pattern = re.compile("{(.*)}(.*)") |
|
364 nonamespace_pattern = re.compile("(.*)") |
|
365 |
|
366 def get_elemname(tag): |
|
367 |
|
368 ns = namespace_pattern.match(tag) |
|
369 nn = nonamespace_pattern.match(tag) |
|
370 if ns: |
|
371 namespace = ns.group(1) |
|
372 elemname = ns.group(2) |
|
373 return (namespace,elemname) |
|
374 elif nn: |
|
375 namespace = "" |
|
376 elemname = nn.group(1) |
|
377 return (namespace,elemname) |
|
378 else: |
|
379 raise exceptions.ParseError("Could not parse tag %s" % tag) |
|
380 |
|
381 class ConfmlRefs(object): |
|
382 |
|
383 ref_pattern = re.compile('^\$\{(.*)\}$') |
|
384 cref_pattern = re.compile('.+\..+') |
|
385 ref_separator = '/' |
|
386 |
|
387 @classmethod |
|
388 def is_ref_like(cls, variableref): |
|
389 """ |
|
390 |
|
391 Returns true if the given variable represents a ref |
|
392 """ |
|
393 return cls.cref_pattern.match(variableref) != None |
|
394 |
|
395 @classmethod |
|
396 def is_confml_ref(cls, variableref): |
|
397 """ |
|
398 |
|
399 Returns true if the given variable ref is a confml reference |
|
400 """ |
|
401 |
|
402 pos = variableref.find(cls.ref_separator) |
|
403 if pos == -1: |
|
404 return cls.ref_pattern.match(variableref) != None |
|
405 else: |
|
406 return cls.is_confml_refs(variableref) |
|
407 |
|
408 @classmethod |
|
409 def is_confml_refs(cls, variableref): |
|
410 """ |
|
411 |
|
412 Returns true if the given variable ref is a confml reference |
|
413 """ |
|
414 ret = False |
|
415 parts = variableref.split(cls.ref_separator) |
|
416 for p in parts: |
|
417 if cls.is_confml_ref(p) == True: |
|
418 ret = True |
|
419 return ret |
|
420 |
|
421 |
|
422 @classmethod |
|
423 def get_confml_ref(cls, variableref): |
|
424 """ |
|
425 |
|
426 Returns true if the given variable ref is a confml reference |
|
427 """ |
|
428 pos = variableref.find(cls.ref_separator) |
|
429 if pos == -1: |
|
430 matchref = cls.ref_pattern.match(variableref) |
|
431 if matchref: |
|
432 return matchref.group(1) |
|
433 else: |
|
434 return cls.get_confml_refs(variableref) |
|
435 |
|
436 @classmethod |
|
437 def get_confml_refs(cls, variableref): |
|
438 """ |
|
439 |
|
440 Returns an array of confml refs based on variableref |
|
441 """ |
|
442 parts = variableref.split(cls.ref_separator) |
|
443 ret = [] |
|
444 for p in parts: |
|
445 matchref = cls.ref_pattern.match(p) |
|
446 if matchref: |
|
447 ref = matchref.group(1) |
|
448 if not ref in ret: |
|
449 ret.append(matchref.group(1)) |
|
450 else: |
|
451 ret.append(p) |
|
452 return ret |