1 /**
2 * Inkboard message -> XML::Event* serializer
3 *
4 * Authors:
5 * David Yip <yipdw@rose-hulman.edu>
6 *
7 * Copyright (c) 2005 Authors
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
12 #include "xml/attribute-record.h"
14 #include "jabber_whiteboard/serializer.h"
16 #include "util/list.h"
17 #include "util/share.h"
19 #include "jabber_whiteboard/message-utilities.h"
20 #include "jabber_whiteboard/message-tags.h"
21 #include "jabber_whiteboard/typedefs.h"
22 #include "jabber_whiteboard/node-tracker.h"
23 #include "jabber_whiteboard/node-utilities.h"
24 #include "jabber_whiteboard/node-tracker-observer.h"
26 namespace Inkscape {
28 namespace Whiteboard {
30 void
31 Serializer::notifyChildAdded(XML::Node& node, XML::Node& child, XML::Node* prev)
32 {
33 // do not recurse upon initial notification
34 this->_newObjectEventHelper(node, child, prev, false);
35 this->_nn.insert(&child);
36 }
38 void
39 Serializer::_newObjectEventHelper(XML::Node& node, XML::Node& child, XML::Node* prev, bool recurse)
40 {
41 // 1. Check if we are tracking the parent node,
42 // and issue it an ID if we are not.
43 std::string parentid = this->_findOrGenerateNodeID(node);
45 // 2. Check if the child node is a special node.
46 // Special nodes are nodes that should appear only once in a document.
47 // If it is, we do not want to generate a new ID for the child; we will use
48 // the existing ID. Otherwise, we will generate a new ID for it, since we
49 // have not yet seen it.
50 std::string childid;
51 if (this->_xnt->isSpecialNode(child.name())) {
52 childid = this->_xnt->get(child);
53 } else {
54 // If the child id already exists in the new node buffer, then we've already seen it.
55 if (!this->actions.tryToTrack(&child, NODE_ADD)) {
56 return;
57 } else {
58 childid = this->_xnt->generateKey();
59 // childid = this->_findOrGenerateNodeID(child);
60 }
61 }
63 // 3. Find this node's previous node, and, if it has one, retrieve its ID.
64 std::string previd;
65 if (prev) {
66 previd = this->_findOrGenerateNodeID(*prev);
67 }
69 // 4. Serialize.
70 Glib::ustring childmsg = MessageUtilities::makeTagWithContent(MESSAGE_CHILD, childid);
71 Glib::ustring parentmsg = MessageUtilities::makeTagWithContent(MESSAGE_PARENT, parentid);
72 Glib::ustring namemsg = MessageUtilities::makeTagWithContent(MESSAGE_NAME, child.name());
73 Glib::ustring nodetype = MessageUtilities::makeTagWithContent(MESSAGE_NODETYPE, NodeUtilities::nodeTypeToString(child));
75 Glib::ustring prevmsg;
76 if (!previd.empty()) {
77 prevmsg = MessageUtilities::makeTagWithContent(MESSAGE_REF, previd);
78 }
80 Glib::ustring buf = MessageUtilities::makeTagWithContent(MESSAGE_NEWOBJ, childmsg + parentmsg + prevmsg + namemsg + nodetype);
83 this->_events.push_back(buf);
85 // 5. Add the child node to the new nodes buffers.
86 this->newnodes.push_back(SerializedEventNodeAction(KeyNodePair(childid, &child), NODE_ADD));
87 this->newkeys[&child] = childid;
88 this->_parent_child_map[&child] = &node;
90 // 6. Scan attributes and content.
91 Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrlist = child.attributeList();
93 for(; attrlist; attrlist++) {
94 this->notifyAttributeChanged(child, attrlist->key, Util::ptr_shared<char>(), attrlist->value);
95 }
97 if (child.content()) {
98 this->notifyContentChanged(child, Util::ptr_shared<char>(), Util::share_string(child.content()));
99 }
101 this->_attributes_scanned.insert(childid);
103 // 7. Repeat this process for each child of this child.
104 if (recurse && child.childCount() > 0) {
105 XML::Node* prev = child.firstChild();
106 for(XML::Node* ch = child.firstChild(); ch; ch = ch->next()) {
107 if (ch == child.firstChild()) {
108 // No prev node in this case.
109 this->_newObjectEventHelper(child, *ch, NULL, true);
110 } else {
111 this->_newObjectEventHelper(child, *ch, prev, true);
112 prev = ch;
113 }
114 }
115 }
117 return;
118 }
121 void
122 Serializer::notifyChildRemoved(XML::Node& node, XML::Node& child, XML::Node* prev)
123 {
124 // 1. Get the ID of the child.
125 std::string childid;
127 _pc_map_type::iterator i = this->_parent_child_map.find(&child);
128 if (i != this->_parent_child_map.end() && i->second != &node) {
129 // Don't look in local! Go for the tracker.
130 childid = this->_xnt->get(child);
131 } else if (i == this->_parent_child_map.end()) {
132 childid = this->_findOrGenerateNodeID(child);
133 } else if (i->second == &node) {
134 childid = this->_findOrGenerateNodeID(child);
135 this->_parent_child_map.erase(i);
136 } else {
137 childid = this->_findOrGenerateNodeID(child);
138 }
140 // 2. Double-deletes don't make any sense. If we've seen this node already and if it's
141 // marked for deletion, return.
142 if (!this->actions.tryToTrack(&child, NODE_REMOVE)) {
143 return;
144 } else {
145 // 2a. Although we do not have to remove all child nodes of this subtree,
146 // we _do_ have to mark each child node as deleted.
147 this->_recursiveMarkAsRemoved(child);
148 }
150 // 2. Mark this node as deleted. We don't want to be faced with the possibility of
151 // generating a new key for this deleted node, so insert it into both maps.
152 this->newnodes.push_back(SerializedEventNodeAction(KeyNodePair(childid, &child), NODE_REMOVE));
153 this->newkeys[&child] = childid;
154 this->_nn.erase(&child);
155 std::string parentid = this->_findOrGenerateNodeID(node);
158 // 4. Serialize the event.
159 this->_attributes_scanned.erase(childid);
160 Glib::ustring childidmsg = MessageUtilities::makeTagWithContent(MESSAGE_CHILD, childid);
161 Glib::ustring parentidmsg = MessageUtilities::makeTagWithContent(MESSAGE_PARENT, parentid);
162 this->_events.push_back(MessageUtilities::makeTagWithContent(MESSAGE_DELETE, childidmsg + parentidmsg));
163 }
166 void
167 Serializer::notifyChildOrderChanged(XML::Node& node, XML::Node& child, XML::Node* old_prev, XML::Node* new_prev)
168 {
169 // 1. Find the ID of the node, or generate it if it does not exist.
170 std::string nodeid = this->_findOrGenerateNodeID(child);
172 // 2. Find the ID of the parent of this node, or generate it if it does not exist.
173 std::string parentid = this->_findOrGenerateNodeID(*(child.parent()));
175 // 3. Get the ID for the new child reference node, or generate it if it does not exist.
176 std::string newprevid = this->_findOrGenerateNodeID(*new_prev);
178 // 4. Get the ID for the old child reference node, or generate it if it does not exist.
179 std::string oldprevid = this->_findOrGenerateNodeID(*old_prev);
181 // 5. Serialize the event.
182 Glib::ustring nodeidmsg = MessageUtilities::makeTagWithContent(MESSAGE_ID, nodeid);
183 Glib::ustring parentidmsg = MessageUtilities::makeTagWithContent(MESSAGE_PARENT, parentid);
184 Glib::ustring oldprevidmsg = MessageUtilities::makeTagWithContent(MESSAGE_OLDVAL, oldprevid);
185 Glib::ustring newprevidmsg = MessageUtilities::makeTagWithContent(MESSAGE_NEWVAL, newprevid);
187 this->_events.push_back(MessageUtilities::makeTagWithContent(MESSAGE_ORDERCHANGE, nodeidmsg + parentidmsg + oldprevidmsg + newprevidmsg));
188 }
190 void
191 Serializer::notifyContentChanged(XML::Node& node, Util::ptr_shared<char> old_content, Util::ptr_shared<char> new_content)
192 {
193 // 1. Find the ID of the node, or generate it if it does not exist.
194 std::string nodeid = this->_findOrGenerateNodeID(node);
196 std::string oldvalmsg, newvalmsg;
198 // 2. If the old and new content are identical, don't send out this change.
199 // (identical meaning "same string" or "same string content")
200 if (old_content == new_content) {
201 return;
202 }
204 if (old_content.pointer() != NULL && new_content.pointer() != NULL) {
205 if (strcmp(old_content.pointer(), new_content.pointer()) == 0) {
206 return;
207 }
208 }
210 // 3. Serialize the event.
211 if (old_content.pointer() != NULL) {
212 oldvalmsg = MessageUtilities::makeTagWithContent(MESSAGE_OLDVAL, old_content.pointer());
213 }
215 if (new_content.pointer() != NULL) {
216 newvalmsg = MessageUtilities::makeTagWithContent(MESSAGE_NEWVAL, new_content.pointer());
217 }
219 Glib::ustring nodeidmsg = MessageUtilities::makeTagWithContent(MESSAGE_ID, nodeid);
220 this->_events.push_back(MessageUtilities::makeTagWithContent(MESSAGE_NODECONTENT, nodeidmsg + oldvalmsg + newvalmsg));
221 }
223 void
224 Serializer::notifyAttributeChanged(XML::Node& node, GQuark name, Util::ptr_shared<char> old_value, Util::ptr_shared<char> new_value)
225 {
226 // 1. Find the ID of the node that has had an attribute modified, or generate it if it
227 // does not exist.
228 std::string nodeid = this->_findOrGenerateNodeID(node);
230 // Proceed with 2-4 if the node has not already been scanned by notifyChildAdded.
231 if (this->_attributes_scanned.find(nodeid) == this->_attributes_scanned.end()) {
232 // 2. Convert the key to a string.
233 Glib::ustring key = g_quark_to_string(name);
235 // 3. If oldval == newval, don't echo this change.
236 if (new_value.pointer() != NULL && old_value.pointer() != NULL) {
237 if (strcmp(new_value.pointer(), old_value.pointer()) == 0) {
238 return;
239 }
240 }
242 // 4. Serialize the event.
243 Glib::ustring keymsg = MessageUtilities::makeTagWithContent(MESSAGE_KEY, key);
244 Glib::ustring oldvalmsg, newvalmsg;
246 if (old_value.pointer() != NULL) {
247 oldvalmsg = MessageUtilities::makeTagWithContent(MESSAGE_OLDVAL, old_value.pointer());
248 }
250 if (new_value.pointer() != NULL) {
251 newvalmsg = MessageUtilities::makeTagWithContent(MESSAGE_NEWVAL, new_value.pointer());
252 }
254 Glib::ustring nodeidmsg = MessageUtilities::makeTagWithContent(MESSAGE_ID, nodeid);
256 this->_events.push_back(MessageUtilities::makeTagWithContent(MESSAGE_CHANGE, nodeidmsg + keymsg + oldvalmsg + newvalmsg));
257 }
258 }
260 void
261 Serializer::synthesizeChildNodeAddEvents()
262 {
263 _New_nodes_type::iterator i = this->_nn.begin();
264 for(; i != this->_nn.end(); ++i) {
265 XML::Node* parent = *i;
266 // The root of the subtree defined by parent has already been considered; now,
267 // recursively look at the rest of the tree.
268 XML::Node* prev = parent->firstChild();
269 for(XML::Node* ch = parent->firstChild(); ch; ch = ch->next()) {
270 if (ch == parent->firstChild()) {
271 this->_newObjectEventHelper(*parent, *ch, NULL, true);
272 } else {
273 this->_newObjectEventHelper(*parent, *ch, prev, true);
274 prev = ch;
275 }
276 }
277 }
278 }
281 void
282 Serializer::_recursiveMarkAsRemoved(XML::Node& node)
283 {
284 this->actions.tryToTrack(&node, NODE_REMOVE);
286 for(XML::Node* ch = node.firstChild(); ch; ch = ch->next()) {
287 this->_recursiveMarkAsRemoved(*ch);
288 }
289 }
291 }
293 }
295 /*
296 Local Variables:
297 mode:c++
298 c-file-style:"stroustrup"
299 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
300 indent-tabs-mode:nil
301 fill-column:99
302 End:
303 */
304 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :