#include #include #include #include #include #include #include #include #include #include #include #include const std::string CmdRetrievalTraits::reqString = string("parameter number or :"); const std::string CmdRetrievalTraits::objName = string("parameter"); const std::string CmdRetrievalTraits::reqString = string("component number or :"); const std::string CmdRetrievalTraits::objName = string("component"); Parameter* LookupPolicy::get(const string& name, size_t num) { Parameter* p = XSContainer::models->lookupParameter(num, name); return p; } Model* LookupPolicy::get(const string& name, size_t num) { string useName = name; if (!name.length()) { useName = Model::DEFAULT(); } // For now, this is only able to access models/components from // the first data group. Model* mod = XSContainer::models->lookup(useName); return mod; } Component* LookupPolicy::get(const string& name, size_t num) { Component* comp=0; Model* mod = LookupPolicy::get(name, num); if (mod) { int dummyGroup=0, dummyOffset=0; comp = mod->componentByNumber(num, dummyGroup, dummyOffset); } return comp; } bool HandlerUtils::getSpecNumParameter(int objc, Tcl_Obj* CONST objv[], const string& opt, size_t argNum, size_t& specNum) { using namespace XSContainer; size_t nLoaded = datasets->numberOfSpectra(); if (!nLoaded) { tcerr << "***XSPEC Error: No spectra are loaded: Unable to process " << opt << " option" << std::endl; return false; } if (objc < argNum) { // default to spectrum 1 specNum = 1; } else { string arg(Tcl_GetString(objv[argNum-1])); specNum = XSutility::isInteger(arg); if (specNum == XSparse::NOTFOUND()) { tcerr << "***XSPEC Error: Argument for spectrum number is not an integer" << std::endl; return false; } if (specNum == 0) { tcerr << "***XSPEC Error: 0 is not a valid spectrum number." << std::endl; return false; } if (specNum > nLoaded) { string isare = nLoaded > 1 ? string("are") : string("is"); tcerr << "***XSPEC Error: Invalid spectrum number: Only " << nLoaded << " spectra " << isare << " loaded." << std::endl; return false; } } return true; } const Model* HandlerUtils::getModFromArg(int objc, Tcl_Obj* CONST objv[]) { string modelName = (objc < 3) ? string("") : string(Tcl_GetString(objv[2])); return getModFromName(modelName); } const Model* HandlerUtils::getModFromName(const string& name) { using namespace XSContainer; const Model* pModel=0; string useName = name.length() ? name : Model::DEFAULT(); ModelMapConstIter im = models->modelSet().find(useName); if (im != models->modelSet().end()) { pModel = im->second; } else { if (useName == Model::DEFAULT()) { tcerr << "No model found with the default name. If looking for a loaded named\n" << " model, its name must be specified." << std::endl; } else { tcerr << "No model found named " << name << std::endl; } } return pModel; } bool HandlerUtils::subExpMatched (const Tcl_RegExpInfo& regExpInfo, const string& search, int index, std::string& group) { bool success = false; if(index <= regExpInfo.nsubs) { long start = ((regExpInfo.matches) + index)->start, end = ((regExpInfo.matches) + index)->end; int matchLength = end - start; if(matchLength > 0 && (success = true)) group = search.substr(start, matchLength); } return success; } bool HandlerUtils::subExpMatched (const Tcl_RegExpInfo& regExpInfo, const string& search, const IntegerArray& indices, std::map& group) { string match; int size = indices.size(); group.clear(); for(int i = 0; i < size; ++i) if(subExpMatched(regExpInfo, search, indices[i], match)) group[indices[i]] = match; return !group.empty(); } bool HandlerUtils::compileAndMatch(const string& pattern, const string& search, const string& whole, Tcl_RegExpInfo& expInfo) { bool success = true; Tcl_Interp* error = Tcl_CreateInterp(); Tcl_Obj* stringObj = Tcl_NewStringObj(pattern.c_str(), pattern.size()); Tcl_IncrRefCount(stringObj); Tcl_RegExp tclRegExp = Tcl_GetRegExpFromObj(error, stringObj, TCL_REG_ADVANCED); Tcl_DecrRefCount(stringObj); if(!tclRegExp && !(success = false)) tcout << error->result << std::endl; else { const char *ptrSearch = search.c_str(), *ptrWhole = whole.c_str(); if(Tcl_RegExpExec(0, tclRegExp, ptrSearch, ptrWhole) > 0) Tcl_RegExpGetInfo(tclRegExp, &expInfo); else success = false; } Tcl_DeleteInterp(error); return success; } bool HandlerUtils::fillArrays(const Tcl_RegExpInfo& expInfo, const std::string& arg, std::vector first, std::vector second, std::vector& _default) { IntegerArray subs(expInfo.nsubs); for(int i = 0; i < expInfo.nsubs; ++i) subs[i] = i + 1; return fillArrays(expInfo, arg, first, second, _default, subs); } bool HandlerUtils::fillArrays(const Tcl_RegExpInfo& expInfo, const std::string& arg, std::vector& first, std::vector& second, std::vector& _default, const IntegerArray& subs) { string strGroup; bool hasWild = false; for(int j = 1; j <= subs.size(); ++j) { int idx = (j - 1) % 2; if(subExpMatched(expInfo, arg, subs[j - 1], strGroup)) { std::istringstream input(strGroup); if(strGroup == "*" && (hasWild = true)) (j <= 2 ? first[idx] : second[idx]) = _default[j - 1]; else if(strGroup == "**") (j <= 2 ? first[idx] : second[idx]) = -2; else (input >> (j <= 2 ? first[idx] : second[idx])).seekg(0) >> _default[j - 1]; } else //I know it looks funny: (j - 1) - 1, but I'm just making it //clear that since j starts at 1, (j - 1) - 1 subscripts //the previous array element (j <= 2 ? first[idx] : second[idx]) = _default[j - 1]; } return hasWild; } void HandlerUtils::IgnoreNoticeParse(const StringArray& args, std::vector& prevRanges, bool& isReal, bool& isRespChanged, bool& isChanged, bool value) { std::vector newSpecRange(2), newChanRange(2); string specRegExp = "(\\d+|\\*{1,2})(?:-(\\d+|\\*{1,2}))?"; string chanRegExp("((?:(\\.\\d+)|\\d+(\\.\\d*)?)([eE](?:-|\\+)?\\d*)?|\\*{1,2})"); string fullReg("^(?:" + specRegExp + ":|(?=.+))(?:" + chanRegExp + "(?:-" + chanRegExp + ")?)?$"); // This section is a patch fix put in to specifically handle the // case where the user leaves whitespace after the colon. If the // following arg does not have colon, we'll assume it's the channel range they intended, // and append it immediately after the colon. StringArray patchedArgs; for (size_t i=0; i groups; bool noChansEntered = false; for(size_t i = 0; i < idx.size(); i += 2) { if(subExpMatched(expInfo, search, IntegerArray(idx.begin() + i, idx.begin() + i + 2), groups) && groups.size() == 1) { vSubs.insert(vSubs.end(), 2, idx[i]); if(groups.begin()->second == "*") prevRanges[i] = prevRanges[i + 1] = -2; } else { // If in here, either 0 or 2 of the range limits // have been found. vSubs.insert(vSubs.end(), idx.begin() + i, idx.begin() + i + 2); if (groups.empty() && i >= 2) noChansEntered = true; } groups.clear(); } bool wildInRange = fillArrays(expInfo, search, newSpecRange, newChanRange, prevRanges, vSubs); subExpMatched(expInfo, search, vDotIdx, groups); isReal = (!groups.empty() || (isReal && (wildInRange||noChansEntered))); //this obvious thing to do here instead of saving/converting all this //data, would be to change the definition of the various setChannel //functions and add a parameter indicating whether the range is real, //for instance the bool 'isReal,' above? But that would be too //tedious right now. IntegerArray spectra(2); spectra[0] = (int)newSpecRange[0]; spectra[1] = (int)newSpecRange[1]; if(isReal) { std::pair realChanRange; realChanRange.first = newChanRange[0]; realChanRange.second = newChanRange[1]; XSContainer::datasets->setChannels(value, realChanRange, spectra); isChanged = true; //values could have changed in setChannels prevRanges[2] = realChanRange.first; prevRanges[3] = realChanRange.second; } else { IntegerArray channels(2); channels[0] = (int)newChanRange[0]; channels[1] = (int)newChanRange[1]; // Handle case of inadvertant channel = 0 here. if (channels[0]==0) channels[0] = 1; if (channels[1]==0) channels[1] = 1; XSContainer::datasets->setChannels(value, channels, spectra); isChanged = true; prevRanges[2] = (Real)channels[0]; prevRanges[3] = (Real)channels[1]; } prevRanges[0] = (Real)spectra[0]; prevRanges[1] = (Real)spectra[1]; isRespChanged = XSContainer::datasets->resetDetectors(spectra); } catch (XSparse::InvalidRange) { // rethrow invalid ranges so that the loop // terminates. throw; } catch (YellowAlert&) { // any other xspec errors, continue to next // range. // result: all ranges and spectra prior too // the exception are executed, and all // complete ranges beyond the one where // the exception was thrown. continue; } } else { tcerr << "***Xspec Error: Invalid ignore/notice string: " << arg << std::endl; return; } } } bool HandlerUtils::fileExists(const string& fileName, std::ofstream& out) { using namespace std; fstream test(fileName.c_str(), ios_base::in); bool exists = true; if(!test && !(exists = false)) out.open(fileName.c_str()); else test.close(); return exists; } bool HandlerUtils::analyzeInfix(const string& infix, string& postfix) { //this table enforces precedence rules between operators. //refer to 'Tanenbaum, Andrew S., Structured Computer //Organization, Prentice-Hall, 1999' for more info static const int action[6][7] = { { 2, 2, 1, 1, 1, 1, 2 }, { 2, 2, 1, 1, 1, 1, 2 }, { 2, 2, 2, 2, 1, 1, 2 }, { 2, 2, 2, 2, 1, 1, 2 }, { 2, 2, 2, 2, 2, 1, 2 }, { 1, 1, 1, 1, 1, 1, 3 } }; std::map chart_pos; chart_pos['+'] = 0; chart_pos['-'] = 1; chart_pos['*'] = 2; chart_pos['/'] = 3; chart_pos['@'] = 4; //symbol we'll use to represent unary minus chart_pos['('] = 5; chart_pos[')'] = 6; bool error = false, lastWasGroup = false; postfix = ""; const string& real = RegEx::REAL(); RegEx regex("^(?:(" + real + ")?\\s*((?:[*/(+-])(?!\\s*[*/)+])|\\))\\s*|(" + real + "))"); RegEx::result_type matches; std::stack op_stack; std::string::const_iterator infix_beg = infix.begin(), infix_end = infix.end(); while(!error && infix_beg != infix_end && regex.regex_search(infix_beg, infix_end, matches)) { bool digitMatched = true; if(matches[1].matched) postfix += matches[1].str(); else if(matches[5].matched) postfix += matches[5].str(); else digitMatched = false; if(matches[4].matched) { char scanned = matches[4].str()[0]; bool unaryPlus = false; //is the operator a unary '+' or '-' if(string("+-").find(scanned) != string::npos && !digitMatched && !lastWasGroup) //discard the unary '+' (scanned == '-' ? scanned = '@' : unaryPlus = true); if(!unaryPlus) { int col = chart_pos[scanned], to_do; bool done = false; do { if(op_stack.size() > 0) { int row = chart_pos[op_stack.top()]; to_do = action[row][col]; lastWasGroup = false; switch(to_do) { case 1: op_stack.push(scanned); break; case 2: postfix += op_stack.top(); op_stack.pop(); break; case 3: op_stack.pop(); lastWasGroup = true; break; } } else if(scanned == ')') error = true; else { op_stack.push(scanned); done = true; } } while(!done && !error && to_do == 2); } } infix_beg = matches[0].second; } if(infix_beg != infix_end) error = 1; if(!error) { char ch; //pop any remaining op_stack operators while(!error && op_stack.size() > 0) { if((ch = op_stack.top()) == '(') error = 1; else { postfix += ch; op_stack.pop(); } } } return !error; } void HandlerUtils::commonStepParsing(int objc, Tcl_Obj* CONST objv[], Grid::ParameterSpec*& prevSettings, Grid::SpecContainer& newStepSettings, bool& isBest, bool needModParam) { // If ANYTHING is found wrong with ANY of the entries, ALL memory // pointed to by newStepSettings will be deleted and the array // will be cleared, prevSettings will be restored to its // initial value, and isBest will be undefined. Grid::ParameterSpec * const savePrevSettings = prevSettings; std::vector args(objc-1,""); IntegerArray inputIndex; StringArray inputString; string strArgString; for (int j = 1; j < objc; ++j) { args[j-1] = Tcl_GetString(objv[j]); strArgString += args[j - 1] + ' '; } static string parIndex("1"); XSparse::collectParams(args,inputIndex,inputString); // we now have a parsed list of strings inputString // indexed by an indirect address array inputIndex. const string& real = RegEx::REAL(); /* string exp; exp += "^(?:(log|nolog)\\s*(?:,\\s*|\\s+(?=\\w)|$))?\\s*"; exp += "(?:(best|current)\\s*(?:,\\s*|\\s+(?=\\w)|$))?\\s*"; exp += "(?:(?:((?:\\w+:)?\\d+)\\s*(?:,\\s*|\\s+(?=-|\\d|\\.))|,)\\s*"; exp += "(?:" + real + "\\s*(?:,\\s*|\\s+(?=-|\\d|\\.))|,)\\s*"; exp += "(?:" + real + "\\s*(?:,\\s*|\\s+(?=\\d)|$)|,)\\s*"; exp += "(?:(\\d+)\\s*(?:,\\s*|\\s+)|,|$)|$)"; */ string delim = "(?:\\s*,\\s*|\\s+)"; string exp; exp += "^(?:(?:best|current)(?:" + delim + "|$))?"; exp += "(?:(?:log|nolog)(?:" + delim + "|$))?"; exp += "((?:\\w+:)?\\d+)?(?:" + delim + "|$)"; //matches [modelname:]param_number exp += "(" + real + ")?(?:" + delim + "|$)"; //matches range min exp += "(" + real + ")?(?:" + delim + "|$)"; //matches range max exp += "(\\d+)?(?:" + delim + "|$)"; //matches # of steps RegEx regex(exp); RegEx::result_type matches; IntegerArray paramIndex; StringArray paramString; isBest = Grid::retrieveBestSetting(inputString,inputIndex, paramString,paramIndex); // okay, now we are dealing only with parameter specifiers. StringArray::iterator is = paramString.begin(); StringArray::iterator isEnd = paramString.end(); IntegerArray::iterator ii = paramIndex.begin(); const string LOG("nNlL"); // since steppar can set multiple parameters, we need an index // to tell the argument count to start again every time we finish // one. baseIndex will point into paramIndex. int baseIndex(0); int flags = 0; string::const_iterator args_beg = strArgString.begin(), args_end = strArgString.end(); bool success = false; try { while(is != isEnd && (success = regex.regex_search(args_beg, args_end, matches, flags))) { // okay the first argument is either a string denoting // log/nolog or a parameter specifier. Can do the following // with random access iterators only. //baseIndex = *ii; ModParam* mp(0); int logSetting = -1; int qParIndex(-1); string parameterName(""); string param; if(matches[1].matched) { string s(matches[1].str()); if(s.length()) parIndex = param = s; else param = parIndex; } else param = parIndex; //parIndex = param; if (needModParam) { // qParIndex will refer to the FULL param index in // this case. Step::retrieveParameter(XSContainer::fit, param, mp, qParIndex, parameterName); } else { // NOTE: In this case we're not interested in retrieving an // actual parameter object, just a string:int ID. // Therefore, qParIndex will refer to index within the // model, and parameterName will actually be the model name. size_t tmpIdx=0; XSparse::stringIntPair(param, parameterName, tmpIdx); qParIndex = static_cast(tmpIdx); } // retrieve parameter specification from map. // if this fails then we check for all necessary // ctor arguments before constructing and placing // in the map. //Step::ParameterSpec* ps( stepData->paramSpecByIndex(qParIndex) ); // now read the next set of arguments until // there is enough for a new parameter spec or // to reset the old. bool psDone = false, ls = false; int levels = 10; std::pair limits(Grid::FLAG(), Grid::FLAG()); if (prevSettings) { limits.first = prevSettings->lowRange; limits.second = prevSettings->highRange; levels = prevSettings->intervals; ls = prevSettings->log; } if ( is != isEnd ) { do { psDone = Grid::getParameterSpecFromPrompt (*is,*ii,baseIndex,limits,levels,ls,logSetting); ++is, ++ii; } while (is != isEnd && !psDone); } Grid::ParameterSpec* newPar = Grid::makeParameterSpec(prevSettings, parameterName, qParIndex,limits,ls,levels); newPar->address = mp; prevSettings = newPar; newStepSettings.push_back(newPar); args_beg = matches[0].second; baseIndex += 4; } //end while loop if(!success) { string errMsg(" Error processing step parameter args, check syntax: \'"); errMsg += strArgString; errMsg += "\'"; throw Grid::InvalidParameter(errMsg); } } catch (...) { prevSettings = savePrevSettings; for (size_t i=0; isourceNumber() < left->sourceNumber(); }