]> git.phdru.name Git - bookmarks_db.git/blob - bkmk_parser.py
Added docstrings, __{version,revision,etc}__ boilerplates.
[bookmarks_db.git] / bkmk_parser.py
1 """Parser for Netscape Navigator's and Mozilla's bookmarks.html
2
3 This file is a part of Bookmarks database and Internet robot.
4 """
5
6 __version__ = "$Revision$"[11:-2]
7 __revision__ = "$Id$"[5:-2]
8 __date__ = "$Date$"[7:-2]
9 __author__ = "Oleg Broytman <phd@phdru.name>"
10 __copyright__ = "Copyright (C) 1997-2011 PhiloSoft Design"
11 __license__ = "GNU GPL"
12
13 import sys, os
14 from m_lib.net.www.html import HTMLParser
15 from bkmk_objects import Folder, Bookmark, Ruler
16
17
18 DEBUG = os.environ.has_key("BKMK_DEBUG")
19
20 if DEBUG:
21    def debug(note):
22       print note
23
24    def dump_names(folder_stack):
25       l = []
26       for object in folder_stack:
27          if object.isFolder:
28             l.append(object.name)
29       return "'%s'" % "' '".join(l)
30
31 else:
32    def debug(note):
33       pass
34    dump_names = debug
35
36
37 DEFAULT_CHARSET = None
38
39 class BkmkParser(HTMLParser):
40    def __init__(self):
41       HTMLParser.__init__(self)
42
43       self.urls = 0
44       self.objects = 0
45
46       self.charset = ""
47       self.recode = None
48
49
50    def handle_data(self, data):
51       if data:
52          if DEFAULT_CHARSET:
53             data = unicode(data, self.charset, "replace").encode(DEFAULT_CHARSET, "xmlcharrefreplace")
54          self.accumulator += data
55
56
57    # Mozilla - get charset
58    def do_meta(self, attrs):
59       http_equiv = ""
60       content = ""
61
62       for attrname, value in attrs:
63          value = value.strip()
64          if attrname == 'http-equiv':
65             http_equiv = value.lower()
66          elif attrname == 'content':
67             content = value
68
69       if http_equiv == "content-type":
70          try:
71             # extract charset from "text/html; charset=UTF-8"
72             self.charset = content.split('=')[1]
73          except IndexError:
74             pass
75          else:
76             global DEFAULT_CHARSET
77             DEFAULT_CHARSET = sys.getdefaultencoding()
78             if DEFAULT_CHARSET == "ascii":
79                try:
80                   import locale
81                except ImportError:
82                   pass
83                else:
84                   DEFAULT_CHARSET = locale.getpreferredencoding()
85
86
87    def start_title(self, attrs):
88       if DEFAULT_CHARSET:
89          self.accumulator += '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=%s">\n' % DEFAULT_CHARSET
90       self.accumulator += "<TITLE>"
91
92    def end_title(self):
93       self.accumulator += "</TITLE>"
94
95
96    # Start root folder
97    def start_h1(self, attrs):
98       root_folder = Folder()
99       self.current_object = root_folder
100       self.root_folder = root_folder
101       self.current_folder = root_folder
102       self.folder_stack = [root_folder]
103
104       self.root_folder.header = self.accumulator.strip()
105       self.accumulator = ''
106
107    def end_h1(self):
108       accumulator = self.accumulator
109       self.accumulator = ''
110
111       debug("Root folder name: `%s'" % accumulator)
112       self.root_folder.name = accumulator
113
114
115    # Start a folder
116    def start_h3(self, attrs):
117       last_modified = None
118       for attrname, value in attrs:
119          value = value.strip()
120          if attrname == 'add_date':
121             add_date = value
122          elif attrname == 'last_modified':
123             last_modified = value
124
125       debug("New folder...")
126       folder = Folder(add_date, last_modified=last_modified)
127       self.current_object = folder
128       self.current_folder.append(folder)
129       self.folder_stack.append(folder) # push new folder
130       self.current_folder = folder
131       self.objects += 1
132
133    def end_h3(self):
134       accumulator = self.accumulator
135       self.accumulator = ''
136
137       debug("Folder name: `%s'" % accumulator)
138       self.current_folder.name = accumulator
139
140
141    # Start a bookmark
142    def start_a(self, attrs):
143       add_date = None
144       last_visit = None
145       last_modified = None
146       keyword = None
147       icon = None
148       charset = None
149
150       for attrname, value in attrs:
151          value = value.strip()
152          if attrname == "href":
153             href = value
154          elif attrname == "add_date":
155             add_date = value
156          elif attrname == "last_visit":
157             last_visit = value
158          elif attrname == "last_modified":
159             last_modified = value
160          elif attrname == "shortcuturl":
161             keyword = value
162          elif attrname == "icon":
163             icon = value
164          elif attrname == "last_charset":
165             charset = value
166
167       debug("Bookmark points to: `%s'" % href)
168       bookmark = Bookmark(href, add_date, last_visit, last_modified,
169          keyword or '', '', icon, charset)
170       self.current_object = bookmark
171       self.current_folder.append(bookmark)
172       self.urls += 1
173       self.objects += 1
174
175    def end_a(self):
176       accumulator = self.accumulator
177       self.accumulator = ''
178
179       debug("Bookmark name: `%s'" % accumulator)
180       bookmark = self.current_folder[-1]
181       bookmark.name = accumulator
182
183
184    def flush(self):
185       accumulator = self.accumulator
186
187       if accumulator:
188          self.accumulator = ''
189
190          current_object = self.current_object
191          if current_object:
192             current_object.comment += accumulator.strip()
193             debug("Comment: `%s'" % current_object.comment)
194
195
196    def start_dl(self, attrs):
197       self.flush()
198
199    do_dt = start_dl
200
201
202    # End of folder
203    def end_dl(self):
204       self.flush()
205       debug("End folder")
206       debug("Folder stack: %s" % dump_names(self.folder_stack))
207       if self.folder_stack:
208          del self.folder_stack[-1] # pop last folder
209          if self.folder_stack:
210             self.current_folder = self.folder_stack[-1]
211          else:
212             debug("FOLDER STACK is EMPTY!!! (1)")
213       else:
214          debug("FOLDER STACK is EMPTY!!! (2)")
215       self.current_object = None
216
217
218    def close(self):
219       HTMLParser.close(self)
220       if self.folder_stack:
221          raise ValueError, "wrong folder stack: %s" % self.folder_stack
222
223
224    def do_dd(self, attrs):
225       pass
226
227    do_p = do_dd
228
229
230    # Start ruler
231    def do_hr(self, attrs):
232       self.flush()
233       debug("Ruler")
234       self.current_folder.append(Ruler())
235       self.current_object = None
236       self.objects += 1
237
238
239    # BR in comment
240    def do_br(self, attrs):
241       self.accumulator += "<BR>"
242
243
244    # Allow < in the text
245    def unknown_starttag(self, tag, attrs):
246       self.accumulator += "<%s>" % tag
247
248
249    # Do not allow unknow end tags
250    def unknown_endtag(self, tag):
251       raise NotImplementedError("Unknow end tag `%s'" % tag)