Updated Spanish translation (Juan Carlos Jiménez).
[mime-editor.git] / type.py
blob5228defc71638f0b75a4b358f387685ad5666809
1 import os
2 import rox
3 from rox import g, basedir
4 from xml.parsers import expat
5 from xml.dom import XML_NAMESPACE, Node
6 import fields
7 import override
8 from override import FREE_NS
10 types = {}
12 home_mime = os.path.join(basedir.xdg_data_home, 'mime')
14 def data(node):
15 return ''.join([text.nodeValue for text in node.childNodes
16 if text.nodeType == Node.TEXT_NODE])
18 def get_type(name):
19 if name not in types:
20 types[name] = MIME_Type(name)
21 return types[name]
23 class MIME_Type:
24 def __init__(self, name):
25 assert name not in types
26 self.media, self.subtype = name.split('/')
28 self.comments = []
29 self.globs = []
30 self.magic = []
31 self.xml = []
32 self.others = []
34 def add_comment(self, lang, comment, user):
35 self.comments.append((lang, comment, user))
37 def add_xml(self, uri, name, user):
38 self.xml.append((uri, name, user))
40 def add_magic(self, prio, root, user):
41 self.magic.append((prio, root, user))
43 def add_other(self, element, user):
44 self.others.append((element, user))
46 def add_glob(self, pattern, user):
47 self.globs.append((pattern, user))
49 def get_comment(self):
50 best = None
51 for lang, comment, user in self.comments:
52 if not lang:
53 return comment
54 best = comment
55 return best or self.get_name()
57 def get_name(self):
58 return self.media + '/' + self.subtype
60 def make(self, klass, list):
61 return [klass(self, item) for item in list]
63 def get_comments(self): return self.make(fields.Comment, self.comments)
64 def get_globs(self): return self.make(fields.Glob, self.globs)
65 def get_magic(self): return self.make(fields.Magic, self.magic)
66 def get_xml(self): return self.make(fields.XML, self.xml)
67 def get_others(self): return self.make(fields.Other, self.others)
69 def remove_user(self):
70 for list in ['comments', 'globs', 'magic', 'xml', 'others']:
71 setattr(self, list,
72 [x for x in getattr(self, list) if not x[-1]])
74 class FieldParser:
75 def __init__(self, type, attrs):
76 self.type = type
78 def start(self, element, attrs): pass
79 def data(self, data): pass
80 def end(self): pass
82 class CommentParser(FieldParser):
83 def __init__(self, type, attrs, user):
84 FieldParser.__init__(self, type, attrs)
85 self.lang = attrs.get(XML_NAMESPACE + ' lang', None)
86 self.comment = ''
87 self.user = user
89 def data(self, data):
90 self.comment += data
92 def end(self):
93 self.type.add_comment(self.lang, self.comment, self.user)
95 class Match:
96 def __init__(self, parent, user):
97 self.parent = parent
98 self.matches = []
99 self.user = user
101 self.offset = self.type = self.value = self.mask = None
103 def write_xml(self, node):
104 for m in self.matches:
105 new = node.ownerDocument.createElementNS(FREE_NS, 'match')
106 new.setAttributeNS(None, 'offset', m.offset)
107 new.setAttributeNS(None, 'type', m.type)
108 new.setAttributeNS(None, 'value', m.value)
109 if m.mask:
110 new.setAttributeNS(None, 'mask', m.mask)
111 node.appendChild(new)
112 m.write_xml(new)
114 def equals_node(self, node):
115 if self.parent:
116 if node.localName != 'match' or node.namespaceURI != FREE_NS:
117 return False
118 def get(x, d):
119 a = node.getAttributeNS(None, x)
120 if a is None or a == '': return d
121 return a
122 offset = get('offset', '?')
123 type = get('type', '?')
124 value = get('value', '?')
125 mask = get('mask', None)
126 if self.offset != offset or self.type != type or \
127 self.value != value or self.mask != mask:
128 return False
130 kids = []
131 for k in node.childNodes:
132 if k.nodeType != Node.ELEMENT_NODE: continue
133 if k.localName != 'match' or k.namespaceURI != FREE_NS: continue
134 kids.append(k)
136 if len(self.matches) != len(kids):
137 return False
139 for m, k in zip(self.matches, kids):
140 if not m.equals_node(k): return False
142 return True
144 class MagicParser(FieldParser):
145 def __init__(self, type, attrs, user):
146 FieldParser.__init__(self, type, attrs)
147 self.prio = int(attrs.get('priority', 50))
148 self.match = Match(None, user)
149 self.user = user
151 def start(self, element, attrs):
152 new = Match(self.match, self.user)
153 new.offset = attrs.get('offset', '?')
154 new.type = attrs.get('type', '?')
155 new.value = attrs.get('value', '?')
156 new.mask = attrs.get('mask', None)
157 self.match.matches.append(new)
158 self.match = new
160 def end(self):
161 if self.match.parent:
162 self.match = self.match.parent
163 else:
164 self.type.add_magic(self.prio, self.match, self.user)
166 class Scanner:
167 def __init__(self):
168 self.level = 0
169 self.type = None
170 self.handler = None
172 def parse(self, path, user):
173 parser = expat.ParserCreate(namespace_separator = ' ')
174 parser.StartElementHandler = self.start
175 parser.EndElementHandler = self.end
176 parser.CharacterDataHandler = self.data
177 self.user = user
178 parser.ParseFile(file(path))
180 def start(self, element, attrs):
181 self.level += 1
182 if self.level == 1:
183 assert element == FREE_NS + ' mime-info'
184 elif self.level == 2:
185 assert element == FREE_NS + ' mime-type'
186 self.type = get_type(attrs['type'])
187 elif self.level == 3:
188 if element == FREE_NS + ' comment':
189 self.handler = CommentParser(self.type, attrs,
190 self.user)
191 elif element == FREE_NS + ' glob':
192 self.type.add_glob(attrs['pattern'], self.user)
193 elif element == FREE_NS + ' magic':
194 self.handler = MagicParser(self.type, attrs,
195 self.user)
196 elif element == FREE_NS + ' root-XML':
197 self.type.add_xml(attrs['namespaceURI'],
198 attrs['localName'],
199 self.user)
200 else:
201 self.type.add_other(element, self.user)
202 else:
203 assert self.handler
204 self.handler.start(element, attrs)
206 def end(self, element):
207 if self.handler:
208 self.handler.end()
209 self.level -=1
210 if self.level == 1:
211 self.type = None
212 elif self.level == 2:
213 self.handler = None
215 def data(self, data):
216 if self.handler:
217 self.handler.data(data)
219 def scan_file(path, user):
220 if not path.endswith('.xml'): return
221 if not os.path.exists(path):
222 return
223 scanner = Scanner()
224 try:
225 scanner.parse(path, user)
226 except:
227 rox.report_exception()
229 def add_type(name, comment, glob):
230 doc = override.get_override()
232 root = doc.documentElement
233 type_node = doc.createElementNS(FREE_NS, 'mime-type')
234 root.appendChild(type_node)
236 type_node.setAttributeNS(None, 'type', name)
238 f = doc.createElementNS(FREE_NS, 'comment')
239 f.appendChild(doc.createTextNode(comment))
240 type_node.appendChild(f)
242 if glob:
243 f = doc.createElementNS(FREE_NS, 'glob')
244 f.setAttributeNS(None, 'pattern', glob)
245 type_node.appendChild(f)
247 override.write_override(doc)
248 import __main__
249 __main__.box.show_type(name)
251 def delete_type(name):
252 doc = override.get_override()
253 removed = False
254 for c in doc.documentElement.childNodes:
255 if c.nodeType != Node.ELEMENT_NODE: continue
256 if c.nodeName != 'mime-type': continue
257 if c.namespaceURI != FREE_NS: continue
258 if c.getAttributeNS(None, 'type') != name: continue
259 doc.documentElement.removeChild(c)
260 removed = True
261 if not removed:
262 rox.alert(_("No user-provided information about this type -- can't remove anything"))
263 return
264 if not rox.confirm(_("Really remove all user-specified information about type %s?") % name,
265 g.STOCK_DELETE):
266 return
267 override.write_override(doc)
269 # Type names defined even without Override.xml
270 system_types = None
272 def init():
273 "(Re)read the database."
274 global types, system_types
275 if system_types is None:
276 types = {}
277 for mime_dir in basedir.load_data_paths('mime'):
278 packages_dir = os.path.join(mime_dir, 'packages')
279 if not os.path.isdir(packages_dir):
280 continue
281 packages = os.listdir(packages_dir)
282 packages.sort()
283 for package in packages:
284 if package == 'Override.xml' and mime_dir == home_mime: continue
285 scan_file(os.path.join(packages_dir, package), False)
286 system_types = types.keys()
287 else:
288 for t in types.keys():
289 if t not in system_types:
290 del types[t]
291 for t in types.values():
292 t.remove_user()
293 scan_file(override.user_override, True)