/**
  * Compile a string into a function or SimpleVariable.
  *
  * <p>Called by {@link #compileString(String)} when that has detected "${".
  *
  * <p>Calls {@link CompoundVariable#getNamedFunction(String)} if it detects: '(' - start of
  * parameter list '}' - end of function call
  *
  * @param reader points to input after the "${"
  * @return the function or variable object (or a String)
  */
 Object makeFunction(StringReader reader) throws InvalidVariableException {
   char[] current = new char[1];
   char previous = ' '; // TODO - why use space?
   StringBuilder buffer = new StringBuilder();
   Object function;
   try {
     while (reader.read(current) == 1) {
       if (current[0] == '\\') {
         if (reader.read(current) == 0) {
           break;
         }
         previous = ' ';
         buffer.append(current[0]);
         continue;
       } else if (current[0] == '(' && previous != ' ') {
         String funcName = buffer.toString();
         function = CompoundVariable.getNamedFunction(funcName);
         if (function instanceof Function) {
           ((Function) function).setParameters(parseParams(reader));
           if (reader.read(current) == 0 || current[0] != '}') {
             reader.reset(); // set to start of string
             char[] cb = new char[100];
             reader.read(cb); // return deliberately ignored
             throw new InvalidVariableException(
                 "Expected } after " + funcName + " function call in " + new String(cb));
           }
           if (function instanceof TestListener) {
             StandardJMeterEngine.register((TestListener) function);
           }
           return function;
         } else { // Function does not exist, so treat as per missing variable
           buffer.append(current[0]);
         }
         continue;
       } else if (current[0] == '}') { // variable, or function with no parameter list
         function = CompoundVariable.getNamedFunction(buffer.toString());
         if (function instanceof Function) { // ensure that setParameters() is called.
           ((Function) function).setParameters(new LinkedList<CompoundVariable>());
         }
         buffer.setLength(0);
         return function;
       } else {
         buffer.append(current[0]);
         previous = current[0];
       }
     }
   } catch (IOException e) {
     log.error("Error parsing function: " + buffer.toString(), e);
     return null;
   }
   log.warn("Probably an invalid function string: " + buffer.toString());
   return buffer.toString();
 }
 /**
  * Compile a String into a list of parameters, each made into a CompoundVariable.
  *
  * <p>Parses strings of the following form:
  *
  * <ul>
  *   <li>text)
  *   <li>text,text)
  *   <li>
  * </ul>
  *
  * @param reader a StringReader pointing to the current input location, just after "("
  * @return a list of CompoundVariable elements
  */
 LinkedList<CompoundVariable> parseParams(StringReader reader) throws InvalidVariableException {
   LinkedList<CompoundVariable> result = new LinkedList<CompoundVariable>();
   StringBuilder buffer = new StringBuilder();
   char[] current = new char[1];
   char previous = ' ';
   int functionRecursion = 0;
   int parenRecursion = 0;
   try {
     while (reader.read(current) == 1) {
       if (current[0] == '\\') { // Process escaped characters
         buffer.append(current[0]); // Store the \
         if (reader.read(current) == 0) {
           break; // end of buffer
         }
         previous = ' ';
         buffer.append(current[0]); // store the following character
         continue;
       } else if (current[0] == ',' && functionRecursion == 0) {
         CompoundVariable param = new CompoundVariable();
         param.setParameters(buffer.toString());
         buffer.setLength(0);
         result.add(param);
       } else if (current[0] == ')' && functionRecursion == 0 && parenRecursion == 0) {
         // Detect functionName() so this does not generate empty string as the parameter
         if (buffer.length() == 0 && result.isEmpty()) {
           return result;
         }
         // Normal exit occurs here
         CompoundVariable param = new CompoundVariable();
         param.setParameters(buffer.toString());
         buffer.setLength(0);
         result.add(param);
         return result;
       } else if (current[0] == '{' && previous == '$') {
         buffer.append(current[0]);
         previous = current[0];
         functionRecursion++;
       } else if (current[0] == '}' && functionRecursion > 0) {
         buffer.append(current[0]);
         previous = current[0];
         functionRecursion--;
       } else if (current[0] == ')' && functionRecursion == 0 && parenRecursion > 0) {
         buffer.append(current[0]);
         previous = current[0];
         parenRecursion--;
       } else if (current[0] == '(' && functionRecursion == 0) {
         buffer.append(current[0]);
         previous = current[0];
         parenRecursion++;
       } else {
         buffer.append(current[0]);
         previous = current[0];
       }
     }
   } catch (IOException e) { // Should not happen with StringReader
     log.error("Error parsing function: " + buffer.toString(), e);
   }
   // Dropped out, i.e. did not find closing ')'
   log.warn("Probably an invalid function string: " + buffer.toString());
   CompoundVariable var = new CompoundVariable();
   var.setParameters(buffer.toString());
   result.add(var);
   return result;
 }