Vuo  2.3.2
VuoCompilerCompositionDiff.cc
Go to the documentation of this file.
1 
11 
12 #include "VuoCompilerCable.hh"
14 #include "VuoCompilerGraph.hh"
15 #include "VuoCompilerNode.hh"
16 #include "VuoCompilerNodeClass.hh"
17 #include "VuoCable.hh"
18 #include "VuoComposition.hh"
19 #include "VuoNode.hh"
20 #include "VuoNodeClass.hh"
21 #include "VuoPort.hh"
22 #include "VuoPublishedPort.hh"
23 #include "VuoStringUtilities.hh"
24 
112 string VuoCompilerCompositionDiff::diff(const string &oldCompositionGraphvizDeclaration, VuoCompilerComposition *newComposition,
113  VuoCompiler *compiler)
114 {
115  VuoCompilerComposition *oldComposition = VuoCompilerComposition::newCompositionFromGraphvizDeclaration(oldCompositionGraphvizDeclaration, compiler);
116  json_object *diffJson = json_object_new_array();
117 
118  diff(oldComposition, newComposition, "", "", VuoCompilerComposition::topLevelCompositionIdentifier, compiler, diffJson);
119 
120  delete oldComposition;
121 
122  string diffString = json_object_to_json_string_ext(diffJson, JSON_C_TO_STRING_PLAIN);
123  VuoStringUtilities::replaceAll(diffString, "\\/", "/");
124  json_object_put(diffJson);
125  return diffString;
126 }
127 
132  const string &parentCompositionIdentifier, const string &parentCompositionPath,
133  const string &unqualifiedCompositionIdentifier,
134  VuoCompiler *compiler, json_object *diffJson)
135 {
136  string compositionIdentifier = (parentCompositionIdentifier.empty() ?
137  unqualifiedCompositionIdentifier :
138  parentCompositionIdentifier + "/" + unqualifiedCompositionIdentifier);
139  string compositionPath = (parentCompositionPath.empty() ?
140  unqualifiedCompositionIdentifier :
141  parentCompositionPath + "/" + unqualifiedCompositionIdentifier);
142 
143  // Diff nodes that have been added or removed with no replacement or refactoring.
144 
145  set<VuoNode *> oldNodes;
146  set<VuoNode *> newNodes;
147  if (oldComposition)
148  oldNodes = oldComposition->getBase()->getNodes();
149  if (newComposition)
150  newNodes = newComposition->getBase()->getNodes();
151 
152  map<string, VuoNode *> oldNodeForIdentifier;
153  map<string, VuoNode *> newNodeForIdentifier;
154  map<string, pair<VuoNode *, VuoNode *> > oldAndNewNodeForIdentifier;
155  for (set<VuoNode *>::iterator i = oldNodes.begin(); i != oldNodes.end(); ++i)
156  {
157  string identifier = (*i)->getCompiler()->getGraphvizIdentifier();
158  oldNodeForIdentifier[identifier] = *i;
159  oldAndNewNodeForIdentifier[identifier].first = *i;
160  }
161  for (set<VuoNode *>::iterator i = newNodes.begin(); i != newNodes.end(); ++i)
162  {
163  string identifier = (*i)->getCompiler()->getGraphvizIdentifier();
164  newNodeForIdentifier[identifier] = *i;
165  oldAndNewNodeForIdentifier[identifier].second = *i;
166  }
167 
168  for (map<string, VuoNode *>::iterator oldNodeIter = oldNodeForIdentifier.begin(); oldNodeIter != oldNodeForIdentifier.end(); ++oldNodeIter)
169  {
170  if (newNodeForIdentifier.find(oldNodeIter->first) == newNodeForIdentifier.end() &&
171  ! isNodeBeingReplaced(compositionIdentifier, oldNodeIter->first) &&
172  ! isNodeBeingRefactored(parentCompositionIdentifier, unqualifiedCompositionIdentifier, oldNodeIter->first))
173  {
174  // { "remove" : "<composition path>/<node identifier>" }
175  json_object *remove = json_object_new_object();
176  json_object *nodePath = json_object_new_string((compositionPath + "/" + oldNodeIter->first).c_str());
177  json_object_object_add(remove, "remove", nodePath);
178  json_object_array_add(diffJson, remove);
179  }
180  }
181  for (map<string, VuoNode *>::iterator newNodeIter = newNodeForIdentifier.begin(); newNodeIter != newNodeForIdentifier.end(); ++newNodeIter)
182  {
183  if (oldNodeForIdentifier.find(newNodeIter->first) == oldNodeForIdentifier.end() &&
184  ! isNodeReplacingAnother(compositionIdentifier, newNodeIter->first) &&
185  ! isNodeBeingRefactored(parentCompositionIdentifier, unqualifiedCompositionIdentifier, newNodeIter->first))
186  {
187  // { "add" : "<composition path>/<node identifier>" }
188  json_object *add = json_object_new_object();
189  json_object *nodePath = json_object_new_string((compositionPath + "/" + newNodeIter->first).c_str());
190  json_object_object_add(add, "add", nodePath);
191  json_object_array_add(diffJson, add);
192  }
193  }
194 
195  // If we're inside a subcomposition instance that is being added/removed, add/remove the manually firable trigger node.
196 
197  if (! oldComposition)
198  {
199  json_object *add = json_object_new_object();
200  json_object *nodePath = json_object_new_string((compositionPath + "/" + VuoCompilerGraph::getManuallyFirableTriggerNodeIdentifier()).c_str());
201  json_object_object_add(add, "add", nodePath);
202  json_object_array_add(diffJson, add);
203  }
204  if (! newComposition)
205  {
206  json_object *remove = json_object_new_object();
207  json_object *nodePath = json_object_new_string((compositionPath + "/" + VuoCompilerGraph::getManuallyFirableTriggerNodeIdentifier()).c_str());
208  json_object_object_add(remove, "remove", nodePath);
209  json_object_array_add(diffJson, remove);
210  }
211 
212  // Diff nodes that have been refactored.
213 
214  auto buildMovePath = [](const Refactoring &r, const string &nodeIdentifier)
215  {
216  return r.compositionIdentifier + "/" + nodeIdentifier;
217  };
218  auto buildToPath = [](const Refactoring &r, const string &nodeIdentifier)
219  {
220  return r.compositionIdentifier + "/" + r.unqualifiedSubcompositionIdentifier + "/" + nodeIdentifier;
221  };
222 
223  for (const Refactoring &r : refactorings)
224  {
225  if (compositionIdentifier == r.compositionIdentifier)
226  {
227  for (const string &nodeIdentifier : r.nodeIdentifiers)
228  {
229  // { "move" : "<composition path>/<node identifier>", "to" : "<subcomposition path>/<node identifier>",
230  json_object *move = json_object_new_object();
231  json_object *oldNodePath = json_object_new_string(buildMovePath(r, nodeIdentifier).c_str());
232  json_object_object_add(move, "move", oldNodePath);
233  json_object *newNodePath = json_object_new_string(buildToPath(r, nodeIdentifier).c_str());
234  json_object_object_add(move, "to", newNodePath);
235  json_object_array_add(diffJson, move);
236  }
237  }
238  else if (parentCompositionIdentifier == r.compositionIdentifier && unqualifiedCompositionIdentifier == r.unqualifiedSubcompositionIdentifier)
239  {
240  for (const string &nodeIdentifier : r.nodeIdentifiers)
241  {
242  // "ports" : [
243  // { "copy" : "<port identifier>", "to" : "<composition path>/<node identifier>:<port identifier>" },
244  // { "copy" : "<port identifier>", "to" : "<composition path>/<node identifier>:<port identifier>" },
245  // ... ] }
246 
247  json_object *move = nullptr;
248  int length = json_object_array_length(diffJson);
249  for (int i = 0; i < length; ++i)
250  {
251  json_object *curr = json_object_array_get_idx(diffJson, i);
252  json_object *moveNodePath = nullptr;
253  json_object *toNodePath = nullptr;
254 
255  if (json_object_object_get_ex(curr, "move", &moveNodePath) && json_object_object_get_ex(curr, "to", &toNodePath) &&
256  buildMovePath(r, nodeIdentifier) == json_object_get_string(moveNodePath) &&
257  buildToPath(r, nodeIdentifier) == json_object_get_string(toNodePath))
258  {
259  move = curr;
260  break;
261  }
262  }
263 
264  json_object *ports = json_object_new_array();
265  for (VuoPort *internalPort : newNodeForIdentifier[nodeIdentifier]->getInputPorts())
266  {
267  for (VuoCable *cable : internalPort->getConnectedCables())
268  {
269  if (cable->isPublished() && cable->getCompiler()->carriesData())
270  {
271  json_object *portObj = json_object_new_object();
272  json_object *portNameOnMovedNode = json_object_new_string(internalPort->getClass()->getName().c_str());
273  json_object_object_add(portObj, "copy", portNameOnMovedNode);
274  json_object *portNameOnSubcompositionNode = json_object_new_string((r.compositionIdentifier + "/" + r.unqualifiedSubcompositionIdentifier + ":" + cable->getFromPort()->getClass()->getName()).c_str());
275  json_object_object_add(portObj, "to", portNameOnSubcompositionNode);
276  json_object_array_add(ports, portObj);
277  }
278  }
279  }
280 
281  json_object_object_add(move, "ports", ports);
282  }
283  }
284  }
285 
286  auto nodeReplacementExists = [this](const string &compositionIdentifier, const string &oldNodeIdentifier, const string &newNodeIdentifier)
287  {
288  NodeReplacement n;
289  n.compositionIdentifier = compositionIdentifier;
290  n.oldNodeIdentifier = oldNodeIdentifier;
291  n.newNodeIdentifier = newNodeIdentifier;
292  return nodeReplacements.find(n) != nodeReplacements.end();
293  };
294 
295  // Add nodes that have the same identifier but different node classes to `nodeReplacements` (if not already there).
296 
297  for (map<string, pair<VuoNode *, VuoNode *> >::iterator i = oldAndNewNodeForIdentifier.begin(); i != oldAndNewNodeForIdentifier.end(); ++i)
298  if (i->second.first && i->second.second && i->second.first->getNodeClass() != i->second.second->getNodeClass() &&
299  ! nodeReplacementExists(compositionIdentifier, i->first, i->first))
300  addNodeReplacement(compositionIdentifier, i->first, i->first);
301 
302  // Add nodes whose node classes are being replaced to `nodeReplacements` (if not already there).
303 
304  for (map<string, pair<VuoNode *, VuoNode *> >::iterator i = oldAndNewNodeForIdentifier.begin(); i != oldAndNewNodeForIdentifier.end(); ++i)
305  {
306  if (i->second.first && i->second.second)
307  {
308  string nodeClassName = i->second.second->getNodeClass()->getClassName();
309  for (const NodeClassReplacement &nodeClassReplacement : nodeClassReplacements)
310  {
311  if (nodeClassReplacement.nodeClassName == nodeClassName && ! nodeReplacementExists(compositionIdentifier, i->first, i->first))
312  {
313  addNodeReplacement(compositionIdentifier, i->first, i->first, nodeClassReplacement.oldAndNewPortNames);
314  break;
315  }
316  }
317  }
318  }
319 
320  // Recursively diff nodes that are subcompositions.
321 
322  for (map<string, pair<VuoNode *, VuoNode *> >::iterator i = oldAndNewNodeForIdentifier.begin(); i != oldAndNewNodeForIdentifier.end(); ++i)
323  {
324  string nodeIdentifier;
325 
326  VuoCompilerComposition *newSubcomposition = NULL;
327  if (i->second.second)
328  {
329  VuoCompilerNodeClass *newNodeClass = i->second.second->getNodeClass()->getCompiler();
330  if (newNodeClass->isSubcomposition())
331  {
332  string newGraphvizDeclaration = newNodeClass->getSourceCode();
333  newSubcomposition = VuoCompilerComposition::newCompositionFromGraphvizDeclaration(newGraphvizDeclaration, compiler);
334 
335  nodeIdentifier = i->second.second->getCompiler()->getIdentifier();
336  }
337  }
338 
339  VuoCompilerComposition *oldSubcomposition = NULL;
340  if (i->second.first)
341  {
342  string nodeClassName = i->second.first->getNodeClass()->getClassName();
343 
344  set<NodeClassReplacement>::iterator replacementIter;
345  for (replacementIter = nodeClassReplacements.begin(); replacementIter != nodeClassReplacements.end(); ++replacementIter)
346  if (replacementIter->nodeClassName == nodeClassName)
347  break;
348 
349  if (replacementIter != nodeClassReplacements.end())
350  {
351  if (! replacementIter->oldSubcompositionSourceCode.empty())
352  {
353  oldSubcomposition = VuoCompilerComposition::newCompositionFromGraphvizDeclaration(replacementIter->oldSubcompositionSourceCode, compiler);
354 
355  nodeIdentifier = i->second.first->getCompiler()->getIdentifier();
356  }
357  }
358  else
359  oldSubcomposition = newSubcomposition;
360  }
361 
362  if (oldSubcomposition || newSubcomposition)
363  diff(oldSubcomposition, newSubcomposition, compositionIdentifier, compositionPath, nodeIdentifier, compiler, diffJson);
364 
365  if (oldSubcomposition != newSubcomposition)
366  delete oldSubcomposition;
367 
368  delete newSubcomposition;
369  }
370 
371  // Diff nodes that are being replaced / replacing another.
372  // For nodes that are subcompositions, this uses the port mappings added to `replacements` during the recursive calls.
373 
374  for (set<NodeReplacement>::iterator nodeReplacementIter = nodeReplacements.begin(); nodeReplacementIter != nodeReplacements.end(); ++nodeReplacementIter)
375  {
376  if (nodeReplacementIter->compositionIdentifier == compositionIdentifier)
377  {
378  // { "replace" : "<composition path>/<node identifier>", "with" : "<composition path>/<node identifier>",
379  // "ports" : [
380  // { "map" : "<port identifier>", "to" : "<port identifier>" },
381  // { "map" : "<port identifier>", "to" : "<port identifier>" },
382  // ... ] }
383 
384  map<string, VuoNode *>::iterator oldNodeIter = oldNodeForIdentifier.find(nodeReplacementIter->oldNodeIdentifier);
385  if (oldNodeIter == oldNodeForIdentifier.end())
386  continue;
387  map<string, VuoNode *>::iterator newNodeIter = newNodeForIdentifier.find(nodeReplacementIter->newNodeIdentifier);
388  if (newNodeIter == newNodeForIdentifier.end())
389  continue;
390 
391  json_object *replaceObj = json_object_new_object();
392  json_object *oldNodePath = json_object_new_string((compositionPath + "/" + nodeReplacementIter->oldNodeIdentifier).c_str());
393  json_object_object_add(replaceObj, "replace", oldNodePath);
394  json_object *newNodePath = json_object_new_string((compositionPath + "/" + nodeReplacementIter->newNodeIdentifier).c_str());
395  json_object_object_add(replaceObj, "with", newNodePath);
396  json_object *ports = json_object_new_array();
397 
398  map<string, string> oldAndNewPortNames = nodeReplacementIter->oldAndNewPortNames;
399 
400  if (nodeReplacementIter->shouldMapIdenticalPortNames)
401  {
402  vector<VuoPort *> oldInputPorts = oldNodeIter->second->getInputPorts();
403  vector<VuoPort *> oldOutputPorts = oldNodeIter->second->getOutputPorts();
404  vector<VuoPort *> newInputPorts = newNodeIter->second->getInputPorts();
405  vector<VuoPort *> newOutputPorts = newNodeIter->second->getOutputPorts();
406  for (vector<VuoPort *>::iterator oldPortIter = oldInputPorts.begin(); oldPortIter != oldInputPorts.end(); ++oldPortIter)
407  {
408  for (vector<VuoPort *>::iterator newPortIter = newInputPorts.begin(); newPortIter != newInputPorts.end(); ++newPortIter)
409  {
410  if (VuoCompilerComposition::portsMatch(*oldPortIter, *newPortIter))
411  {
412  string oldPortName = (*oldPortIter)->getClass()->getName();
413  string newPortName = (*newPortIter)->getClass()->getName();
414  oldAndNewPortNames[oldPortName] = newPortName;
415  break;
416  }
417  }
418  }
419  for (vector<VuoPort *>::iterator oldPortIter = oldOutputPorts.begin(); oldPortIter != oldOutputPorts.end(); ++oldPortIter)
420  {
421  for (vector<VuoPort *>::iterator newPortIter = newOutputPorts.begin(); newPortIter != newOutputPorts.end(); ++newPortIter)
422  {
423  if (VuoCompilerComposition::portsMatch(*oldPortIter, *newPortIter))
424  {
425  string oldPortName = (*oldPortIter)->getClass()->getName();
426  string newPortName = (*newPortIter)->getClass()->getName();
427  oldAndNewPortNames[oldPortName] = newPortName;
428  break;
429  }
430  }
431  }
432  }
433 
434  for (map<string, string>::const_iterator portMapIter = oldAndNewPortNames.begin(); portMapIter != oldAndNewPortNames.end(); ++portMapIter)
435  {
436  json_object *portObj = json_object_new_object();
437  json_object *oldPortName = json_object_new_string(portMapIter->first.c_str());
438  json_object_object_add(portObj, "map", oldPortName);
439  json_object *newPortName = json_object_new_string(portMapIter->second.c_str());
440  json_object_object_add(portObj, "to", newPortName);
441  json_object_array_add(ports, portObj);
442  }
443 
444  json_object_object_add(replaceObj, "ports", ports);
445  json_object_array_add(diffJson, replaceObj);
446  }
447  }
448 
449  // Diff published ports.
450 
451  bool publishedPortsChanged[2] = { false, false };
452  vector<VuoPublishedPort *> oldPublishedPorts[2];
453  vector<VuoPublishedPort *> newPublishedPorts[2];
454  string nodeIdentifier[2];
455  map<string, string> oldAndNewPublishedPortNames[2];
456 
457  for (int i = 0; i < 2; ++i)
458  {
459  if (i == 0)
460  {
461  if (oldComposition)
462  oldPublishedPorts[i] = oldComposition->getBase()->getPublishedInputPorts();
463  if (newComposition)
464  newPublishedPorts[i] = newComposition->getBase()->getPublishedInputPorts();
465  nodeIdentifier[i] = "PublishedInputs";
466  }
467  else
468  {
469  if (oldComposition)
470  oldPublishedPorts[i] = oldComposition->getBase()->getPublishedOutputPorts();
471  if (newComposition)
472  newPublishedPorts[i] = newComposition->getBase()->getPublishedOutputPorts();
473  nodeIdentifier[i] = "PublishedOutputs";
474  }
475 
476  for (size_t j = 0; j < oldPublishedPorts[i].size() && j < newPublishedPorts[i].size(); ++j)
477  {
478  VuoPublishedPort *oldPort = oldPublishedPorts[i][j];
479  VuoPublishedPort *newPort = newPublishedPorts[i][j];
480 
481  if (VuoCompilerComposition::portsMatch(oldPort, newPort))
482  {
483  string oldPortName = oldPort->getClass()->getName();
484  string newPortName = newPort->getClass()->getName();
485  oldAndNewPublishedPortNames[i][oldPortName] = newPortName;
486  }
487  else
488  publishedPortsChanged[i] = true;
489  }
490 
491  if (oldPublishedPorts[i].size() != newPublishedPorts[i].size())
492  publishedPortsChanged[i] = true;
493  }
494 
495  if (publishedPortsChanged[0] || publishedPortsChanged[1])
496  {
497  bool addBoth = oldPublishedPorts[0].empty() && oldPublishedPorts[1].empty();
498  bool removeBoth = newPublishedPorts[0].empty() && newPublishedPorts[1].empty();
499 
500  for (int i = 0; i < 2; ++i)
501  {
502  if (removeBoth)
503  {
504  // { "remove" : "<composition path>/PublishedInputs" }
505  json_object *remove = json_object_new_object();
506  json_object *nodePath = json_object_new_string((compositionPath + "/" + nodeIdentifier[i]).c_str());
507  json_object_object_add(remove, "remove", nodePath);
508  json_object_array_add(diffJson, remove);
509 
510  if (i == 0)
511  {
512  json_object *remove = json_object_new_object();
513  json_object *nodePath = json_object_new_string((compositionPath + "/" + "PublishedInputsTrigger").c_str());
514  json_object_object_add(remove, "remove", nodePath);
515  json_object_array_add(diffJson, remove);
516  }
517  }
518  else if (addBoth)
519  {
520  // { "add" : "<composition path>/PublishedInputs" }
521  json_object *add = json_object_new_object();
522  json_object *nodePath = json_object_new_string((compositionPath + "/" + nodeIdentifier[i]).c_str());
523  json_object_object_add(add, "add", nodePath);
524  json_object_array_add(diffJson, add);
525 
526  if (i == 0)
527  {
528  json_object *add = json_object_new_object();
529  json_object *nodePath = json_object_new_string((compositionPath + "/" + "PublishedInputsTrigger").c_str());
530  json_object_object_add(add, "add", nodePath);
531  json_object_array_add(diffJson, add);
532  }
533  }
534  else if (publishedPortsChanged[i])
535  {
536  // { "replace" : "<composition path>/PublishedInputs", "with" : "<composition path>/PublishedInputs",
537  // "ports" : [
538  // { "map" : "<published port name>", "to" : "<published port name>" },
539  // { "map" : "<published port name>", "to" : "<published port name>" },
540  // ... ] }
541  json_object *replaceObj = json_object_new_object();
542  json_object *nodePath = json_object_new_string((compositionPath + "/" + nodeIdentifier[i]).c_str());
543  json_object_get(nodePath);
544  json_object_object_add(replaceObj, "replace", nodePath);
545  json_object_object_add(replaceObj, "with", nodePath);
546  json_object *ports = json_object_new_array();
547  for (map<string, string>::iterator j = oldAndNewPublishedPortNames[i].begin(); j != oldAndNewPublishedPortNames[i].end(); ++j)
548  {
549  json_object *portObj = json_object_new_object();
550  json_object *oldPublishedPortName = json_object_new_string(j->first.c_str());
551  json_object_object_add(portObj, "map", oldPublishedPortName);
552  json_object *newPublishedPortName = json_object_new_string(j->second.c_str());
553  json_object_object_add(portObj, "to", newPublishedPortName);
554  json_object_array_add(ports, portObj);
555  }
556  json_object_object_add(replaceObj, "ports", ports);
557  json_object_array_add(diffJson, replaceObj);
558  }
559  }
560  }
561 
562  if (! parentCompositionIdentifier.empty())
563  {
564  for (set<NodeReplacement>::iterator i = nodeReplacements.begin(); i != nodeReplacements.end(); ++i)
565  {
566  if (i->compositionIdentifier == parentCompositionIdentifier &&
567  i->oldNodeIdentifier == unqualifiedCompositionIdentifier && i->newNodeIdentifier == unqualifiedCompositionIdentifier)
568  {
569  NodeReplacement n = *i;
570 
571  for (int j = 0; j < 2; ++j)
572  n.oldAndNewPortNames.insert(oldAndNewPublishedPortNames[j].begin(), oldAndNewPublishedPortNames[j].end());
573 
574  n.shouldMapIdenticalPortNames = false;
575 
576  nodeReplacements.erase(i);
577  nodeReplacements.insert(n);
578  break;
579  }
580  }
581  }
582 }
583 
587 bool operator<(const VuoCompilerCompositionDiff::NodeReplacement &lhs, const VuoCompilerCompositionDiff::NodeReplacement &rhs)
588 {
589  return (lhs.compositionIdentifier != rhs.compositionIdentifier ?
590  lhs.compositionIdentifier < rhs.compositionIdentifier :
591  (lhs.oldNodeIdentifier != rhs.oldNodeIdentifier ?
592  lhs.oldNodeIdentifier < rhs.oldNodeIdentifier :
593  lhs.newNodeIdentifier < rhs.newNodeIdentifier));
594 }
595 
599 bool operator<(const VuoCompilerCompositionDiff::NodeClassReplacement &lhs, const VuoCompilerCompositionDiff::NodeClassReplacement &rhs)
600 {
601  return (lhs.nodeClassName < rhs.nodeClassName);
602 }
603 
607 bool operator<(const VuoCompilerCompositionDiff::Refactoring &lhs, const VuoCompilerCompositionDiff::Refactoring &rhs)
608 {
609  return (lhs.compositionIdentifier != rhs.compositionIdentifier ?
610  lhs.compositionIdentifier < rhs.compositionIdentifier :
611  lhs.unqualifiedSubcompositionIdentifier < rhs.unqualifiedSubcompositionIdentifier);
612 }
613 
618 void VuoCompilerCompositionDiff::addNodeReplacementInTopLevelComposition(const string &oldNodeIdentifier, const string &newNodeIdentifier)
619 {
620  addNodeReplacement(VuoCompilerComposition::topLevelCompositionIdentifier, oldNodeIdentifier, newNodeIdentifier);
621 }
622 
626 void VuoCompilerCompositionDiff::addNodeReplacementInTopLevelComposition(const string &oldNodeIdentifier, const string &newNodeIdentifier,
627  const map<string, string> &oldAndNewPortNames)
628 {
629  addNodeReplacement(VuoCompilerComposition::topLevelCompositionIdentifier, oldNodeIdentifier, newNodeIdentifier, oldAndNewPortNames);
630 }
631 
636 void VuoCompilerCompositionDiff::addNodeReplacement(const string &compositionIdentifier, const string &oldNodeIdentifier, const string &newNodeIdentifier)
637 {
638  NodeReplacement n;
639  n.compositionIdentifier = compositionIdentifier;
640  n.oldNodeIdentifier = oldNodeIdentifier;
641  n.newNodeIdentifier = newNodeIdentifier;
642  n.shouldMapIdenticalPortNames = true;
643  nodeReplacements.insert(n);
644 }
645 
649 void VuoCompilerCompositionDiff::addNodeReplacement(const string &compositionIdentifier, const string &oldNodeIdentifier, const string &newNodeIdentifier,
650  const map<string, string> &oldAndNewPortNames)
651 {
652  NodeReplacement n;
653  n.compositionIdentifier = compositionIdentifier;
654  n.oldNodeIdentifier = oldNodeIdentifier;
655  n.newNodeIdentifier = newNodeIdentifier;
656  n.oldAndNewPortNames = oldAndNewPortNames;
657  n.shouldMapIdenticalPortNames = false;
658  nodeReplacements.insert(n);
659 }
660 
667 {
668  NodeClassReplacement n;
669  n.nodeClassName = oldNodeClass->getBase()->getClassName();
670 
671  auto makeOldAndNewPortNames = [&n] (const vector<VuoPortClass *> &oldPortClasses, const vector<VuoPortClass *> &newPortClasses)
672  {
673  for (VuoPortClass *oldPortClass : oldPortClasses)
674  {
675  for (VuoPortClass *newPortClass : newPortClasses)
676  {
677  if (VuoCompilerComposition::portClassesMatch(oldPortClass, newPortClass))
678  {
679  n.oldAndNewPortNames[oldPortClass->getName()] = newPortClass->getName();
680  break;
681  }
682  }
683  }
684  };
685  vector<VuoPortClass *> oldInputPorts = oldNodeClass->getBase()->getInputPortClasses();
686  vector<VuoPortClass *> oldOutputPorts = oldNodeClass->getBase()->getOutputPortClasses();
687  vector<VuoPortClass *> newInputPorts = newNodeClass->getBase()->getInputPortClasses();
688  vector<VuoPortClass *> newOutputPorts = newNodeClass->getBase()->getOutputPortClasses();
689  makeOldAndNewPortNames(oldInputPorts, newInputPorts);
690  makeOldAndNewPortNames(oldOutputPorts, newOutputPorts);
691 
692  if (oldNodeClass->isSubcomposition())
693  n.oldSubcompositionSourceCode = oldNodeClass->getSourceCode();
694 
695  nodeClassReplacements.insert(n);
696 }
697 
702 {
703  moduleReplacements.insert(moduleKey);
704 }
705 
713 void VuoCompilerCompositionDiff::addRefactoring(const string &compositionIdentifier, const set<VuoCompilerNode *> &nodesMoved, VuoCompilerNode *subcompositionMovedTo)
714 {
715  Refactoring r;
716 
717  r.compositionIdentifier = compositionIdentifier;
718  r.unqualifiedSubcompositionIdentifier = subcompositionMovedTo->getIdentifier();
719 
720  for (VuoCompilerNode *node : nodesMoved)
721  r.nodeIdentifiers.insert(node->getIdentifier());
722 
723  refactorings.insert(r);
724 }
725 
730 {
731  set<string> moduleKeys;
732 
733  for (const NodeClassReplacement &ncr : nodeClassReplacements)
734  moduleKeys.insert(ncr.nodeClassName);
735 
736  for (const string &moduleKey : moduleReplacements)
737  moduleKeys.insert(moduleKey);
738 
739  return moduleKeys;
740 }
741 
745 bool VuoCompilerCompositionDiff::isNodeBeingReplaced(const string &compositionIdentifier, const string &oldNodeIdentifier) const
746 {
747  for (set<NodeReplacement>::const_iterator i = nodeReplacements.begin(); i != nodeReplacements.end(); ++i)
748  if ((*i).compositionIdentifier == compositionIdentifier && (*i).oldNodeIdentifier == oldNodeIdentifier)
749  return true;
750 
751  return false;
752 }
753 
757 bool VuoCompilerCompositionDiff::isNodeReplacingAnother(const string &compositionIdentifier, const string &newNodeIdentifier) const
758 {
759  for (set<NodeReplacement>::const_iterator i = nodeReplacements.begin(); i != nodeReplacements.end(); ++i)
760  if ((*i).compositionIdentifier == compositionIdentifier && (*i).newNodeIdentifier == newNodeIdentifier)
761  return true;
762 
763  return false;
764 }
765 
769 bool VuoCompilerCompositionDiff::isNodeBeingRefactored(const string &parentCompositionIdentifier, const string &compositionIdentifier, const string &nodeIdentifier) const
770 {
771  for (const Refactoring &r : refactorings)
772  if ((r.compositionIdentifier == compositionIdentifier && r.nodeIdentifiers.find(nodeIdentifier) != r.nodeIdentifiers.end()) ||
773  (r.compositionIdentifier == parentCompositionIdentifier && r.unqualifiedSubcompositionIdentifier == compositionIdentifier && r.nodeIdentifiers.find(nodeIdentifier) != r.nodeIdentifiers.end()))
774  return true;
775 
776  return false;
777 }