Vuo  2.0.0
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 += "Some of the non-installed nodes are Pro nodes. "
290  "%link to enable Pro nodes.";
291  linkUrl = "https://vuo.org/buy";
292  linkText = "Upgrade to Vuo Pro";
293  }
294 
295  // Assumes the hint is always displayed in HTML. Doesn't use VuoCompilerIssue's formatting.
296  if (missingProNode && missingOldNode)
297  hint += "<br><br>";
298 
299  if (missingOldNode)
300  {
301  hint += "Some of the non-installed nodes were updated or removed in Vuo 0.9 or earlier. "
302  "To work with this composition in Vuo 1.0 or later, first make a "
303  "backup copy of the composition, then open the composition in Vuo 0.9 and save it. "
304  "This will automatically upgrade the composition so you can use it in Vuo 1.0 or later.";
305  }
306 
307  // If changing this text, also change VuoEditorWindow::hideBuildActivityIndicator() and VuoEditorWindow::displayExportErrorBox().
308  string summary = "Nodes not installed";
309 
310  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", "", summary, details);
311  issue.setNodes(missingNodes);
312  issue.setHint(hint);
313  issue.setLink(linkUrl, linkText);
314  issues->append(issue);
315  throw VuoCompilerException(issues, false);
316  }
317 }
318 
325 void VuoCompilerComposition::checkForMissingTypes(VuoCompilerIssues *issues)
326 {
327  map<string, set<string> > missingTypes;
328  for (VuoNode *node : getBase()->getNodes())
329  {
330  if (! node->getNodeClass()->hasCompiler())
331  continue;
332 
333  vector<VuoPort *> ports;
334  vector<VuoPort *> inputPorts = node->getInputPorts();
335  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
336  vector<VuoPort *> outputPorts = node->getOutputPorts();
337  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
338 
339  for (VuoPort *port : ports)
340  {
341  VuoType *type = static_cast<VuoCompilerPort *>(port->getCompiler())->getDataVuoType();
342  if (type && ! type->hasCompiler() && ! dynamic_cast<VuoGenericType *>(type))
343  missingTypes[type->getModuleKey()].insert(node->getNodeClass()->getClassName());
344  }
345  }
346 
347  if (! missingTypes.empty())
348  {
349  for (auto i : missingTypes)
350  {
351  vector<string> nodeClassNames(i.second.begin(), i.second.end());
352  std::sort(nodeClassNames.begin(), nodeClassNames.end());
353 
354  string summary = "Data type not installed";
355  string details = i.first + " — used by " + VuoStringUtilities::join(nodeClassNames, ", ");
356  string hint = "Check with the developer of the nodes that use this data type.";
357 
358  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", "", summary, details);
359  issue.setHint(hint);
360  issues->append(issue);
361  }
362 
363  throw VuoCompilerException(issues, false);
364  }
365 }
366 
374 {
375  checkForEventFlowIssues(set<VuoCompilerCable *>(), issues);
376 }
377 
385 void VuoCompilerComposition::checkForEventFlowIssues(set<VuoCompilerCable *> potentialCables, VuoCompilerIssues *issues)
386 {
387  VuoCompilerGraph *graph;
388  if (! potentialCables.empty())
389  graph = new VuoCompilerGraph(this, nullptr, potentialCables);
390  else
391  graph = getCachedGraph();
392 
393  graph->checkForInfiniteFeedback(issues);
394  graph->checkForDeadlockedFeedback(issues);
395 
396  if (! potentialCables.empty())
397  delete graph;
398 }
399 
403 set< set<VuoCompilerPort *> > VuoCompilerComposition::groupGenericPortsByType(void)
404 {
405  set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts;
406 
407  // Put the generic ports into sets by node, with additional sets for published input and output ports.
408  set< pair< vector<VuoPort *>, VuoNode *> > portsGroupedByNode;
409  set<VuoNode *> nodes = getBase()->getNodes();
410  for (set<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
411  {
412  VuoNode *node = *i;
413  if (! node->hasCompiler())
414  continue;
415 
416  vector<VuoPort *> inputPorts = node->getInputPorts();
417  vector<VuoPort *> outputPorts = node->getOutputPorts();
418  vector<VuoPort *> ports;
419  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
420  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
421 
422  portsGroupedByNode.insert( make_pair(ports, node) );
423  }
424  vector<VuoPublishedPort *> publishedInputPorts = getBase()->getPublishedInputPorts();
425  vector<VuoPublishedPort *> publishedOutputPorts = getBase()->getPublishedOutputPorts();
426  vector<VuoPort *> publishedInputBasePorts( publishedInputPorts.begin(), publishedInputPorts.end() );
427  vector<VuoPort *> publishedOutputBasePorts( publishedOutputPorts.begin(), publishedOutputPorts.end() );
428  portsGroupedByNode.insert( make_pair(publishedInputBasePorts, static_cast<VuoNode *>(NULL)) );
429  portsGroupedByNode.insert( make_pair(publishedOutputBasePorts, static_cast<VuoNode *>(NULL)) );
430 
431  // Within each set of generic ports by node (or published ports), group the generic ports into sets by generic type.
432  for (set< pair< vector<VuoPort *>, VuoNode* > >::iterator i = portsGroupedByNode.begin(); i != portsGroupedByNode.end(); ++i)
433  {
434  vector<VuoPort *> ports = i->first;
435 
436  map<string, set<VuoCompilerPort *> > genericPortsForType;
437  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
438  {
439  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
440  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
441 
442  if (genericType)
443  {
444  string innermostGenericTypeName = VuoType::extractInnermostTypeName(genericType->getModuleKey());
445  genericPortsForType[innermostGenericTypeName].insert(port);
446  }
447  }
448 
449  for (map<string, set<VuoCompilerPort *> >::iterator j = genericPortsForType.begin(); j != genericPortsForType.end(); ++j)
450  {
451  set<VuoCompilerPort *> genericPorts = j->second;
452  setsOfConnectedGenericPorts.insert(genericPorts);
453  }
454  }
455 
456  // Merge sets of generic ports from different nodes that are connected through a data-carrying cable.
457  set<VuoCable *> cables = getBase()->getCables();
458  for (set<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
459  {
460  VuoCable *cable = *i;
461 
462  if (! (cable->getFromPort() && cable->getFromPort()->hasCompiler() &&
463  cable->getToPort() && cable->getToPort()->hasCompiler() &&
464  cable->getCompiler()->carriesData()))
465  continue;
466 
467  VuoCompilerPort *fromPort = static_cast<VuoCompilerPort *>(cable->getFromPort()->getCompiler());
468  VuoCompilerPort *toPort = static_cast<VuoCompilerPort *>(cable->getToPort()->getCompiler());
469  set< set<VuoCompilerPort *> >::iterator fromSetIter = setsOfConnectedGenericPorts.end(); // the set containing fromPort
470  set< set<VuoCompilerPort *> >::iterator toSetIter = setsOfConnectedGenericPorts.end(); // the set containing toPort
471  for (set< set<VuoCompilerPort *> >::iterator j = setsOfConnectedGenericPorts.begin(); j != setsOfConnectedGenericPorts.end(); ++j)
472  {
473  if (j->find(fromPort) != j->end())
474  fromSetIter = j;
475  if (j->find(toPort) != j->end())
476  toSetIter = j;
477  }
478  if (fromSetIter != setsOfConnectedGenericPorts.end() && toSetIter != setsOfConnectedGenericPorts.end() &&
479  fromSetIter != toSetIter)
480  {
481  set<VuoCompilerPort *> mergedSet;
482  mergedSet.insert(fromSetIter->begin(), fromSetIter->end());
483  mergedSet.insert(toSetIter->begin(), toSetIter->end());
484  setsOfConnectedGenericPorts.insert(mergedSet);
485  setsOfConnectedGenericPorts.erase(fromSetIter);
486  setsOfConnectedGenericPorts.erase(toSetIter);
487  }
488  }
489 
490  return setsOfConnectedGenericPorts;
491 }
492 
500 {
501  set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts = groupGenericPortsByType();
502 
503  // Give each set of connected generic ports a unique generic type.
504  set<string> usedTypeNames;
505  for (set< set<VuoCompilerPort *> >::iterator i = setsOfConnectedGenericPorts.begin(); i != setsOfConnectedGenericPorts.end(); ++i)
506  {
507  set<VuoCompilerPort *> connectedGenericPorts = *i;
508 
509  // Find the smallest-numbered generic type within the set that isn't already used by another set.
510  // If no such type exists, create a fresh type.
511 
512  vector<string> sortedTypeNames;
513  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
514  {
515  VuoCompilerPort *port = *j;
516  VuoGenericType *genericTypeFromPort = static_cast<VuoGenericType *>(port->getDataVuoType());
517  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
518  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
519  if (genericTypeFromPort != genericTypeFromPortClass)
520  {
521  string typeName = VuoGenericType::extractInnermostTypeName( genericTypeFromPort->getModuleKey() );
522  sortedTypeNames.push_back(typeName);
523  }
524  }
525  VuoGenericType::sortGenericTypeNames(sortedTypeNames);
526 
527  string commonTypeName;
528  for (vector<string>::iterator j = sortedTypeNames.begin(); j != sortedTypeNames.end(); ++j)
529  {
530  string portType = *j;
531  if (usedTypeNames.find(portType) == usedTypeNames.end())
532  {
533  commonTypeName = portType;
534  break;
535  }
536  }
537 
538  if (commonTypeName.empty())
539  commonTypeName = createFreshGenericTypeName();
540 
541  usedTypeNames.insert(commonTypeName);
542 
543  // Form the set of compatible types for each composition-level generic type by finding the intersection of
544  // the sets of compatible types for each node-class-level generic type.
545  vector<string> compatibleTypeNames;
546  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
547  {
548  VuoCompilerPort *port = *j;
549  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
550  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
551  VuoGenericType::Compatibility compatibility;
552  vector<string> compatibleTypeNamesForPort = genericTypeFromPortClass->getCompatibleSpecializedTypes(compatibility);
553  vector<string> innermostCompatibleTypeNamesForPort;
554  for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
555  innermostCompatibleTypeNamesForPort.push_back( VuoType::extractInnermostTypeName(*k) );
556 
557  if (! innermostCompatibleTypeNamesForPort.empty())
558  {
559  if (compatibleTypeNames.empty())
560  compatibleTypeNames = innermostCompatibleTypeNamesForPort;
561  else
562  {
563  for (int k = compatibleTypeNames.size() - 1; k >= 0; --k)
564  if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleTypeNames[k]) ==
565  innermostCompatibleTypeNamesForPort.end())
566  compatibleTypeNames.erase(compatibleTypeNames.begin() + k);
567  }
568  }
569  }
570 
571  // Apply the composition-level generic type name to each port.
572  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
573  {
574  VuoCompilerPort *port = *j;
575  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
576  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
577 
578  string typeNameForPort = VuoGenericType::replaceInnermostGenericTypeName(genericTypeFromPortClass->getModuleKey(), commonTypeName);
579  vector<string> compatibleTypeNamesForPort;
580  string prefix = (VuoType::isListTypeName(typeNameForPort) ? VuoType::listTypeNamePrefix : "");
581  for (vector<string>::iterator k = compatibleTypeNames.begin(); k != compatibleTypeNames.end(); ++k)
582  compatibleTypeNamesForPort.push_back(prefix + *k);
583 
584  VuoGenericType *commonTypeForPort = new VuoGenericType(typeNameForPort, compatibleTypeNamesForPort);
585  port->setDataVuoType(commonTypeForPort);
586  }
587  }
588 
589  // Update the list of type suffixes that can't currently be used when creating fresh types.
590  for (map<unsigned int, bool>::iterator i = genericTypeSuffixUsed.begin(); i != genericTypeSuffixUsed.end(); ++i)
591  {
592  unsigned int suffix = i->first;
593  bool used = (usedTypeNames.find( VuoGenericType::createGenericTypeName(suffix) ) != usedTypeNames.end());
594  genericTypeSuffixUsed[suffix] = used;
595  }
596 }
597 
601 string VuoCompilerComposition::createFreshGenericTypeName(void)
602 {
603  for (unsigned int i = 1; ; ++i)
604  {
605  if (! genericTypeSuffixUsed[i])
606  {
607  genericTypeSuffixUsed[i] = true;
609  }
610  }
611 }
612 
624 set<VuoPort *> VuoCompilerComposition::getCorrelatedGenericPorts(VuoNode *entryNode, VuoPort *entryPort, bool useOriginalType)
625 {
627 
628  auto getGenericTypeName = [&useOriginalType] (VuoNode *node, VuoPort *port)
629  {
630  VuoGenericType *genericType = nullptr;
631 
632  if (useOriginalType)
633  {
634  if (node && node->hasCompiler())
635  {
636  VuoCompilerNodeClass *nodeClass = node->getNodeClass()->getCompiler();
637  VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(nodeClass);
638  if (specializedNodeClass)
639  genericType = dynamic_cast<VuoGenericType *>(specializedNodeClass->getOriginalPortType(port->getClass()));
640  }
641  }
642  else
643  {
644  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
645  genericType = dynamic_cast<VuoGenericType *>(compilerPort->getDataVuoType());
646  }
647 
648  return genericType ? VuoType::extractInnermostTypeName(genericType->getModuleKey()) : "";
649  };
650 
651  set<VuoPort *> correlatedPorts;
652  set<VuoNode *> nodesEnqueuedOrVisited;
653  list< pair<VuoNode *, string> > nodesToVisit;
654 
655  string entryGenericType = getGenericTypeName(entryNode, entryPort);
656  if (! entryGenericType.empty())
657  {
658  nodesToVisit.push_back( make_pair(entryNode, entryGenericType) );
659  nodesEnqueuedOrVisited.insert(entryNode);
660  }
661 
662  while (! nodesToVisit.empty())
663  {
664  VuoNode *currNode = nodesToVisit.front().first;
665  string currGenericType = nodesToVisit.front().second;
666  nodesToVisit.pop_front();
667 
668  // Find all ports on the node that share (or would share if unspecialized) the same data type.
669 
670  list<VuoPort *> correlatedPortsOnNode;
671  for (VuoPort *currPort : currNode->getInputPorts())
672  if (getGenericTypeName(currNode, currPort) == currGenericType)
673  correlatedPortsOnNode.push_back(currPort);
674  for (VuoPort *currPort : currNode->getOutputPorts())
675  if (getGenericTypeName(currNode, currPort) == currGenericType)
676  correlatedPortsOnNode.push_back(currPort);
677 
678  correlatedPorts.insert(correlatedPortsOnNode.begin(), correlatedPortsOnNode.end());
679 
680  // Follow all data-carrying cables from those ports to other nodes.
681 
682  for (VuoPort *port : correlatedPortsOnNode)
683  {
684  for (VuoCable *cable : port->getConnectedCables())
685  {
686  if (! (cable->hasCompiler() && cable->getCompiler()->carriesData()))
687  continue;
688 
689  VuoNode *otherNode = nullptr;
690  VuoPort *otherPort = nullptr;
691  if (currNode != cable->getFromNode())
692  {
693  otherNode = cable->getFromNode();
694  otherPort = cable->getFromPort();
695  }
696  else if (currNode != cable->getToNode())
697  {
698  otherNode = cable->getToNode();
699  otherPort = cable->getToPort();
700  }
701 
702  if (! otherPort)
703  continue;
704 
705  if (nodesEnqueuedOrVisited.find(otherNode) == nodesEnqueuedOrVisited.end())
706  {
707  string otherGenericType = getGenericTypeName(otherNode, otherPort);
708  if (! otherGenericType.empty())
709  {
710  nodesToVisit.push_back( make_pair(otherNode, otherGenericType) );
711  nodesEnqueuedOrVisited.insert(otherNode);
712  }
713  }
714  }
715  }
716  }
717 
718  return correlatedPorts;
719 }
720 
725 {
726  VuoCompilerGraph *graph = getCachedGraph();
728  return trigger ? trigger->getBase() : NULL;
729 }
730 
735 {
736  this->module = module;
737 }
738 
743 {
744  Module *takenModule = module;
745  module = NULL;
746  return takenModule;
747 }
748 
755 void VuoCompilerComposition::setUniqueGraphvizIdentifierForNode(VuoNode *node, const string &preferredIdentifier, const string &identifierPrefix)
756 {
757  if (! node->hasCompiler())
758  return;
759 
760  for (VuoNode *currNode : getBase()->getNodes())
761  if (currNode != node && currNode->hasCompiler())
762  nodeGraphvizIdentifierUsed[ currNode->getCompiler()->getGraphvizIdentifier() ] = currNode;
763 
764  auto isIdentifierAvailable = [this, node] (const string &identifier)
765  {
766  auto it = nodeGraphvizIdentifierUsed.find(identifier);
767  if (it == nodeGraphvizIdentifierUsed.end())
768  return true;
769 
770  return it->second == node;
771  };
772 
773  string nonEmptyPreferredIdentifier = (! preferredIdentifier.empty() ? preferredIdentifier : node->getCompiler()->getGraphvizIdentifier());
774  string nonEmptyIdentifierPrefix = (! identifierPrefix.empty() ? identifierPrefix : node->getCompiler()->getGraphvizIdentifierPrefix());
775 
776  string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable, nonEmptyPreferredIdentifier, nonEmptyIdentifierPrefix);
777 
778  nodeGraphvizIdentifierUsed[uniqueIdentifier] = node;
779  node->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
780 }
781 
786 {
787  nodeGraphvizIdentifierUsed.clear();
788 }
789 
797 {
798  if (! comment->hasCompiler())
799  return;
800 
801  for (VuoComment *currComment : getBase()->getComments())
802  if (currComment != comment && currComment->hasCompiler())
803  commentGraphvizIdentifierUsed[ currComment->getCompiler()->getGraphvizIdentifier() ] = currComment;
804 
805  auto isIdentifierAvailable = [this, comment] (const string &identifier)
806  {
807  auto it = commentGraphvizIdentifierUsed.find(identifier);
808  if (it == commentGraphvizIdentifierUsed.end())
809  return true;
810 
811  return it->second == comment;
812  };
813 
814  string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable,
815  comment->getCompiler()->getGraphvizIdentifier(),
817  commentGraphvizIdentifierUsed[uniqueIdentifier] = comment;
818  comment->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
819 }
820 
825 {
826  commentGraphvizIdentifierUsed.clear();
827 }
828 
836 void VuoCompilerComposition::setManuallyFirableInputPort(VuoNode *nodeContainingPort, VuoPort *portFiredInto)
837 {
838  this->manuallyFirableInputNode = nodeContainingPort;
839  this->manuallyFirableInputPort = portFiredInto;
840 }
841 
846 {
847  return manuallyFirableInputNode;
848 }
849 
854 {
855  return manuallyFirableInputPort;
856 }
857 
861 string VuoCompilerComposition::getGraphvizDeclaration(VuoProtocol *activeProtocol, string header, string footer)
862 {
863  return getGraphvizDeclarationForComponents(getBase()->getNodes(),
864  getBase()->getCables(),
865  getBase()->getComments(),
866  getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, true),
867  getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, false),
868  header,
869  footer);
870 }
871 
876  set<VuoCable *> cableSet,
877  set<VuoComment *> commentSet,
878  vector<VuoPublishedPort *> publishedInputPorts,
879  vector<VuoPublishedPort *> publishedOutputPorts,
880  string header, string footer, double xPositionOffset, double yPositionOffset)
881 {
882  // Sort nodes.
883  vector<VuoNode *> nodes;
884  for (set<VuoNode *>::iterator i = nodeSet.begin(); i != nodeSet.end(); ++i)
885  nodes.push_back(*i);
886 
887  sort(nodes.begin(), nodes.end(), compareGraphvizIdentifiersOfNodes);
888 
889  // Sort cables.
890  vector<VuoCable *> cables;
891  for (set<VuoCable *>::iterator i = cableSet.begin(); i != cableSet.end(); ++i)
892  cables.push_back(*i);
893 
894  sort(cables.begin(), cables.end(), compareGraphvizIdentifiersOfCables);
895 
896  // Sort comments.
897  vector<VuoComment *> comments;
898  for (set<VuoComment *>::iterator i = commentSet.begin(); i != commentSet.end(); ++i)
899  comments.push_back(*i);
900 
901  sort(comments.begin(), comments.end(), compareGraphvizIdentifiersOfComments);
902 
903  string compositionHeader = (! header.empty()? header : defaultGraphDeclaration);
904  string compositionFooter = (! footer.empty()? footer : "\n");
905 
906  ostringstream output;
907  string nodeCommentSectionDivider = "\n";
908  if ((nodes.empty() && publishedInputPorts.empty() && publishedOutputPorts.empty()) ||
909  (comments.empty() && cables.empty()))
910  nodeCommentSectionDivider = "";
911 
912  string commentCableSectionDivider = "\n";
913  if (comments.empty() || cables.empty())
914  commentCableSectionDivider = "";
915 
916  // Print header
917  output << compositionHeader;
918  output << "{" << endl;
919 
920  // Print nodes
921  for (vector<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
922  {
923  string nodeDeclaration = ((*i)->hasCompiler() ?
924  (*i)->getCompiler()->getGraphvizDeclaration(true, xPositionOffset, yPositionOffset,
925  manuallyFirableInputNode == *i ? manuallyFirableInputPort : nullptr) :
926  (*i)->getRawGraphvizDeclaration());
927  output << nodeDeclaration << endl;
928  }
929 
930  // Print pseudo-nodes for published ports
931  if (! publishedInputPorts.empty())
932  {
934  for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
935  output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\r";
936  output << "\"";
937  for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
938  output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
939  output << "];" << endl;
940  }
941  if (! publishedOutputPorts.empty())
942  {
944  for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
945  output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\l";
946  output << "\"";
947 
948  for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
949  output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
950 
951  output << "];" << endl;
952  }
953 
954  output << nodeCommentSectionDivider;
955 
956  // Print comments
957  for (vector<VuoComment *>::iterator i = comments.begin(); i != comments.end(); ++i)
958  {
959  string commentDeclaration = ((*i)->hasCompiler() ?
960  (*i)->getCompiler()->getGraphvizDeclaration(xPositionOffset, yPositionOffset) : "");
961  output << commentDeclaration << endl;
962  }
963 
964  output << commentCableSectionDivider;
965 
966  // Print cables
967  for (vector<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
968  {
969  VuoCable *cable = *i;
970  output << cable->getCompiler()->getGraphvizDeclaration() << endl;
971  }
972 
973  output << "}";
974  output << compositionFooter;
975 
976  return output.str();
977 }
978 
983 {
984  VuoCompilerTargetSet compositeTarget;
985  for (auto node : getBase()->getNodes())
987  return compositeTarget;
988 }
989 
993 bool VuoCompilerComposition::compareGraphvizIdentifiersOfNodes(VuoNode *lhs, VuoNode *rhs)
994 {
995  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : lhs->getRawGraphvizIdentifier());
996  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : rhs->getRawGraphvizIdentifier());
997  return lhsIdentifier.compare(rhsIdentifier) < 0;
998 }
999 
1003 bool VuoCompilerComposition::compareGraphvizIdentifiersOfCables(VuoCable *lhs, VuoCable *rhs)
1004 {
1005  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizDeclaration() : "");
1006  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizDeclaration() : "");
1007  return lhsIdentifier.compare(rhsIdentifier) < 0;
1008 }
1009 
1013 bool VuoCompilerComposition::compareGraphvizIdentifiersOfComments(VuoComment *lhs, VuoComment *rhs)
1014 {
1015  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : "");
1016  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : "");
1017  return lhsIdentifier.compare(rhsIdentifier) < 0;
1018 }
1019 
1025 {
1026  if (! oldPort || ! newPort)
1027  return false;
1028 
1029  string oldPortName = oldPort->getClass()->getName();
1030  string newPortName = newPort->getClass()->getName();
1031  if (oldPortName == newPortName)
1032  {
1033  if (oldPort->hasCompiler() && newPort->hasCompiler())
1034  {
1035  VuoType *oldType = static_cast<VuoCompilerPort *>( oldPort->getCompiler() )->getDataVuoType();
1036  VuoType *newType = static_cast<VuoCompilerPort *>( newPort->getCompiler() )->getDataVuoType();
1037  if (oldType == newType)
1038  return true;
1039  }
1040  else
1041  return true;
1042  }
1043 
1044  return false;
1045 }
1046 
1052 {
1053  if (! oldPortClass || ! newPortClass)
1054  return false;
1055 
1056  string oldPortName = oldPortClass->getName();
1057  string newPortName = newPortClass->getName();
1058  if (oldPortName == newPortName)
1059  {
1060  if (oldPortClass->hasCompiler() && newPortClass->hasCompiler())
1061  {
1062  VuoType *oldType = static_cast<VuoCompilerPortClass *>( oldPortClass->getCompiler() )->getDataVuoType();
1063  VuoType *newType = static_cast<VuoCompilerPortClass *>( newPortClass->getCompiler() )->getDataVuoType();
1064  if (oldType == newType)
1065  return true;
1066  }
1067  else
1068  return true;
1069  }
1070 
1071  return false;
1072 }