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/node-utilities.h"
24 #include "jabber_whiteboard/message-utilities.h"
25 #include "jabber_whiteboard/node-tracker.h"
27 #include <iostream>
29 namespace Inkscape {
31 namespace Whiteboard {
33 // This method can be instructed to not build a message string but only collect nodes that _would_ be transmitted
34 // and subsequently added to the tracker. This can be useful in the case where an Inkboard user is the only one
35 // in a chatroom and therefore needs to fill out the node tracker, but does not need to build the message string.
36 // This can be controlled with the only_collect_nodes flag, which will only create pointers to new XML::Nodes
37 // in the maps referenced by newidsbuf and newnodesbuf. Passing NULL as the message buffer has the same effect.
38 //
39 // only_collect_nodes defaults to false because most invocations of this method also use the message string.
40 void
41 MessageUtilities::newObjectMessage(Glib::ustring &msgbuf,
42 KeyNodeTable& newnodesbuf,
43 NewChildObjectMessageList& childmsgbuf,
44 XMLNodeTracker* xmt,
45 Inkscape::XML::Node const* node,
46 bool only_collect_nodes,
47 bool collect_children)
48 {
49 // Initialize pointers
50 Glib::ustring id, refid, parentid;
52 gchar const* name = NULL;
53 XML::Node* parent = NULL;
54 XML::Node* ref = NULL;
56 bool only_add_children = false;
58 //g_log(NULL, G_LOG_LEVEL_DEBUG, "newObjectMessage: processing node %p of type %s", node, NodeUtilities::nodeTypeToString(*node).data());
60 if (node != NULL) {
61 parent = sp_repr_parent(node);
62 if (parent != NULL) {
63 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for parent node %p (on node %p)", parent, node);
64 parentid = NodeUtilities::findNodeID(*parent, xmt, newnodesbuf);
65 if (parentid.empty()) {
66 g_warning("Parent %p is not being tracked, creating new ID", parent);
67 parentid = xmt->generateKey();
68 newnodesbuf.put(parentid, parent);
69 }
71 if ( node != parent->firstChild() && parent != NULL ) {
72 ref = parent->firstChild();
73 while (ref->next() != node) {
74 ref = ref->next();
75 }
76 }
77 }
79 if (ref != NULL) {
80 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for ref node %p (on %p)", ref, node);
81 refid = NodeUtilities::findNodeID(*ref, xmt, newnodesbuf);
82 if (refid.empty() && ref != NULL) {
83 g_warning("Ref %p is not being tracked, creating new ID", ref);
84 refid = xmt->generateKey();
85 newnodesbuf.put(refid, ref);
86 }
87 }
89 name = static_cast< gchar const* >(node->name());
90 }
92 // Generate an id for this object and append it onto the list, if
93 // it's not already in the tracker
94 if (!xmt->isSpecialNode(node->name())) {
95 if (!xmt->isTracking(*node)) {
96 id = xmt->generateKey();
97 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Inserting %p with id %s", node, id.c_str());
98 newnodesbuf.put(id, node);
99 } else {
100 id = xmt->get(*node);
101 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Found id %s for node %p; not inserting into new nodes buffers.", id.c_str(), node);
102 }
103 } else {
104 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Processing special node; not generating key");
105 id = xmt->get(*node);
106 if (id.empty()) {
107 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());
108 id = xmt->generateKey();
109 newnodesbuf.put(id, node);
110 }
111 only_add_children = true;
112 }
114 // If we're only adding children (i.e. this is a special node)
115 // don't process the given node.
116 if( !only_add_children && !id.empty() && msgbuf != NULL && !only_collect_nodes ) {
117 // <MESSAGE_NEWOBJ>
118 msgbuf = msgbuf + "<" + MESSAGE_NEWOBJ + ">";
120 // <MESSAGE_PARENT>
121 msgbuf = msgbuf + "<" + MESSAGE_PARENT + ">";
123 if(!parentid.empty()) {
124 msgbuf += parentid;
125 }
127 // </MESSAGE_NEWOBJ><MESSAGE_CHILD>id</MESSAGE_CHILD>
128 msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
130 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
131 msgbuf += id;
133 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
135 if(!refid.empty()) {
136 // <MESSAGE_REF>refid</MESSAGE_REF>
137 msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
139 msgbuf += refid;
141 msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
142 }
144 // <MESSAGE_NODETYPE>*node.type()</MESSAGE_NODETYPE>
145 msgbuf = msgbuf + "<" + MESSAGE_NODETYPE + ">" + NodeUtilities::nodeTypeToString(*node);
146 msgbuf = msgbuf + "</" + MESSAGE_NODETYPE + ">";
148 if (node->content() != NULL) {
149 // <MESSAGE_CONTENT>node->content()</MESSAGE_CONTENT>
150 msgbuf = msgbuf + "<" + MESSAGE_CONTENT + ">" + node->content();
151 msgbuf = msgbuf + "</" + MESSAGE_CONTENT + ">";
152 }
154 // <MESSAGE_NAME>name</MESSAGE_NAME>
155 msgbuf = msgbuf + "<" + MESSAGE_NAME + ">";
157 if( name != NULL )
158 msgbuf += name;
160 msgbuf = msgbuf + "</" + MESSAGE_NAME + ">";
162 // </MESSAGE_NEWOBJ>
163 msgbuf = msgbuf + "</" + MESSAGE_NEWOBJ + ">";
164 } else if (id.empty()) {
165 // if ID is NULL, then we have a real problem -- we were not able to find a key
166 // nor generate one. The only thing we can really do here is abort, since we have
167 // no way to let the other client(s) uniquely identify this object.
168 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!"));
169 return;
170 } else {
172 }
174 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Generated message");
176 if (!only_collect_nodes && msgbuf != NULL && !id.empty()) {
177 // Collect new object's attributes and append them onto the msgbuf
178 Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrlist = node->attributeList();
180 for(; attrlist; attrlist++) {
181 MessageUtilities::objectChangeMessage(msgbuf,
182 xmt, id, g_quark_to_string(attrlist->key),
183 NULL, attrlist->value, false);
184 }
185 }
187 if (!only_collect_nodes)
188 childmsgbuf.push_back(msgbuf);
190 if (!id.empty() && collect_children) {
191 Glib::ustring childbuf;
192 // Collect any child objects of this new object
193 for ( Inkscape::XML::Node const *child = node->firstChild(); child != NULL; child = child->next() ) {
194 childbuf.clear();
195 MessageUtilities::newObjectMessage(childbuf,
196 newnodesbuf, childmsgbuf, xmt, child, only_collect_nodes);
197 if (!only_collect_nodes) {
198 // we're recursing down the tree, so we're picking up child nodes first
199 // and parents afterwards
200 // childmsgbuf.push_front(childbuf);
201 }
203 }
204 }
205 }
207 void
208 MessageUtilities::objectChangeMessage(Glib::ustring &msgbuf,
209 XMLNodeTracker* xmt,
210 const Glib::ustring &id,
211 gchar const* key,
212 gchar const* oldval,
213 gchar const* newval,
214 bool is_interactive)
215 {
216 // Construct message
218 // <MESSAGE_CHANGE><MESSAGE_ID>id</MESSAGE_ID>
219 msgbuf = msgbuf + "<" + MESSAGE_CHANGE + ">";
220 msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
221 msgbuf += id;
222 msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
224 // <MESSAGE_KEY>key</MESSAGE_KEY>
225 msgbuf = msgbuf + "<" + MESSAGE_KEY + ">";
226 if (key != NULL) {
227 msgbuf += key;
228 }
229 msgbuf = msgbuf + "</" + MESSAGE_KEY + ">";
231 // <MESSAGE_OLDVAL>oldval</MESSAGE_OLDVAL>
232 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
233 if (oldval != NULL) {
234 msgbuf += oldval;
235 }
236 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
238 // <MESSAGE_NEWVAL>newval</MESSAGE_NEWVAL>
239 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
240 if (newval != NULL) {
241 msgbuf += newval;
242 }
243 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
245 // <MESSAGE_ISINTERACTIVE>is_interactive</MESSAGE_ISINTERACTIVE>
246 msgbuf = msgbuf + "<" + MESSAGE_ISINTERACTIVE + ">";
247 if (is_interactive) {
248 msgbuf += "true";
249 } else {
250 msgbuf += "false";
251 }
252 msgbuf = msgbuf + "</" + MESSAGE_ISINTERACTIVE + ">";
254 // </MESSAGE_CHANGE>
255 msgbuf = msgbuf + "</" + MESSAGE_CHANGE + ">";
256 }
258 void
259 MessageUtilities::objectDeleteMessage(Glib::ustring &msgbuf,
260 XMLNodeTracker* xmt,
261 Inkscape::XML::Node const& parent,
262 Inkscape::XML::Node const& child,
263 Inkscape::XML::Node const* prev)
264 {
265 /*
266 gchar const* parentid = NULL;
267 gchar const* previd = NULL;
268 gchar const* childid = NULL;
270 childid = child.attribute("id");
271 parentid = parent.attribute("id");
272 if (prev != NULL) {
273 previd = prev->attribute("id");
274 }*/
276 Glib::ustring parentid, previd, childid;
278 childid = xmt->get(child);
279 parentid = xmt->get(parent);
280 previd = xmt->get(*prev);
282 if (childid.empty())
283 return;
286 // <MESSAGE_DELETE><MESSAGE_PARENT>parentid</MESSAGE_PARENT>
287 msgbuf = msgbuf + "<" + MESSAGE_DELETE + ">" + "<" + MESSAGE_PARENT + ">";
288 if (!parentid.empty()) {
289 msgbuf += parentid;
290 }
291 msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
293 // <MESSAGE_CHILD>childid</MESSAGE_CHILD>
294 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
295 if (!childid.empty()) {
296 msgbuf += childid;
297 }
298 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
300 // <MESSAGE_REF>previd</MESSAGE_REF>
301 msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
302 if (!previd.empty()) {
303 msgbuf += previd;
304 }
305 msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
307 // </MESSAGE_DELETE>
308 msgbuf = msgbuf + "</" + MESSAGE_DELETE + ">";
309 }
311 void
312 MessageUtilities::contentChangeMessage(Glib::ustring& msgbuf,
313 const Glib::ustring &nodeid,
314 Util::ptr_shared<char> old_value,
315 Util::ptr_shared<char> new_value)
316 {
317 if (nodeid.empty())
318 return;
320 // <MESSAGE_NODECONTENT>
321 msgbuf = msgbuf + "<" + MESSAGE_NODECONTENT + ">";
323 // <MESSAGE_ID>nodeid</MESSAGE_ID>
324 msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
325 msgbuf += nodeid;
326 msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
328 // <MESSAGE_OLDVAL>old_value</MESSAGE_OLDVAL>
329 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
330 msgbuf += old_value.pointer();
331 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
333 // <MESSAGE_NEWVAL>new_value</MESSAGE_NEWVAL>
334 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
335 msgbuf += new_value.pointer();
336 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
338 // </MESSAGE_NODECONTENT>
339 msgbuf = msgbuf + "</" + MESSAGE_NODECONTENT + ">";
340 }
342 void
343 MessageUtilities::childOrderChangeMessage(Glib::ustring& msgbuf,
344 const Glib::ustring &childid,
345 const Glib::ustring &oldprevid,
346 const Glib::ustring &newprevid)
347 {
348 if (childid.empty())
349 return;
351 // <MESSAGE_ORDERCHANGE>
352 msgbuf = msgbuf + "<" + MESSAGE_ORDERCHANGE + ">";
354 // <MESSAGE_ID>nodeid</MESSAGE_ID>
355 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
356 msgbuf += childid;
357 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
359 // <MESSAGE_OLDVAL>oldprevid</MESSAGE_OLDVAL>
360 /*
361 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
362 msgbuf += (*oldprevid);
363 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
364 */
366 // <MESSAGE_NEWVAL>newprevid</MESSAGE_NEWVAL>
367 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
368 msgbuf += newprevid;
369 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
371 // </MESSAGE_ORDERCHANGE>
372 msgbuf = msgbuf + "</" + MESSAGE_ORDERCHANGE + ">";
374 }
377 bool
378 MessageUtilities::getFirstMessageTag(struct Node& buf, const Glib::ustring &msg)
379 {
380 if (msg.empty())
381 return false;
383 // See if we have a valid start tag, i.e. < ... >. If we do,
384 // continue; if not, stop and return NULL.
385 //
386 // find_first_of returns ULONG_MAX when it cannot find the first
387 // instance of the given character.
389 Glib::ustring::size_type startDelim = msg.find_first_of('<');
390 if (startDelim != ULONG_MAX) {
391 Glib::ustring::size_type endDelim = msg.find_first_of('>');
392 if (endDelim != ULONG_MAX) {
393 if (endDelim > startDelim) {
394 buf.tag = msg.substr(startDelim+1, (endDelim-startDelim)-1);
395 if (buf.tag.find_first_of('/') == ULONG_MAX) { // start tags should not be end tags
398 // construct end tag (</buf.data>)
399 Glib::ustring endTag(buf.tag);
400 endTag.insert(0, "/");
402 Glib::ustring::size_type endTagLoc = msg.find(endTag, endDelim);
403 if (endTagLoc != ULONG_MAX) {
404 buf.data = msg.substr(endDelim+1, ((endTagLoc - 1) - (endDelim + 1)));
405 buf.next_pos = endTagLoc + endTag.length() + 1;
407 return true;
408 }
409 }
410 }
411 }
412 }
414 return false;
415 }
417 bool
418 MessageUtilities::findTag(struct Node& buf, const Glib::ustring &msg)
419 {
420 if (msg.empty())
421 return false;
423 // Read desired tag type out of buffer, and append
424 // < > to it
426 Glib::ustring searchterm("<");
427 searchterm += buf.tag;
428 searchterm + ">";
430 Glib::ustring::size_type tagStart = msg.find(searchterm, 0);
431 if (tagStart != ULONG_MAX) {
432 // Find ending tag starting at the point at the end of
433 // the start tag.
434 searchterm.insert(1, "/");
435 Glib::ustring::size_type tagEnd = msg.find(searchterm, tagStart + searchterm.length());
436 if (tagEnd != ULONG_MAX) {
437 Glib::ustring::size_type start = tagStart + searchterm.length();
438 buf.data = msg.substr(start, tagEnd - start);
439 return true;
440 }
441 }
442 return false;
443 }
445 Glib::ustring
446 MessageUtilities::makeTagWithContent(const Glib::ustring &tagname,
447 const Glib::ustring &content)
448 {
449 Glib::ustring buf = "<" + tagname + ">";
450 buf += content;
451 buf += "</" + tagname + ">";
452 return buf;
453 }
456 }
458 }
462 /*
463 Local Variables:
464 mode:c++
465 c-file-style:"stroustrup"
466 c-file-offsets:((innamespace . 0)(inline-open . 0))
467 indent-tabs-mode:nil
468 fill-column:99
469 End:
470 */
471 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :