/**
   * Gets a reply to an input. Assumes that the input has already had all necessary substitutions
   * and pre-processing performed, and that the input is a single sentence.
   *
   * @param input the input sentence
   * @param that the input that value
   * @param topic the input topic value
   * @param userid the userid requesting the reply
   * @param botid
   * @return the reply to the input sentence
   */
  private static String getReply(
      String input, String that, String topic, String userid, String botid) {
    // Push the input onto the <input/> stack.
    PredicateMaster.push(INPUT, input, userid, botid);

    // Create a new TemplateParser.
    TemplateParser parser;
    try {
      parser = new TemplateParser(input, userid, botid);
    } catch (TemplateParserException e) {
      throw new DeveloperError(e);
    }

    String reply = null;
    try {
      reply = getMatchResult(input, that, topic, userid, botid, parser);
    } catch (DeveloperError e) {
      Log.devfail(e);
      Log.devfail("Exiting due to developer error.", Log.ERROR);
      System.exit(1);
    } catch (UserError e) {
      Log.userfail(e);
      Log.devfail("Exiting due to user error.", Log.ERROR);
      System.exit(1);
    } catch (RuntimeException e) {
      Log.devfail(e);
      Log.devfail("Exiting due to unforeseen runtime exception.", Log.ERROR);
      System.exit(1);
    }
    if (reply == null) {
      Log.devfail("getMatchReply generated a null reply!", Log.ERROR);
      System.exit(1);
    }

    // Push the reply onto the <that/> stack.
    PredicateMaster.push(THAT, reply, userid, botid);

    return Toolkit.filterWhitespace(reply);
  }