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"
17 #include "util/ucompose.hpp"
19 #include "xml/node.h"
20 #include "xml/attribute-record.h"
21 #include "xml/repr.h"
23 #include "jabber_whiteboard/defines.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.
42 Glib::ustring
43 MessageUtilities::objectToString(Inkscape::XML::Node *element)
44 {
45 if(element->type() == Inkscape::XML::TEXT_NODE)
46 return String::ucompose("<text>%1</text>",element->content());
48 Glib::ustring attributes;
50 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const>
51 iter = element->attributeList() ; iter ; ++iter )
52 {
53 attributes.append(g_quark_to_string(iter->key));
54 attributes.append("=\"");
55 attributes.append(iter->value);
56 attributes.append("\" ");
57 }
59 return String::ucompose("<%1 %2/>",element->name(),attributes);
60 }
61 /*
62 void
63 MessageUtilities::newObjectMessage(Glib::ustring &msgbuf,
64 KeyNodeTable& newnodesbuf,
65 NewChildObjectMessageList& childmsgbuf,
66 XMLNodeTracker* xmt,
67 Inkscape::XML::Node const* node,
68 bool only_collect_nodes,
69 bool collect_children)
70 {
71 // Initialize pointers
72 Glib::ustring id, refid, parentid;
74 gchar const* name = NULL;
75 XML::Node* parent = NULL;
76 XML::Node* ref = NULL;
78 bool only_add_children = false;
80 //g_log(NULL, G_LOG_LEVEL_DEBUG, "newObjectMessage: processing node %p of type %s", node, NodeUtilities::nodeTypeToString(*node).data());
82 if (node != NULL) {
83 parent = sp_repr_parent(node);
84 if (parent != NULL) {
85 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for parent node %p (on node %p)", parent, node);
86 parentid = NodeUtilities::findNodeID(*parent, xmt, newnodesbuf);
87 if (parentid.empty()) {
88 g_warning("Parent %p is not being tracked, creating new ID", parent);
89 parentid = xmt->generateKey();
90 newnodesbuf.put(parentid, parent);
91 }
93 if ( node != parent->firstChild() && parent != NULL ) {
94 ref = parent->firstChild();
95 while (ref->next() != node) {
96 ref = ref->next();
97 }
98 }
99 }
101 if (ref != NULL) {
102 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Attempting to find ID for ref node %p (on %p)", ref, node);
103 refid = NodeUtilities::findNodeID(*ref, xmt, newnodesbuf);
104 if (refid.empty() && ref != NULL) {
105 g_warning("Ref %p is not being tracked, creating new ID", ref);
106 refid = xmt->generateKey();
107 newnodesbuf.put(refid, ref);
108 }
109 }
111 name = static_cast< gchar const* >(node->name());
112 }
114 // Generate an id for this object and append it onto the list, if
115 // it's not already in the tracker
116 if (!xmt->isSpecialNode(node->name())) {
117 if (!xmt->isTracking(*node)) {
118 id = xmt->generateKey();
119 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Inserting %p with id %s", node, id.c_str());
120 newnodesbuf.put(id, node);
121 } else {
122 id = xmt->get(*node);
123 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Found id %s for node %p; not inserting into new nodes buffers.", id.c_str(), node);
124 }
125 } else {
126 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Processing special node; not generating key");
127 id = xmt->get(*node);
128 if (id.empty()) {
129 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());
130 id = xmt->generateKey();
131 newnodesbuf.put(id, node);
132 }
133 only_add_children = true;
134 }
136 // If we're only adding children (i.e. this is a special node)
137 // don't process the given node.
138 if( !only_add_children && !id.empty() && msgbuf != NULL && !only_collect_nodes ) {
139 // <MESSAGE_NEWOBJ>
140 msgbuf = msgbuf + "<" + MESSAGE_NEWOBJ + ">";
142 // <MESSAGE_PARENT>
143 msgbuf = msgbuf + "<" + MESSAGE_PARENT + ">";
145 if(!parentid.empty()) {
146 msgbuf += parentid;
147 }
149 // </MESSAGE_NEWOBJ><MESSAGE_CHILD>id</MESSAGE_CHILD>
150 msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
152 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
153 msgbuf += id;
155 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
157 if(!refid.empty()) {
158 // <MESSAGE_REF>refid</MESSAGE_REF>
159 msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
161 msgbuf += refid;
163 msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
164 }
166 // <MESSAGE_NODETYPE>*node.type()</MESSAGE_NODETYPE>
167 msgbuf = msgbuf + "<" + MESSAGE_NODETYPE + ">" + NodeUtilities::nodeTypeToString(*node);
168 msgbuf = msgbuf + "</" + MESSAGE_NODETYPE + ">";
170 if (node->content() != NULL) {
171 // <MESSAGE_CONTENT>node->content()</MESSAGE_CONTENT>
172 msgbuf = msgbuf + "<" + MESSAGE_CONTENT + ">" + node->content();
173 msgbuf = msgbuf + "</" + MESSAGE_CONTENT + ">";
174 }
176 // <MESSAGE_NAME>name</MESSAGE_NAME>
177 msgbuf = msgbuf + "<" + MESSAGE_NAME + ">";
179 if( name != NULL )
180 msgbuf += name;
182 msgbuf = msgbuf + "</" + MESSAGE_NAME + ">";
184 // </MESSAGE_NEWOBJ>
185 msgbuf = msgbuf + "</" + MESSAGE_NEWOBJ + ">";
186 } else if (id.empty()) {
187 // if ID is NULL, then we have a real problem -- we were not able to find a key
188 // nor generate one. The only thing we can really do here is abort, since we have
189 // no way to let the other client(s) uniquely identify this object.
190 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!"));
191 return;
192 } else {
194 }
196 //g_log(NULL, G_LOG_LEVEL_DEBUG, "Generated message");
198 if (!only_collect_nodes && msgbuf != NULL && !id.empty()) {
199 // Collect new object's attributes and append them onto the msgbuf
200 Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrlist = node->attributeList();
202 for(; attrlist; attrlist++) {
203 MessageUtilities::objectChangeMessage(msgbuf,
204 xmt, id, g_quark_to_string(attrlist->key),
205 NULL, attrlist->value, false);
206 }
207 }
209 if (!only_collect_nodes)
210 childmsgbuf.push_back(msgbuf);
212 if (!id.empty() && collect_children) {
213 Glib::ustring childbuf;
214 // Collect any child objects of this new object
215 for ( Inkscape::XML::Node const *child = node->firstChild(); child != NULL; child = child->next() ) {
216 childbuf.clear();
217 MessageUtilities::newObjectMessage(childbuf,
218 newnodesbuf, childmsgbuf, xmt, child, only_collect_nodes);
219 if (!only_collect_nodes) {
220 // we're recursing down the tree, so we're picking up child nodes first
221 // and parents afterwards
222 // childmsgbuf.push_front(childbuf);
223 }
225 }
226 }
227 }
229 void
230 MessageUtilities::objectChangeMessage(Glib::ustring &msgbuf,
231 XMLNodeTracker* xmt,
232 const Glib::ustring &id,
233 gchar const* key,
234 gchar const* oldval,
235 gchar const* newval,
236 bool is_interactive)
237 {
238 // Construct message
240 // <MESSAGE_CHANGE><MESSAGE_ID>id</MESSAGE_ID>
241 msgbuf = msgbuf + "<" + MESSAGE_CHANGE + ">";
242 msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
243 msgbuf += id;
244 msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
246 // <MESSAGE_KEY>key</MESSAGE_KEY>
247 msgbuf = msgbuf + "<" + MESSAGE_KEY + ">";
248 if (key != NULL) {
249 msgbuf += key;
250 }
251 msgbuf = msgbuf + "</" + MESSAGE_KEY + ">";
253 // <MESSAGE_OLDVAL>oldval</MESSAGE_OLDVAL>
254 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
255 if (oldval != NULL) {
256 msgbuf += oldval;
257 }
258 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
260 // <MESSAGE_NEWVAL>newval</MESSAGE_NEWVAL>
261 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
262 if (newval != NULL) {
263 msgbuf += newval;
264 }
265 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
267 // <MESSAGE_ISINTERACTIVE>is_interactive</MESSAGE_ISINTERACTIVE>
268 msgbuf = msgbuf + "<" + MESSAGE_ISINTERACTIVE + ">";
269 if (is_interactive) {
270 msgbuf += "true";
271 } else {
272 msgbuf += "false";
273 }
274 msgbuf = msgbuf + "</" + MESSAGE_ISINTERACTIVE + ">";
276 // </MESSAGE_CHANGE>
277 msgbuf = msgbuf + "</" + MESSAGE_CHANGE + ">";
278 }
280 void
281 MessageUtilities::objectDeleteMessage(Glib::ustring &msgbuf,
282 XMLNodeTracker* xmt,
283 Inkscape::XML::Node const& parent,
284 Inkscape::XML::Node const& child,
285 Inkscape::XML::Node const* prev)
286 {
287 /*
288 gchar const* parentid = NULL;
289 gchar const* previd = NULL;
290 gchar const* childid = NULL;
292 childid = child.attribute("id");
293 parentid = parent.attribute("id");
294 if (prev != NULL) {
295 previd = prev->attribute("id");
296 }
298 Glib::ustring parentid, previd, childid;
300 childid = xmt->get(child);
301 parentid = xmt->get(parent);
302 previd = xmt->get(*prev);
304 if (childid.empty())
305 return;
308 // <MESSAGE_DELETE><MESSAGE_PARENT>parentid</MESSAGE_PARENT>
309 msgbuf = msgbuf + "<" + MESSAGE_DELETE + ">" + "<" + MESSAGE_PARENT + ">";
310 if (!parentid.empty()) {
311 msgbuf += parentid;
312 }
313 msgbuf = msgbuf + "</" + MESSAGE_PARENT + ">";
315 // <MESSAGE_CHILD>childid</MESSAGE_CHILD>
316 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
317 if (!childid.empty()) {
318 msgbuf += childid;
319 }
320 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
322 // <MESSAGE_REF>previd</MESSAGE_REF>
323 msgbuf = msgbuf + "<" + MESSAGE_REF + ">";
324 if (!previd.empty()) {
325 msgbuf += previd;
326 }
327 msgbuf = msgbuf + "</" + MESSAGE_REF + ">";
329 // </MESSAGE_DELETE>
330 msgbuf = msgbuf + "</" + MESSAGE_DELETE + ">";
331 }
333 void
334 MessageUtilities::contentChangeMessage(Glib::ustring& msgbuf,
335 const Glib::ustring &nodeid,
336 Util::ptr_shared<char> old_value,
337 Util::ptr_shared<char> new_value)
338 {
339 if (nodeid.empty())
340 return;
342 // <MESSAGE_NODECONTENT>
343 msgbuf = msgbuf + "<" + MESSAGE_NODECONTENT + ">";
345 // <MESSAGE_ID>nodeid</MESSAGE_ID>
346 msgbuf = msgbuf + "<" + MESSAGE_ID + ">";
347 msgbuf += nodeid;
348 msgbuf = msgbuf + "</" + MESSAGE_ID + ">";
350 // <MESSAGE_OLDVAL>old_value</MESSAGE_OLDVAL>
351 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
352 msgbuf += old_value.pointer();
353 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
355 // <MESSAGE_NEWVAL>new_value</MESSAGE_NEWVAL>
356 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
357 msgbuf += new_value.pointer();
358 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
360 // </MESSAGE_NODECONTENT>
361 msgbuf = msgbuf + "</" + MESSAGE_NODECONTENT + ">";
362 }
364 void
365 MessageUtilities::childOrderChangeMessage(Glib::ustring& msgbuf,
366 const Glib::ustring &childid,
367 const Glib::ustring &oldprevid,
368 const Glib::ustring &newprevid)
369 {
370 if (childid.empty())
371 return;
373 // <MESSAGE_ORDERCHANGE>
374 msgbuf = msgbuf + "<" + MESSAGE_ORDERCHANGE + ">";
376 // <MESSAGE_ID>nodeid</MESSAGE_ID>
377 msgbuf = msgbuf + "<" + MESSAGE_CHILD + ">";
378 msgbuf += childid;
379 msgbuf = msgbuf + "</" + MESSAGE_CHILD + ">";
381 // <MESSAGE_OLDVAL>oldprevid</MESSAGE_OLDVAL>
382 /*
383 msgbuf = msgbuf + "<" + MESSAGE_OLDVAL + ">";
384 msgbuf += (*oldprevid);
385 msgbuf = msgbuf + "</" + MESSAGE_OLDVAL + ">";
388 // <MESSAGE_NEWVAL>newprevid</MESSAGE_NEWVAL>
389 msgbuf = msgbuf + "<" + MESSAGE_NEWVAL + ">";
390 msgbuf += newprevid;
391 msgbuf = msgbuf + "</" + MESSAGE_NEWVAL + ">";
393 // </MESSAGE_ORDERCHANGE>
394 msgbuf = msgbuf + "</" + MESSAGE_ORDERCHANGE + ">";
396 }
399 bool
400 MessageUtilities::getFirstMessageTag(struct Node& buf, const Glib::ustring &msg)
401 {
402 if (msg.empty())
403 return false;
405 // See if we have a valid start tag, i.e. < ... >. If we do,
406 // continue; if not, stop and return NULL.
407 //
408 // find_first_of returns ULONG_MAX when it cannot find the first
409 // instance of the given character.
411 Glib::ustring::size_type startDelim = msg.find_first_of('<');
412 if (startDelim != ULONG_MAX) {
413 Glib::ustring::size_type endDelim = msg.find_first_of('>');
414 if (endDelim != ULONG_MAX) {
415 if (endDelim > startDelim) {
416 buf.tag = msg.substr(startDelim+1, (endDelim-startDelim)-1);
417 if (buf.tag.find_first_of('/') == ULONG_MAX) { // start tags should not be end tags
420 // construct end tag (</buf.data>)
421 Glib::ustring endTag(buf.tag);
422 endTag.insert(0, "/");
424 Glib::ustring::size_type endTagLoc = msg.find(endTag, endDelim);
425 if (endTagLoc != ULONG_MAX) {
426 buf.data = msg.substr(endDelim+1, ((endTagLoc - 1) - (endDelim + 1)));
427 buf.next_pos = endTagLoc + endTag.length() + 1;
429 return true;
430 }
431 }
432 }
433 }
434 }
436 return false;
437 }
439 bool
440 MessageUtilities::findTag(struct Node& buf, const Glib::ustring &msg)
441 {
442 if (msg.empty())
443 return false;
445 // Read desired tag type out of buffer, and append
446 // < > to it
448 Glib::ustring searchterm("<");
449 searchterm += buf.tag;
450 searchterm + ">";
452 Glib::ustring::size_type tagStart = msg.find(searchterm, 0);
453 if (tagStart != ULONG_MAX) {
454 // Find ending tag starting at the point at the end of
455 // the start tag.
456 searchterm.insert(1, "/");
457 Glib::ustring::size_type tagEnd = msg.find(searchterm, tagStart + searchterm.length());
458 if (tagEnd != ULONG_MAX) {
459 Glib::ustring::size_type start = tagStart + searchterm.length();
460 buf.data = msg.substr(start, tagEnd - start);
461 return true;
462 }
463 }
464 return false;
465 }
467 Glib::ustring
468 MessageUtilities::makeTagWithContent(const Glib::ustring &tagname,
469 const Glib::ustring &content)
470 {
471 Glib::ustring buf = "<" + tagname + ">";
472 buf += content;
473 buf += "</" + tagname + ">";
474 return buf;
475 }
476 */
478 }
480 }
484 /*
485 Local Variables:
486 mode:c++
487 c-file-style:"stroustrup"
488 c-file-offsets:((innamespace . 0)(inline-open . 0))
489 indent-tabs-mode:nil
490 fill-column:99
491 End:
492 */
493 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :