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