Handle thumbnail creation failure according to the standard.
[rox-lib.git] / ROX-Lib2 / python / rox / AppInfo.py
blob5b53020aaca124afa474873602907eaebdb919fe
1 """Parse the AppInfo files.
2 Written by Christopher Arndt and Stephen Watson."""
4 import sys
5 from xml.dom import Node, minidom, XML_NAMESPACE
7 def _getlangs(langs):
8 if langs is None:
9 langs = ['en', 'C', '']
10 try:
11 import rox #.i18n
12 langs = rox.i18n.langs + langs
13 except:
14 pass
15 elif type(langs) is type(''):
16 langs = [langs]
17 return langs
19 def _data(node):
20 """Return all the text directly inside this DOM Node."""
21 return ''.join([text.nodeValue for text in node.childNodes
22 if text.nodeType == Node.TEXT_NODE])
24 class AppInfo:
25 """Parsed AppInfo.xml file. Current only deals with the <About>,
26 <Summary> and <AppMenu> elements"""
28 def __init__(self, source):
29 """Read the file and parse the <About> element."""
30 self._doc = minidom.parse(source)
31 self._parseAbout()
32 self._parseSummary()
33 self._parseMenu()
35 def _parseAbout(self):
36 """Collect 'About' info in a dictionary by language."""
37 self.about = {}
38 for ab in self._doc.documentElement.getElementsByTagName('About'):
39 lang = ab.getAttributeNS(XML_NAMESPACE, 'lang')
40 self.about[lang] = {}
42 for node in ab.childNodes:
43 if node.nodeType != Node.ELEMENT_NODE:
44 continue
45 name = node.localName or None
46 label = node.getAttribute('label') or name
47 self.about[lang][name] = label, _data(node)
49 def _parseSummary(self):
50 """Collect 'Summary' info in a dictionary by language."""
51 self.summary = {}
52 for summary in self._doc.documentElement.getElementsByTagName('Summary'):
53 lang=summary.getAttributeNS(XML_NAMESPACE, 'lang')
54 self.summary[lang] = _data(summary)
56 def _parseMenu(self):
57 # XXX do <AppMenu> parsing - data structure?
58 self.menu = []
60 for node in self._doc.getElementsByTagName('AppMenu'):
61 self._parseAppMenu(self.menu, node)
63 def _parseAppMenu(self, menus, node):
64 """Recursivly parse the <AppMenu>s"""
65 for item in node.getElementsByTagName('Item'):
66 opt=item.getAttribute('option')
67 icon=item.getAttribute('icon')
68 labels={}
69 for label in item.getElementsByTagName('Label'):
70 lang=label.getAttributeNS(XML_NAMESPACE, 'lang')
71 labels[lang]=_data(label)
72 sub_menus=[]
73 for sub in item.getElementsByTagName('AppMenu'):
74 self._parseAppMenu(sub_menus, sub)
75 menus.append({'option': opt, 'label':
76 labels, 'icon' : icon,
77 'sub-menus': sub_menus})
79 def getAbout(self, elname, langs=None):
80 """Return an entry from the <About> section.
82 elname is the name of the element to return text from
83 langs is a list of acceptable languages, or None to use
84 rox.i18n.langs
85 """
86 langs = _getlangs(langs)
88 for lang in langs:
89 if self.about.has_key(lang):
90 if self.about[lang].has_key(elname):
91 return self.about[lang][elname]
92 else:
93 return None
95 def getAuthors(self, langs=None):
96 """Return the contents of the <Authors> element in the
97 <About> section (also tries <Author> if needed."""
99 auth=self.getAbout('Authors', langs)
100 if auth is None:
101 auth=self.getAbout('Author', langs)
102 if auth is None:
103 return ''
104 return auth[1]
106 def setAbout(self, elname, value, lang):
107 """Set the value of an element in the <About> section.
109 If no element 'elname' is present in the <About> section with the
110 matching 'lang' attribute, append a new one. If no such section
111 exists yet, create it first.
112 value must be a unicode string.
115 if not self.about.has_key(lang):
116 ab = self._doc.createElement('About')
117 ab.setAttributeNS(XML_NAMESPACE, 'xml:lang', lang)
118 self._doc.documentElement.appendChild(ab)
120 for ab in self._doc.documentElement.getElementsByTagName('About'):
121 if ab.getAttributeNS(XML_NAMESPACE, 'lang') == lang:
122 for node in ab.childNodes:
123 if node.nodeType == Node.ELEMENT_NODE and \
124 node.localName == elname:
125 ab.removeChild(node)
126 el = self._doc.createElement(elname)
127 text = self._doc.createTextNode(value)
128 el.appendChild(text)
129 ab.appendChild(el)
130 self._parseAbout()
132 def getSummary(self, langs=None):
133 """Return the content of the <Summary> element.
135 langs is a list of acceptable languages, or None to use
136 rox.i18n.langs
138 langs = _getlangs(langs)
140 for lang in langs:
141 if self.summary.has_key(lang):
142 return self.summary[lang]
143 else:
144 return None
146 def setSummary(self, value, lang):
147 """Set content of the Summary element with matching 'lang' attribute.
149 If no such element is present, append a new one to the DOM object.
150 value must be a unicode string.
153 if self.summary.has_key(lang):
154 for summary in self._doc.documentElement.getElementsByTagName(
155 'Summary'):
156 if summary.getAttributeNS(XML_NAMESPACE, 'lang') == lang:
157 summary.parentNode.removeChild(summary)
158 summary = self._doc.createElement('Summary')
159 summary.setAttributeNS(XML_NAMESPACE, 'xml:lang', lang)
160 text = self._doc.createTextNode(value)
161 summary.appendChild(text)
162 self._doc.documentElement.appendChild(summary)
163 self._parseSummary()
165 def findElements(self, elname, ns=None):
166 """Return all instances of the named element with an optional name
167 space. They are returned as DOM objects."""
168 if ns is None:
169 return self._doc.getElementsByTagName(elname)
170 return self._doc.getElementsByTagNameNS(ns, elname)
172 def _getMenu(self, item, langs):
173 ritem={'option': item['option'], 'icon': item['icon']}
175 labels=item['label']
177 for lang in langs:
178 #print labels, lang, labels.has_key(lang)
179 if labels.has_key(lang):
180 ritem['label']=labels[lang]
181 break
182 else:
183 try:
184 ritem['label']=labels['']
185 except:
186 ritem['label']=''
188 subs=[]
189 for s in item['sub-menus']:
190 subs.append(self._getMenu(s, langs))
191 ritem['sub-menus']=subs
193 return ritem
195 def getAppMenu(self, langs=None):
196 """Return list of all menu entries. Each item in the list is a
197 dictionary with these keys:
198 'option': the option to pass to the app for each item
199 'label': the label of the item in an appropriate language
200 'icon': the icon of the item
201 'sub-menus': list of sub menu items"""
203 langs = _getlangs(langs)
205 menus=[]
206 for item in self.menu:
207 menus.append(self._getMenu(item, langs))
209 return menus
211 def _getTypeList(self, element):
212 types=[]
213 for node in self._doc.getElementsByTagName(element):
214 for t in node.getElementsByTagNameNS(None, 'MimeType'):
215 types.append(t.getAttribute('type'))
216 return types
218 def getCanRun(self):
219 """Return list of named MIME types that this application declares
220 it can handle."""
221 return self._getTypeList('CanRun')
223 def getCanThumbnail(self):
224 """Return list of named MIME types that this application declares
225 it can generate thumbnails for."""
226 return self._getTypeList('CanThumbnail')
228 def __str__(self):
229 return self._doc.toprettyxml('', '', encoding='utf-8')
231 def writeToFile(self, fname):
232 fp = open(fname, 'wb')
233 fp.write(str(self))
234 fp.close()
236 # Some test code
237 if __name__=='__main__':
238 print _getlangs(None)
239 ai=AppInfo(sys.argv[1])
240 #print ai
241 print 'Summary: %s' % ai.getSummary()
242 print ai.getAbout('Authors')
243 print 'Authors: %s' % ai.getAuthors()
244 #print ai.findElements('AppMenu')
245 print ai.menu
246 for menu in ai.getAppMenu():
247 print '%s (%s) -> %d sub' % (menu['label'], menu['option'],
248 len(menu['sub-menus']))
249 #print ai.findElements('CanRun')
250 print ai.getCanRun()
251 print ai.getCanThumbnail()