1 /**
2 * Whiteboard session manager
3 * XML node tracking facility
4 *
5 * Authors:
6 * David Yip <yipdw@rose-hulman.edu>
7 *
8 * Copyright (c) 2005 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include "sp-object.h"
14 #include "sp-item-group.h"
15 #include "document.h"
16 #include "document-private.h"
18 #include "xml/node.h"
20 #include "util/compose.hpp"
22 #include "jabber_whiteboard/session-manager.h"
23 #include "jabber_whiteboard/node-tracker.h"
26 // TODO: remove redundant calls to isTracking(); it's a rather unnecessary
27 // performance burden.
28 namespace Inkscape {
30 namespace Whiteboard {
32 // Lookup tables
34 /**
35 * Keys for special nodes.
36 *
37 * A special node is a node that can only appear once in a document.
38 */
39 char const* specialnodekeys[] = {
40 DOCUMENT_ROOT_NODE,
41 DOCUMENT_NAMEDVIEW_NODE,
42 };
44 /**
45 * Names of special nodes.
46 *
47 * A special node is a node that can only appear once in a document.
48 */
49 char const* specialnodenames[] = {
50 DOCUMENT_ROOT_NAME,
51 DOCUMENT_NAMEDVIEW_NAME,
52 };
54 XMLNodeTracker::XMLNodeTracker(SessionManager* sm) :
55 _rootKey(DOCUMENT_ROOT_NODE),
56 _namedviewKey(DOCUMENT_NAMEDVIEW_NODE)
57 {
58 this->_sm = sm;
59 this->_counter = 0;
61 // Construct special node maps
62 this->createSpecialNodeTables();
63 this->reset();
64 }
66 XMLNodeTracker::~XMLNodeTracker()
67 {
68 this->_clear();
69 }
71 void
72 XMLNodeTracker::put(std::string key, XML::Node const& node)
73 {
74 this->put(key, const_cast< XML::Node& >(node));
75 }
77 void
78 XMLNodeTracker::put(std::string key, XML::Node& node)
79 {
80 KeyToTrackerNodeMap::iterator i = this->_keyToNode.find(key);
81 if (i != this->_keyToNode.end()) {
82 this->_keyToNode.erase(i);
83 }
84 this->_keyToNode.insert(std::make_pair< std::string, XML::Node* >(key, &node));
86 TrackerNodeToKeyMap::iterator j = this->_nodeToKey.find(&node);
87 if (j != this->_nodeToKey.end()) {
88 this->_nodeToKey.erase(j);
89 }
90 this->_nodeToKey.insert(std::make_pair< XML::Node*, std::string >(&node, key));
91 }
93 void
94 XMLNodeTracker::put(KeyToNodeMap& newids, NodeToKeyMap& newnodes)
95 {
96 // TODO: redo
97 KeyToNodeMap::iterator i = newids.begin();
99 for(; i != newids.end(); i++) {
100 this->put((*i).first, *((*i).second));
101 }
102 }
104 void
105 XMLNodeTracker::process(KeyToNodeActionList& actions)
106 {
107 KeyToNodeActionList::iterator i = actions.begin();
108 for(; i != actions.end(); i++) {
109 // Get the action to perform.
110 SerializedEventNodeAction action = *i;
111 switch(action.second) {
112 case NODE_ADD:
113 this->put(action.first.first, *action.first.second);
114 break;
115 case NODE_REMOVE:
116 // this->remove(const_cast< XML::Node& >(*action.first.second));
117 break;
118 default:
119 break;
120 }
121 }
122 }
124 XML::Node*
125 XMLNodeTracker::get(std::string& key)
126 {
127 KeyToTrackerNodeMap::iterator i = this->_keyToNode.find(key);
128 if (i != this->_keyToNode.end()) {
129 return (*i).second;
130 } else {
131 g_warning("Key %s is not being tracked!", key.c_str());
132 return NULL;
133 }
134 }
136 XML::Node*
137 XMLNodeTracker::get(std::string const& key)
138 {
139 return this->get(const_cast< std::string& >(key));
140 }
143 std::string const
144 XMLNodeTracker::get(XML::Node& node)
145 {
146 TrackerNodeToKeyMap::iterator i = this->_nodeToKey.find(&node);
147 if (i != this->_nodeToKey.end()) {
148 return (*i).second;
149 } else {
150 return "";
151 }
152 }
154 std::string const
155 XMLNodeTracker::get(XML::Node const& node)
156 {
157 return this->get(const_cast< XML::Node& >(node));
158 }
160 bool
161 XMLNodeTracker::isTracking(std::string& key)
162 {
163 return (this->_keyToNode.find(key) != this->_keyToNode.end());
164 }
166 bool
167 XMLNodeTracker::isTracking(std::string const& key)
168 {
169 return this->isTracking(const_cast< std::string& >(key));
170 }
172 bool
173 XMLNodeTracker::isTracking(XML::Node& node)
174 {
175 return (this->_nodeToKey.find(&node) != this->_nodeToKey.end());
176 }
178 bool
179 XMLNodeTracker::isTracking(XML::Node const& node)
180 {
181 return this->isTracking(const_cast< XML::Node& >(node));
182 }
184 bool
185 XMLNodeTracker::isRootNode(XML::Node& node)
186 {
187 XML::Node* docroot = sp_document_repr_root(this->_sm->document());
188 return (docroot == &node);
189 }
192 void
193 XMLNodeTracker::remove(std::string& key)
194 {
195 if (this->isTracking(key)) {
196 XML::Node* element = this->get(key);
197 this->_keyToNode.erase(key);
198 this->_nodeToKey.erase(element);
199 }
200 }
202 void
203 XMLNodeTracker::remove(XML::Node& node)
204 {
205 if (this->isTracking(node)) {
206 std::string const element = this->get(node);
207 this->_nodeToKey.erase(&node);
208 this->_keyToNode.erase(element);
209 }
210 }
212 bool
213 XMLNodeTracker::isSpecialNode(char const* name)
214 {
215 return (this->_specialnodes.find(name) != this->_specialnodes.end());
216 }
218 bool
219 XMLNodeTracker::isSpecialNode(std::string const& name)
220 {
221 return (this->_specialnodes.find(name.data()) != this->_specialnodes.end());
222 }
224 std::string const
225 XMLNodeTracker::getSpecialNodeKeyFromName(Glib::ustring const& name)
226 {
227 return this->_specialnodes[name.data()];
228 }
230 std::string const
231 XMLNodeTracker::getSpecialNodeKeyFromName(Glib::ustring const* name)
232 {
233 return this->_specialnodes[name->data()];
234 }
236 std::string
237 XMLNodeTracker::generateKey(gchar const* JID)
238 {
239 return String::compose("%1;%2", this->_counter++, JID);
240 }
242 std::string
243 XMLNodeTracker::generateKey()
244 {
245 SessionData* sd = this->_sm->session_data;
246 std::bitset< NUM_FLAGS >& status = sd->status;
247 if (status[IN_CHATROOM]) {
248 // This is not strictly required for chatrooms: chatrooms will
249 // function just fine with the user-to-user ID scheme. However,
250 // the user-to-user scheme can lead to loss of anonymity
251 // in anonymous chat rooms, since it contains the real JID
252 // of a user.
253 return String::compose("%1;%2@%3/%4", this->_counter++, sd->chat_name, sd->chat_server, sd->chat_handle);
254 } else {
255 return String::compose("%1;%2", this->_counter++, lm_connection_get_jid(sd->connection));
256 }
257 }
259 void
260 XMLNodeTracker::createSpecialNodeTables()
261 {
262 int const sz = sizeof(specialnodekeys) / sizeof(char const*);
263 for(int i = 0; i < sz; i++) {
264 this->_specialnodes[specialnodenames[i]] = specialnodekeys[i];
265 }
266 }
269 // rather nasty and crufty debugging function
270 void
271 XMLNodeTracker::dump()
272 {
273 g_log(NULL, G_LOG_LEVEL_DEBUG, "XMLNodeTracker dump for %s", lm_connection_get_jid(this->_sm->session_data->connection));
274 KeyToTrackerNodeMap::iterator i = this->_keyToNode.begin();
275 TrackerNodeToKeyMap::iterator j = this->_nodeToKey.begin();
276 std::map< char const*, char const* >::iterator k = this->_specialnodes.begin();
279 g_log(NULL, G_LOG_LEVEL_DEBUG, "%u entries in keyToNode, %u entries in nodeToKey", this->_keyToNode.size(), this->_nodeToKey.size());
281 if (this->_keyToNode.size() != this->_nodeToKey.size()) {
282 g_warning("Map sizes do not match!");
283 }
285 g_log(NULL, G_LOG_LEVEL_DEBUG, "XMLNodeTracker keyToNode dump");
286 while(i != this->_keyToNode.end()) {
287 if (!((*i).first.empty())) {
288 if ((*i).second != NULL) {
289 g_log(NULL, G_LOG_LEVEL_DEBUG, "%s\t->\t%p (%s) (%s)", (*i).first.c_str(), (*i).second, (*i).second->name(), (*i).second->content());
290 } else {
291 g_log(NULL, G_LOG_LEVEL_DEBUG, "%s\t->\t(null)", (*i).first.c_str());
292 }
293 } else {
294 g_log(NULL, G_LOG_LEVEL_DEBUG, "(null)\t->\t%p (%s)", (*i).second, (*i).second->name());
295 }
296 i++;
297 }
299 g_log(NULL, G_LOG_LEVEL_DEBUG, "XMLNodeTracker nodeToKey dump");
300 while(j != this->_nodeToKey.end()) {
301 if (!((*j).second.empty())) {
302 if ((*j).first) {
303 g_log(NULL, G_LOG_LEVEL_DEBUG, "%p\t->\t%s (parent %p)", (*j).first, (*j).second.c_str(), (*j).first->parent());
304 } else {
305 g_log(NULL, G_LOG_LEVEL_DEBUG, "(null)\t->\t%s", (*j).second.c_str());
306 }
307 } else {
308 g_log(NULL, G_LOG_LEVEL_DEBUG, "%p\t->\t(null)", (*j).first);
309 }
310 j++;
311 }
313 g_log(NULL, G_LOG_LEVEL_DEBUG, "_specialnodes dump");
314 while(k != this->_specialnodes.end()) {
315 g_log(NULL, G_LOG_LEVEL_DEBUG, "%s\t->\t%s", (*k).first, (*k).second);
316 k++;
317 }
318 }
320 void
321 XMLNodeTracker::reset()
322 {
323 this->_clear();
325 // Find and insert special nodes
326 // root node
327 this->put(this->_rootKey, *(sp_document_repr_root(this->_sm->document())));
329 // namedview node
330 SPObject* namedview = sp_item_group_get_child_by_name((SPGroup *)this->_sm->document()->root, NULL, DOCUMENT_NAMEDVIEW_NAME);
331 if (!namedview) {
332 g_warning("namedview node does not exist; it will be created during synchronization");
333 } else {
334 this->put(this->_namedviewKey, *(SP_OBJECT_REPR(namedview)));
335 }
336 }
338 void
339 XMLNodeTracker::_clear()
340 {
341 // Remove all keys in both trackers, and delete each key.
342 this->_keyToNode.clear();
343 this->_nodeToKey.clear();
345 /*
346 TrackerNodeToKeyMap::iterator j = this->_nodeToKey.begin();
347 for(; j != this->_nodeToKey.end(); j++) {
348 this->_nodeToKey.erase(j);
349 }
350 */
351 }
353 }
355 }
359 /*
360 Local Variables:
361 mode:c++
362 c-file-style:"stroustrup"
363 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
364 indent-tabs-mode:nil
365 fill-column:99
366 End:
367 */
368 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :