Vuo  2.3.2
VuoCompilerNodeClass.cc
Go to the documentation of this file.
1 
10 #include <sstream>
15 #include "VuoCompilerNode.hh"
16 #include "VuoCompilerNodeClass.hh"
19 #include "VuoCompilerPort.hh"
22 #include "VuoFileUtilities.hh"
23 #include "VuoGenericType.hh"
24 #include "VuoJsonUtilities.hh"
25 #include "VuoNode.hh"
26 #include "VuoNodeClass.hh"
27 #include "VuoPort.hh"
28 #include "VuoStringUtilities.hh"
29 
30 
36 {
37  INPUT_DATA_ABSENT = 1 << 0,
38  INPUT_DATA_PRESENT = 1 << 1,
39  OUTPUT_DATA_ABSENT = 1 << 2,
40  OUTPUT_DATA_PRESENT = 1 << 3,
41  INPUT_EVENT_ABSENT = 1 << 4,
42  INPUT_EVENT_PRESENT = 1 << 5,
43  OUTPUT_EVENT_ABSENT = 1 << 6,
44  OUTPUT_EVENT_PRESENT = 1 << 7,
45  OUTPUT_TRIGGER_ABSENT = 1 << 8,
46  OUTPUT_TRIGGER_PRESENT = 1 << 9,
47  INSTANCE_DATA_ABSENT = 1 << 10,
48  INSTANCE_DATA_PRESENT = 1 << 11
49 };
50 
51 
55 VuoCompilerNodeClass::VuoCompilerNodeClass(string className, Module *module)
56  : VuoBaseDetail<VuoNodeClass>("VuoCompilerNodeClass with dummy base", new VuoNodeClass(className, vector<string>(), vector<string>())),
57  VuoCompilerModule(getBase(), module)
58 {
59  instanceDataClass = NULL;
60  initFunction = NULL;
61  finiFunction = NULL;
62  callbackStartFunction = NULL;
63  callbackUpdateFunction = NULL;
64  callbackStopFunction = NULL;
65  _isSubcomposition = false;
66 
67  parse();
68 }
69 
77  : VuoBaseDetail<VuoNodeClass>("VuoCompilerNodeClass with substantial base", new VuoNodeClass(
78  compilerNodeClass->getBase()->getClassName(),
79  compilerNodeClass->getBase()->getRefreshPortClass(),
80  compilerNodeClass->getBase()->getInputPortClasses(),
81  compilerNodeClass->getBase()->getOutputPortClasses())),
82  VuoCompilerModule(getBase(), compilerNodeClass->module)
83 {
84  getBase()->setDefaultTitle(compilerNodeClass->getBase()->getDefaultTitle());
85  getBase()->setDescription(compilerNodeClass->getBase()->getDescription());
86  getBase()->setVersion(compilerNodeClass->getBase()->getVersion());
87  getBase()->setKeywords(compilerNodeClass->getBase()->getKeywords());
88  getBase()->setNodeSet(compilerNodeClass->getBase()->getNodeSet());
89 #if VUO_PRO
90  getBase()->setPro(compilerNodeClass->getBase()->isPro());
91  setDependsOnPro(compilerNodeClass->dependsOnPro());
92 #endif
94  getBase()->setDeprecated(compilerNodeClass->getBase()->getDeprecated());
95  getBase()->setCompiler(this);
96 
97  this->dependencies = compilerNodeClass->dependencies;
98  this->compatibleTargets = compilerNodeClass->compatibleTargets;
99  this->eventFunction = compilerNodeClass->eventFunction;
100  this->initFunction = compilerNodeClass->initFunction;
101  this->finiFunction = compilerNodeClass->finiFunction;
102  this->callbackStartFunction = compilerNodeClass->callbackStartFunction;
103  this->callbackUpdateFunction = compilerNodeClass->callbackUpdateFunction;
104  this->callbackStopFunction = compilerNodeClass->callbackStopFunction;
105  this->instanceDataClass = compilerNodeClass->instanceDataClass;
106  this->triggerDescriptions = compilerNodeClass->triggerDescriptions;
107  this->compatibleSpecializedForGenericTypeName = compilerNodeClass->compatibleSpecializedForGenericTypeName;
108  this->portsWithExplicitEventBlockingNone = compilerNodeClass->portsWithExplicitEventBlockingNone;
109  this->sourcePath = compilerNodeClass->sourcePath;
110  this->sourceCode = compilerNodeClass->sourceCode;
111  this->containedNodes = compilerNodeClass->containedNodes;
113 
114  compilerNodeClass->getBase()->setInputPortClasses(vector<VuoPortClass *>());
115  compilerNodeClass->getBase()->setOutputPortClasses(vector<VuoPortClass *>());
116  compilerNodeClass->instanceDataClass = NULL;
117 
118  _isSubcomposition = false;
119  updateSubcompositionStatus();
120 }
121 
126  : VuoBaseDetail<VuoNodeClass>("VuoCompilerNodeClass with existing base", baseNodeClass),
127  VuoCompilerModule(baseNodeClass, NULL)
128 {
129  getBase()->setCompiler(this);
130 
131  instanceDataClass = NULL;
132  eventFunction = NULL;
133  initFunction = NULL;
134  finiFunction = NULL;
135  callbackStartFunction = NULL;
136  callbackUpdateFunction = NULL;
137  callbackStopFunction = NULL;
138  _isSubcomposition = false;
139 }
140 
145 {
146  delete instanceDataClass;
147 
148  vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
149  for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
150  delete (*i)->getCompiler();
151 
152  vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
153  for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
154  delete (*i)->getCompiler();
155 }
156 
160 VuoNode * VuoCompilerNodeClass::newNode(string title, double x, double y)
161 {
162  if (title.empty())
164 
165  VuoNode *n = getBase()->newNode(title, x, y);
166  instantiateCompilerNode(n);
167  return n;
168 }
169 
174 {
175  VuoNode *n = getBase()->newNode(nodeToCopyMetadataFrom);
176  instantiateCompilerNode(n);
177  return n;
178 }
179 
185 void VuoCompilerNodeClass::instantiateCompilerNode(VuoNode *node)
186 {
187  // Instantiate compiler ports.
188  vector<VuoPort *> inputPorts = node->getInputPorts();
189  vector<VuoPort *> outputPorts = node->getOutputPorts();
190  vector<VuoPort *> ports;
191  ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
192  ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
193 
194  for (vector<VuoPort *>::iterator i = ports.begin(); i != ports.end(); ++i)
195  {
196  VuoPort *basePort = *i;
197  VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(basePort->getClass()->getCompiler());
198  basePort->setCompiler(portClass->newPort(basePort));
199  }
200 
201  // Instantiate compiler node.
202  new VuoCompilerNode(node);
203 }
204 
209 {
210  return (new VuoCompilerNodeClass(baseNodeClass))->getBase();
211 }
212 
217 VuoNodeClass * VuoCompilerNodeClass::newNodeClass(string nodeClassName, Module * module)
218 {
219  if (! isNodeClass(module, nodeClassName))
220  return NULL;
221 
222  VuoCompilerNodeClass * cnc = new VuoCompilerNodeClass(nodeClassName, module);
223 
224  // Reconstruct, this time with a base VuoNodeClass containing actual (non-dummy) ports.
225  VuoCompilerNodeClass * cnc2 = new VuoCompilerNodeClass(cnc);
226  delete cnc;
227  return cnc2->getBase();
228 }
229 
234 bool VuoCompilerNodeClass::isNodeClass(Module *module, string moduleKey)
235 {
236  return hasOriginalOrMangledGlobal("nodeEvent", module, moduleKey) ||
237  hasOriginalOrMangledGlobal("nodeInstanceEvent", module, moduleKey);
238 }
239 
243 void VuoCompilerNodeClass::parse(void)
244 {
246 
247  parseEventFunction();
248  if (instanceDataClass)
249  {
250  parseInitFunction();
251  parseFiniFunction();
252  parseCallbackStartFunction();
253  parseCallbackUpdateFunction();
254  parseCallbackStopFunction();
255  }
256 
257  // Add each non-generic port type as a dependency.
258  vector<VuoPortClass *> portClasses;
259  vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
260  vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
261  portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
262  portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
263  for (vector<VuoPortClass *>::iterator i = portClasses.begin(); i != portClasses.end(); ++i)
264  {
265  VuoType *type = static_cast<VuoCompilerPortClass *>((*i)->getCompiler())->getDataVuoType();
266  if (type && ! dynamic_cast<VuoGenericType *>(type))
267  {
268  string typeName = type->getModuleKey();
269  dependencies.insert(typeName);
270  string innermostTypeName = VuoType::extractInnermostTypeName(typeName);
271  dependencies.insert(innermostTypeName);
272  }
273  }
274 
275  updateSubcompositionStatus();
276 }
277 
281 set<string> VuoCompilerNodeClass::globalsToRename(void)
282 {
283  set<string> globals = VuoCompilerModule::globalsToRename();
284  globals.insert("nodeEvent");
285  globals.insert("nodeInstanceEvent");
286  globals.insert("nodeInstanceInit");
287  globals.insert("nodeInstanceFini");
288  globals.insert("nodeInstanceTriggerStart");
289  globals.insert("nodeInstanceTriggerUpdate");
290  globals.insert("nodeInstanceTriggerStop");
291  return globals;
292 }
293 
297 void VuoCompilerNodeClass::parseMetadata(void)
298 {
300 
301  json_object *nodeDetails = NULL;
302  if (json_object_object_get_ex(moduleDetails, "node", &nodeDetails))
303  {
304  getBase()->setExampleCompositionFileNames(VuoJsonUtilities::parseArrayOfStrings(nodeDetails, "exampleCompositions"));
305  getBase()->setDeprecated(VuoJsonUtilities::parseBool(nodeDetails, "isDeprecated"));
306 
307 #if VUO_PRO
308  setDependsOnPro(VuoJsonUtilities::parseBool(nodeDetails, "dependsOnPro"));
309 #endif
310 
311  json_object *triggersArray = NULL;
312  if (json_object_object_get_ex(nodeDetails, "triggers", &triggersArray))
313  triggerDescriptions = VuoCompilerTriggerDescription::parseFromJson(triggersArray);
314 
315  json_object *nodesObj = NULL;
316  if (json_object_object_get_ex(nodeDetails, "nodes", &nodesObj))
317  {
318  json_object_object_foreach(nodesObj, nodeIdentifier, nodeClassNameObj)
319  {
320  string nodeClassName = json_object_get_string(nodeClassNameObj);
321  containedNodes.insert(make_pair(nodeIdentifier, nodeClassName));
322  }
323  }
324  }
325 
326  parseGenericTypes(moduleDetails, defaultSpecializedForGenericTypeName, compatibleSpecializedForGenericTypeName);
327 }
328 
335  map<string, string> &defaultSpecializedForGenericTypeName,
336  map<string, vector<string> > &compatibleSpecializedForGenericTypeName)
337 {
338  json_object *genericTypeDetails = NULL;
339  if (json_object_object_get_ex(moduleDetails, "genericTypes", &genericTypeDetails))
340  {
341  json_object_object_foreach(genericTypeDetails, genericTypeName, genericTypeDetailsForOneType)
342  {
343  string defaultType = VuoJsonUtilities::parseString(genericTypeDetailsForOneType, "defaultType");
344  if (! defaultType.empty())
345  defaultSpecializedForGenericTypeName[genericTypeName] = defaultType;
346 
347  vector<string> compatibleTypes = VuoJsonUtilities::parseArrayOfStrings(genericTypeDetailsForOneType, "compatibleTypes");
348  if (! compatibleTypes.empty())
349  compatibleSpecializedForGenericTypeName[genericTypeName] = compatibleTypes;
350  }
351  }
352 }
353 
360 void VuoCompilerNodeClass::parseEventFunction(void)
361 {
362  eventFunction = parser->getFunction(nameForGlobal("nodeEvent"));
363  VuoNodeArgumentAcceptance acceptsInstanceData = INSTANCE_DATA_ABSENT;
364 
365  if (! eventFunction)
366  {
367  eventFunction = parser->getFunction(nameForGlobal("nodeInstanceEvent"));
368  acceptsInstanceData = INSTANCE_DATA_PRESENT;
369 
370  if (! eventFunction)
371  {
372  VUserLog("Error: Node class '%s' is missing function nodeEvent or nodeInstanceEvent.", getBase()->getClassName().c_str());
373  return;
374  }
375  }
376 
377  parseParameters(eventFunction,
378  INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
379  OUTPUT_DATA_ABSENT | OUTPUT_DATA_PRESENT |
380  INPUT_EVENT_ABSENT | INPUT_EVENT_PRESENT |
381  OUTPUT_EVENT_ABSENT | OUTPUT_EVENT_PRESENT |
382  OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
383  acceptsInstanceData);
384 }
385 
392 void VuoCompilerNodeClass::parseInitFunction(void)
393 {
394  initFunction = parser->getFunction(nameForGlobal("nodeInstanceInit"));
395  if (! initFunction)
396  return;
397 
398  parseParameters(initFunction,
399  INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
400  OUTPUT_DATA_ABSENT |
401  INPUT_EVENT_ABSENT |
402  OUTPUT_EVENT_ABSENT |
403  OUTPUT_TRIGGER_ABSENT |
404  INSTANCE_DATA_ABSENT);
405 }
406 
410 void VuoCompilerNodeClass::parseFiniFunction(void)
411 {
412  finiFunction = parser->getFunction(nameForGlobal("nodeInstanceFini"));
413 }
414 
424 void VuoCompilerNodeClass::parseCallbackStartFunction(void)
425 {
426  callbackStartFunction = parser->getFunction(nameForGlobal("nodeInstanceTriggerStart"));
427  if (! callbackStartFunction)
428  return;
429 
430  parseParameters(callbackStartFunction,
431  INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
432  OUTPUT_DATA_ABSENT |
433  INPUT_EVENT_ABSENT |
434  OUTPUT_EVENT_ABSENT |
435  OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
436  INSTANCE_DATA_PRESENT);
437 }
438 
448 void VuoCompilerNodeClass::parseCallbackUpdateFunction(void)
449 {
450  callbackUpdateFunction = parser->getFunction(nameForGlobal("nodeInstanceTriggerUpdate"));
451  if (! callbackUpdateFunction)
452  return;
453 
454  parseParameters(callbackUpdateFunction,
455  INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
456  OUTPUT_DATA_ABSENT |
457  INPUT_EVENT_ABSENT |
458  OUTPUT_EVENT_ABSENT |
459  OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
460  INSTANCE_DATA_PRESENT);
461 }
462 
466 void VuoCompilerNodeClass::parseCallbackStopFunction(void)
467 {
468  callbackStopFunction = parser->getFunction(nameForGlobal("nodeInstanceTriggerStop"));
469  if (! callbackStopFunction)
470  return;
471 
472  parseParameters(callbackStopFunction,
473  INPUT_DATA_ABSENT |
474  OUTPUT_DATA_ABSENT |
475  INPUT_EVENT_ABSENT |
476  OUTPUT_EVENT_ABSENT |
477  OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
478  INSTANCE_DATA_PRESENT);
479 }
480 
488 void VuoCompilerNodeClass::parseParameters(Function *function, unsigned long acceptanceFlags)
489 {
490  vector<pair<Argument *, string> > annotatedArguments = parser->getAnnotatedArguments(function);
491 
492  vector<VuoCompilerNodeArgumentClass *> inputArgumentClasses;
493  vector<VuoCompilerNodeArgumentClass *> outputArgumentClasses;
494  map<string, VuoType *> vuoTypeForArgumentName;
495  map<string, json_object *> detailsForArgumentName;
496  map<string, VuoCompilerPortClass *> portClassForArgumentName;
497  map<string, VuoCompilerDataClass *> dataClassForArgumentName;
498 
499  bool sawInputData = false;
500  bool sawOutputData = false;
501  bool sawInputEvent = false;
502  bool sawOutputEvent = false;
503  bool sawOutputTrigger = false;
504  bool sawInstanceData = false;
505 
506  // Create the node argument class for each annotated argument.
507  for (vector<pair<Argument *, string> >::iterator i = annotatedArguments.begin(); i != annotatedArguments.end(); ++i)
508  {
509  Argument *argument = i->first;
510  string annotation = i->second;
511  string argumentName = parser->getArgumentNameInSourceCode(argument->getName());
512 
513  VuoCompilerNodeArgumentClass *argumentClass = NULL;
514  VuoPortClass *existingPortClass = NULL;
515  json_object *details = NULL;
516  VuoType *type = NULL;
517 
518  if ((argumentClass = parseInputDataParameter(annotation, argument)) != NULL)
519  {
520  existingPortClass = getExistingPortClass(argumentClass, true);
521  if (! existingPortClass)
522  {
523  inputArgumentClasses.push_back(argumentClass);
524  dataClassForArgumentName[argumentName] = static_cast<VuoCompilerDataClass *>(argumentClass);
525  }
526 
527  sawInputData = true;
528  }
529  else if ((argumentClass = parseOutputDataParameter(annotation, argument)) != NULL)
530  {
531  existingPortClass = getExistingPortClass(argumentClass, false);
532  if (! existingPortClass)
533  {
534  outputArgumentClasses.push_back(argumentClass);
535  dataClassForArgumentName[argumentName] = static_cast<VuoCompilerDataClass *>(argumentClass);
536  }
537 
538  sawOutputData = true;
539  }
540  else if ((argumentClass = parseInputEventParameter(annotation, argument)) != NULL)
541  {
542  existingPortClass = getExistingPortClass(argumentClass, true);
543  if (! existingPortClass)
544  {
545  inputArgumentClasses.push_back(argumentClass);
546  portClassForArgumentName[argumentName] = static_cast<VuoCompilerPortClass *>(argumentClass);
547  }
548 
549  sawInputEvent = true;
550  }
551  else if ((argumentClass = parseOutputEventParameter(annotation, argument)) != NULL)
552  {
553  existingPortClass = getExistingPortClass(argumentClass, false);
554  if (! existingPortClass)
555  {
556  outputArgumentClasses.push_back(argumentClass);
557  portClassForArgumentName[argumentName] = static_cast<VuoCompilerPortClass *>(argumentClass);
558  }
559 
560  sawOutputEvent = true;
561  }
562  else if ((argumentClass = parseTriggerParameter(annotation, argument)) != NULL)
563  {
564  existingPortClass = getExistingPortClass(argumentClass, false);
565  if (! existingPortClass)
566  {
567  outputArgumentClasses.push_back(argumentClass);
568  portClassForArgumentName[argumentName] = static_cast<VuoCompilerPortClass *>(argumentClass);
569  }
570 
571  sawOutputTrigger = true;
572  }
573  else if ((argumentClass = parseInstanceDataParameter(annotation, argument)) != NULL)
574  {
575  if (instanceDataClass)
576  existingPortClass = instanceDataClass->getBase();
577  else
578  instanceDataClass = static_cast<VuoCompilerInstanceDataClass *>(argumentClass);
579 
580  sawInstanceData = true;
581  }
582  else if ((type = parseTypeParameter(annotation)) != NULL)
583  {
584  vuoTypeForArgumentName[argumentName] = type;
585  }
586  else if ((details = parseDetailsParameter(annotation)) != NULL)
587  {
588  detailsForArgumentName[argumentName] = details;
589  }
590 
591  VuoCompilerNodeArgumentClass *argumentClassInNodeClass = (existingPortClass ? existingPortClass->getCompiler() : argumentClass);
592  if (argumentClassInNodeClass)
593  {
594  size_t argumentIndex = argument->getArgNo();
595  if (function == eventFunction)
596  argumentClassInNodeClass->setIndexInEventFunction(argumentIndex);
597  else if (function == initFunction)
598  argumentClassInNodeClass->setIndexInInitFunction(argumentIndex);
599  else if (function == callbackStartFunction)
600  argumentClassInNodeClass->setIndexInCallbackStartFunction(argumentIndex);
601  else if (function == callbackUpdateFunction)
602  argumentClassInNodeClass->setIndexInCallbackUpdateFunction(argumentIndex);
603  else if (function == callbackStopFunction)
604  argumentClassInNodeClass->setIndexInCallbackStopFunction(argumentIndex);
605  }
606 
607  if (existingPortClass)
608  delete argumentClass;
609  }
610 
611  // Check that all required arguments and no disallowed arguments are present.
612  {
613  string functionName = function->getName().str();
614  string wronglyAbsentMessage = " is required in " + functionName;
615  string wronglyPresentMessage = " is not allowed in " + functionName;
616 
617  if (sawInputData && ! (acceptanceFlags & INPUT_DATA_PRESENT))
618  VUserLog("Error: %s", ("VuoInputData" + wronglyPresentMessage).c_str());
619  if (sawOutputData && ! (acceptanceFlags & OUTPUT_DATA_PRESENT))
620  VUserLog("Error: %s", ("VuoOutputData" + wronglyPresentMessage).c_str());
621  if (sawInputEvent && ! (acceptanceFlags & INPUT_EVENT_PRESENT))
622  VUserLog("Error: %s", ("VuoInputEvent" + wronglyPresentMessage).c_str());
623  if (sawOutputEvent && ! (acceptanceFlags & OUTPUT_EVENT_PRESENT))
624  VUserLog("Error: %s", ("VuoOutputEvent" + wronglyPresentMessage).c_str());
625  if (sawOutputTrigger && ! (acceptanceFlags & OUTPUT_TRIGGER_PRESENT))
626  VUserLog("Error: %s", ("VuoOutputTrigger" + wronglyPresentMessage).c_str());
627  if (sawInstanceData && ! (acceptanceFlags & INSTANCE_DATA_PRESENT))
628  VUserLog("Error: %s", ("VuoInstanceData" + wronglyPresentMessage).c_str());
629 
630  if (! sawInputData && ! (acceptanceFlags & INPUT_DATA_ABSENT))
631  VUserLog("Error: %s", ("VuoInputData" + wronglyAbsentMessage).c_str());
632  if (! sawOutputData && ! (acceptanceFlags & OUTPUT_DATA_ABSENT))
633  VUserLog("Error: %s", ("VuoOutputData" + wronglyAbsentMessage).c_str());
634  if (! sawInputEvent && ! (acceptanceFlags & INPUT_EVENT_ABSENT))
635  VUserLog("Error: %s", ("VuoInputEvent" + wronglyAbsentMessage).c_str());
636  if (! sawOutputEvent && ! (acceptanceFlags & OUTPUT_EVENT_ABSENT))
637  VUserLog("Error: %s", ("VuoOutputEvent" + wronglyAbsentMessage).c_str());
638  if (! sawOutputTrigger && ! (acceptanceFlags & OUTPUT_TRIGGER_ABSENT))
639  VUserLog("Error: %s", ("VuoOutputTrigger" + wronglyAbsentMessage).c_str());
640  if (! sawInstanceData && ! (acceptanceFlags & INSTANCE_DATA_ABSENT))
641  VUserLog("Error: %s", ("VuoInstanceData" + wronglyAbsentMessage).c_str());
642  }
643 
644  // For each event portion of a data-and-event port, find the corresponding data portion. Rename the event portion to match.
645  map<string, VuoCompilerInputEventPortClass *> inputEventPortClassForDataClassName;
646  for (map<string, VuoCompilerPortClass *>::iterator i = portClassForArgumentName.begin(); i != portClassForArgumentName.end(); ++i)
647  {
648  string argumentName = i->first;
649  VuoCompilerInputEventPortClass *eventPortClass = dynamic_cast<VuoCompilerInputEventPortClass *>( i->second );
650 
651  if (eventPortClass)
652  {
653  bool isDataInDetails = false;
654  string dataPortName = VuoJsonUtilities::parseString(detailsForArgumentName[argumentName], "data", "", &isDataInDetails);
655  if (isDataInDetails)
656  {
657  eventPortClass->getBase()->setName(dataPortName);
658  inputEventPortClassForDataClassName[dataPortName] = eventPortClass;
659  }
660  }
661  }
662  map<string, VuoCompilerOutputEventPortClass *> outputEventPortClassForDataClassName;
663  for (map<string, VuoCompilerPortClass *>::iterator i = portClassForArgumentName.begin(); i != portClassForArgumentName.end(); ++i)
664  {
665  string argumentName = i->first;
666  VuoCompilerOutputEventPortClass *eventPortClass = dynamic_cast<VuoCompilerOutputEventPortClass *>( i->second );
667 
668  if (eventPortClass)
669  {
670  bool isDataInDetails = false;
671  string dataPortName = VuoJsonUtilities::parseString(detailsForArgumentName[argumentName], "data", "", &isDataInDetails);
672  if (isDataInDetails)
673  {
674  eventPortClass->getBase()->setName(dataPortName);
675  outputEventPortClassForDataClassName[dataPortName] = eventPortClass;
676  }
677  }
678  }
679 
680  // Match up data classes with event port classes. Add event and trigger port classes to addedInputPortClasses/addedOutputPortClasses.
681  vector<VuoPortClass *> addedInputPortClasses;
682  for (vector<VuoCompilerNodeArgumentClass *>::iterator i = inputArgumentClasses.begin(); i != inputArgumentClasses.end(); ++i)
683  {
684  VuoCompilerInputDataClass *dataClass = dynamic_cast<VuoCompilerInputDataClass *>(*i);
685  if (dataClass)
686  {
687  VuoCompilerInputEventPortClass *eventPortClass = inputEventPortClassForDataClassName[dataClass->getBase()->getName()];
688 
689  if (! eventPortClass)
690  {
691  eventPortClass = new VuoCompilerInputEventPortClass(dataClass->getBase()->getName());
692  eventPortClass->setDataClass(dataClass);
693  addedInputPortClasses.push_back(eventPortClass->getBase());
694  }
695  else
696  {
697  // Since setDataClass() changes the base, we need to remove the old base and add the new one.
698  vector<VuoPortClass *>::iterator oldBase = find(addedInputPortClasses.begin(), addedInputPortClasses.end(), eventPortClass->getBase());
699  eventPortClass->setDataClass(dataClass);
700  if (oldBase != addedInputPortClasses.end())
701  {
702  addedInputPortClasses.insert(oldBase,eventPortClass->getBase());
703  addedInputPortClasses.erase(oldBase);
704  }
705  }
706  }
707  else
708  addedInputPortClasses.push_back(((VuoCompilerPortClass *)(*i))->getBase());
709  }
710  vector<VuoPortClass *> addedOutputPortClasses;
711  for (vector<VuoCompilerNodeArgumentClass *>::iterator i = outputArgumentClasses.begin(); i != outputArgumentClasses.end(); ++i)
712  {
713  VuoCompilerOutputDataClass *dataClass = dynamic_cast<VuoCompilerOutputDataClass *>(*i);
714  if (dataClass)
715  {
716  VuoCompilerOutputEventPortClass *eventPortClass = outputEventPortClassForDataClassName[dataClass->getBase()->getName()];
717  if (! eventPortClass)
718  {
719  eventPortClass = new VuoCompilerOutputEventPortClass(dataClass->getBase()->getName());
720  eventPortClass->setDataClass(dataClass);
721  addedOutputPortClasses.push_back(eventPortClass->getBase());
722  }
723  else
724  {
725  // Since setDataClass() changes the base, we need to remove the old base and add the new one.
726  vector<VuoPortClass *>::iterator oldBase = find(addedOutputPortClasses.begin(), addedOutputPortClasses.end(), eventPortClass->getBase());
727  eventPortClass->setDataClass(dataClass);
728  if (oldBase != addedOutputPortClasses.end())
729  {
730  addedOutputPortClasses.insert(oldBase,eventPortClass->getBase());
731  addedOutputPortClasses.erase(oldBase);
732  }
733  }
734  }
735  else
736  addedOutputPortClasses.push_back(((VuoCompilerPortClass *)(*i))->getBase());
737  }
738 
739  // Set the refresh port class if it hasn't been already (from a previous function).
740  if (! getBase()->getRefreshPortClass()->hasCompiler())
741  {
742  VuoPortClass *refreshPortClass = (new VuoCompilerInputEventPortClass("refresh"))->getBase();
743 
744  // Remove the existing dummy refresh port class.
745  vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
746  inputPortClasses.erase(inputPortClasses.begin());
747  getBase()->setInputPortClasses(inputPortClasses);
748 
749  // Set the new refresh port class.
750  getBase()->setRefreshPortClass(refreshPortClass);
751  addedInputPortClasses.insert(addedInputPortClasses.begin(), refreshPortClass);
752  }
753 
754  // Append the port classes defined in this function to the node class's list of port classes.
755  vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
756  inputPortClasses.insert(inputPortClasses.end(), addedInputPortClasses.begin(), addedInputPortClasses.end());
757  getBase()->setInputPortClasses(inputPortClasses);
758  vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
759  outputPortClasses.insert(outputPortClasses.end(), addedOutputPortClasses.begin(), addedOutputPortClasses.end());
760  getBase()->setOutputPortClasses(outputPortClasses);
761 
762  // Set the VuoType for each added port.
763  vector<VuoPortClass *> portClasses;
764  portClasses.insert(portClasses.end(), addedInputPortClasses.begin(), addedInputPortClasses.end());
765  portClasses.insert(portClasses.end(), addedOutputPortClasses.begin(), addedOutputPortClasses.end());
766  for (vector<VuoPortClass *>::iterator i = portClasses.begin(); i != portClasses.end(); ++i)
767  {
768  VuoCompilerEventPortClass *eventPortClass = dynamic_cast<VuoCompilerEventPortClass *>((*i)->getCompiler());
769  if (eventPortClass)
770  {
771  VuoCompilerDataClass *dataClass = eventPortClass->getDataClass();
772  if (dataClass)
773  {
774  string dataClassName = dataClass->getBase()->getName();
775  map<string, VuoType *>::iterator vuoTypeIter = vuoTypeForArgumentName.find(dataClassName);
776  if (vuoTypeIter != vuoTypeForArgumentName.end())
777  {
778  dataClass->setVuoType(vuoTypeIter->second);
779  vuoTypeForArgumentName.erase(vuoTypeIter);
780  }
781  }
782  }
783 
784  VuoCompilerTriggerPortClass *triggerPortClass = dynamic_cast<VuoCompilerTriggerPortClass *>((*i)->getCompiler());
785  if (triggerPortClass)
786  {
787  string triggerName = triggerPortClass->getBase()->getName();
788  map<string, VuoType *>::iterator vuoTypeIter = vuoTypeForArgumentName.find(triggerName);
789  if (vuoTypeIter != vuoTypeForArgumentName.end())
790  {
791  triggerPortClass->setDataVuoType(vuoTypeIter->second);
792  vuoTypeForArgumentName.erase(vuoTypeIter);
793  }
794  }
795  }
796  for (map<string, VuoType *>::iterator i = vuoTypeForArgumentName.begin(); i != vuoTypeForArgumentName.end(); ++i)
797  delete i->second;
798 
799  // Set the details for each added port.
800  for (map<string, json_object *>::iterator i = detailsForArgumentName.begin(); i != detailsForArgumentName.end(); ++i)
801  {
802  string argumentName = i->first;
803  json_object *details = i->second;
804 
805  VuoCompilerPortClass *portClass = portClassForArgumentName[argumentName];
806  if (portClass)
807  portClass->setDetails(details);
808  else
809  {
810  VuoCompilerDataClass *dataClass = dataClassForArgumentName[argumentName];
811  if (dataClass)
812  dataClass->setDetails(details);
813  }
814  }
815 
816  // Set the event-blocking behavior for each added input port.
817  for (vector<VuoPortClass *>::iterator i = addedInputPortClasses.begin(); i != addedInputPortClasses.end(); ++i)
818  {
819  VuoCompilerInputEventPortClass *eventPortClass = dynamic_cast<VuoCompilerInputEventPortClass *>( (*i)->getCompiler() );
820  bool isEventBlockingInDetails = false;
821  string eventBlockingStr = VuoJsonUtilities::parseString(eventPortClass->getDetails(), "eventBlocking", "", &isEventBlockingInDetails);
822  if (isEventBlockingInDetails)
823  {
824  VuoPortClass::EventBlocking eventBlocking;
825  if (eventBlockingStr == "none")
826  eventBlocking = VuoPortClass::EventBlocking_None;
827  else if (eventBlockingStr == "door")
828  eventBlocking = VuoPortClass::EventBlocking_Door;
829  else if (eventBlockingStr == "wall")
830  eventBlocking = VuoPortClass::EventBlocking_Wall;
831  else
832  {
833  VUserLog("Error: Unknown option for \"eventBlocking\": %s", eventBlockingStr.c_str());
834  continue;
835  }
836  eventPortClass->getBase()->setEventBlocking(eventBlocking);
837 
838  if (eventBlocking == VuoPortClass::EventBlocking_None)
839  portsWithExplicitEventBlockingNone.insert(eventPortClass);
840  }
841  }
842 
843  // Set the event-throttling behavior for each added trigger port.
844  for (vector<VuoPortClass *>::iterator i = addedOutputPortClasses.begin(); i != addedOutputPortClasses.end(); ++i)
845  {
846  VuoCompilerTriggerPortClass *triggerPortClass = dynamic_cast<VuoCompilerTriggerPortClass *>( (*i)->getCompiler() );
847  if (triggerPortClass)
848  {
849  bool isEventThrottlingInDetails = false;
850  string eventThrottlingStr = VuoJsonUtilities::parseString(triggerPortClass->getDetails(), "eventThrottling", "", &isEventThrottlingInDetails);
851  if (isEventThrottlingInDetails)
852  {
853  VuoPortClass::EventThrottling eventThrottling;
854  if (eventThrottlingStr == "enqueue")
855  eventThrottling = VuoPortClass::EventThrottling_Enqueue;
856  else if (eventThrottlingStr == "drop")
857  eventThrottling = VuoPortClass::EventThrottling_Drop;
858  else
859  {
860  VUserLog("Error: Unknown option for \"throttling\": %s", eventThrottlingStr.c_str());
861  continue;
862  }
863  triggerPortClass->getBase()->setDefaultEventThrottling(eventThrottling);
864  }
865  }
866  }
867 
868  // Update the port action status for each input port.
869  for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
870  {
871  VuoPortClass *portClass = *i;
872  VuoCompilerInputEventPortClass *eventPortClass = static_cast<VuoCompilerInputEventPortClass *>((*i)->getCompiler());
873  if (eventPortClass->getBase() != getBase()->getRefreshPortClass())
874  {
875  bool isPortActionInDetails = false;
876  bool hasPortAction = VuoJsonUtilities::parseBool(eventPortClass->getDetails(), "hasPortAction", false, &isPortActionInDetails);
877 
878  if (isPortActionInDetails)
879  portClass->setPortAction(hasPortAction);
880  else if (! eventPortClass->getDataClass() ||
881  portsWithExplicitEventBlockingNone.find(eventPortClass) != portsWithExplicitEventBlockingNone.end())
882  portClass->setPortAction(true);
883  }
884  }
885 
886  // Set whether each added input port's data is passed as an unlowered struct pointer.
887  for (VuoPortClass *portClass : addedInputPortClasses)
888  {
889  if (portClass->getPortType() == VuoPortClass::dataAndEventPort)
890  {
891  VuoCompilerInputEventPortClass *eventPortClass = static_cast<VuoCompilerInputEventPortClass *>(portClass->getCompiler());
892  VuoCompilerInputDataClass *dataClass = eventPortClass->getDataClass();
893 
894  if (function == eventFunction)
895  {
896  Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInEventFunction() );
897  dataClass->setUnloweredStructPointerInEventFunction(firstParameterType);
898  }
899  else if (function == initFunction)
900  {
901  Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInInitFunction() );
902  dataClass->setUnloweredStructPointerInInitFunction(firstParameterType);
903  }
904  else if (function == callbackStartFunction)
905  {
906  Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInCallbackStartFunction() );
907  dataClass->setUnloweredStructPointerInCallbackStartFunction(firstParameterType);
908  }
909  else if (function == callbackUpdateFunction)
910  {
911  Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInCallbackUpdateFunction() );
912  dataClass->setUnloweredStructPointerInCallbackUpdateFunction(firstParameterType);
913  }
914  else if (function == callbackStopFunction)
915  {
916  Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInCallbackStopFunction() );
917  dataClass->setUnloweredStructPointerInCallbackStopFunction(firstParameterType);
918  }
919  }
920  }
921 }
922 
926 VuoCompilerInputDataClass * VuoCompilerNodeClass::parseInputDataParameter(string annotation, Argument *a)
927 {
928  if (annotation != "vuoInputData")
929  return NULL;
930 
931  string argumentName = parser->getArgumentNameInSourceCode(a->getName());
932  return new VuoCompilerInputDataClass(argumentName);
933 }
934 
938 VuoCompilerOutputDataClass * VuoCompilerNodeClass::parseOutputDataParameter(string annotation, Argument *a)
939 {
940  if (annotation != "vuoOutputData")
941  return NULL;
942 
943  string argumentName = parser->getArgumentNameInSourceCode(a->getName());
944  if (! a->getType()->isPointerTy())
945  {
946  VUserLog("Error: Output port data %s must be a pointer.", argumentName.c_str());
947  return NULL;
948  }
949 
950  return new VuoCompilerOutputDataClass(argumentName);
951 }
952 
956 VuoCompilerInputEventPortClass * VuoCompilerNodeClass::parseInputEventParameter(string annotation, Argument *a)
957 {
958  if (! VuoStringUtilities::beginsWith(annotation, "vuoInputEvent"))
959  return NULL;
960 
961  string argumentName = parser->getArgumentNameInSourceCode(a->getName());
962 
963  return new VuoCompilerInputEventPortClass(argumentName);
964 }
965 
969 VuoCompilerOutputEventPortClass * VuoCompilerNodeClass::parseOutputEventParameter(string annotation, Argument *a)
970 {
971  if (! VuoStringUtilities::beginsWith(annotation, "vuoOutputEvent"))
972  return NULL;
973 
974  string argumentName = parser->getArgumentNameInSourceCode(a->getName());
975  if (! a->getType()->isPointerTy())
976  {
977  VUserLog("Error: Output port %s must be a pointer.", argumentName.c_str());
978  return NULL;
979  }
980 
981  return new VuoCompilerOutputEventPortClass(argumentName);
982 }
983 
987 VuoCompilerTriggerPortClass * VuoCompilerNodeClass::parseTriggerParameter(string annotation, Argument *a)
988 {
989  if (! VuoStringUtilities::beginsWith(annotation, "vuoOutputTrigger:"))
990  return NULL;
991 
992  string argumentName = parser->getArgumentNameInSourceCode(a->getName());
993  if (! a->getType()->isPointerTy())
994  {
995  VUserLog("Error: Output trigger %s must be a pointer.", argumentName.c_str());
996  return NULL;
997  }
998 
999  return new VuoCompilerTriggerPortClass(argumentName);
1000 }
1001 
1005 VuoCompilerInstanceDataClass * VuoCompilerNodeClass::parseInstanceDataParameter(string annotation, Argument *a)
1006 {
1007  if (annotation != "vuoInstanceData")
1008  return NULL;
1009 
1010  string argumentName = parser->getArgumentNameInSourceCode(a->getName());
1011  if (! a->getType()->isPointerTy())
1012  {
1013  VUserLog("Error: Node instance data %s must be a pointer.", argumentName.c_str());
1014  return NULL;
1015  }
1016 
1017  Type *instanceDataType = static_cast<PointerType *>(a->getType())->getElementType();
1018  return new VuoCompilerInstanceDataClass(argumentName, instanceDataType);
1019 }
1020 
1024 VuoType * VuoCompilerNodeClass::parseTypeParameter(string annotation)
1025 {
1026  if (! VuoStringUtilities::beginsWith(annotation, "vuoType:"))
1027  return NULL;
1028 
1029  string typeName = VuoStringUtilities::substrAfter(annotation, "vuoType:");
1030 
1031  VuoType *type;
1032  if (typeName == "void")
1033  {
1034  type = NULL;
1035  }
1036  else if (VuoGenericType::isGenericTypeName(typeName))
1037  {
1038  string innermostTypeName = VuoType::extractInnermostTypeName(typeName);
1039  vector<string> compatibleTypes;
1040  map<string, vector<string> >::iterator compatibleTypesIter = compatibleSpecializedForGenericTypeName.find(innermostTypeName);
1041  if (compatibleTypesIter != compatibleSpecializedForGenericTypeName.end())
1042  {
1043  string prefix = (VuoType::isListTypeName(typeName) ? VuoType::listTypeNamePrefix : "");
1044  vector<string> innermostCompatibleTypes = compatibleTypesIter->second;
1045  for (vector<string>::iterator i = innermostCompatibleTypes.begin(); i != innermostCompatibleTypes.end(); ++i)
1046  compatibleTypes.push_back(prefix + *i);
1047  }
1048 
1049  type = new VuoGenericType(typeName, compatibleTypes);
1050  }
1051  else
1052  {
1053  type = new VuoType(typeName);
1054  }
1055  return type;
1056 }
1057 
1061 json_object * VuoCompilerNodeClass::parseDetailsParameter(string annotation)
1062 {
1063  if (! VuoStringUtilities::beginsWith(annotation, "vuoDetails:"))
1064  return NULL;
1065 
1066  json_object *detailsObj = NULL;
1067  string details = VuoStringUtilities::substrAfter(annotation, "vuoDetails:");
1068  if (details.find_first_not_of(' ') != string::npos)
1069  {
1070  detailsObj = json_tokener_parse(details.c_str());
1071  if (! detailsObj)
1072  VUserLog("Error: Couldn't parse vuoDetails for `%s`: %s", getBase()->getClassName().c_str(), details.c_str());
1073  }
1074  return detailsObj;
1075 }
1076 
1081 VuoPortClass * VuoCompilerNodeClass::getExistingPortClass(VuoCompilerNodeArgumentClass *argumentClass, bool isInput)
1082 {
1083  string argumentName = argumentClass->getBase()->getName();
1084  VuoPortClass *existingInputPortClass = getInputPortClassWithName(argumentName);
1085  VuoPortClass *existingOutputPortClass = getOutputPortClassWithName(argumentName);
1086  if (existingInputPortClass || existingOutputPortClass)
1087  {
1088  if (! isInput && getBase()->getClassName() == VuoNodeClass::publishedInputNodeClassName)
1089  {
1090  if (! existingOutputPortClass)
1091  return NULL;
1092  }
1093  else if ((isInput && existingOutputPortClass) || (! isInput && existingInputPortClass))
1094  {
1095  VUserLog("Error: Port %s is declared as an input port in one function and an output port in another function.", argumentName.c_str());
1096  return NULL;
1097  }
1098 
1099  VuoPortClass *existingPortClass = (existingInputPortClass ? existingInputPortClass : existingOutputPortClass);
1100  if (dynamic_cast<VuoCompilerDataClass *>(argumentClass))
1101  existingPortClass = dynamic_cast<VuoCompilerEventPortClass *>(existingPortClass->getCompiler())->getDataClass()->getBase();
1102 
1103  return existingPortClass;
1104  }
1105  else
1106  {
1107  return NULL;
1108  }
1109 }
1110 
1119 {
1120  return VuoStringUtilities::transcodeToIdentifier(getBase()->getClassName());
1121 }
1122 
1127 {
1128  return eventFunction;
1129 }
1130 
1135 {
1136  return initFunction;
1137 }
1138 
1143 {
1144  return finiFunction;
1145 }
1146 
1151 {
1152  return callbackStartFunction;
1153 }
1154 
1159 {
1160  return callbackUpdateFunction;
1161 }
1162 
1167 {
1168  return callbackStopFunction;
1169 }
1170 
1175 {
1176  return parser->getFunction(nameForGlobal("compositionAddNodeMetadata"));
1177 }
1178 
1183 {
1184  return parser->getFunction(nameForGlobal("compositionPerformDataOnlyTransmissions"));
1185 }
1186 
1191 {
1192  return parser->getFunction(nameForGlobal("compositionSetPublishedInputPortValue"));
1193 }
1194 
1198 Function * VuoCompilerNodeClass::getTriggerWorkerFunction(string portIdentifier)
1199 {
1201 }
1202 
1206 vector<VuoCompilerTriggerDescription *> VuoCompilerNodeClass::getTriggerDescriptions(void)
1207 {
1208  return triggerDescriptions;
1209 }
1210 
1215 {
1216  vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
1217  for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
1218  if ((*i)->getName() == portName)
1219  return *i;
1220 
1221  return NULL;
1222 }
1223 
1228 {
1229  vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
1230  for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
1231  if ((*i)->getName() == portName)
1232  return *i;
1233 
1234  return NULL;
1235 }
1236 
1241 {
1242  return instanceDataClass;
1243 }
1244 
1249 {
1250  ostringstream documentation;
1251 
1252  documentation << "/**" << endl;
1253 
1254  string description = getBase()->getDescription();
1255  // Description is empty for nodes in the SDK's framework,
1256  // since the `descriptions/` folder is removed from each node set.
1257  if (!description.empty())
1258  {
1259  documentation << " * " << description << endl;
1260  documentation << " * " << endl;
1261  }
1262 
1263  vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
1264  vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
1265 
1266  vector< pair<string, VuoPortClass *> > portClasses;
1267  for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
1268  {
1269  portClasses.push_back( make_pair("in", *i) );
1270  }
1271  for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
1272  {
1273  bool isTrigger = dynamic_cast<VuoCompilerTriggerPortClass *>((*i)->getCompiler());
1274  portClasses.push_back( make_pair(isTrigger ? "gen" : "out", *i) );
1275  }
1276 
1277  for (vector< pair<string, VuoPortClass *> >::iterator i = portClasses.begin(); i != portClasses.end(); ++i)
1278  {
1279  string portKind = i->first;
1280  VuoPortClass *portClass = i->second;
1281  VuoCompilerPortClass *compilerPortClass = static_cast<VuoCompilerPortClass *>(portClass->getCompiler());
1282  VuoType *portType = compilerPortClass->getDataVuoType();
1283  string portTypeName = (portType ? portType->getModuleKey() : "event");
1284  string portClassName = portClass->getName();
1285  documentation << " * @param[" << portKind << "] " << portTypeName << " " << portClassName << endl;
1286  }
1287 
1288  documentation << " */";
1289 
1290  return documentation.str();
1291 }
1292 
1298 {
1299  map<string, string>::iterator typeNameIter = defaultSpecializedForGenericTypeName.find(genericTypeName);
1300  if (typeNameIter != defaultSpecializedForGenericTypeName.end())
1301  return typeNameIter->second;
1302 
1303  return "";
1304 }
1305 
1313 {
1314  vector<string> keywords;
1315 
1316  // Automatically add trigger-related keywords for nodes containing trigger ports.
1317  bool nodeHasTriggerPort = false;
1318  vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
1319  for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
1320  {
1321  if ((*i)->getPortType() == VuoPortClass::triggerPort)
1322  {
1323  nodeHasTriggerPort = true;
1324  break;
1325  }
1326  }
1327 
1328  if (nodeHasTriggerPort)
1329  {
1330  keywords.push_back("bang");
1331  keywords.push_back("events");
1332  keywords.push_back("trigger");
1333  keywords.push_back("fire");
1334  }
1335 
1336  bool nodeTitleBeginsWithSend = VuoStringUtilities::beginsWith(getBase()->getDefaultTitle(), "Send");
1337  bool nodeTitleBeginsWithReceive = VuoStringUtilities::beginsWith(getBase()->getDefaultTitle(), "Receive");
1338 
1339  if (nodeTitleBeginsWithSend || nodeTitleBeginsWithReceive)
1340  {
1341  keywords.push_back("i/o");
1342  keywords.push_back("interface");
1343 
1344  if (nodeTitleBeginsWithSend)
1345  {
1346  keywords.push_back("output");
1347  keywords.push_back("consumer");
1348  }
1349 
1350  if (nodeTitleBeginsWithReceive)
1351  {
1352  keywords.push_back("input");
1353  keywords.push_back("provider");
1354  }
1355  }
1356 
1357  if (VuoStringUtilities::beginsWith(getBase()->getClassName(), "vuo.type."))
1358  keywords.push_back("conversion");
1359 
1360  if (isSubcomposition())
1361  keywords.push_back("subcomposition");
1362 
1363  if (isLikelyImageFilter())
1364  keywords.push_back("filter");
1365 
1366  if (isLikelyImageGenerator())
1367  keywords.push_back("generator");
1368 
1370  keywords.push_back("transition");
1371 
1372 #if VUO_PRO
1373  if (getBase()->isPro())
1374  {
1375  keywords.push_back("premium");
1376  keywords.push_back("pro");
1377  }
1378 #endif
1379 
1380  return keywords;
1381 }
1382 
1388 {
1389  return ((getImagePortCount(true) == 1) && ((getImagePortCount(false) == 1)));
1390 }
1391 
1397 {
1398  return ((getImagePortCount(true) == 0) && ((getImagePortCount(false) == 1)));
1399 }
1400 
1406 {
1407  return ((getImagePortCount(true) == 2) && ((getImagePortCount(false) == 1)));
1408 }
1409 
1418 {
1419  int imagePortCount = 0;
1420 
1421  auto portsToSearch = isInput ? getBase()->getInputPortClasses() : getBase()->getOutputPortClasses();
1422  for (VuoPortClass *p : portsToSearch)
1423  {
1424  string name = p->getName();
1425  std::transform(name.begin(), name.end(), name.begin(), ::tolower);
1426  if (isInput && (name == "mask"))
1427  continue;
1428 
1429  VuoCompilerPortClass *cpc = dynamic_cast<VuoCompilerPortClass *>(p->getCompiler());
1430  if (!cpc)
1431  continue;
1432  VuoType *dataType = cpc->getDataVuoType();
1433  if (!dataType)
1434  continue;
1435  if (dataType->getModuleKey() == "VuoImage")
1436  imagePortCount++;
1437  }
1438 
1439  return imagePortCount;
1440 }
1441 
1446 {
1447  return (instanceDataClass != NULL);
1448 }
1449 
1454 {
1455  return _isSubcomposition;
1456 }
1457 
1458 void VuoCompilerNodeClass::updateSubcompositionStatus()
1459 {
1460  _isSubcomposition = parser && (getCompositionAddNodeMetadataFunction() != NULL);
1461 }
1462 
1467 {
1468  string dir, file, ext;
1469  VuoFileUtilities::splitPath(sourcePath, dir, file, ext);
1470  return ext == "fs";
1471 }
1472 
1476 void VuoCompilerNodeClass::setSourcePath(const string &sourcePath)
1477 {
1478  this->sourcePath = sourcePath;
1479 }
1480 
1485 {
1486  return sourcePath;
1487 }
1488 
1492 void VuoCompilerNodeClass::setSourceCode(const string &sourceCode)
1493 {
1494  this->sourceCode = sourceCode;
1495 }
1496 
1501 {
1502  return sourceCode;
1503 }
1504 
1508 set< pair<string, string> > VuoCompilerNodeClass::getContainedNodes(void)
1509 {
1510  return containedNodes;
1511 }