Vuo  2.0.2
VuoCompilerComposition.cc
Go to the documentation of this file.
1 
10 #include "VuoCable.hh"
12 #include "VuoCompiler.hh"
13 #include "VuoCompilerCable.hh"
14 #include "VuoCompilerComment.hh"
15 #include "VuoCompilerException.hh"
16 #include "VuoCompilerGraph.hh"
18 #include "VuoCompilerIssue.hh"
19 #include "VuoCompilerNode.hh"
20 #include "VuoCompilerPortClass.hh"
24 #include "VuoComposition.hh"
25 #include "VuoGenericType.hh"
26 #include "VuoNode.hh"
27 #include "VuoNodeClass.hh"
28 #include "VuoComment.hh"
29 #include "VuoPublishedPort.hh"
30 #include "VuoStringUtilities.hh"
31 #include <sstream>
32 
33 const string VuoCompilerComposition::defaultGraphDeclaration = "digraph G\n";
35 
41  : VuoBaseDetail<VuoComposition>("VuoCompilerComposition", baseComposition)
42 {
43  getBase()->setCompiler(this);
44 
45  graph = nullptr;
46  graphHash = 0;
47  manuallyFirableInputNode = nullptr;
48  manuallyFirableInputPort = nullptr;
49  module = nullptr;
50 
51  if (parser)
52  {
53  vector<VuoNode *> nodes = parser->getNodes();
54  for (vector<VuoNode *>::iterator node = nodes.begin(); node != nodes.end(); ++node)
55  getBase()->addNode(*node);
56 
57  vector<VuoCable *> cables = parser->getCables();
58  for (vector<VuoCable *>::iterator cable = cables.begin(); cable != cables.end(); ++cable)
59  getBase()->addCable(*cable);
60 
61  vector<VuoPublishedPort *> publishedInputPorts = parser->getPublishedInputPorts();
62  for (int index = 0; index < publishedInputPorts.size(); ++index)
63  getBase()->addPublishedInputPort(publishedInputPorts.at(index), index);
64 
65  vector<VuoPublishedPort *> publishedOutputPorts = parser->getPublishedOutputPorts();
66  for (int index = 0; index < publishedOutputPorts.size(); ++index)
67  getBase()->addPublishedOutputPort(publishedOutputPorts.at(index), index);
68 
69  vector<VuoComment *> comments = parser->getComments();
70  for (vector<VuoComment *>::iterator comment = comments.begin(); comment != comments.end(); ++comment)
71  getBase()->addComment(*comment);
72 
73  manuallyFirableInputNode = parser->getManuallyFirableInputNode();
74  manuallyFirableInputPort = parser->getManuallyFirableInputPort();
75 
76  getBase()->setMetadata(parser->getMetadata(), true);
77 
79 
80  for (vector<VuoNode *>::iterator node = nodes.begin(); node != nodes.end(); ++node)
81  if ((*node)->hasCompiler())
82  nodeGraphvizIdentifierUsed[ (*node)->getCompiler()->getGraphvizIdentifier() ] = *node;
83  }
84 }
85 
90 {
91  delete graph;
93 }
94 
101 {
102  VuoCompilerGraphvizParser *parser = VuoCompilerGraphvizParser::newParserFromCompositionString(compositionGraphvizDeclaration, compiler);
103  VuoCompilerComposition *composition = new VuoCompilerComposition(new VuoComposition(), parser);
104  delete parser;
105  return composition;
106 }
107 
113  map<VuoCable *, VuoPort *> &cablesToTransferFromPort,
114  map<VuoCable *, VuoPort *> &cablesToTransferToPort,
115  set<VuoCable *> &cablesToRemove) const
116 {
117  set<VuoCable *> cables = getBase()->getCables();
118  for (set<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
119  {
120  VuoCable *cable = *i;
121 
122  bool foundMismatch = false;
123  if (cable->getFromNode() == oldNode)
124  {
125  VuoPort *oldPort = cable->getFromPort();
126  VuoPort *newPort = newNode->getOutputPortWithName( oldPort->getClass()->getName() );
127 
128  if (portsMatch(oldPort, newPort))
129  cablesToTransferFromPort[cable] = newPort;
130  else
131  foundMismatch = true;
132  }
133  if (cable->getToNode() == oldNode)
134  {
135  VuoPort *oldPort = cable->getToPort();
136  VuoPort *newPort = newNode->getInputPortWithName( oldPort->getClass()->getName() );
137 
138  if (portsMatch(oldPort, newPort))
139  cablesToTransferToPort[cable] = newPort;
140  else
141  foundMismatch = true;
142  }
143 
144  if (foundMismatch)
145  {
146  cablesToRemove.insert(cable);
147 
148  map<VuoCable *, VuoPort *>::iterator fromIter = cablesToTransferFromPort.find(cable);
149  if (fromIter != cablesToTransferFromPort.end())
150  cablesToTransferFromPort.erase(fromIter);
151 
152  map<VuoCable *, VuoPort *>::iterator toIter = cablesToTransferToPort.find(cable);
153  if (toIter != cablesToTransferToPort.end())
154  cablesToTransferToPort.erase(toIter);
155  }
156  }
157 }
158 
167 {
168  map<VuoCable *, VuoPort *> cablesToTransferFromPort;
169  map<VuoCable *, VuoPort *> cablesToTransferToPort;
170  set<VuoCable *> cablesToRemove;
171 
172  getChangesToReplaceNode(oldNode, newNode, cablesToTransferFromPort, cablesToTransferToPort, cablesToRemove);
173 
174  getBase()->removeNode(oldNode);
175  getBase()->addNode(newNode);
176 
177  for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
178  i->first->setFrom(newNode, i->second);
179  for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
180  i->first->setTo(newNode, i->second);
181  for (set<VuoCable *>::iterator i = cablesToRemove.begin(); i != cablesToRemove.end(); ++i)
182  getBase()->removeCable(*i);
183 }
184 
189 {
190  long currentHash = VuoCompilerGraph::getHash(this);
191  bool compositionChanged = (currentHash != graphHash);
192 
193  bool shouldAddPublishedNodeImplementations = false;
194  if (! compositionChanged && compiler)
195  {
196  VuoCompilerNode *publishedInputNode = graph->getPublishedInputNode();
197  if (publishedInputNode && ! publishedInputNode->getBase()->getNodeClass()->getCompiler()->getEventFunction())
198  shouldAddPublishedNodeImplementations = true;
199  }
200 
201  if (compositionChanged || shouldAddPublishedNodeImplementations)
202  {
203  delete graph;
204  graph = new VuoCompilerGraph(this, compiler);
205  graphHash = currentHash;
206  }
207 
208  return graph;
209 }
210 
215 {
216  delete graph;
217  graph = nullptr;
218  graphHash = 0;
219 }
220 
228 {
230  checkForMissingTypes(issues);
231  checkForEventFlowIssues(issues);
232 }
233 
241 {
242  set<VuoNode *> missingNodes;
243  set<VuoNode *> nodes = getBase()->getNodes();
244  for (set<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
245  {
246  VuoNode *node = *i;
247  if (! node->getNodeClass()->hasCompiler())
248  missingNodes.insert(node);
249  }
250 
251  if (! missingNodes.empty())
252  {
253  // Assumes the details are always displayed in plain text. Doesn't use VuoCompilerIssue's formatting.
254  vector<string> uniqueNodeDetails;
255  bool missingProNode = false;
256  bool missingOldNode = false;
257  for (set<VuoNode *>::iterator i = missingNodes.begin(); i != missingNodes.end(); ++i)
258  {
259  VuoNode *node = *i;
260 
261  string nodeDetail = node->getTitle() + " (" + (*i)->getNodeClass()->getClassName() + ")";
262 
263 #if VUO_PRO
264  if (node->getNodeClass()->isPro())
265  {
266  nodeDetail += " [Vuo Pro]";
267  missingProNode = true;
268  }
269 #endif
270 
271  if (node->getNodeClass()->getDescription().find("This node was updated or removed in Vuo 0.9 or earlier.") != string::npos)
272  {
273  nodeDetail += " [Vuo 0.9 or earlier]";
274  missingOldNode = true;
275  }
276 
277  if (find(uniqueNodeDetails.begin(), uniqueNodeDetails.end(), nodeDetail) == uniqueNodeDetails.end())
278  uniqueNodeDetails.push_back(nodeDetail);
279  }
280  sort(uniqueNodeDetails.begin(), uniqueNodeDetails.end());
281  string details = "\n\n" + VuoStringUtilities::join(uniqueNodeDetails, "\n");
282 
283  string hint;
284  string linkUrl;
285  string linkText;
286 
287  if (missingProNode)
288  {
289  hint += "<p>Some of the non-installed nodes are Pro nodes. "
290  "%link to enable Pro nodes.</p>";
291  linkUrl = "https://vuo.org/buy";
292  linkText = "Upgrade to Vuo Pro";
293  }
294 
295  if (missingOldNode)
296  {
297  hint += "<p>Some of the non-installed nodes were updated or removed in Vuo 0.9 or earlier. "
298  "To work with this composition in Vuo 1.0 or later, first make a "
299  "backup copy of the composition, then open the composition in Vuo 0.9 and save it. "
300  "This will automatically upgrade the composition so you can use it in Vuo 1.0 or later.</p>";
301  }
302 
303  // If changing this text, also change VuoEditorWindow::hideBuildActivityIndicator() and VuoEditorWindow::displayExportErrorBox().
304  string summary = "Nodes not installed";
305 
306  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", "", summary, details);
307  issue.setNodes(missingNodes);
308  issue.setHint(hint);
309  issue.setLink(linkUrl, linkText);
310  issues->append(issue);
311  throw VuoCompilerException(issues, false);
312  }
313 }
314 
321 void VuoCompilerComposition::checkForMissingTypes(VuoCompilerIssues *issues)
322 {
323  map<string, set<string> > missingTypes;
324  for (VuoNode *node : getBase()->getNodes())
325  {
326  if (! node->getNodeClass()->hasCompiler())
327  continue;
328 
329  vector<VuoPort *> ports;
330  vector<VuoPort *> inputPorts = node->getInputPorts();
331  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
332  vector<VuoPort *> outputPorts = node->getOutputPorts();
333  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
334 
335  for (VuoPort *port : ports)
336  {
337  VuoType *type = static_cast<VuoCompilerPort *>(port->getCompiler())->getDataVuoType();
338  if (type && ! type->hasCompiler() && ! dynamic_cast<VuoGenericType *>(type))
339  missingTypes[type->getModuleKey()].insert(node->getNodeClass()->getClassName());
340  }
341  }
342 
343  if (! missingTypes.empty())
344  {
345  for (auto i : missingTypes)
346  {
347  vector<string> nodeClassNames(i.second.begin(), i.second.end());
348  std::sort(nodeClassNames.begin(), nodeClassNames.end());
349 
350  string summary = "Data type not installed";
351  string details = i.first + " — used by " + VuoStringUtilities::join(nodeClassNames, ", ");
352  string hint = "Check with the developer of the nodes that use this data type.";
353 
354  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", "", summary, details);
355  issue.setHint(hint);
356  issues->append(issue);
357  }
358 
359  throw VuoCompilerException(issues, false);
360  }
361 }
362 
370 {
371  checkForEventFlowIssues(set<VuoCompilerCable *>(), issues);
372 }
373 
381 void VuoCompilerComposition::checkForEventFlowIssues(set<VuoCompilerCable *> potentialCables, VuoCompilerIssues *issues)
382 {
383  VuoCompilerGraph *graph;
384  if (! potentialCables.empty())
385  graph = new VuoCompilerGraph(this, nullptr, potentialCables);
386  else
387  graph = getCachedGraph();
388 
389  graph->checkForInfiniteFeedback(issues);
390  graph->checkForDeadlockedFeedback(issues);
391 
392  if (! potentialCables.empty())
393  delete graph;
394 }
395 
399 set< set<VuoCompilerPort *> > VuoCompilerComposition::groupGenericPortsByType(void)
400 {
401  set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts;
402 
403  // Put the generic ports into sets by node, with additional sets for published input and output ports.
404  set< pair< vector<VuoPort *>, VuoNode *> > portsGroupedByNode;
405  set<VuoNode *> nodes = getBase()->getNodes();
406  for (set<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
407  {
408  VuoNode *node = *i;
409  if (! node->hasCompiler())
410  continue;
411 
412  vector<VuoPort *> inputPorts = node->getInputPorts();
413  vector<VuoPort *> outputPorts = node->getOutputPorts();
414  vector<VuoPort *> ports;
415  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
416  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
417 
418  portsGroupedByNode.insert( make_pair(ports, node) );
419  }
420  vector<VuoPublishedPort *> publishedInputPorts = getBase()->getPublishedInputPorts();
421  vector<VuoPublishedPort *> publishedOutputPorts = getBase()->getPublishedOutputPorts();
422  vector<VuoPort *> publishedInputBasePorts( publishedInputPorts.begin(), publishedInputPorts.end() );
423  vector<VuoPort *> publishedOutputBasePorts( publishedOutputPorts.begin(), publishedOutputPorts.end() );
424  portsGroupedByNode.insert( make_pair(publishedInputBasePorts, static_cast<VuoNode *>(NULL)) );
425  portsGroupedByNode.insert( make_pair(publishedOutputBasePorts, static_cast<VuoNode *>(NULL)) );
426 
427  // Within each set of generic ports by node (or published ports), group the generic ports into sets by generic type.
428  for (set< pair< vector<VuoPort *>, VuoNode* > >::iterator i = portsGroupedByNode.begin(); i != portsGroupedByNode.end(); ++i)
429  {
430  vector<VuoPort *> ports = i->first;
431 
432  map<string, set<VuoCompilerPort *> > genericPortsForType;
433  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
434  {
435  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
436  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
437 
438  if (genericType)
439  {
440  string innermostGenericTypeName = VuoType::extractInnermostTypeName(genericType->getModuleKey());
441  genericPortsForType[innermostGenericTypeName].insert(port);
442  }
443  }
444 
445  for (map<string, set<VuoCompilerPort *> >::iterator j = genericPortsForType.begin(); j != genericPortsForType.end(); ++j)
446  {
447  set<VuoCompilerPort *> genericPorts = j->second;
448  setsOfConnectedGenericPorts.insert(genericPorts);
449  }
450  }
451 
452  // Merge sets of generic ports from different nodes that are connected through a data-carrying cable.
453  set<VuoCable *> cables = getBase()->getCables();
454  for (set<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
455  {
456  VuoCable *cable = *i;
457 
458  if (! (cable->getFromPort() && cable->getFromPort()->hasCompiler() &&
459  cable->getToPort() && cable->getToPort()->hasCompiler() &&
460  cable->getCompiler()->carriesData()))
461  continue;
462 
463  VuoCompilerPort *fromPort = static_cast<VuoCompilerPort *>(cable->getFromPort()->getCompiler());
464  VuoCompilerPort *toPort = static_cast<VuoCompilerPort *>(cable->getToPort()->getCompiler());
465  set< set<VuoCompilerPort *> >::iterator fromSetIter = setsOfConnectedGenericPorts.end(); // the set containing fromPort
466  set< set<VuoCompilerPort *> >::iterator toSetIter = setsOfConnectedGenericPorts.end(); // the set containing toPort
467  for (set< set<VuoCompilerPort *> >::iterator j = setsOfConnectedGenericPorts.begin(); j != setsOfConnectedGenericPorts.end(); ++j)
468  {
469  if (j->find(fromPort) != j->end())
470  fromSetIter = j;
471  if (j->find(toPort) != j->end())
472  toSetIter = j;
473  }
474  if (fromSetIter != setsOfConnectedGenericPorts.end() && toSetIter != setsOfConnectedGenericPorts.end() &&
475  fromSetIter != toSetIter)
476  {
477  set<VuoCompilerPort *> mergedSet;
478  mergedSet.insert(fromSetIter->begin(), fromSetIter->end());
479  mergedSet.insert(toSetIter->begin(), toSetIter->end());
480  setsOfConnectedGenericPorts.insert(mergedSet);
481  setsOfConnectedGenericPorts.erase(fromSetIter);
482  setsOfConnectedGenericPorts.erase(toSetIter);
483  }
484  }
485 
486  return setsOfConnectedGenericPorts;
487 }
488 
496 {
497  set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts = groupGenericPortsByType();
498 
499  // Give each set of connected generic ports a unique generic type.
500  set<string> usedTypeNames;
501  for (set< set<VuoCompilerPort *> >::iterator i = setsOfConnectedGenericPorts.begin(); i != setsOfConnectedGenericPorts.end(); ++i)
502  {
503  set<VuoCompilerPort *> connectedGenericPorts = *i;
504 
505  // Find the smallest-numbered generic type within the set that isn't already used by another set.
506  // If no such type exists, create a fresh type.
507 
508  vector<string> sortedTypeNames;
509  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
510  {
511  VuoCompilerPort *port = *j;
512  VuoGenericType *genericTypeFromPort = static_cast<VuoGenericType *>(port->getDataVuoType());
513  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
514  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
515  if (genericTypeFromPort != genericTypeFromPortClass)
516  {
517  string typeName = VuoGenericType::extractInnermostTypeName( genericTypeFromPort->getModuleKey() );
518  sortedTypeNames.push_back(typeName);
519  }
520  }
521  VuoGenericType::sortGenericTypeNames(sortedTypeNames);
522 
523  string commonTypeName;
524  for (vector<string>::iterator j = sortedTypeNames.begin(); j != sortedTypeNames.end(); ++j)
525  {
526  string portType = *j;
527  if (usedTypeNames.find(portType) == usedTypeNames.end())
528  {
529  commonTypeName = portType;
530  break;
531  }
532  }
533 
534  if (commonTypeName.empty())
535  commonTypeName = createFreshGenericTypeName();
536 
537  usedTypeNames.insert(commonTypeName);
538 
539  // Form the set of compatible types for each composition-level generic type by finding the intersection of
540  // the sets of compatible types for each node-class-level generic type.
541  vector<string> compatibleTypeNames;
542  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
543  {
544  VuoCompilerPort *port = *j;
545  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
546  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
547  VuoGenericType::Compatibility compatibility;
548  vector<string> compatibleTypeNamesForPort = genericTypeFromPortClass->getCompatibleSpecializedTypes(compatibility);
549  vector<string> innermostCompatibleTypeNamesForPort;
550  for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
551  innermostCompatibleTypeNamesForPort.push_back( VuoType::extractInnermostTypeName(*k) );
552 
553  if (! innermostCompatibleTypeNamesForPort.empty())
554  {
555  if (compatibleTypeNames.empty())
556  compatibleTypeNames = innermostCompatibleTypeNamesForPort;
557  else
558  {
559  for (int k = compatibleTypeNames.size() - 1; k >= 0; --k)
560  if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleTypeNames[k]) ==
561  innermostCompatibleTypeNamesForPort.end())
562  compatibleTypeNames.erase(compatibleTypeNames.begin() + k);
563  }
564  }
565  }
566 
567  // Apply the composition-level generic type name to each port.
568  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
569  {
570  VuoCompilerPort *port = *j;
571  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
572  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
573 
574  string typeNameForPort = VuoGenericType::replaceInnermostGenericTypeName(genericTypeFromPortClass->getModuleKey(), commonTypeName);
575  vector<string> compatibleTypeNamesForPort;
576  string prefix = (VuoType::isListTypeName(typeNameForPort) ? VuoType::listTypeNamePrefix : "");
577  for (vector<string>::iterator k = compatibleTypeNames.begin(); k != compatibleTypeNames.end(); ++k)
578  compatibleTypeNamesForPort.push_back(prefix + *k);
579 
580  VuoGenericType *commonTypeForPort = new VuoGenericType(typeNameForPort, compatibleTypeNamesForPort);
581  port->setDataVuoType(commonTypeForPort);
582  }
583  }
584 
585  // Update the list of type suffixes that can't currently be used when creating fresh types.
586  for (map<unsigned int, bool>::iterator i = genericTypeSuffixUsed.begin(); i != genericTypeSuffixUsed.end(); ++i)
587  {
588  unsigned int suffix = i->first;
589  bool used = (usedTypeNames.find( VuoGenericType::createGenericTypeName(suffix) ) != usedTypeNames.end());
590  genericTypeSuffixUsed[suffix] = used;
591  }
592 }
593 
597 string VuoCompilerComposition::createFreshGenericTypeName(void)
598 {
599  for (unsigned int i = 1; ; ++i)
600  {
601  if (! genericTypeSuffixUsed[i])
602  {
603  genericTypeSuffixUsed[i] = true;
605  }
606  }
607 }
608 
620 set<VuoPort *> VuoCompilerComposition::getCorrelatedGenericPorts(VuoNode *entryNode, VuoPort *entryPort, bool useOriginalType)
621 {
623 
624  auto getGenericTypeName = [&useOriginalType] (VuoNode *node, VuoPort *port)
625  {
626  VuoGenericType *genericType = nullptr;
627 
628  if (useOriginalType)
629  {
630  if (node && node->hasCompiler())
631  {
632  VuoCompilerNodeClass *nodeClass = node->getNodeClass()->getCompiler();
633  VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(nodeClass);
634  if (specializedNodeClass)
635  genericType = dynamic_cast<VuoGenericType *>(specializedNodeClass->getOriginalPortType(port->getClass()));
636  }
637  }
638  else
639  {
640  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
641  genericType = dynamic_cast<VuoGenericType *>(compilerPort->getDataVuoType());
642  }
643 
644  return genericType ? VuoType::extractInnermostTypeName(genericType->getModuleKey()) : "";
645  };
646 
647  set<VuoPort *> correlatedPorts;
648  set<VuoNode *> nodesEnqueuedOrVisited;
649  list< pair<VuoNode *, string> > nodesToVisit;
650 
651  string entryGenericType = getGenericTypeName(entryNode, entryPort);
652  if (! entryGenericType.empty())
653  {
654  nodesToVisit.push_back( make_pair(entryNode, entryGenericType) );
655  nodesEnqueuedOrVisited.insert(entryNode);
656  }
657 
658  while (! nodesToVisit.empty())
659  {
660  VuoNode *currNode = nodesToVisit.front().first;
661  string currGenericType = nodesToVisit.front().second;
662  nodesToVisit.pop_front();
663 
664  // Find all ports on the node that share (or would share if unspecialized) the same data type.
665 
666  list<VuoPort *> correlatedPortsOnNode;
667  for (VuoPort *currPort : currNode->getInputPorts())
668  if (getGenericTypeName(currNode, currPort) == currGenericType)
669  correlatedPortsOnNode.push_back(currPort);
670  for (VuoPort *currPort : currNode->getOutputPorts())
671  if (getGenericTypeName(currNode, currPort) == currGenericType)
672  correlatedPortsOnNode.push_back(currPort);
673 
674  correlatedPorts.insert(correlatedPortsOnNode.begin(), correlatedPortsOnNode.end());
675 
676  // Follow all data-carrying cables from those ports to other nodes.
677 
678  for (VuoPort *port : correlatedPortsOnNode)
679  {
680  for (VuoCable *cable : port->getConnectedCables())
681  {
682  if (! (cable->hasCompiler() && cable->getCompiler()->carriesData()))
683  continue;
684 
685  VuoNode *otherNode = nullptr;
686  VuoPort *otherPort = nullptr;
687  if (currNode != cable->getFromNode())
688  {
689  otherNode = cable->getFromNode();
690  otherPort = cable->getFromPort();
691  }
692  else if (currNode != cable->getToNode())
693  {
694  otherNode = cable->getToNode();
695  otherPort = cable->getToPort();
696  }
697 
698  if (! otherPort)
699  continue;
700 
701  if (nodesEnqueuedOrVisited.find(otherNode) == nodesEnqueuedOrVisited.end())
702  {
703  string otherGenericType = getGenericTypeName(otherNode, otherPort);
704  if (! otherGenericType.empty())
705  {
706  nodesToVisit.push_back( make_pair(otherNode, otherGenericType) );
707  nodesEnqueuedOrVisited.insert(otherNode);
708  }
709  }
710  }
711  }
712  }
713 
714  return correlatedPorts;
715 }
716 
721 {
722  VuoCompilerGraph *graph = getCachedGraph();
724  return trigger ? trigger->getBase() : NULL;
725 }
726 
731 {
732  this->module = module;
733 }
734 
739 {
740  Module *takenModule = module;
741  module = NULL;
742  return takenModule;
743 }
744 
751 void VuoCompilerComposition::setUniqueGraphvizIdentifierForNode(VuoNode *node, const string &preferredIdentifier, const string &identifierPrefix)
752 {
753  if (! node->hasCompiler())
754  return;
755 
756  for (VuoNode *currNode : getBase()->getNodes())
757  if (currNode != node && currNode->hasCompiler())
758  nodeGraphvizIdentifierUsed[ currNode->getCompiler()->getGraphvizIdentifier() ] = currNode;
759 
760  auto isIdentifierAvailable = [this, node] (const string &identifier)
761  {
762  auto it = nodeGraphvizIdentifierUsed.find(identifier);
763  if (it == nodeGraphvizIdentifierUsed.end())
764  return true;
765 
766  return it->second == node;
767  };
768 
769  string nonEmptyPreferredIdentifier = (! preferredIdentifier.empty() ? preferredIdentifier : node->getCompiler()->getGraphvizIdentifier());
770  string nonEmptyIdentifierPrefix = (! identifierPrefix.empty() ? identifierPrefix : node->getCompiler()->getGraphvizIdentifierPrefix());
771 
772  string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable, nonEmptyPreferredIdentifier, nonEmptyIdentifierPrefix);
773 
774  nodeGraphvizIdentifierUsed[uniqueIdentifier] = node;
775  node->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
776 }
777 
782 {
783  nodeGraphvizIdentifierUsed.clear();
784 }
785 
793 {
794  if (! comment->hasCompiler())
795  return;
796 
797  for (VuoComment *currComment : getBase()->getComments())
798  if (currComment != comment && currComment->hasCompiler())
799  commentGraphvizIdentifierUsed[ currComment->getCompiler()->getGraphvizIdentifier() ] = currComment;
800 
801  auto isIdentifierAvailable = [this, comment] (const string &identifier)
802  {
803  auto it = commentGraphvizIdentifierUsed.find(identifier);
804  if (it == commentGraphvizIdentifierUsed.end())
805  return true;
806 
807  return it->second == comment;
808  };
809 
810  string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable,
811  comment->getCompiler()->getGraphvizIdentifier(),
813  commentGraphvizIdentifierUsed[uniqueIdentifier] = comment;
814  comment->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
815 }
816 
821 {
822  commentGraphvizIdentifierUsed.clear();
823 }
824 
832 void VuoCompilerComposition::setManuallyFirableInputPort(VuoNode *nodeContainingPort, VuoPort *portFiredInto)
833 {
834  this->manuallyFirableInputNode = nodeContainingPort;
835  this->manuallyFirableInputPort = portFiredInto;
836 }
837 
842 {
843  return manuallyFirableInputNode;
844 }
845 
850 {
851  return manuallyFirableInputPort;
852 }
853 
857 string VuoCompilerComposition::getGraphvizDeclaration(VuoProtocol *activeProtocol, string header, string footer)
858 {
859  return getGraphvizDeclarationForComponents(getBase()->getNodes(),
860  getBase()->getCables(),
861  getBase()->getComments(),
862  getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, true),
863  getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, false),
864  header,
865  footer);
866 }
867 
872  set<VuoCable *> cableSet,
873  set<VuoComment *> commentSet,
874  vector<VuoPublishedPort *> publishedInputPorts,
875  vector<VuoPublishedPort *> publishedOutputPorts,
876  string header, string footer, double xPositionOffset, double yPositionOffset)
877 {
878  // Sort nodes.
879  vector<VuoNode *> nodes;
880  for (set<VuoNode *>::iterator i = nodeSet.begin(); i != nodeSet.end(); ++i)
881  nodes.push_back(*i);
882 
883  sort(nodes.begin(), nodes.end(), compareGraphvizIdentifiersOfNodes);
884 
885  // Sort cables.
886  vector<VuoCable *> cables;
887  for (set<VuoCable *>::iterator i = cableSet.begin(); i != cableSet.end(); ++i)
888  cables.push_back(*i);
889 
890  sort(cables.begin(), cables.end(), compareGraphvizIdentifiersOfCables);
891 
892  // Sort comments.
893  vector<VuoComment *> comments;
894  for (set<VuoComment *>::iterator i = commentSet.begin(); i != commentSet.end(); ++i)
895  comments.push_back(*i);
896 
897  sort(comments.begin(), comments.end(), compareGraphvizIdentifiersOfComments);
898 
899  string compositionHeader = (! header.empty()? header : defaultGraphDeclaration);
900  string compositionFooter = (! footer.empty()? footer : "\n");
901 
902  ostringstream output;
903  string nodeCommentSectionDivider = "\n";
904  if ((nodes.empty() && publishedInputPorts.empty() && publishedOutputPorts.empty()) ||
905  (comments.empty() && cables.empty()))
906  nodeCommentSectionDivider = "";
907 
908  string commentCableSectionDivider = "\n";
909  if (comments.empty() || cables.empty())
910  commentCableSectionDivider = "";
911 
912  // Print header
913  output << compositionHeader;
914  output << "{" << endl;
915 
916  // Print nodes
917  for (vector<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
918  {
919  string nodeDeclaration = ((*i)->hasCompiler() ?
920  (*i)->getCompiler()->getGraphvizDeclaration(true, xPositionOffset, yPositionOffset,
921  manuallyFirableInputNode == *i ? manuallyFirableInputPort : nullptr) :
922  (*i)->getRawGraphvizDeclaration());
923  output << nodeDeclaration << endl;
924  }
925 
926  // Print pseudo-nodes for published ports
927  if (! publishedInputPorts.empty())
928  {
930  for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
931  output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\r";
932  output << "\"";
933  for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
934  output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
935  output << "];" << endl;
936  }
937  if (! publishedOutputPorts.empty())
938  {
940  for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
941  output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\l";
942  output << "\"";
943 
944  for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
945  output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
946 
947  output << "];" << endl;
948  }
949 
950  output << nodeCommentSectionDivider;
951 
952  // Print comments
953  for (vector<VuoComment *>::iterator i = comments.begin(); i != comments.end(); ++i)
954  {
955  string commentDeclaration = ((*i)->hasCompiler() ?
956  (*i)->getCompiler()->getGraphvizDeclaration(xPositionOffset, yPositionOffset) : "");
957  output << commentDeclaration << endl;
958  }
959 
960  output << commentCableSectionDivider;
961 
962  // Print cables
963  for (vector<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
964  {
965  VuoCable *cable = *i;
966  output << cable->getCompiler()->getGraphvizDeclaration() << endl;
967  }
968 
969  output << "}";
970  output << compositionFooter;
971 
972  return output.str();
973 }
974 
979 {
980  VuoCompilerTargetSet compositeTarget;
981  for (auto node : getBase()->getNodes())
983  return compositeTarget;
984 }
985 
989 bool VuoCompilerComposition::compareGraphvizIdentifiersOfNodes(VuoNode *lhs, VuoNode *rhs)
990 {
991  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : lhs->getRawGraphvizIdentifier());
992  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : rhs->getRawGraphvizIdentifier());
993  return lhsIdentifier.compare(rhsIdentifier) < 0;
994 }
995 
999 bool VuoCompilerComposition::compareGraphvizIdentifiersOfCables(VuoCable *lhs, VuoCable *rhs)
1000 {
1001  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizDeclaration() : "");
1002  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizDeclaration() : "");
1003  return lhsIdentifier.compare(rhsIdentifier) < 0;
1004 }
1005 
1009 bool VuoCompilerComposition::compareGraphvizIdentifiersOfComments(VuoComment *lhs, VuoComment *rhs)
1010 {
1011  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : "");
1012  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : "");
1013  return lhsIdentifier.compare(rhsIdentifier) < 0;
1014 }
1015 
1021 {
1022  if (! oldPort || ! newPort)
1023  return false;
1024 
1025  string oldPortName = oldPort->getClass()->getName();
1026  string newPortName = newPort->getClass()->getName();
1027  if (oldPortName == newPortName)
1028  {
1029  if (oldPort->hasCompiler() && newPort->hasCompiler())
1030  {
1031  VuoType *oldType = static_cast<VuoCompilerPort *>( oldPort->getCompiler() )->getDataVuoType();
1032  VuoType *newType = static_cast<VuoCompilerPort *>( newPort->getCompiler() )->getDataVuoType();
1033  if (oldType == newType)
1034  return true;
1035  }
1036  else
1037  return true;
1038  }
1039 
1040  return false;
1041 }
1042 
1048 {
1049  if (! oldPortClass || ! newPortClass)
1050  return false;
1051 
1052  string oldPortName = oldPortClass->getName();
1053  string newPortName = newPortClass->getName();
1054  if (oldPortName == newPortName)
1055  {
1056  if (oldPortClass->hasCompiler() && newPortClass->hasCompiler())
1057  {
1058  VuoType *oldType = static_cast<VuoCompilerPortClass *>( oldPortClass->getCompiler() )->getDataVuoType();
1059  VuoType *newType = static_cast<VuoCompilerPortClass *>( newPortClass->getCompiler() )->getDataVuoType();
1060  if (oldType == newType)
1061  return true;
1062  }
1063  else
1064  return true;
1065  }
1066 
1067  return false;
1068 }