/** Dumps the entire contents of the cache to the Log. */
  protected void DumpMapData() {
    String Helper;
    Iterator<String> GroupIter;
    Iterator<SearchMap> PatternIterator;
    Iterator<String> CDIterator;
    SearchGroup tmpSearchGroup;
    SearchMap tmpSearchMap;
    String PrintHelper;
    int counter = 0;

    OpenRate.getOpenRateFrameworkLog()
        .info("Dumping Map Data for RegexMatchCache <" + getSymbolicName() + ">");
    OpenRate.getOpenRateFrameworkLog().info("Groups:");

    // Iterate thorough the entries in the group
    GroupIter = GroupCache.keySet().iterator();
    while (GroupIter.hasNext()) {
      Helper = GroupIter.next();
      OpenRate.getOpenRateFrameworkLog().info("  " + Helper);
    }

    // Now dump the data
    GroupIter = GroupCache.keySet().iterator();
    while (GroupIter.hasNext()) {
      Helper = GroupIter.next();
      OpenRate.getOpenRateFrameworkLog().info("Dumping group map data for <" + Helper + ">");
      tmpSearchGroup = GroupCache.get(Helper);
      PatternIterator = tmpSearchGroup.SearchGroup.iterator();

      while (PatternIterator.hasNext()) {
        OpenRate.getOpenRateFrameworkLog().info("===ENTRY " + counter + "===");
        counter++;
        PrintHelper = "  (";

        tmpSearchMap = PatternIterator.next();
        for (int i = 0; i < tmpSearchMap.matchPattern.length; i++) {
          PrintHelper =
              PrintHelper
                  + "["
                  + tmpSearchMap.matchType[i]
                  + ":"
                  + tmpSearchMap.matchPattern[i]
                  + ":"
                  + tmpSearchMap.matchValue[i]
                  + "] ";
        }

        // dump the result array
        PrintHelper += ") --> (";

        CDIterator = tmpSearchMap.Results.iterator();
        while (CDIterator.hasNext()) {
          PrintHelper = PrintHelper + CDIterator.next() + ",";
        }

        PrintHelper += ")";
        OpenRate.getOpenRateFrameworkLog().info(PrintHelper);
      }
    }
  }
  /**
   * processControlEvent is the method that will be called when an event is received for a module
   * that has registered itself as a client of the External Control Interface
   *
   * @param Command - command that is understand by the client module
   * @param Init - we are performing initial configuration if true
   * @param Parameter - parameter for the command
   * @return The result string of the operation
   */
  @Override
  public String processControlEvent(String Command, boolean Init, String Parameter) {
    SearchGroup tmpSearchGroup;
    Collection<String> tmpGroups;
    Iterator<String> GroupIter;
    String tmpGroupName;
    int Objects = 0;
    int ResultCode = -1;

    // Return the number of objects in the cache
    if (Command.equalsIgnoreCase(SERVICE_GROUP_COUNT)) {
      return Integer.toString(GroupCache.size());
    }

    if (Command.equalsIgnoreCase(SERVICE_OBJECT_COUNT)) {
      tmpGroups = GroupCache.keySet();
      GroupIter = tmpGroups.iterator();

      while (GroupIter.hasNext()) {
        tmpGroupName = GroupIter.next();
        tmpSearchGroup = GroupCache.get(tmpGroupName);
        Objects += tmpSearchGroup.SearchGroup.size();
      }

      return Integer.toString(Objects);
    }

    // Return the number of objects in the cache
    if (Command.equalsIgnoreCase(SERVICE_DUMP_MAP)) {
      // onl< dump on a positive command
      if (Parameter.equalsIgnoreCase("true")) {
        DumpMapData();
      }

      ResultCode = 0;
    }

    if (ResultCode == 0) {
      OpenRate.getOpenRateFrameworkLog()
          .debug(LogUtil.LogECICacheCommand(getSymbolicName(), Command, Parameter));

      return "OK";
    } else {
      return super.processControlEvent(Command, Init, Parameter);
    }
  }
  /**
   * Evaluate an input against the search group. This is the generalised from which you may want to
   * create specialised versions for a defined number of parameters, for reasons of performance.
   *
   * <p>This function returns all of the entries that are matched, in priority order. This is useful
   * for aggregation processing etc.
   *
   * @param Group The Regex group to search
   * @param Parameters The list of fields to search
   * @return List of all matches
   */
  public ArrayList<String> getAllEntries(String Group, String[] Parameters) {
    int i;
    SearchGroup tmpSearchGroup;
    SearchMap tmpSearchMap;
    Pattern tmpPattern;
    boolean found;
    double tmpParamValue;
    ArrayList<String> matches;

    matches = new ArrayList<>();

    // recover the object
    tmpSearchGroup = GroupCache.get(Group);

    if (tmpSearchGroup == null) {
      // Return a default value, we did not find the group
      return matches;
    } else {
      // Iterate thorough the entries in the group
      Iterator<SearchMap> GroupIter = tmpSearchGroup.SearchGroup.listIterator();

      while (GroupIter.hasNext()) {
        tmpSearchMap = GroupIter.next();

        // Initialise the found flag and the counter
        found = true;
        i = 0;

        // Now check the elements of the map
        while ((i < Parameters.length) & found) {
          switch (tmpSearchMap.matchType[i]) {
              // Regex inclusion case
            case 0:
              {
                tmpPattern = tmpSearchMap.matchPattern[i];

                if (!tmpPattern.matcher(Parameters[i]).matches()) {
                  // We did not get a match, move on
                  found = false;
                }
                break;
              }

              // Regex exclusion case
            case 6:
              {
                tmpPattern = tmpSearchMap.matchPattern[i];

                if (tmpPattern.matcher(Parameters[i]).matches()) {
                  // We did not get a match, move on
                  found = false;
                }
                break;
              }

              // "=" case
            case 1:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpSearchMap.matchValue[i] != tmpParamValue) {
                  // We did not get a match, move on
                  found = false;
                }
                break;
              }

              // ">" case
            case 2:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue <= tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  found = false;
                }
                break;
              }

              // "<" case
            case 3:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue >= tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  found = false;
                }
                break;
              }

              // ">=" case
            case 4:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue < tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  found = false;
                }
                break;
              }

              // "<=" case
            case 5:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue > tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  found = false;
                }
                break;
              }
          }

          // Increment the loop counter
          i++;
        }

        if (found) {
          matches.add(tmpSearchMap.Results.get(0));
        }
      }

      return matches;
    }
  }
  /**
   * Evaluate an input against the search group. This is the generalised from which you may want to
   * create specialised versions for a defined number of parameters, for reasons of performance.
   *
   * <p>This function returns only the first match.
   *
   * @param Group The Regular expression group to search
   * @param Parameters The list of fields to search
   * @return Result The result of the search as a SearchMap object
   */
  private SearchMap getMatchingSearchResult(String Group, String[] Parameters) {
    int i;
    SearchGroup tmpSearchGroup;
    SearchMap tmpSearchMap;
    Pattern tmpPattern;
    boolean Found;
    double tmpParamValue;

    // recover the object
    tmpSearchGroup = GroupCache.get(Group);

    if (tmpSearchGroup == null) {
      // Return a default value
      return null;
    } else {
      // Iterate thorough the entries in the group
      Iterator<SearchMap> GroupIter = tmpSearchGroup.SearchGroup.listIterator();

      while (GroupIter.hasNext()) {
        tmpSearchMap = GroupIter.next();

        // Initialise the found flag and the counter
        Found = true;
        i = 0;

        // Now check the elements of the map
        while ((i < Parameters.length) & Found) {
          switch (tmpSearchMap.matchType[i]) {
              // Regex inclusion case
            case 0:
              {
                tmpPattern = tmpSearchMap.matchPattern[i];

                if (Parameters[i] == null) {
                  // we cannot match on null values - warn once and out...
                  OpenRate.getOpenRateFrameworkLog()
                      .warning(
                          "Null value found in regex match on parameter <"
                              + i
                              + "> in module <"
                              + getSymbolicName()
                              + ">");
                  return null;
                }

                if (!tmpPattern.matcher(Parameters[i]).matches()) {
                  // We did not get a match, move on
                  Found = false;
                }
                break;
              }

              // Regex exclusion case
            case 6:
              {
                tmpPattern = tmpSearchMap.matchPattern[i];

                if (tmpPattern.matcher(Parameters[i]).matches()) {
                  // We did not get a match, move on
                  Found = false;
                }
                break;
              }

              // "=" case
            case 1:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpSearchMap.matchValue[i] != tmpParamValue) {
                  // We did not get a match, move on
                  Found = false;
                }
                break;
              }

              // ">" case
            case 2:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue <= tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  Found = false;
                }
                break;
              }

              // "<" case
            case 3:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue >= tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  Found = false;
                }
                break;
              }

              // ">=" case
            case 4:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue < tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  Found = false;
                }
                break;
              }

              // "<=" case
            case 5:
              {
                tmpParamValue = Double.parseDouble(Parameters[i]);
                if (tmpParamValue > tmpSearchMap.matchValue[i]) {
                  // We did not get a match, move on
                  Found = false;
                }
                break;
              }
          }

          // Increment the loop counter
          i++;
        }

        if (Found) {
          return tmpSearchMap;
        }
      }

      // Return a default value - we found nothing
      return null;
    }
  }
  /**
   * Add a value into the Regex Map Cache, defining the result value that should be returned in the
   * case of a match. The order of evaluation of the items in the group is the order that they are
   * defined, but it would be a simple task to order them by some value after loading.
   *
   * @param Group The Regex group to add this pattern to
   * @param fields The list of fields to add to the group
   * @param ResultList The list of result fields to add
   * @throws OpenRate.exception.InitializationException
   */
  private void addEntry(String Group, String[] fields, ArrayList<String> ResultList)
      throws InitializationException {
    int i;
    SearchMap tmpSearchMap;
    SearchGroup tmpSearchGroup;
    String Helper;
    String FirstChar;
    String SecondChar;
    String valueToParse;
    String[] checkedFields;
    ArrayList<String> checkedResultList;

    // Allow the user to check the input fields
    checkedFields = validateSearchMap(fields);

    // Allow the user to check the return fields
    checkedResultList = validateResultList(ResultList);

    // See if we already know this group, if not add it
    if (GroupCache.containsKey(Group)) {
      // Get the existing value
      tmpSearchGroup = GroupCache.get(Group);
    } else {
      // We don't know it, so add it
      tmpSearchGroup = new SearchGroup();
      tmpSearchGroup.SearchGroup = new ArrayList<>();
      GroupCache.put(Group, tmpSearchGroup);
    }

    // Create the new search Object.
    tmpSearchMap = new SearchMap();

    // Compile and add the search map
    tmpSearchMap.matchPattern = new Pattern[checkedFields.length];
    tmpSearchMap.matchType = new int[checkedFields.length];
    tmpSearchMap.matchValue = new double[checkedFields.length];

    for (i = 0; i < fields.length; i++) {
      // get the short version of the string for understanding what it is
      Helper = fields[i].replaceAll(" ", "") + "  ";

      FirstChar = Helper.substring(0, 1);
      SecondChar = Helper.substring(1, 2);

      if ((FirstChar.equals("<")) | (FirstChar.equals(">")) | (FirstChar.equals("="))) {
        // try to parse for simple numerical comparison
        if (FirstChar.equals("=")) {
          tmpSearchMap.matchType[i] = 1;
          valueToParse = Helper.substring(1).trim();

          try {
            tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
          } catch (NumberFormatException ex) {
            throw new InitializationException(
                "Could not parse value <" + valueToParse + "> as a double value",
                getSymbolicName());
          }

          continue;
        }

        if (FirstChar.equals(">")) {
          if (SecondChar.equals("=")) {
            tmpSearchMap.matchType[i] = 4;
            valueToParse = Helper.substring(2).trim();
            try {
              tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
            } catch (NumberFormatException ex) {
              throw new InitializationException(
                  "Could not parse value <" + valueToParse + "> as a double value",
                  getSymbolicName());
            }

            continue;
          } else {
            // we got this far, must be just ">"
            tmpSearchMap.matchType[i] = 2;
            valueToParse = Helper.substring(1).trim();

            try {
              tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
            } catch (NumberFormatException ex) {
              throw new InitializationException(
                  "Could not parse value <" + valueToParse + "> as a double value",
                  getSymbolicName());
            }

            continue;
          }
        }

        if (FirstChar.equals("<")) {
          if (SecondChar.equals("=")) {
            tmpSearchMap.matchType[i] = 5;
            valueToParse = Helper.substring(2).trim();

            try {
              tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
            } catch (NumberFormatException ex) {
              throw new InitializationException(
                  "Could not parse value <" + valueToParse + "> as a double value",
                  getSymbolicName());
            }
          } else {
            // we got this far, must be just "<"
            tmpSearchMap.matchType[i] = 3;
            valueToParse = Helper.substring(1).trim();

            try {
              tmpSearchMap.matchValue[i] = Double.parseDouble(valueToParse);
            } catch (NumberFormatException ex) {
              throw new InitializationException(
                  "Could not parse value <" + valueToParse + "> as a double value",
                  getSymbolicName());
            }
          }
        }
      } else {
        if (FirstChar.equals("!")) {
          // This is a regex negation, remove the ! and set the flag and regex
          // for the rest
          tmpSearchMap.matchPattern[i] = Pattern.compile(fields[i].substring(1));
          tmpSearchMap.matchType[i] = 6;
        } else {
          // if we got this far it is Real Regex inclusion
          try {
            tmpSearchMap.matchPattern[i] = Pattern.compile(fields[i]);
          } catch (PatternSyntaxException pse) {
            message =
                "Error compiling regex pattern <"
                    + fields[i]
                    + "> in module <"
                    + getSymbolicName()
                    + ">. message <"
                    + pse.getMessage()
                    + ">";
            OpenRate.getOpenRateFrameworkLog().error(message);
            throw new InitializationException(message, getSymbolicName());
          }

          tmpSearchMap.matchType[i] = 0;
        }
      }
    }

    tmpSearchMap.Results = checkedResultList;
    tmpSearchGroup.SearchGroup.add(tmpSearchMap);
  }