|
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 re |
|
18 import os |
|
19 import sys |
|
20 import logging |
|
21 import xml.parsers.expat |
|
22 import unzip |
|
23 import shutil |
|
24 |
|
25 try: |
|
26 from cElementTree import ElementTree |
|
27 except ImportError: |
|
28 try: |
|
29 from elementtree import ElementTree |
|
30 except ImportError: |
|
31 try: |
|
32 from xml.etree import cElementTree as ElementTree |
|
33 except ImportError: |
|
34 from xml.etree import ElementTree |
|
35 |
|
36 |
|
37 from cone.public import exceptions,plugin,utils,api |
|
38 from themeplugin import theme_function |
|
39 from theme_resource import ThemeResource |
|
40 from theme_container import ThemeContainer, ActiveTheme |
|
41 |
|
42 class ThemeImpl(plugin.ImplBase): |
|
43 """ |
|
44 This class provides converting *.tpf files to target device |
|
45 |
|
46 |
|
47 Building process: |
|
48 1. All tpf files are founded in the cpf file according to "preinstalled directories" |
|
49 and "CVC settings" which are defined in the thememl file. |
|
50 |
|
51 2. The tpf files are extracted to temporary directories. Every tpf file has self temporary directory |
|
52 |
|
53 3. *.tdf, *.svg files are builded to *.mbm, *.skn,... by using Carbide.UI command-line. |
|
54 The path of Carbide.UI is defined in the thememl file. |
|
55 |
|
56 Here is two possible cases: |
|
57 3a) The theme has defined UID number in thememl file. |
|
58 The Carbide.UI is run with parameter -uid %number%. |
|
59 Then this UID number (after converting to decimal format) is saved to |
|
60 platform setting in the step 5 |
|
61 |
|
62 3b) The theme has not defined UID number |
|
63 The Carbide.UI is run without parameter -uid %number% and then |
|
64 the PID number is getted from *.pkg file and setted to platform setting in the decimal format |
|
65 in the step 5 |
|
66 |
|
67 4. *.mbm, *.skn,... are copied to output directory according to content of the pkg file. |
|
68 The *.pkg file contains the record where the *.mbm, *.skn,... are be copied. |
|
69 Sample: "themepackage.mbm" - "private\10207114\import\99d49b086e6097b8\themepackage.mbm" |
|
70 |
|
71 5. UID or PID number are saved to platform setting which is defined in the thememl file |
|
72 6. Temporary directories are removed |
|
73 """ |
|
74 |
|
75 |
|
76 IMPL_TYPE_ID = "thememl" |
|
77 DEFAULT_INVOCATION_PHASE = 'pre' |
|
78 |
|
79 |
|
80 def __init__(self,ref,configuration): |
|
81 """ |
|
82 Overloading the default constructor |
|
83 """ |
|
84 plugin.ImplBase.__init__(self,ref,configuration) |
|
85 self.logger = logging.getLogger('cone.thememl') |
|
86 |
|
87 def build(self): |
|
88 """ |
|
89 Building process of themes |
|
90 """ |
|
91 # Get absolute path so that copying works correctly |
|
92 # despite working directory changing |
|
93 abs_output = os.path.abspath(os.path.join(self.output, "content")) |
|
94 |
|
95 # get *.tpf files from the configuration |
|
96 list_tpf = self.list_tpf_files(self.list_active_theme, self.list_theme_dir) |
|
97 |
|
98 theme_container = ThemeContainer(list_tpf,self.configuration) |
|
99 theme_container.carbide = self.carbide |
|
100 theme_container.create_themes() |
|
101 theme_container.prepare_active_themes(self.list_active_theme) |
|
102 theme_container.build_theme(self.theme_version) |
|
103 theme_container.copy_resources_to_output(abs_output) |
|
104 theme_container.set_active_PID_to_model() |
|
105 theme_container.removeTempDirs() |
|
106 |
|
107 |
|
108 |
|
109 def list_tpf_files(self,list_active_theme, list_theme_dir): |
|
110 """ |
|
111 returns the list of tpf files which are in the configuration |
|
112 """ |
|
113 list_tpf=[] |
|
114 default_view = self.configuration.get_default_view() |
|
115 |
|
116 for active_theme in list_active_theme: |
|
117 path=active_theme.get_setting_ref().replace("/",".") |
|
118 feature = default_view.get_feature(path+".localPath") |
|
119 setting = feature.get_data() |
|
120 if setting != None: |
|
121 list_tpf.append(setting.get_value()) |
|
122 |
|
123 for theme_dir in list_theme_dir: |
|
124 theme_dir=theme_dir.replace("/",".") |
|
125 feature = default_view.get_feature(theme_dir+".localPath") |
|
126 setting = feature.get_data() |
|
127 if setting != None: |
|
128 list_tpf.append(setting.get_value()) |
|
129 |
|
130 |
|
131 return self.find_tpf_files(list_tpf) |
|
132 |
|
133 |
|
134 def find_tpf_files(self, list_tpf_path): |
|
135 """ |
|
136 finds *.tpf files in the data container |
|
137 """ |
|
138 list_tpf={} |
|
139 |
|
140 datacontainer = self.configuration.layered_content() |
|
141 contentfiles = datacontainer.flatten() |
|
142 for reskey in contentfiles.keys(): |
|
143 respath = contentfiles[reskey] |
|
144 |
|
145 if respath.endswith(".tpf"): |
|
146 # Strip file name from the resource path |
|
147 respath_basename = os.path.split(respath)[0] |
|
148 |
|
149 for tpf_path in list_tpf_path: |
|
150 # os.path.split() strips trailing slash, so do that here too |
|
151 |
|
152 tpf_path = "/content/" + tpf_path |
|
153 if tpf_path.endswith(".tpf"): |
|
154 if respath.endswith(tpf_path): |
|
155 list_tpf[respath]=0 |
|
156 break |
|
157 |
|
158 if tpf_path.endswith('/'): |
|
159 tpf_path = tpf_path.rstrip('/') |
|
160 if respath_basename.endswith(tpf_path): |
|
161 list_tpf[respath]=0 |
|
162 break |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 return list_tpf.keys() |
|
169 |
|
170 |
|
171 def generate(self, context=None): |
|
172 """ |
|
173 Generate the given implementation. |
|
174 """ |
|
175 self.parse_impl() |
|
176 self.build() |
|
177 |
|
178 return |
|
179 |
|
180 def generate_layers(self,layers): |
|
181 """ |
|
182 Generate the given Configuration layers. |
|
183 """ |
|
184 self.logger.info('Generating layers %s' % layers) |
|
185 self.create_output(layers) |
|
186 return |
|
187 |
|
188 def has_ref(self,ref): |
|
189 """ |
|
190 @returns True if the implementation uses the given ref as input value. |
|
191 Otherwise return False. |
|
192 """ |
|
193 return None |
|
194 |
|
195 def parse_impl(self): |
|
196 if self.configuration: |
|
197 resource =self.configuration.get_resource(self.ref) |
|
198 reader = ThemeImplReader() |
|
199 try: |
|
200 self.logger.info('Parses %s' % self.ref) |
|
201 reader.fromstring(resource.read()) |
|
202 self.carbide = reader.carbide |
|
203 except (SyntaxError),e: |
|
204 logging.getLogger('cone.thememl(%s)' % resource.get_path()).error('Invalid xml in layer root file. Exception: %s' % (e)) |
|
205 raise exceptions.ParseError('Invalid xml in layer root file (%s). Exception: %s' % (resource.get_path(),e)) |
|
206 self.list_theme_dir=reader.list_theme_dir |
|
207 self.list_active_theme=reader.list_active_theme |
|
208 self.theme_version = reader.theme_version |
|
209 resource.close() |
|
210 |
|
211 return |
|
212 |
|
213 def list_output_files(self): |
|
214 """ |
|
215 Return a list of output files as an array. |
|
216 """ |
|
217 # What to return if the output files cannot be known in advance? |
|
218 return [] |
|
219 |
|
220 |
|
221 class ThemeImplReader(plugin.ReaderBase): |
|
222 """ |
|
223 Parses a single thememl file |
|
224 """ |
|
225 NAMESPACE = 'http://www.s60.com/xml/thememl/1' |
|
226 FILE_EXTENSIONS = ['thememl'] |
|
227 |
|
228 def __init__(self): |
|
229 self.namespaces = [self.NAMESPACE] |
|
230 self.list_theme_dir = [] |
|
231 self.list_active_theme = [] |
|
232 self.theme_version = "" |
|
233 self.logger = logging.getLogger('cone.thememl') |
|
234 self.carbide = r"C:\Program Files\Nokia\Carbide.ui Theme Edition 3.4" |
|
235 |
|
236 @classmethod |
|
237 def read_impl(cls, resource_ref, configuration, etree): |
|
238 reader = ThemeImplReader() |
|
239 reader.parse_thememl(etree) |
|
240 |
|
241 impl = ThemeImpl(resource_ref, configuration) |
|
242 impl.list_theme_dir = reader.list_theme_dir |
|
243 impl.list_active_theme = reader.list_active_theme |
|
244 impl.theme_version = reader.theme_version |
|
245 return impl |
|
246 |
|
247 def fromstring(self, xml_as_string): |
|
248 etree = ElementTree.fromstring(xml_as_string) |
|
249 self.parse_thememl(etree) |
|
250 |
|
251 def parse_thememl(self,etree): |
|
252 |
|
253 list_setting_uid={} |
|
254 |
|
255 #parses the version of the theme |
|
256 el_theme_version= etree.find("{%s}themeVersion" % self.namespaces[0]) |
|
257 if el_theme_version != None: |
|
258 self.theme_version = el_theme_version.text |
|
259 |
|
260 car= etree.find("{%s}carbideuiPath" % self.namespaces[0]) |
|
261 envpattern = ".*(%(.*)%).*" |
|
262 if car != None: |
|
263 mo = re.match(envpattern, car.text) |
|
264 if mo: |
|
265 if os.environ.has_key(mo.group(2)): |
|
266 self.carbide = car.text.replace(mo.group(1), os.environ[mo.group(2)]) |
|
267 else: |
|
268 self.carbide = car.text |
|
269 else: |
|
270 self.carbide = car.text |
|
271 |
|
272 |
|
273 #parses the path of directories where are tpf files |
|
274 el_list_theme_dir = etree.findall("{%s}themeDir" % self.namespaces[0]) |
|
275 for el_theme_dir in el_list_theme_dir: |
|
276 if el_theme_dir != None: |
|
277 self.list_theme_dir.append(el_theme_dir.text) |
|
278 |
|
279 #parses the active themes and theirs ref setting and platform settings |
|
280 el_list_active_theme = etree.findall("{%s}activeTheme" % self.namespaces[0]) |
|
281 for el_active_theme in el_list_active_theme: |
|
282 uid = el_active_theme.get("uid") |
|
283 active_theme = ActiveTheme() |
|
284 |
|
285 active_theme.set_uid(uid) |
|
286 for el_ref_setting in el_active_theme.getiterator("{%s}refSetting" % self.namespaces[0]): |
|
287 active_theme.set_setting_ref(el_ref_setting.text) |
|
288 |
|
289 |
|
290 for el_setting_uid in el_active_theme.getiterator("{%s}platformUID" % self.namespaces[0]): |
|
291 setting_uid = el_setting_uid.text |
|
292 if list_setting_uid.has_key(setting_uid): |
|
293 raise exceptions.ParseError('The file contains duplicate setting uid: %s' % setting_uid) |
|
294 else: |
|
295 list_setting_uid[setting_uid]=0 |
|
296 active_theme.set_setting_uids(setting_uid) |
|
297 |
|
298 self.list_active_theme.append(active_theme) |