]> git.phdru.name Git - bookmarks_db.git/blob - bkmk_objects.py
Style: Fix flake8 E302 expected 2 blank lines, found 1
[bookmarks_db.git] / bkmk_objects.py
1 """Objects to represent bookmarks.html structure
2
3 This file is a part of Bookmarks database and Internet robot.
4
5 """
6
7 __author__ = "Oleg Broytman <phd@phdru.name>"
8 __copyright__ = "Copyright (C) 2000-2023 PhiloSoft Design"
9 __license__ = "GNU GPL"
10
11 __all__ = ['Folder', 'Bookmark', 'Ruler', 'Walker', 'Writer', 'Robot',
12            'InverseLinker', 'Linear', 'make_linear', 'make_tree', 'break_tree',
13            'quote_title', 'unquote_title', 'parse_params', 'set_params',
14            ]
15
16
17 import os, urllib
18
19 BKMK_FORMAT = os.environ.get("BKMK_FORMAT", "MOZILLA")
20
21
22 class Folder(list):
23     isFolder = 1
24     isBookmark = 0
25
26     def __init__(self, add_date=None, comment='', last_modified=None):
27         super(Folder, self).__init__()
28         self.comment = comment
29         self.add_date = add_date
30         self.last_modified = last_modified
31
32     def walk_depth(self, walker, level=0):
33         if hasattr(self, "header"):  # root folder
34             prune = 0
35             walker.root_folder(self)
36         else:
37             prune = walker.prune_folder(self)
38             if not prune:
39                 walker.start_folder(self, level)
40
41         if not prune:
42             for object in self:
43                 if object.isFolder:
44                     object.walk_depth(walker, level+1)
45                 elif object.isBookmark:
46                     walker.bookmark(object, level)
47                 else:
48                     walker.ruler(object, level)
49
50             walker.end_folder(self, level)
51
52
53 class Bookmark(object):
54     isFolder = 0
55     isBookmark = 1
56
57     def __init__(self, href, add_date, last_visit=None, last_modified=None,
58                  keyword=None, comment='', icon_href=None, icon=None,
59                  charset=None, parser_charset=None):
60         protocol, request = urllib.splittype(href)
61         user, password, port = None, None, None
62         host, path = urllib.splithost(request)
63         if host:
64             user, host = urllib.splituser(host)
65             if user:
66                 user, password = urllib.splitpasswd(user)
67             host, port = urllib.splitport(host)
68             if port: port = int(port)
69
70         if protocol == 'place':
71             href = protocol + ":"
72         else:
73             href = protocol + "://"
74         if user:
75             href += urllib.quote(user)
76             if password:
77                 href += ':' + urllib.quote(password)
78             href += '@'
79         if host:
80             href += host.decode(parser_charset or 'utf-8').encode('idna')
81             if port:
82                 href += ':%d' % port
83         if path:
84             href += path
85
86         self.href = href
87         self.add_date = add_date
88         self.last_visit = last_visit
89         self.last_modified = last_modified
90         self.keyword = keyword
91         self.comment = comment
92         self.icon_href = icon_href
93         self.icon = icon
94         self.charset = charset
95
96
97 class Ruler(object):
98     isFolder = 0
99     isBookmark = 0
100
101
102 class Walker(object):
103     """
104        Interface class. Any instance that will be passed to Folder.walk_depth
105        may be derived from this class. It is not mandatory - unlike Java
106        Python does not require interface classes; but it is convenient to have
107        some methods predefined to no-op, in case you do not want to
108        provide end_folder etc.
109     """
110
111     def root_folder(self, r):
112         pass
113
114     def start_folder(self, f, level):
115         pass
116
117     def end_folder(self, f, level):
118         pass
119
120     def bookmark(self, b, level):
121         pass
122
123     def ruler(self, r, level):
124         pass
125
126     def prune_folder(self, folder):
127         return 0
128
129
130 class Writer(Walker):
131     def __init__(self, outfile, prune=None):
132         self.outfile = outfile
133         self.prune = prune
134
135     def prune_folder(self, folder):
136         return self.prune == folder.name
137
138
139 class Robot(object):
140     def __init__(self, log):
141         self.log = log
142
143     def stop(self):
144         pass  # Nothing to do on cleanup
145
146
147 # Helper class to make inverese links (nodes linked to their parent)
148 class InverseLinker(Walker):
149     def root_folder(self, r):
150         self.parent_stack = [r]
151
152     def start_folder(self, f, level):
153         f.parent = self.parent_stack[-1]
154         self.parent_stack.append(f)  # Push the folder onto the stack of parents
155
156     def end_folder(self, f, level):
157         del self.parent_stack[-1]   # Pop off the stack
158
159     def bookmark(self, b, level):
160         b.parent = self.parent_stack[-1]
161
162     def ruler(self, r, level):
163         r.parent = self.parent_stack[-1]
164
165
166 # Helper class to make linear represenatation of the tree
167 class Linear(Walker):
168     def root_folder(self, r):
169         r.linear = [r]
170         self.linear = r.linear
171
172     def add_object(self, object):
173         self.linear.append(object)
174
175     def start_folder(self, f, level):
176         self.add_object(f)
177
178     def bookmark(self, b, level):
179         self.add_object(b)
180
181     def ruler(self, r, level):
182         self.add_object(r)
183
184
185 # Helper - make linked linear represenatation of the tree, suitable to be stored in sequential storage
186 def make_linear(root_folder):
187     linker = InverseLinker()
188     root_folder.walk_depth(linker)
189
190     linear = Linear()
191     root_folder.walk_depth(linear)
192
193
194 # Helper, opposite of make_linear - make a tree from the linked linear representation
195 def make_tree(linear):
196     root_folder = linear[0]
197     del linear[0]
198
199     for object in linear:
200         object.parent.append(object)
201
202     return root_folder
203
204
205 def break_tree(linear):
206     del linear[0]
207
208     for object in linear:
209         del object.parent
210
211
212 def quote_title(title):
213     if BKMK_FORMAT == "MOZILLA":
214         title = title.replace("'", "&#39;")
215     return title
216
217
218 def unquote_title(title):
219     if BKMK_FORMAT == "MOZILLA":
220         from HTMLParser import HTMLParser
221         title = HTMLParser().unescape(title.replace("&amp;", '&').decode('utf-8'))
222         title = title.encode('utf-8').replace("&#39;", "'")
223     return title
224
225
226 def parse_params(param_str):
227     params = param_str.split(':')
228     main_param = params.pop(0)
229     param_list = {}
230     for param in params:
231         key, value = param.split('=', 1)
232         param_list[key] = value
233     return main_param, param_list
234
235
236 def set_params(obj, params):
237     if hasattr(params, "items"):
238         params = params.items()
239     for key, value in params:
240         setattr(obj, key, value)