1 /**
2 * Message generation utilities
3 *
4 * Authors:
5 * David Yip <yipdw@rose-hulman.edu>
6 * Jonas Collaros, Stephen Montgomery
7 *
8 * Copyright (c) 2004-2005 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include <glibmm/i18n.h>
15 #include "util/share.h"
16 #include "util/list.h"
18 #include "xml/node.h"
19 #include "xml/attribute-record.h"
20 #include "xml/repr.h"
22 #include "jabber_whiteboard/defines.h"
23 #include "jabber_whiteboard/typedefs.h"
24 #include "jabber_whiteboard/node-utilities.h"
25 #include "jabber_whiteboard/message-utilities.h"
26 #include "jabber_whiteboard/node-tracker.h"
28 #include <iostream>
30 namespace Inkscape {
32 namespace Whiteboard {
34 // This method can be instructed to not build a message string but only collect nodes that _would_ be transmitted
35 // and subsequently added to the tracker. This can be useful in the case where an Inkboard user is the only one
36 // in a chatroom and therefore needs to fill out the node tracker, but does not need to build the message string.
37 // This can be controlled with the only_collect_nodes flag, which will only create pointers to new XML::Nodes
38 // in the maps referenced by newidsbuf and newnodesbuf. Passing NULL as the message buffer has the same effect.
39 //
40 // only_collect_nodes defaults to false because most invocations of this method also use the message string.
41 void
42 MessageUtilities::newObjectMessage(Glib::ustring &msgbuf,
43 KeyNodeTable& newnodesbuf,
44 NewChildObjectMessageList& childmsgbuf,
45 XMLNodeTracker* xmt,
46 Inkscape::XML::Node const* node,
47 bool only_collect_nodes,
48 bool collect_children)
49 {
50 // Initialize pointers
51 Glib::ustring id, refid, parentid;
53 gchar const* name = NULL;
54 XML::Node* parent = NULL;
55 XML::Node* ref = NULL;
57 bool only_add_children = false;
59 //g_log(NULL, G_LOG_LEVEL_DEBUG, "newObjectMessage: processing node %p of type %s", node, NodeUtilities::nodeTypeToString(*node).data());
61 if (node != NULL) {
62 parent = sp_repr_parent(node);
63 if (parent != NULL) {
64 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for parent node %p (on node %p)", parent, node);
65 parentid = NodeUtilities::findNodeID(*parent, xmt, newnodesbuf);
66 if (parentid.empty()) {
67 g_warning("Parent %p is not being tracked, creating new ID", parent);
68 parentid = xmt->generateKey();
69 newnodesbuf.put(parentid, parent);
70 }
72 if ( node != parent->firstChild() && parent != NULL ) {
73 ref = parent->firstChild();
74 while (ref->next() != node) {
75 ref = ref->next();
76 }
77 }
78 }
80 if (ref != NULL) {
81 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for ref node %p (on %p)", ref, node);
82 refid = NodeUtilities::findNodeID(*ref, xmt, newnodesbuf);
83 if (refid.empty() && ref != NULL) {
84 g_warning("Ref %p is not being tracked, creating new ID", ref);
85 refid = xmt->generateKey();
86 newnodesbuf.put(refid, ref);
87 }
88 }
90 name = static_cast< gchar const* >(node->name());
91 }
93 // Generate an id for this object and append it onto the list, if
94 // it's not already in the tracker
95 if (!xmt->isSpecialNode(node->name())) {
96 if (!xmt->isTracking(*node)) {
97 id = xmt->generateKey();
98 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Inserting %p with id %s", node, id.c_str());
99 newnodesbuf.put(id, node);
100 } else {
101 id = xmt->get(*node);
102 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Found id %s for node %p; not inserting into new nodes buffers.", id.c_str(), node);
103 }
104 } else {
105 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Processing special node; not generating key");
106 id = xmt->get(*node);
107 if (id.empty()) {
108 g_warning("Node %p (name %s) is a special node, but it could not be found in the node tracker (possible unexpected duplicate?) Generating unique ID anyway.", node, node->name());
109 id = xmt->generateKey();
110 newnodesbuf.put(id, node);
111 }
112 only_add_children = true;
113 }
115 // If we're only adding children (i.e. this is a special node)
116 // don't process the given node.
117 if( !only_add_children && !id.empty() && msgbuf != NULL && !only_collect_nodes ) {
118 // <MESSAGE_NEWOBJ>
119 msgbuf = msgbuf + "<" + MESSAGE_NEWOBJ + ">";
121 // <MESSAGE_PARENT>
122 msgbuf = msgbuf + "<" + MESSAGE_PARENT + ">";
124 if(!parentid.empty()) {
125 msgbuf += parentid;
126 }
128 // </MESSAGE_NEWOBJ><MESSAGE_CHILD>id</MESSAGE_CHILD>
129 msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
131 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
132 msgbuf += id;
134 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
136 if(!refid.empty()) {
137 // <MESSAGE_REF>refid</MESSAGE_REF>
138 msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
140 msgbuf += refid;
142 msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
143 }
145 // <MESSAGE_NODETYPE>*node.type()</MESSAGE_NODETYPE>
146 msgbuf = msgbuf + "<" + MESSAGE_NODETYPE + ">" + NodeUtilities::nodeTypeToString(*node);
147 msgbuf = msgbuf + "</" + MESSAGE_NODETYPE + ">";
149 if (node->content() != NULL) {
150 // <MESSAGE_CONTENT>node->content()</MESSAGE_CONTENT>
151 msgbuf = msgbuf + "<" + MESSAGE_CONTENT + ">" + node->content();
152 msgbuf = msgbuf + "</" + MESSAGE_CONTENT + ">";
153 }
155 // <MESSAGE_NAME>name</MESSAGE_NAME>
156 msgbuf = msgbuf + "<" + MESSAGE_NAME + ">";
158 if( name != NULL )
159 msgbuf += name;
161 msgbuf = msgbuf + "</" + MESSAGE_NAME + ">";
163 // </MESSAGE_NEWOBJ>
164 msgbuf = msgbuf + "</" + MESSAGE_NEWOBJ + ">";
165 } else if (id.empty()) {
166 // if ID is NULL, then we have a real problem -- we were not able to find a key
167 // nor generate one. The only thing we can really do here is abort, since we have
168 // no way to let the other client(s) uniquely identify this object.
169 g_warning(_("ID for new object is NULL even after generation and lookup attempts: the new object will NOT be sent, nor will any of its child objects!"));
170 return;
171 } else {
173 }
175 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Generated message");
177 if (!only_collect_nodes && msgbuf != NULL && !id.empty()) {
178 // Collect new object's attributes and append them onto the msgbuf
179 Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrlist = node->attributeList();
181 for(; attrlist; attrlist++) {
182 MessageUtilities::objectChangeMessage(msgbuf,
183 xmt, id, g_quark_to_string(attrlist->key),
184 NULL, attrlist->value, false);
185 }
186 }
188 if (!only_collect_nodes)
189 childmsgbuf.push_back(msgbuf);
191 if (!id.empty() && collect_children) {
192 Glib::ustring childbuf;
193 // Collect any child objects of this new object
194 for ( Inkscape::XML::Node const *child = node->firstChild(); child != NULL; child = child->next() ) {
195 childbuf.clear();
196 MessageUtilities::newObjectMessage(childbuf,
197 newnodesbuf, childmsgbuf, xmt, child, only_collect_nodes);
198 if (!only_collect_nodes) {
199 // we're recursing down the tree, so we're picking up child nodes first
200 // and parents afterwards
201 // childmsgbuf.push_front(childbuf);
202 }
204 }
205 }
206 }
208 void
209 MessageUtilities::objectChangeMessage(Glib::ustring &msgbuf,
210 XMLNodeTracker* xmt,
211 const Glib::ustring &id,
212 gchar const* key,
213 gchar const* oldval,
214 gchar const* newval,
215 bool is_interactive)
216 {
217 // Construct message
219 // <MESSAGE_CHANGE><MESSAGE_ID>id</MESSAGE_ID>
220 msgbuf = msgbuf + "<" + MESSAGE_CHANGE + ">";
221 msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
222 msgbuf += id;
223 msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
225 // <MESSAGE_KEY>key</MESSAGE_KEY>
226 msgbuf = msgbuf + "<" + MESSAGE_KEY + ">";
227 if (key != NULL) {
228 msgbuf += key;
229 }
230 msgbuf = msgbuf + "</" + MESSAGE_KEY + ">";
232 // <MESSAGE_OLDVAL>oldval</MESSAGE_OLDVAL>
233 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
234 if (oldval != NULL) {
235 msgbuf += oldval;
236 }
237 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
239 // <MESSAGE_NEWVAL>newval</MESSAGE_NEWVAL>
240 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
241 if (newval != NULL) {
242 msgbuf += newval;
243 }
244 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
246 // <MESSAGE_ISINTERACTIVE>is_interactive</MESSAGE_ISINTERACTIVE>
247 msgbuf = msgbuf + "<" + MESSAGE_ISINTERACTIVE + ">";
248 if (is_interactive) {
249 msgbuf += "true";
250 } else {
251 msgbuf += "false";
252 }
253 msgbuf = msgbuf + "</" + MESSAGE_ISINTERACTIVE + ">";
255 // </MESSAGE_CHANGE>
256 msgbuf = msgbuf + "</" + MESSAGE_CHANGE + ">";
257 }
259 void
260 MessageUtilities::objectDeleteMessage(Glib::ustring &msgbuf,
261 XMLNodeTracker* xmt,
262 Inkscape::XML::Node const& parent,
263 Inkscape::XML::Node const& child,
264 Inkscape::XML::Node const* prev)
265 {
266 /*
267 gchar const* parentid = NULL;
268 gchar const* previd = NULL;
269 gchar const* childid = NULL;
271 childid = child.attribute("id");
272 parentid = parent.attribute("id");
273 if (prev != NULL) {
274 previd = prev->attribute("id");
275 }*/
277 Glib::ustring parentid, previd, childid;
279 childid = xmt->get(child);
280 parentid = xmt->get(parent);
281 previd = xmt->get(*prev);
283 if (childid.empty())
284 return;
287 // <MESSAGE_DELETE><MESSAGE_PARENT>parentid</MESSAGE_PARENT>
288 msgbuf = msgbuf + "<" + MESSAGE_DELETE + ">" + "<" + MESSAGE_PARENT + ">";
289 if (!parentid.empty()) {
290 msgbuf += parentid;
291 }
292 msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
294 // <MESSAGE_CHILD>childid</MESSAGE_CHILD>
295 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
296 if (!childid.empty()) {
297 msgbuf += childid;
298 }
299 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
301 // <MESSAGE_REF>previd</MESSAGE_REF>
302 msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
303 if (!previd.empty()) {
304 msgbuf += previd;
305 }
306 msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
308 // </MESSAGE_DELETE>
309 msgbuf = msgbuf + "</" + MESSAGE_DELETE + ">";
310 }
312 void
313 MessageUtilities::contentChangeMessage(Glib::ustring& msgbuf,
314 const Glib::ustring &nodeid,
315 Util::ptr_shared<char> old_value,
316 Util::ptr_shared<char> new_value)
317 {
318 if (nodeid.empty())
319 return;
321 // <MESSAGE_NODECONTENT>
322 msgbuf = msgbuf + "<" + MESSAGE_NODECONTENT + ">";
324 // <MESSAGE_ID>nodeid</MESSAGE_ID>
325 msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
326 msgbuf += nodeid;
327 msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
329 // <MESSAGE_OLDVAL>old_value</MESSAGE_OLDVAL>
330 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
331 msgbuf += old_value.pointer();
332 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
334 // <MESSAGE_NEWVAL>new_value</MESSAGE_NEWVAL>
335 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
336 msgbuf += new_value.pointer();
337 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
339 // </MESSAGE_NODECONTENT>
340 msgbuf = msgbuf + "</" + MESSAGE_NODECONTENT + ">";
341 }
343 void
344 MessageUtilities::childOrderChangeMessage(Glib::ustring& msgbuf,
345 const Glib::ustring &childid,
346 const Glib::ustring &oldprevid,
347 const Glib::ustring &newprevid)
348 {
349 if (childid.empty())
350 return;
352 // <MESSAGE_ORDERCHANGE>
353 msgbuf = msgbuf + "<" + MESSAGE_ORDERCHANGE + ">";
355 // <MESSAGE_ID>nodeid</MESSAGE_ID>
356 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
357 msgbuf += childid;
358 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
360 // <MESSAGE_OLDVAL>oldprevid</MESSAGE_OLDVAL>
361 /*
362 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
363 msgbuf += (*oldprevid);
364 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
365 */
367 // <MESSAGE_NEWVAL>newprevid</MESSAGE_NEWVAL>
368 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
369 msgbuf += newprevid;
370 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
372 // </MESSAGE_ORDERCHANGE>
373 msgbuf = msgbuf + "</" + MESSAGE_ORDERCHANGE + ">";
375 }
378 bool
379 MessageUtilities::getFirstMessageTag(struct Node& buf, const Glib::ustring &msg)
380 {
381 if (msg.empty())
382 return false;
384 // See if we have a valid start tag, i.e. < ... >. If we do,
385 // continue; if not, stop and return NULL.
386 //
387 // find_first_of returns ULONG_MAX when it cannot find the first
388 // instance of the given character.
390 Glib::ustring::size_type startDelim = msg.find_first_of('<');
391 if (startDelim != ULONG_MAX) {
392 Glib::ustring::size_type endDelim = msg.find_first_of('>');
393 if (endDelim != ULONG_MAX) {
394 if (endDelim > startDelim) {
395 buf.tag = msg.substr(startDelim+1, (endDelim-startDelim)-1);
396 if (buf.tag.find_first_of('/') == ULONG_MAX) { // start tags should not be end tags
399 // construct end tag (</buf.data>)
400 Glib::ustring endTag(buf.tag);
401 endTag.insert(0, "/");
403 Glib::ustring::size_type endTagLoc = msg.find(endTag, endDelim);
404 if (endTagLoc != ULONG_MAX) {
405 buf.data = msg.substr(endDelim+1, ((endTagLoc - 1) - (endDelim + 1)));
406 buf.next_pos = endTagLoc + endTag.length() + 1;
408 return true;
409 }
410 }
411 }
412 }
413 }
415 return false;
416 }
418 bool
419 MessageUtilities::findTag(struct Node& buf, const Glib::ustring &msg)
420 {
421 if (msg.empty())
422 return false;
424 // Read desired tag type out of buffer, and append
425 // < > to it
427 Glib::ustring searchterm("<");
428 searchterm += buf.tag;
429 searchterm + ">";
431 Glib::ustring::size_type tagStart = msg.find(searchterm, 0);
432 if (tagStart != ULONG_MAX) {
433 // Find ending tag starting at the point at the end of
434 // the start tag.
435 searchterm.insert(1, "/");
436 Glib::ustring::size_type tagEnd = msg.find(searchterm, tagStart + searchterm.length());
437 if (tagEnd != ULONG_MAX) {
438 Glib::ustring::size_type start = tagStart + searchterm.length();
439 buf.data = msg.substr(start, tagEnd - start);
440 return true;
441 }
442 }
443 return false;
444 }
446 Glib::ustring
447 MessageUtilities::makeTagWithContent(const Glib::ustring &tagname,
448 const Glib::ustring &content)
449 {
450 Glib::ustring buf = "<" + tagname + ">";
451 buf += content;
452 buf += "</" + tagname + ">";
453 return buf;
454 }
457 }
459 }
463 /*
464 Local Variables:
465 mode:c++
466 c-file-style:"stroustrup"
467 c-file-offsets:((innamespace . 0)(inline-open . 0))
468 indent-tabs-mode:nil
469 fill-column:99
470 End:
471 */
472 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :