Vuo  2.3.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  if (issues)
311  {
312  issues->append(issue);
313  throw VuoCompilerException(issues, false);
314  }
315  else
316  throw VuoCompilerException(issue);
317  }
318 }
319 
326 void VuoCompilerComposition::checkForMissingTypes(VuoCompilerIssues *issues)
327 {
328  map<string, set<string> > missingTypes;
329  for (VuoNode *node : getBase()->getNodes())
330  {
331  if (! node->getNodeClass()->hasCompiler())
332  continue;
333 
334  vector<VuoPort *> ports;
335  vector<VuoPort *> inputPorts = node->getInputPorts();
336  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
337  vector<VuoPort *> outputPorts = node->getOutputPorts();
338  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
339 
340  for (VuoPort *port : ports)
341  {
342  VuoType *type = static_cast<VuoCompilerPort *>(port->getCompiler())->getDataVuoType();
343  if (type && ! type->hasCompiler() && ! dynamic_cast<VuoGenericType *>(type))
344  missingTypes[type->getModuleKey()].insert(node->getNodeClass()->getClassName());
345  }
346  }
347 
348  if (! missingTypes.empty())
349  {
350  auto exceptionIssues = new VuoCompilerIssues;
351  for (auto i : missingTypes)
352  {
353  vector<string> nodeClassNames(i.second.begin(), i.second.end());
354  std::sort(nodeClassNames.begin(), nodeClassNames.end());
355 
356  string summary = "Data type not installed";
357  string details = i.first + " — used by " + VuoStringUtilities::join(nodeClassNames, ", ");
358  string hint = "Check with the developer of the nodes that use this data type.";
359 
360  VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", "", summary, details);
361  issue.setHint(hint);
362  exceptionIssues->append(issue);
363  if (issues)
364  issues->append(issue);
365  }
366 
367  throw VuoCompilerException(exceptionIssues, true);
368  }
369 }
370 
378 {
379  checkForEventFlowIssues(set<VuoCompilerCable *>(), issues);
380 }
381 
389 void VuoCompilerComposition::checkForEventFlowIssues(set<VuoCompilerCable *> potentialCables, VuoCompilerIssues *issues)
390 {
391  VuoCompilerGraph *graph;
392  if (! potentialCables.empty())
393  graph = new VuoCompilerGraph(this, nullptr, potentialCables);
394  else
395  graph = getCachedGraph();
396 
397  graph->checkForInfiniteFeedback(issues);
398  graph->checkForDeadlockedFeedback(issues);
399 
400  if (! potentialCables.empty())
401  delete graph;
402 }
403 
407 set< set<VuoCompilerPort *> > VuoCompilerComposition::groupGenericPortsByType(void)
408 {
409  set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts;
410 
411  // Put the generic ports into sets by node, with additional sets for published input and output ports.
412  set< pair< vector<VuoPort *>, VuoNode *> > portsGroupedByNode;
413  set<VuoNode *> nodes = getBase()->getNodes();
414  for (set<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
415  {
416  VuoNode *node = *i;
417  if (! node->hasCompiler())
418  continue;
419 
420  vector<VuoPort *> inputPorts = node->getInputPorts();
421  vector<VuoPort *> outputPorts = node->getOutputPorts();
422  vector<VuoPort *> ports;
423  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
424  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
425 
426  portsGroupedByNode.insert( make_pair(ports, node) );
427  }
428  vector<VuoPublishedPort *> publishedInputPorts = getBase()->getPublishedInputPorts();
429  vector<VuoPublishedPort *> publishedOutputPorts = getBase()->getPublishedOutputPorts();
430  vector<VuoPort *> publishedInputBasePorts( publishedInputPorts.begin(), publishedInputPorts.end() );
431  vector<VuoPort *> publishedOutputBasePorts( publishedOutputPorts.begin(), publishedOutputPorts.end() );
432  portsGroupedByNode.insert( make_pair(publishedInputBasePorts, static_cast<VuoNode *>(NULL)) );
433  portsGroupedByNode.insert( make_pair(publishedOutputBasePorts, static_cast<VuoNode *>(NULL)) );
434 
435  // Within each set of generic ports by node (or published ports), group the generic ports into sets by generic type.
436  for (set< pair< vector<VuoPort *>, VuoNode* > >::iterator i = portsGroupedByNode.begin(); i != portsGroupedByNode.end(); ++i)
437  {
438  vector<VuoPort *> ports = i->first;
439 
440  map<string, set<VuoCompilerPort *> > genericPortsForType;
441  for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
442  {
443  VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
444  VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
445 
446  if (genericType)
447  {
448  string innermostGenericTypeName = VuoType::extractInnermostTypeName(genericType->getModuleKey());
449  genericPortsForType[innermostGenericTypeName].insert(port);
450  }
451  }
452 
453  for (map<string, set<VuoCompilerPort *> >::iterator j = genericPortsForType.begin(); j != genericPortsForType.end(); ++j)
454  {
455  set<VuoCompilerPort *> genericPorts = j->second;
456  setsOfConnectedGenericPorts.insert(genericPorts);
457  }
458  }
459 
460  // Merge sets of generic ports from different nodes that are connected through a data-carrying cable.
461  set<VuoCable *> cables = getBase()->getCables();
462  for (set<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
463  {
464  VuoCable *cable = *i;
465 
466  if (! (cable->getFromPort() && cable->getFromPort()->hasCompiler() &&
467  cable->getToPort() && cable->getToPort()->hasCompiler() &&
468  cable->getCompiler()->carriesData()))
469  continue;
470 
471  VuoCompilerPort *fromPort = static_cast<VuoCompilerPort *>(cable->getFromPort()->getCompiler());
472  VuoCompilerPort *toPort = static_cast<VuoCompilerPort *>(cable->getToPort()->getCompiler());
473  set< set<VuoCompilerPort *> >::iterator fromSetIter = setsOfConnectedGenericPorts.end(); // the set containing fromPort
474  set< set<VuoCompilerPort *> >::iterator toSetIter = setsOfConnectedGenericPorts.end(); // the set containing toPort
475  for (set< set<VuoCompilerPort *> >::iterator j = setsOfConnectedGenericPorts.begin(); j != setsOfConnectedGenericPorts.end(); ++j)
476  {
477  if (j->find(fromPort) != j->end())
478  fromSetIter = j;
479  if (j->find(toPort) != j->end())
480  toSetIter = j;
481  }
482  if (fromSetIter != setsOfConnectedGenericPorts.end() && toSetIter != setsOfConnectedGenericPorts.end() &&
483  fromSetIter != toSetIter)
484  {
485  set<VuoCompilerPort *> mergedSet;
486  mergedSet.insert(fromSetIter->begin(), fromSetIter->end());
487  mergedSet.insert(toSetIter->begin(), toSetIter->end());
488  setsOfConnectedGenericPorts.insert(mergedSet);
489  setsOfConnectedGenericPorts.erase(fromSetIter);
490  setsOfConnectedGenericPorts.erase(toSetIter);
491  }
492  }
493 
494  return setsOfConnectedGenericPorts;
495 }
496 
504 {
505  set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts = groupGenericPortsByType();
506 
507  // Give each set of connected generic ports a unique generic type.
508  set<string> usedTypeNames;
509  for (set< set<VuoCompilerPort *> >::iterator i = setsOfConnectedGenericPorts.begin(); i != setsOfConnectedGenericPorts.end(); ++i)
510  {
511  set<VuoCompilerPort *> connectedGenericPorts = *i;
512 
513  // Find the smallest-numbered generic type within the set that isn't already used by another set.
514  // If no such type exists, create a fresh type.
515 
516  vector<string> sortedTypeNames;
517  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
518  {
519  VuoCompilerPort *port = *j;
520  VuoGenericType *genericTypeFromPort = static_cast<VuoGenericType *>(port->getDataVuoType());
521  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
522  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
523  if (genericTypeFromPort != genericTypeFromPortClass)
524  {
525  string typeName = VuoGenericType::extractInnermostTypeName( genericTypeFromPort->getModuleKey() );
526  sortedTypeNames.push_back(typeName);
527  }
528  }
529  VuoGenericType::sortGenericTypeNames(sortedTypeNames);
530 
531  string commonTypeName;
532  for (vector<string>::iterator j = sortedTypeNames.begin(); j != sortedTypeNames.end(); ++j)
533  {
534  string portType = *j;
535  if (usedTypeNames.find(portType) == usedTypeNames.end())
536  {
537  commonTypeName = portType;
538  break;
539  }
540  }
541 
542  if (commonTypeName.empty())
543  commonTypeName = createFreshGenericTypeName();
544 
545  usedTypeNames.insert(commonTypeName);
546 
547  // Form the set of compatible types for each composition-level generic type by finding the intersection of
548  // the sets of compatible types for each node-class-level generic type.
549  vector<string> compatibleTypeNames;
550  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
551  {
552  VuoCompilerPort *port = *j;
553  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
554  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
555  VuoGenericType::Compatibility compatibility;
556  vector<string> compatibleTypeNamesForPort = genericTypeFromPortClass->getCompatibleSpecializedTypes(compatibility);
557  vector<string> innermostCompatibleTypeNamesForPort;
558  for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
559  innermostCompatibleTypeNamesForPort.push_back( VuoType::extractInnermostTypeName(*k) );
560 
561  if (! innermostCompatibleTypeNamesForPort.empty())
562  {
563  if (compatibleTypeNames.empty())
564  compatibleTypeNames = innermostCompatibleTypeNamesForPort;
565  else
566  {
567  for (int k = compatibleTypeNames.size() - 1; k >= 0; --k)
568  if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleTypeNames[k]) ==
569  innermostCompatibleTypeNamesForPort.end())
570  compatibleTypeNames.erase(compatibleTypeNames.begin() + k);
571  }
572  }
573  }
574 
575  // Apply the composition-level generic type name to each port.
576  for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
577  {
578  VuoCompilerPort *port = *j;
579  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
580  VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
581 
582  string typeNameForPort = VuoGenericType::replaceInnermostGenericTypeName(genericTypeFromPortClass->getModuleKey(), commonTypeName);
583  vector<string> compatibleTypeNamesForPort;
584  string prefix = (VuoType::isListTypeName(typeNameForPort) ? VuoType::listTypeNamePrefix : "");
585  for (vector<string>::iterator k = compatibleTypeNames.begin(); k != compatibleTypeNames.end(); ++k)
586  compatibleTypeNamesForPort.push_back(prefix + *k);
587 
588  VuoGenericType *commonTypeForPort = new VuoGenericType(typeNameForPort, compatibleTypeNamesForPort);
589  port->setDataVuoType(commonTypeForPort);
590  }
591  }
592 
593  // Update the list of type suffixes that can't currently be used when creating fresh types.
594  for (map<unsigned int, bool>::iterator i = genericTypeSuffixUsed.begin(); i != genericTypeSuffixUsed.end(); ++i)
595  {
596  unsigned int suffix = i->first;
597  bool used = (usedTypeNames.find( VuoGenericType::createGenericTypeName(suffix) ) != usedTypeNames.end());
598  genericTypeSuffixUsed[suffix] = used;
599  }
600 }
601 
605 string VuoCompilerComposition::createFreshGenericTypeName(void)
606 {
607  for (unsigned int i = 1; ; ++i)
608  {
609  if (! genericTypeSuffixUsed[i])
610  {
611  genericTypeSuffixUsed[i] = true;
613  }
614  }
615 }
616 
628 set<VuoPort *> VuoCompilerComposition::getCorrelatedGenericPorts(VuoNode *entryNode, VuoPort *entryPort, bool useOriginalType)
629 {
631 
632  auto getGenericTypeName = [&useOriginalType] (VuoNode *node, VuoPort *port)
633  {
634  VuoGenericType *genericType = nullptr;
635 
636  if (useOriginalType)
637  {
638  if (node && node->hasCompiler())
639  {
640  VuoCompilerNodeClass *nodeClass = node->getNodeClass()->getCompiler();
641  VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(nodeClass);
642  if (specializedNodeClass)
643  genericType = dynamic_cast<VuoGenericType *>(specializedNodeClass->getOriginalPortType(port->getClass()));
644  }
645  }
646  else
647  {
648  VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
649  genericType = dynamic_cast<VuoGenericType *>(compilerPort->getDataVuoType());
650  }
651 
652  return genericType ? VuoType::extractInnermostTypeName(genericType->getModuleKey()) : "";
653  };
654 
655  set<VuoPort *> correlatedPorts;
656  set<VuoNode *> nodesEnqueuedOrVisited;
657  list< pair<VuoNode *, string> > nodesToVisit;
658 
659  string entryGenericType = getGenericTypeName(entryNode, entryPort);
660  if (! entryGenericType.empty())
661  {
662  nodesToVisit.push_back( make_pair(entryNode, entryGenericType) );
663  nodesEnqueuedOrVisited.insert(entryNode);
664  }
665 
666  while (! nodesToVisit.empty())
667  {
668  VuoNode *currNode = nodesToVisit.front().first;
669  string currGenericType = nodesToVisit.front().second;
670  nodesToVisit.pop_front();
671 
672  // Find all ports on the node that share (or would share if unspecialized) the same data type.
673 
674  list<VuoPort *> correlatedPortsOnNode;
675  for (VuoPort *currPort : currNode->getInputPorts())
676  if (getGenericTypeName(currNode, currPort) == currGenericType)
677  correlatedPortsOnNode.push_back(currPort);
678  for (VuoPort *currPort : currNode->getOutputPorts())
679  if (getGenericTypeName(currNode, currPort) == currGenericType)
680  correlatedPortsOnNode.push_back(currPort);
681 
682  correlatedPorts.insert(correlatedPortsOnNode.begin(), correlatedPortsOnNode.end());
683 
684  // Follow all data-carrying cables from those ports to other nodes.
685 
686  for (VuoPort *port : correlatedPortsOnNode)
687  {
688  for (VuoCable *cable : port->getConnectedCables())
689  {
690  if (! (cable->hasCompiler() && cable->getCompiler()->carriesData()))
691  continue;
692 
693  VuoNode *otherNode = nullptr;
694  VuoPort *otherPort = nullptr;
695  if (currNode != cable->getFromNode())
696  {
697  otherNode = cable->getFromNode();
698  otherPort = cable->getFromPort();
699  }
700  else if (currNode != cable->getToNode())
701  {
702  otherNode = cable->getToNode();
703  otherPort = cable->getToPort();
704  }
705 
706  if (! otherPort)
707  continue;
708 
709  if (nodesEnqueuedOrVisited.find(otherNode) == nodesEnqueuedOrVisited.end())
710  {
711  string otherGenericType = getGenericTypeName(otherNode, otherPort);
712  if (! otherGenericType.empty())
713  {
714  nodesToVisit.push_back( make_pair(otherNode, otherGenericType) );
715  nodesEnqueuedOrVisited.insert(otherNode);
716  }
717  }
718  }
719  }
720  }
721 
722  return correlatedPorts;
723 }
724 
729 {
730  VuoCompilerGraph *graph = getCachedGraph();
732  return trigger ? trigger->getBase() : NULL;
733 }
734 
739 {
740  this->module = module;
741 }
742 
747 {
748  Module *takenModule = module;
749  module = NULL;
750  return takenModule;
751 }
752 
759 void VuoCompilerComposition::setUniqueGraphvizIdentifierForNode(VuoNode *node, const string &preferredIdentifier, const string &identifierPrefix)
760 {
761  if (! node->hasCompiler())
762  return;
763 
764  for (VuoNode *currNode : getBase()->getNodes())
765  if (currNode != node && currNode->hasCompiler())
766  nodeGraphvizIdentifierUsed[ currNode->getCompiler()->getGraphvizIdentifier() ] = currNode;
767 
768  auto isIdentifierAvailable = [this, node] (const string &identifier)
769  {
770  auto it = nodeGraphvizIdentifierUsed.find(identifier);
771  if (it == nodeGraphvizIdentifierUsed.end())
772  return true;
773 
774  return it->second == node;
775  };
776 
777  string nonEmptyPreferredIdentifier = (! preferredIdentifier.empty() ? preferredIdentifier : node->getCompiler()->getGraphvizIdentifier());
778  string nonEmptyIdentifierPrefix = (! identifierPrefix.empty() ? identifierPrefix : node->getCompiler()->getGraphvizIdentifierPrefix());
779 
780  string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable, nonEmptyPreferredIdentifier, nonEmptyIdentifierPrefix);
781 
782  nodeGraphvizIdentifierUsed[uniqueIdentifier] = node;
783  node->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
784 }
785 
790 {
791  nodeGraphvizIdentifierUsed.clear();
792 }
793 
801 {
802  if (! comment->hasCompiler())
803  return;
804 
805  for (VuoComment *currComment : getBase()->getComments())
806  if (currComment != comment && currComment->hasCompiler())
807  commentGraphvizIdentifierUsed[ currComment->getCompiler()->getGraphvizIdentifier() ] = currComment;
808 
809  auto isIdentifierAvailable = [this, comment] (const string &identifier)
810  {
811  auto it = commentGraphvizIdentifierUsed.find(identifier);
812  if (it == commentGraphvizIdentifierUsed.end())
813  return true;
814 
815  return it->second == comment;
816  };
817 
818  string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable,
819  comment->getCompiler()->getGraphvizIdentifier(),
821  commentGraphvizIdentifierUsed[uniqueIdentifier] = comment;
822  comment->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
823 }
824 
829 {
830  commentGraphvizIdentifierUsed.clear();
831 }
832 
840 void VuoCompilerComposition::setManuallyFirableInputPort(VuoNode *nodeContainingPort, VuoPort *portFiredInto)
841 {
842  this->manuallyFirableInputNode = nodeContainingPort;
843  this->manuallyFirableInputPort = portFiredInto;
844 }
845 
850 {
851  return manuallyFirableInputNode;
852 }
853 
858 {
859  return manuallyFirableInputPort;
860 }
861 
865 string VuoCompilerComposition::getGraphvizDeclaration(VuoProtocol *activeProtocol, string header, string footer)
866 {
867  return getGraphvizDeclarationForComponents(getBase()->getNodes(),
868  getBase()->getCables(),
869  getBase()->getComments(),
870  getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, true),
871  getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, false),
872  header,
873  footer);
874 }
875 
880  set<VuoCable *> cableSet,
881  set<VuoComment *> commentSet,
882  vector<VuoPublishedPort *> publishedInputPorts,
883  vector<VuoPublishedPort *> publishedOutputPorts,
884  string header, string footer, double xPositionOffset, double yPositionOffset)
885 {
886  // Sort nodes.
887  vector<VuoNode *> nodes;
888  for (set<VuoNode *>::iterator i = nodeSet.begin(); i != nodeSet.end(); ++i)
889  nodes.push_back(*i);
890 
891  sort(nodes.begin(), nodes.end(), compareGraphvizIdentifiersOfNodes);
892 
893  // Sort cables.
894  vector<VuoCable *> cables;
895  for (set<VuoCable *>::iterator i = cableSet.begin(); i != cableSet.end(); ++i)
896  cables.push_back(*i);
897 
898  sort(cables.begin(), cables.end(), compareGraphvizIdentifiersOfCables);
899 
900  // Sort comments.
901  vector<VuoComment *> comments;
902  for (set<VuoComment *>::iterator i = commentSet.begin(); i != commentSet.end(); ++i)
903  comments.push_back(*i);
904 
905  sort(comments.begin(), comments.end(), compareGraphvizIdentifiersOfComments);
906 
907  string compositionHeader = (! header.empty()? header : defaultGraphDeclaration);
908  string compositionFooter = (! footer.empty()? footer : "\n");
909 
910  ostringstream output;
911  string nodeCommentSectionDivider = "\n";
912  if ((nodes.empty() && publishedInputPorts.empty() && publishedOutputPorts.empty()) ||
913  (comments.empty() && cables.empty()))
914  nodeCommentSectionDivider = "";
915 
916  string commentCableSectionDivider = "\n";
917  if (comments.empty() || cables.empty())
918  commentCableSectionDivider = "";
919 
920  // Print header
921  output << compositionHeader;
922  output << "{" << endl;
923 
924  // Print nodes
925  for (vector<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
926  {
927  string nodeDeclaration = ((*i)->hasCompiler() ?
928  (*i)->getCompiler()->getGraphvizDeclaration(true, xPositionOffset, yPositionOffset,
929  manuallyFirableInputNode == *i ? manuallyFirableInputPort : nullptr) :
930  (*i)->getRawGraphvizDeclaration());
931  output << nodeDeclaration << endl;
932  }
933 
934  // Print pseudo-nodes for published ports
935  if (! publishedInputPorts.empty())
936  {
938  for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
939  output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\r";
940  output << "\"";
941  for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
942  output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
943  output << "];" << endl;
944  }
945  if (! publishedOutputPorts.empty())
946  {
948  for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
949  output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\l";
950  output << "\"";
951 
952  for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
953  output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
954 
955  output << "];" << endl;
956  }
957 
958  output << nodeCommentSectionDivider;
959 
960  // Print comments
961  for (vector<VuoComment *>::iterator i = comments.begin(); i != comments.end(); ++i)
962  {
963  string commentDeclaration = ((*i)->hasCompiler() ?
964  (*i)->getCompiler()->getGraphvizDeclaration(xPositionOffset, yPositionOffset) : "");
965  output << commentDeclaration << endl;
966  }
967 
968  output << commentCableSectionDivider;
969 
970  // Print cables
971  for (vector<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
972  {
973  VuoCable *cable = *i;
974  output << cable->getCompiler()->getGraphvizDeclaration() << endl;
975  }
976 
977  output << "}";
978  output << compositionFooter;
979 
980  return output.str();
981 }
982 
989 {
990  VuoCompilerIssues *issues = new VuoCompilerIssues;
992 
993  VuoCompilerCompatibility compositeTarget(nullptr);
994  for (auto node : getBase()->getNodes())
995  compositeTarget = compositeTarget.intersection( node->getNodeClass()->getCompiler()->getCompatibleTargets() );
996 
997  return compositeTarget;
998 }
999 
1003 bool VuoCompilerComposition::compareGraphvizIdentifiersOfNodes(VuoNode *lhs, VuoNode *rhs)
1004 {
1005  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : lhs->getRawGraphvizIdentifier());
1006  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : rhs->getRawGraphvizIdentifier());
1007  return lhsIdentifier.compare(rhsIdentifier) < 0;
1008 }
1009 
1013 bool VuoCompilerComposition::compareGraphvizIdentifiersOfCables(VuoCable *lhs, VuoCable *rhs)
1014 {
1015  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizDeclaration() : "");
1016  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizDeclaration() : "");
1017  return lhsIdentifier.compare(rhsIdentifier) < 0;
1018 }
1019 
1023 bool VuoCompilerComposition::compareGraphvizIdentifiersOfComments(VuoComment *lhs, VuoComment *rhs)
1024 {
1025  string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : "");
1026  string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : "");
1027  return lhsIdentifier.compare(rhsIdentifier) < 0;
1028 }
1029 
1035 {
1036  if (! oldPort || ! newPort)
1037  return false;
1038 
1039  string oldPortName = oldPort->getClass()->getName();
1040  string newPortName = newPort->getClass()->getName();
1041  if (oldPortName == newPortName)
1042  {
1043  if (oldPort->hasCompiler() && newPort->hasCompiler())
1044  {
1045  VuoType *oldType = static_cast<VuoCompilerPort *>( oldPort->getCompiler() )->getDataVuoType();
1046  VuoType *newType = static_cast<VuoCompilerPort *>( newPort->getCompiler() )->getDataVuoType();
1047  if (oldType == newType)
1048  return true;
1049  }
1050  else
1051  return true;
1052  }
1053 
1054  return false;
1055 }
1056 
1062 {
1063  if (! oldPortClass || ! newPortClass)
1064  return false;
1065 
1066  string oldPortName = oldPortClass->getName();
1067  string newPortName = newPortClass->getName();
1068  if (oldPortName == newPortName)
1069  {
1070  if (oldPortClass->hasCompiler() && newPortClass->hasCompiler())
1071  {
1072  VuoType *oldType = static_cast<VuoCompilerPortClass *>( oldPortClass->getCompiler() )->getDataVuoType();
1073  VuoType *newType = static_cast<VuoCompilerPortClass *>( newPortClass->getCompiler() )->getDataVuoType();
1074  if (oldType == newType)
1075  return true;
1076  }
1077  else
1078  return true;
1079  }
1080 
1081  return false;
1082 }