Vuo 2.4.2
Loading...
Searching...
No Matches
VuoCompilerNodeClass.cc
Go to the documentation of this file.
1
10#include <sstream>
15#include "VuoCompilerNode.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
55VuoCompilerNodeClass::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
160VuoNode * 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
185void 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
217VuoNodeClass * 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.
226 delete cnc;
227 return cnc2->getBase();
228}
229
234bool VuoCompilerNodeClass::isNodeClass(Module *module, string moduleKey)
235{
236 return hasOriginalOrMangledGlobal("nodeEvent", module, moduleKey) ||
237 hasOriginalOrMangledGlobal("nodeInstanceEvent", module, moduleKey);
238}
239
243void 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
281set<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
297void VuoCompilerNodeClass::parseMetadata(void)
298{
300
301 json_object *nodeDetails = NULL;
302 if (json_object_object_get_ex(moduleDetails, "node", &nodeDetails))
303 {
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
360void 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
392void 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
410void VuoCompilerNodeClass::parseFiniFunction(void)
411{
412 finiFunction = parser->getFunction(nameForGlobal("nodeInstanceFini"));
413}
414
424void 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
448void 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
466void 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
488void 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")
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 {
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
926VuoCompilerInputDataClass * 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
938VuoCompilerOutputDataClass * 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
956VuoCompilerInputEventPortClass * 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
969VuoCompilerOutputEventPortClass * 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
987VuoCompilerTriggerPortClass * 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
1005VuoCompilerInstanceDataClass * 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
1024VuoType * 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
1061json_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
1081VuoPortClass * 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
1198Function * VuoCompilerNodeClass::getTriggerWorkerFunction(string portIdentifier)
1199{
1201}
1202
1206vector<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
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
1458void 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
1476void VuoCompilerNodeClass::setSourcePath(const string &sourcePath)
1477{
1478 this->sourcePath = sourcePath;
1479}
1480
1485{
1486 return sourcePath;
1487}
1488
1492void VuoCompilerNodeClass::setSourceCode(const string &sourceCode)
1493{
1494 this->sourceCode = sourceCode;
1495}
1496
1501{
1502 return sourceCode;
1503}
1504
1508set< pair<string, string> > VuoCompilerNodeClass::getContainedNodes(void)
1509{
1510 return containedNodes;
1511}