/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include "UIConfig.h" #include "defines.h" #include "lex.h" #include "SelectionNode.h" #include "ErrorDialogManager.h" #include "AttributeParameter.h" #include "Parameter.h" #include "SelectorInstance.h" #include "ListIterator.h" // // Define the input parameter numberings. These MUST coincide with the mdf. // #define ID_PARAM_NUM 1 #define CSTR_PARAM_NUM 2 // Current output string #define CVAL_PARAM_NUM 3 // Current output value #define STRLIST_PARAM_NUM 4 // group or stringlist #define VALLIST_PARAM_NUM 5 // integer or value list #define CULL_PARAM_NUM 6 // removes zero length strings #define LABEL_PARAM_NUM 7 #define EXPECTED_SELECTOR_INPUTS LABEL_PARAM_NUM // // Used to defer setting the outputs when we know we have many options // to append to the list of options. Otherwise, appending lots of items // takes LOTS of time. // int SelectionNode::OptionsLeftToSet = 0; SelectionNode::SelectionNode(NodeDefinition *nd, Network *net, int instnc, boolean alwaysEnlistOutputs) : InteractorNode(nd, net, instnc) { this->optionCount = 0; this->alwaysEnlistOutputs = alwaysEnlistOutputs; this->deferNewOptionInstallation = new DeferrableAction(SelectionNode::InstallNewOptions, (void*)this); } SelectionNode::~SelectionNode() { delete this->deferNewOptionInstallation; } void SelectionNode::InstallNewOptions(void *staticData, void *requestData) { SelectionNode *sn = (SelectionNode*)staticData; sn->installNewOptions(NULL,NULL,FALSE); } // // Set up the interactor with the default options // boolean SelectionNode::initialize() { if (this->getInputCount() != EXPECTED_SELECTOR_INPUTS) { fprintf(stderr, "Expected %d inputs for %s interactor, please check the mdf file.\n", EXPECTED_SELECTOR_INPUTS, this->getNameString()); return FALSE; } // // Set the default options // //this->selectedOption = 0; // This helps the next 2 lines this->initValueOptionsAttribute(this->getInitialValueList()); this->initStringOptionsAttribute(this->getInitialStringList()); this->installNewOptions(NULL,NULL,FALSE); #if 000 // // Set the default output values (must be one of the above options) // this->setOutputValue(1,"1", DXType::UndefinedType, FALSE); this->setOutputValue(2,"on", DXType::UndefinedType, FALSE); #else this->clearSelections(FALSE, FALSE); #if 00 this->addSelectedOptionIndex(1,TRUE); #else this->addSelectedOptionIndex(1,FALSE); #endif #endif // // Make the shadows defaulting (even though we have a current output) // so that the executive module can tell when it is executing a just // placed module and one that is read in from a .net or .cfg file. // When read in, the output will be set again which should make the // corresponding shadowing input be non-defaulting. // this->setShadowingInputsDefaulting(); // Set the input id for the executive module. // this->setMessageIdParameter(); return TRUE; } // // If either of the list inputs has changed, we must determine the size // of the smaller of the two lists and reset this->optionCount to that value. // NOTE: If this routine is being called, then one of the values must have a // set value (even though both are allowed not too). // void SelectionNode::ioParameterStatusChanged(boolean input, int index, NodeParameterStatusChange status) { if (input && (status & Node::ParameterValueChanged)) { if ((index == VALLIST_PARAM_NUM) || (index == STRLIST_PARAM_NUM)) this->installNewOptions(NULL,NULL, FALSE); } this->InteractorNode::ioParameterStatusChanged(input, index, status); } // // Use the value list and string list to set the new list of options. // Also, notify all instances that the state has changed. // If vlist == NULL, update from the VALLIST parameter. // If slist == NULL, update from the STRLIST parameter. // void SelectionNode::installNewOptions(const char *vlist, const char *slist, boolean send) { static int kloodge = 0; if (kloodge != 0) // Stop installNewOptions -> ioParameterValueChanged -> return; // installNewOptions loop. if (this->deferNewOptionInstallation->isActionDeferred()) { this->deferNewOptionInstallation->requestAction((void*)send); return; } this->deferVisualNotification(); if (vlist == NULL) vlist = this->getInputSetValueString(VALLIST_PARAM_NUM); kloodge++; this->setValueOptionsAttribute(vlist); kloodge--; vlist = this->getValueOptionsAttribute(); if (slist == NULL) slist = this->getInputSetValueString(STRLIST_PARAM_NUM); kloodge++; this->setStringOptionsAttribute(slist); kloodge--; slist = this->getStringOptionsAttribute(); int vcnt = DXValue::GetListItemCount(vlist, DXType::UndefinedType); int scnt = DXValue::GetListItemCount(slist, DXType::UndefinedType); this->optionCount = (vcnt < scnt ? vcnt : scnt); if (!SelectionNode::OptionsLeftToSet) { #if 000 if (vcnt && scnt && (this->getOptionCount() == 0)) this->addSelectedOptionIndex(1, FALSE, FALSE); #else // // Remove any selections that are out of range. // ListIterator iter(this->selectedOptions); int i, pos = 0; while (i = (int)iter.getNext()) { pos++; if (i > this->getOptionCount()) { this->selectedOptions.deleteElement(pos); iter.setList(this->selectedOptions); pos = 0; } } // // Make sure there is at least one item selected, otherwise // the SelectorInteractor gets upset // if (vcnt && scnt && this->selectedOptions.getSize() == 0) this->addSelectedOptionIndex(1, FALSE,FALSE); #endif this->notifyVisualsOfStateChange(); #if 00 if (this->getOptionCount() >= selection) { // // Update the outputs. // char *vval = this->getOptionValueString(selection); char *sval = this->getOptionNameString(selection); ASSERT(vval && sval); this->setOutputValue(1,vval,DXType::UndefinedType,FALSE); this->setOutputValue(2,sval,DXType::UndefinedType,FALSE); delete vval; delete sval; } #else this->updateOutputs(); #endif if (send) this->sendValues(FALSE); } this->undeferVisualNotification(); } // // Define the mapping of inputs that shadow outputs. // Returns an input index (greater than 1) or 0 if there is no shadowing input // for the given output index. // FIXME: should this be somehow defined in the mdf. // int SelectionNode::getShadowingInput(int output_index) { int input_index; switch (output_index) { case 1: input_index = CVAL_PARAM_NUM; break; case 2: input_index = CSTR_PARAM_NUM; break; default: input_index = 0; break; } return input_index; } boolean SelectionNode::cfgPrintInteractorAuxInfo(FILE *f) { return this->cfgPrintSelectionsComment(f) && this->cfgPrintOptionComments(f); } boolean SelectionNode::cfgParseComment(const char *comment, const char *filename, int lineno) { return this->cfgParseSelectionsComment(comment, filename,lineno) || this->cfgParseOptionComment(comment, filename, lineno) || this->InteractorNode::cfgParseComment(comment, filename, lineno); } // // Get the list of all option values // boolean SelectionNode::initValueOptionsAttribute(const char *vlist) { AttributeParameter *p = (AttributeParameter*) this->getInputParameter( VALLIST_PARAM_NUM); return p->initAttributeValue(vlist); } boolean SelectionNode::setValueOptionsAttribute(const char *vlist) { AttributeParameter *p = (AttributeParameter*) this->getInputParameter( VALLIST_PARAM_NUM); return p->setAttributeValue(vlist); } const char *SelectionNode::getValueOptionsAttribute() { AttributeParameter *p = (AttributeParameter*) this->getInputParameter( VALLIST_PARAM_NUM); return p->getAttributeValueString(); } // // Get the list of all option strings // boolean SelectionNode::initStringOptionsAttribute(const char *vlist) { AttributeParameter *p = (AttributeParameter*) this->getInputParameter( STRLIST_PARAM_NUM); return p->initAttributeValue(vlist); } boolean SelectionNode::setStringOptionsAttribute(const char *vlist) { AttributeParameter *p = (AttributeParameter*) this->getInputParameter( STRLIST_PARAM_NUM); return p->setAttributeValue(vlist); } const char *SelectionNode::getStringOptionsAttribute() { AttributeParameter *p = (AttributeParameter*) this->getInputParameter( STRLIST_PARAM_NUM); return p->getAttributeValueString(); } // // Get the value of the indicated option. // The returned string must be deleted by the caller. // char *SelectionNode::getOptionValueString(int optind) { const char *s = this->getValueOptionsAttribute(); Type t = DXType::DetermineListItemType(s); return DXValue::GetListItem(s,optind,t | DXType::ListType); } // // Get the name of the indicated option. // The return string is not double quoted if keep_quotes is FALSE. // The return string must be deleted by the caller. // char *SelectionNode::getOptionNameString(int optind, boolean keep_quotes) { const char *list = this->getStringOptionsAttribute(); char *value, *s = DXValue::GetListItem(list,optind,DXType::StringListType); value = s; if (s && !keep_quotes) { // // Strip off the double quotes. // char *c, *p; p = s; c = value = new char [STRLEN(s)]; p++; while (*p != '"') { *c = *p; c++; p++; } *c = '\0'; delete s; } return value; } boolean SelectionNode::cfgPrintSelectionsComment(FILE *f) { int count = this->getOptionCount(); int selectionCount = this->getSelectedOptionCount(); // // If the option list is empty (i.e. count == 0) then make sure the // selected option index is reasonable (i.e. >= 0). // if (fprintf(f, "// selections: maximum = %d, current = ", count) <= 0) return FALSE; if (count <= 0 || selectionCount <= 0) { if (fprintf(f,"-1") <= 0) return FALSE; } else { int i; for (i=1 ; i<=count ; i++) // File uses 0 based indexing if (this->isOptionSelected(i) && (fprintf(f,"%d ",i-1) <= 0)) return FALSE; } if (fprintf(f,"\n") <= 0) return FALSE; return TRUE; } // // // boolean SelectionNode::cfgParseSelectionsComment(const char *comment, const char *filename, int lineno) { int items_parsed; int nchars,n_options; int option; ASSERT(comment); if (strncmp(comment," selections",11)) return FALSE; items_parsed = sscanf(comment, " selections: maximum = %d, current = %d%n", &n_options, &option, &nchars); // // Indicate that we expect n_options, and that we should not update // outputs until this goes back to 0 (i.e. we've seen n_options // 'options[...' comments. // SelectionNode::OptionsLeftToSet = n_options; if (items_parsed != 2) { ErrorMessage("Can't parse 'selections' comment (file %s, line %d)", filename, lineno); return FALSE; } /* * Set current option(s). */ this->clearSelections(FALSE,FALSE); if (option >= 0) { // 1 based indexing this->addSelectedOptionIndex(option+1, FALSE, FALSE); const char *p = comment + nchars; while (sscanf(p,"%d%n",&option,&nchars) == 1) { if (option >= 0) // 1 based indexing this->addSelectedOptionIndex(option+1, FALSE, FALSE); p += nchars; } } return TRUE; } boolean SelectionNode::cfgPrintOptionComments(FILE *f) { char *str, *val; boolean r = TRUE; int i = 0; int count = this->getOptionCount(); while (r && i++ < count) { str = this->getOptionNameString(i); val = this->getOptionValueString(i); if (fprintf(f, "// option[%d]: name = \"%s\", value = %s\n", i-1, // File uses zero based indexing str, val) < 0) r = FALSE; delete str; delete val; } return r; } boolean SelectionNode::cfgParseOptionComment(const char *comment, const char *filename, int lineno) { int items_parsed; int opt_index; char value[128]; char name[512]; ASSERT(comment); if (strncmp(comment," option[",8)) return FALSE; items_parsed = sscanf(comment, " option[%d]: name = \"%[^\"]\", value = %[^\n]", &opt_index, name, value); // index is 1 based internally opt_index++; if (items_parsed != 3) { ErrorMessage("Can't parse 'option' comment (file %s, line %d)", filename, lineno); return FALSE; } if ((opt_index < 1) || (opt_index > this->getOptionCount()+1)) { ErrorMessage("Bad option index, (file %s, line %d)", filename, lineno); return FALSE; } #if 000 if (this->getSelectedOptionIndex() == opt_index) { if (this->setOutputValue(2,name,DXType::StringType,FALSE) == DXType::UndefinedType) { ErrorMessage( "Can't set string value of Selection option, (file %s, line %d)", filename, lineno); return FALSE; } if (this->setOutputValue(1,value, DXType::UndefinedType,FALSE) == DXType::UndefinedType) { ErrorMessage( "Can't set value of Selection option, (file %s, line %d)", filename, lineno); return FALSE; } } #endif // // This test should tell us if we are parsing the first of a set of // options. // //if (this->getOptionCount() > opt_index) { if (opt_index == 1) { this->optionCount = 0; } boolean r; r = this->appendOptionPair(value, name); if (!r) { ErrorMessage("Can not add option to option list (file %s, line %d)", filename, lineno); return FALSE; } #if 000 #else // // At this point we successfully parsed an option, so indicate that // there is one less to set. If this is the last option, then // update and send the new output values. // if (--SelectionNode::OptionsLeftToSet == 0) { this->updateOutputs(); this->sendValues(FALSE); } #endif return TRUE; } // // Add an option pair to the list of option pairs. // Do not send the values to the executive or notify CDBs or interactors. // boolean SelectionNode::appendOptionPair(const char *value, const char *label) { boolean r; const char *slist; const char *vlist; char *str; if (this->getOptionCount() == 0) { slist = "{ }"; vlist = "{ }"; } else { slist = this->getStringOptionsAttribute(); vlist = this->getValueOptionsAttribute(); } // // Make sure the label has double quotes around it. // if (str = FindDelimitedString(label,'"','"')) { str = DuplicateString(label); } else { str = new char [STRLEN(label) + 4]; sprintf(str,"\"%s\"",label); } char *nvlist = DXValue::AppendListItem(vlist, value); char *nslist = DXValue::AppendListItem(slist, str); delete str; if (nvlist && nslist) { this->installNewOptions(nvlist,nslist, FALSE); r = TRUE; } else r = FALSE; if (nvlist) delete nvlist; if (nslist) delete nslist; return r; } // The messages we parse can contain one or more of the following... // // 'value list={...}' or 'string list={...}' or 'index=%d' // // If any input or output values are to be changed, don't send them // because the module backing up the interactor will have just executed // and if the UI is in 'execute on change' mode then there will be an // extra execution. // // Returns the number of attributes parsed. // int SelectionNode::handleInteractorMsgInfo(const char *line) { int i, index, val_count = 0, str_count = 0, values = 0; int old_option_count = this->getOptionCount(); char *p, *vlist = NULL, *slist = NULL, buf[1024]; // // Handle the 'value list={...}' part of the message. // if (p = strstr((char*)line,"value list=")) { vlist = FindDelimitedString(p,'{','}'); } // // Handle the 'string list={...}' part of the message. // if (p = strstr((char*)line,"string list=")) { slist = FindDelimitedString(p,'{','}',NULL,"\""); } // // Install the options if we saw both a value list and a string list // if (vlist && slist) { values++; this->deferNewOptionInstallation->deferAction(); this->setInputAttributeFromServer(VALLIST_PARAM_NUM,vlist, DXType::UndefinedType); this->setInputAttributeFromServer(STRLIST_PARAM_NUM,slist, DXType::UndefinedType); // // The AttributeParameter's primary value won't get updated unless // the types match. In the case of the value parameter, an integer // (or NULL) value will result in a list type attribute, in which // case the primary and secondary (attribute) values should not be // kept in sync. However, if the value is currently a list type // then it should be ok to install another list type into the // primary values. So, if the value parameter is not an integer and // not NULL (i.e. a list type), then force a sync of the attribute // parameter with the primary value. // if (this->getInputSetValueType(VALLIST_PARAM_NUM) & DXType::ListType) { AttributeParameter *ap = (AttributeParameter*) this->getInputParameter(VALLIST_PARAM_NUM); ap->syncPrimaryValue(TRUE); } this->deferNewOptionInstallation->undeferAction(); } if (vlist) delete vlist; if (slist) delete slist; // // Handle the 'index=%d' part of the message. // if (p = strstr((char*)line,"index=")) { int selections[1024], nselects = 0,nchars; values++; while (*p != '=') p++; p++; if (*p == '{') p++; while (sscanf(p,"%d%n",&index,&nchars) == 1) { if ((index >= 0) && (index < this->getOptionCount())) { selections[nselects] = index+1; nselects++; } p+=nchars; } // // Make sure the internal output value and the shadowed inputs // are in sync both internally and in the executive. // this->setSelectedOptions(selections,nselects,FALSE,FALSE); this->updateOutputs(TRUE); } return values; } // // Get a a SelectorInstance instead of an InteractorInstance. // InteractorInstance *SelectionNode::newInteractorInstance() { SelectorInstance *si = new SelectorInstance(this); return (SelectorInstance*)si; } // // Determine if this node is of the given class. // boolean SelectionNode::isA(Symbol classname) { Symbol s = theSymbolManager->registerSymbol(ClassSelectionNode); if (s == classname) return TRUE; else return this->InteractorNode::isA(classname); } // // Update the selected option index, and if requested, update the output // values to match the values indicated by the index. // void SelectionNode::changeSelectedOptionIndex(int index, boolean set, boolean send, boolean update_outputs) { int i, pos; ASSERT(index > 0); // ASSERT(SelectionNode::OptionsLeftToSet || (index <= this->getOptionCount())); ListIterator iter(this->selectedOptions); for (pos=1 ; ((i = (int)iter.getNext()) < index) && (i != 0); pos++) ; if (set) { if (i == index) // The index is already set return; } else if (!i || (i != index)) // The index is not there to remove return; if (set) this->selectedOptions.insertElement((void*)index, pos); else this->selectedOptions.deleteElement(pos); // // Update the parameter values if requested. // if (update_outputs) { this->updateOutputs(); if (send) this->sendValues(FALSE); } } static void enlist_items(char **values, int cnt, char *buf, boolean forceList, boolean quoteItems) { int i; if (!cnt) { strcpy(buf,"NULL"); return; } if (forceList || (cnt > 1)) strcpy(buf,"{ "); else buf[0] = '\0'; char *p = buf + STRLEN(buf); for (i=0 ; i 1)) strcpy(p,"}"); } // // Update the output values based on the current selections, then send // the values to the server. // void SelectionNode::updateOutputs(boolean fromServer) { char *output; int cnt = 0, soptions = this->getSelectedOptionCount(); char **values = NULL, **names = NULL; // // Don't notify the interactors that the value has changed // (yes notification is redundant), until after we have set both values. // this->deferVisualNotification(); if (soptions > 0) { int i, totoptions = this->getOptionCount(); int vallen=0, namelen=0; values = new char*[soptions+1]; names = new char*[soptions+1]; for (cnt=0, i=1 ; i<= totoptions ; i++) { if (this->isOptionSelected(i)) { values[cnt] = this->getOptionValueString(i); vallen += STRLEN(values[cnt]) + 4; names[cnt] = this->getOptionNameString(i); namelen += STRLEN(names[cnt]) + 4; cnt++; } } ASSERT(cnt>0); output = new char[ MAX(namelen,vallen) + 8]; enlist_items(values,cnt,output,this->alwaysEnlistOutputs,FALSE); } else { output = new char[8]; strcpy(output,"NULL"); } if (fromServer) this->setShadowedOutputSentFromServer(1,output, DXType::UndefinedType); else this->setOutputValue(1,output,DXType::UndefinedType, FALSE); // // Undefer now, so that the next setOutputValue() causes all instances // to have their output updated. // this->undeferVisualNotification(); if (soptions > 0) enlist_items(names,cnt,output,this->alwaysEnlistOutputs,TRUE); if (fromServer) this->setShadowedOutputSentFromServer(2,output, DXType::UndefinedType); else this->setOutputValue(2,output,DXType::UndefinedType, FALSE); if (cnt > 0) { ASSERT(names && values); while (cnt--) { delete names[cnt]; delete values[cnt]; } delete names; delete values; } delete output; } void SelectionNode::addSelectedOptionIndex(int index, boolean send, boolean update_outputs) { this->changeSelectedOptionIndex(index,TRUE, send, update_outputs); } #if 00 // Not used void SelectionNode::removeSelectedOptionIndex(int index, boolean send, boolean update_outputs) { this->changeSelectedOptionIndex(index,FALSE, send, update_outputs); } #endif boolean SelectionNode::isOptionSelected(int index) { ASSERT(index > 0); ASSERT(index <= this->getOptionCount()); return this->selectedOptions.isMember((void*)index); } void SelectionNode::clearSelections(boolean send, boolean update) { this->selectedOptions.clear(); if (update) { this->updateOutputs(); if (send) this->sendValues(FALSE); } } void SelectionNode::setSelectedOptions(int *setIndices, int count, boolean send, boolean update) { this->clearSelections(FALSE,FALSE); int i, options = this->getOptionCount(); for (i=0 ; i 0 && index <= options) this->addSelectedOptionIndex(index,FALSE,FALSE); } if (update) { this->updateOutputs(); if (send) this->sendValues(FALSE); } } boolean SelectionNode::printJavaType(FILE* jf, const char* indent, const char* var) { Parameter *p = this->getOutputParameter(1); if (p->hasValue()) { Type t = p->getValueType(); if (t & DXType::IntegerType) { if (t & DXType::ListType) fprintf (jf, "%s%s.setOutputType(BinaryInstance.INTEGER|BinaryInstance.LIST);\n", indent,var); else fprintf (jf, "%s%s.setOutputType(BinaryInstance.INTEGER);\n", indent,var); } else if (t & DXType::ScalarType) { if (t & DXType::ListType) fprintf (jf, "%s%s.setOutputType(BinaryInstance.SCALAR|BinaryInstance.LIST);\n", indent,var); else fprintf (jf, "%s%s.setOutputType(BinaryInstance.SCALAR);\n", indent,var); } } else { fprintf (jf, "%s// The output has no value saved in the net\n", indent); fprintf (jf, "%s%s.setOutputType(BinaryInstance.STRING);\n", indent,var); } return TRUE; } boolean SelectionNode::printJavaValue (FILE* jf) { const char* indent = " "; const char* var_name = this->getJavaVariable(); // // Load up the items in the SelectorNode into the java interactor // int ocnt = this->getOptionCount(); List selection_stmts; if (ocnt) { fprintf (jf, "%sVector %s_vn = new Vector(%d);\n", indent, var_name, ocnt); fprintf (jf, "%sVector %s_vo = new Vector(%d);\n", indent, var_name, ocnt); int i; for (i=1; i<=ocnt; i++) { const char* optname = this->getOptionNameString(i); char* optvalue = DuplicateString(this->getOptionValueString(i)); if (this->isOptionSelected(i)) { char* stmt = new char[128]; sprintf (stmt, "%s%s.selectOption(%d);\n", indent, var_name, i); selection_stmts.appendElement((void*)stmt); } char* head = optvalue; int k; for (k=strlen(optvalue)-1; k>=0; k--) if ((optvalue[k] == ' ') || (optvalue[k] == '"')) optvalue[k] = '\0'; else break; while (*optvalue) { if ((*optvalue == ' ') || (*optvalue == '"')) optvalue++; else break; } fprintf (jf, "%s%s_vn.addElement(\"%s\");\n", indent,var_name,optname); fprintf (jf, "%s%s_vo.addElement(\"%s\");\n", indent,var_name,optvalue); delete head; } if (fprintf (jf, "%s%s.setValues(%s_vn, %s_vo);\n", indent, var_name, var_name, var_name) <= 0) return FALSE; ListIterator it(selection_stmts); char* cp; while (cp = (char*)it.getNext()) { fprintf (jf, cp); delete cp; } } return TRUE; }