|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a class to read XBEL bookmark files. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 from PyQt5.QtCore import QXmlStreamReader, QXmlStreamEntityResolver, \ |
|
13 QIODevice, QFile, QCoreApplication, QXmlStreamNamespaceDeclaration, \ |
|
14 QDateTime, Qt |
|
15 |
|
16 from .BookmarkNode import BookmarkNode |
|
17 |
|
18 |
|
19 class XmlEntityResolver(QXmlStreamEntityResolver): |
|
20 """ |
|
21 Class implementing an XML entity resolver for bookmark files. |
|
22 """ |
|
23 def resolveUndeclaredEntity(self, entity): |
|
24 """ |
|
25 Public method to resolve undeclared entities. |
|
26 |
|
27 @param entity entity to be resolved (string) |
|
28 @return resolved entity (string) |
|
29 """ |
|
30 if entity == "nbsp": |
|
31 return " " |
|
32 return "" |
|
33 |
|
34 |
|
35 class XbelReader(QXmlStreamReader): |
|
36 """ |
|
37 Class implementing a reader object for XBEL bookmark files. |
|
38 """ |
|
39 def __init__(self): |
|
40 """ |
|
41 Constructor |
|
42 """ |
|
43 super(XbelReader, self).__init__() |
|
44 |
|
45 self.__resolver = XmlEntityResolver() |
|
46 self.setEntityResolver(self.__resolver) |
|
47 |
|
48 def read(self, fileNameOrDevice): |
|
49 """ |
|
50 Public method to read an XBEL bookmark file. |
|
51 |
|
52 @param fileNameOrDevice name of the file to read (string) |
|
53 or reference to the device to read (QIODevice) |
|
54 @return reference to the root node (BookmarkNode) |
|
55 """ |
|
56 if isinstance(fileNameOrDevice, QIODevice): |
|
57 self.setDevice(fileNameOrDevice) |
|
58 else: |
|
59 f = QFile(fileNameOrDevice) |
|
60 if not f.exists(): |
|
61 return BookmarkNode(BookmarkNode.Root) |
|
62 f.open(QFile.ReadOnly) |
|
63 self.setDevice(f) |
|
64 |
|
65 root = BookmarkNode(BookmarkNode.Root) |
|
66 while not self.atEnd(): |
|
67 self.readNext() |
|
68 if self.isStartElement(): |
|
69 version = self.attributes().value("version") |
|
70 if self.name() == "xbel" and \ |
|
71 (not version or version == "1.0"): |
|
72 self.__readXBEL(root) |
|
73 else: |
|
74 self.raiseError(QCoreApplication.translate( |
|
75 "XbelReader", |
|
76 "The file is not an XBEL version 1.0 file.")) |
|
77 |
|
78 return root |
|
79 |
|
80 def __readXBEL(self, node): |
|
81 """ |
|
82 Private method to read and parse the XBEL file. |
|
83 |
|
84 @param node reference to the node to attach to (BookmarkNode) |
|
85 """ |
|
86 if not self.isStartElement() and self.name() != "xbel": |
|
87 return |
|
88 |
|
89 while not self.atEnd(): |
|
90 self.readNext() |
|
91 if self.isEndElement(): |
|
92 break |
|
93 |
|
94 if self.isStartElement(): |
|
95 if self.name() == "folder": |
|
96 self.__readFolder(node) |
|
97 elif self.name() == "bookmark": |
|
98 self.__readBookmarkNode(node) |
|
99 elif self.name() == "separator": |
|
100 self.__readSeparator(node) |
|
101 else: |
|
102 self.__skipUnknownElement() |
|
103 |
|
104 def __readFolder(self, node): |
|
105 """ |
|
106 Private method to read and parse a folder subtree. |
|
107 |
|
108 @param node reference to the node to attach to (BookmarkNode) |
|
109 """ |
|
110 if not self.isStartElement() and self.name() != "folder": |
|
111 return |
|
112 |
|
113 folder = BookmarkNode(BookmarkNode.Folder, node) |
|
114 folder.expanded = self.attributes().value("folded") == "no" |
|
115 folder.added = QDateTime.fromString( |
|
116 self.attributes().value("added"), Qt.ISODate) |
|
117 |
|
118 while not self.atEnd(): |
|
119 self.readNext() |
|
120 if self.isEndElement(): |
|
121 break |
|
122 |
|
123 if self.isStartElement(): |
|
124 if self.name() == "title": |
|
125 self.__readTitle(folder) |
|
126 elif self.name() == "desc": |
|
127 self.__readDescription(folder) |
|
128 elif self.name() == "folder": |
|
129 self.__readFolder(folder) |
|
130 elif self.name() == "bookmark": |
|
131 self.__readBookmarkNode(folder) |
|
132 elif self.name() == "separator": |
|
133 self.__readSeparator(folder) |
|
134 elif self.name() == "info": |
|
135 self.__readInfo() |
|
136 else: |
|
137 self.__skipUnknownElement() |
|
138 |
|
139 def __readTitle(self, node): |
|
140 """ |
|
141 Private method to read the title element. |
|
142 |
|
143 @param node reference to the bookmark node title belongs to |
|
144 (BookmarkNode) |
|
145 """ |
|
146 if not self.isStartElement() and self.name() != "title": |
|
147 return |
|
148 |
|
149 node.title = self.readElementText() |
|
150 |
|
151 def __readDescription(self, node): |
|
152 """ |
|
153 Private method to read the desc element. |
|
154 |
|
155 @param node reference to the bookmark node desc belongs to |
|
156 (BookmarkNode) |
|
157 """ |
|
158 if not self.isStartElement() and self.name() != "desc": |
|
159 return |
|
160 |
|
161 node.desc = self.readElementText() |
|
162 |
|
163 def __readSeparator(self, node): |
|
164 """ |
|
165 Private method to read a separator element. |
|
166 |
|
167 @param node reference to the bookmark node the separator belongs to |
|
168 (BookmarkNode) |
|
169 """ |
|
170 sep = BookmarkNode(BookmarkNode.Separator, node) |
|
171 sep.added = QDateTime.fromString( |
|
172 self.attributes().value("added"), Qt.ISODate) |
|
173 |
|
174 # empty elements have a start and end element |
|
175 while not self.atEnd(): |
|
176 self.readNext() |
|
177 if self.isEndElement(): |
|
178 break |
|
179 |
|
180 if self.isStartElement(): |
|
181 if self.name() == "info": |
|
182 self.__readInfo() |
|
183 else: |
|
184 self.__skipUnknownElement() |
|
185 |
|
186 def __readBookmarkNode(self, node): |
|
187 """ |
|
188 Private method to read and parse a bookmark subtree. |
|
189 |
|
190 @param node reference to the node to attach to (BookmarkNode) |
|
191 """ |
|
192 if not self.isStartElement() and self.name() != "bookmark": |
|
193 return |
|
194 |
|
195 bookmark = BookmarkNode(BookmarkNode.Bookmark, node) |
|
196 bookmark.url = self.attributes().value("href") |
|
197 bookmark.added = QDateTime.fromString( |
|
198 self.attributes().value("added"), Qt.ISODate) |
|
199 bookmark.modified = QDateTime.fromString( |
|
200 self.attributes().value("modified"), Qt.ISODate) |
|
201 bookmark.visited = QDateTime.fromString( |
|
202 self.attributes().value("visited"), Qt.ISODate) |
|
203 |
|
204 while not self.atEnd(): |
|
205 self.readNext() |
|
206 if self.isEndElement(): |
|
207 break |
|
208 |
|
209 if self.isStartElement(): |
|
210 if self.name() == "title": |
|
211 self.__readTitle(bookmark) |
|
212 elif self.name() == "desc": |
|
213 self.__readDescription(bookmark) |
|
214 elif self.name() == "info": |
|
215 self.__readInfo() |
|
216 else: |
|
217 self.__skipUnknownElement() |
|
218 |
|
219 if not bookmark.title: |
|
220 bookmark.title = QCoreApplication.translate( |
|
221 "XbelReader", "Unknown title") |
|
222 |
|
223 def __readInfo(self): |
|
224 """ |
|
225 Private method to read and parse an info subtree. |
|
226 """ |
|
227 self.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration( |
|
228 "bookmark", "http://www.python.org")) |
|
229 self.skipCurrentElement() |
|
230 |
|
231 def __skipUnknownElement(self): |
|
232 """ |
|
233 Private method to skip over all unknown elements. |
|
234 """ |
|
235 self.skipCurrentElement() |