-
Notifications
You must be signed in to change notification settings - Fork 47
/
Namespace.cpp
300 lines (246 loc) · 9.71 KB
/
Namespace.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#include "Namespace.h"
#include "inameobserver.h"
#include "itextstream.h"
#include "module/StaticModule.h"
#include <list>
class ConnectNamespacedWalker :
public scene::NodeVisitor
{
Namespace* _nspace;
public:
ConnectNamespacedWalker(Namespace* nspace) :
_nspace(nspace)
{}
virtual bool pre(const scene::INodePtr& node)
{
NamespacedPtr namespaced = Node_getNamespaced(node);
if (!namespaced)
{
return true;
}
INamespace* foreignNamespace = namespaced->getNamespace();
// Do not reconnect to same namespace, this causes invalid name changes
if (foreignNamespace == _nspace)
{
rWarning() << "ConnectNamespacedWalker: node '" << node->name()
<< "' is already attached to namespace at " << _nspace
<< std::endl;
return true;
}
else if (foreignNamespace)
{
// The node is already connected to a different namespace, disconnect
namespaced->disconnectNameObservers();
namespaced->detachNames();
namespaced->setNamespace(NULL);
}
// Set the namespace reference and add all "names"
namespaced->setNamespace(_nspace);
namespaced->attachNames();
return true;
}
};
class DisconnectNamespacedWalker :
public scene::NodeVisitor
{
public:
virtual bool pre(const scene::INodePtr& node) {
NamespacedPtr namespaced = Node_getNamespaced(node);
// Only do this, if the item is Namespaced and attached to a Namespace
if (namespaced != NULL && namespaced->getNamespace() != NULL) {
// Remove names and clear namespace
namespaced->detachNames();
namespaced->setNamespace(NULL);
}
return true;
}
};
class ConnectNameObserverWalker :
public scene::NodeVisitor
{
public:
virtual bool pre(const scene::INodePtr& node) {
NamespacedPtr namespaced = Node_getNamespaced(node);
// Only do this, if the item is Namespaced and attached to a Namespace
if (namespaced != NULL && namespaced->getNamespace() != NULL) {
namespaced->connectNameObservers();
}
return true;
}
};
class DisconnectNameObserverWalker :
public scene::NodeVisitor
{
public:
virtual bool pre(const scene::INodePtr& node) {
NamespacedPtr namespaced = Node_getNamespaced(node);
// Only do this, if the item is Namespaced and attached to a Namespace
if (namespaced != NULL && namespaced->getNamespace() != NULL) {
namespaced->disconnectNameObservers();
}
return true;
}
};
// A walker importing all names into this namespace
struct GatherNamespacedWalker : public scene::NodeVisitor
{
// The set of imported names to keep track of what got imported already
std::set<NamespacedPtr> result;
virtual bool pre(const scene::INodePtr& node)
{
// Insert node if it is a Namespaced object
NamespacedPtr namespaced = Node_getNamespaced(node);
if (namespaced)
{
result.insert(namespaced);
}
return true;
}
};
void Namespace::connect(const scene::INodePtr& root)
{
// Now traverse the subgraph and connect the nodes
ConnectNamespacedWalker firstWalker(this);
root->traverse(firstWalker);
ConnectNameObserverWalker secondWalker;
root->traverse(secondWalker);
}
void Namespace::disconnect(const scene::INodePtr& root)
{
// First, disconnect all NameObservers
DisconnectNameObserverWalker firstWalker;
root->traverse(firstWalker);
// Second, remove all "names" from the namespace and clear the reference
DisconnectNamespacedWalker secondWalker;
root->traverse(secondWalker);
}
bool Namespace::nameExists(const std::string& name)
{
assert(!name.empty());
return _uniqueNames.nameExists(name);
}
bool Namespace::insert(const std::string& name) {
return _uniqueNames.insert(name);
}
bool Namespace::erase(const std::string& name) {
return _uniqueNames.erase(name);
}
std::string Namespace::addUniqueName(const std::string& originalName)
{
return _uniqueNames.insertUnique(originalName);
}
Namespace::~Namespace()
{
assert(_observers.empty());
}
void Namespace::addNameObserver(const std::string& name, NameObserver& observer) {
// Just insert the observer
_observers.insert(ObserverMap::value_type(name, &observer));
}
void Namespace::removeNameObserver(const std::string& name, NameObserver& observer) {
// Lookup the iterator boundaries and find the observer
for (ObserverMap::iterator i = _observers.lower_bound(name), upperBound = _observers.upper_bound(name);
i != _observers.end() && i != upperBound; i++)
{
if (i->second == &observer) {
_observers.erase(i);
break;
}
}
}
void Namespace::nameChanged(const std::string& oldName, const std::string& newName) {
// Check if we should do anything at all
if (oldName == newName) {
return;
}
// Remove the name from our UniqueNameSet
if (!_uniqueNames.erase(oldName)) {
rError() << "[Namespace]: Could not remove old name before rename: " << oldName << "\n";
}
// Insert the new name, the NameObservers expect the new name to be present in the namespace
_uniqueNames.insert(newName);
// Notify the observers
// greebo: Note, we compare the name after checking upper_bound() once more to catch cases
// where a name gets changed from path_2 to path_23 for instance, which could result in a new
// upper_bound(). The operator!= subsequently fails as the iterator points to the element
// *after* the new upper_bound(), and the wrong observer gets called.
// Performance should not be a problem as this only gets executed on name changes, and there
// is a finite number of observers watching a single name anyway.
for (ObserverMap::iterator i = _observers.lower_bound(oldName);
i != _observers.end() && i != _observers.upper_bound(oldName) && i->first == oldName;
/* in-loop increment */)
{
assert(i->second != NULL);
// Call the observer, but increment the iterator beforehand,
// as the observer might remove this one.
(i++)->second->onNameChange(oldName, newName);
}
// greebo: usually, there are no more observers left in the multimap at this point
// as the default observing classes remove themselves from the namespace when the
// name changes. However, it's possible that there are some observers left,
// so we need to redirect them to the new name.
// This list will temporarily hold observers, as we need to change
// their association after the name change.
std::list<NameObserver*> temp;
// Now go through that list again, and rename all remaining observers which
// point at the old name (ideally, there are none left at this point)
for (ObserverMap::iterator i = _observers.lower_bound(oldName);
i != _observers.end() && i != _observers.upper_bound(oldName);
/* in-loop increment */)
{
temp.push_back(i->second);
_observers.erase(i++);
}
// greebo: Now, associate each observer with the new name (it has changed after all)
for (std::list<NameObserver*>::iterator i = temp.begin(); i != temp.end(); i++)
{
_observers.insert(ObserverMap::value_type(newName, *i));
}
}
void Namespace::ensureNoConflicts(const scene::INodePtr& root)
{
// Instantiate a new, temporary namespace for the nodes below root
Namespace foreignNamespace;
// Move all nodes below (and including) root into this temporary namespace
foreignNamespace.connect(root);
// Collect all namespaced items from the foreign root
GatherNamespacedWalker walker;
root->traverse(walker);
rDebug() << "Namespace::ensureNoConflicts(): imported set of "
<< walker.result.size() << " namespaced nodes" << std::endl;
// Build a union set containing all imported names and all existing names.
// We need to know all existing names to ensure that newly created names are
// unique in *both* namespaces
UniqueNameSet allNames = _uniqueNames;
allNames.merge(foreignNamespace._uniqueNames);
// Process each object in the to-be-imported tree of nodes, ensuring that it
// has a unique name
for (const NamespacedPtr& n : walker.result)
{
// If the imported node conflicts with a name in THIS namespace, then it
// needs to be given a new name which is unique in BOTH namespaces.
if (_uniqueNames.nameExists(n->getName()))
{
// Name exists in the target namespace, get a new name
std::string uniqueName = allNames.insertUnique(n->getName());
rMessage() << "Namespace::ensureNoConflicts(): '" << n->getName()
<< "' already exists in this namespace. Rename it to '"
<< uniqueName << "'\n";
// Change the name of the imported node, this should trigger all
// observers in the foreign namespace
n->changeName(uniqueName);
}
else
{
// Name does not exist yet, insert it into the local combined
// namespace (but not our destination namespace, this will be
// populated in the subsequent call to connect()).
allNames.insert(n->getName());
}
}
// at this point, all names in the foreign namespace have been converted to
// something unique in this namespace. The calling code can now move the
// nodes into this namespace without name conflicts
// Disconnect the root from the foreign namespace again, it will be destroyed now
foreignNamespace.disconnect(root);
}