docs: protected String transformQuery(String query, QueryTransformer qt) {
          StringBuffer modified_query = new StringBuffer();
          Matcher matcher = qt.m_queryPattern.matcher(query);
          int count = 0;
          while (matcher.find()) {
              StringBuffer replaceText = new StringBuffer(qt.m_initialText);
              String wholeMatch = matcher.group();
              String lastGroup = wholeMatch;
              List<String> groups = new ArrayList<String>();
              if (qt.m_debugPrint) {
                  if (count < 1) {
                      System.out.println("In NonVoltDBBackend.transformQuery,\n  with query    : " + query);
                      System.out.println("  QueryTransformer:\n    " + qt);
                  }
                  System.out.println("  " + ++count + ".wholeMatch: " + wholeMatch);
              }
              for (String groupName : qt.m_groups) {
                  String group = matcher.group(groupName);
                  groups.add(group);
                  if (qt.m_debugPrint) {
                      System.out.println("    group     : " + group);
                  }
                  if (group == null) {
                      continue;
                  } else if (!qt.m_useWholeMatch) {
                      String groupValue = group, suffixValue = qt.m_suffix;
                      // Check for the case where a multiplier & minimum are used
                      if (qt.m_multiplier != null && qt.m_minimum != null) {
                          groupValue = Long.toString(Math.round(Math.max(Integer.parseInt(group) * qt.m_multiplier, qt.m_minimum)));
                      }
                      // Check for the ending that indicates to use the alternate suffix
                      if (qt.m_altSuffix != null && group.toUpperCase().endsWith(qt.m_useAltSuffixAfter)) {
                          suffixValue = qt.m_altSuffix;
                      }
                      // If a specific column type was specified for this
                      // QueryTransformer, check that it matches
                      if (columnTypeMatches(qt.m_columnType, qt.m_allColumnsShouldMatchType,
                                            query, qt.m_debugPrint, groupValue)) {
                          // Make sure not to swallow up extra ')', in this group
                          replaceText.append(handleParens(groupValue, qt.m_prefix, suffixValue, qt.m_debugPrint));
                      } else {
                          // Since column type does not match, don't change anything
                          replaceText.append(wholeMatch);
                      }
                  }
                  lastGroup = group;
              }
              if (qt.m_debugPrint) {
                  System.out.println("    lastGroup : " + lastGroup);
              }
              if (qt.m_useWholeMatch) {
                  boolean noChangesNeeded = false;
                  // If the matched string contains one of the strings in the
                  // (possibly empty) list of excluded strings, then no changes
                  // are needed
                  if (qt.m_exclude != null) {
                      for (String excl : qt.m_exclude) {
                          if (wholeMatch.contains(excl)) {
                              noChangesNeeded = true;
                              if (qt.m_debugPrint) {
                                  System.out.println("    noChangesNeeded, because wholeMatch contains excl:");
                                  System.out.println("      wholeMatch: " + wholeMatch);
                                  System.out.println("      m_exclude : " + qt.m_exclude);
                                  System.out.println("      excl      : " + excl);
                              }
                          }
                      }
                  }
                  // When columnType is specified, it means only modify queries
                  // that use that type; so if the relevant column(s) are not of
                  // the specified type, no changes are needed
                  if (!noChangesNeeded) {
                      String[] groupsArray = new String[groups.size()];
                      groupsArray = groups.toArray(groupsArray);
                      if (!columnTypeMatches(qt.m_columnType, qt.m_allColumnsShouldMatchType,
                              query, qt.m_debugPrint, groupsArray)) {
                          noChangesNeeded = true;
                          if (qt.m_debugPrint) {
                              System.out.println("    noChangesNeeded, because columnType(s) do not Match");
                          }
                      }
                  }
                  if (noChangesNeeded) {
                      // Make no changes to the query, if one of the excluded
                      // strings was found, or when the columnType is specified,
                      // but does not match the column type(s) found in this query
                      replaceText.append(wholeMatch);
                  } else {
                      // Check for the case where the group (or the whole text) is to be replaced with replacementText
                      if (qt.m_replacementText != null) {
                          wholeMatch = wholeMatch.replace(lastGroup, qt.m_replacementText);
                      }
                      // Check for the case where each group is to be replaced using groupReplacementTexts
                      if (qt.m_groupReplacementTexts != null && !qt.m_groupReplacementTexts.isEmpty()) {
                          for (int i=0; i < Math.min(groups.size(), qt.m_groupReplacementTexts.size()); i++) {
                              if (groups.get(i) != null && qt.m_groupReplacementTexts.get(i) != null) {
                                  if (qt.m_debugPrint) {
                                      String instances = (qt.m_groupReplaceAll ? "all instances" : "first instance");
                                      System.out.println("    replacing "+instances+" of groups(i) "
                                                        + "with groupReplacementTexts(i).");
                                      System.out.println("      wholeMatch: " + wholeMatch);
                                      System.out.println("      i         : " + i);
                                      System.out.println("      groups [i]: " + groups.get(i));
                                      System.out.println("      gRepTxt[i]: " + qt.m_groupReplacementTexts.get(i));
                                  }
                                  if (qt.m_groupReplaceAll) {
                                      // Extra escaping to make sure that "\" remains as "\" and "$"
                                      // remains "$", despite replace's efforts to change them
                                      wholeMatch = wholeMatch.replace ( groups.get(i), protectSpecialChars (
                                                       qt.m_groupReplacementTexts.get(i), qt.m_debugPrint ));
                                  } else {
                                      wholeMatch = wholeMatch.replaceFirst ( groups.get(i), protectSpecialChars (
                                                       qt.m_groupReplacementTexts.get(i), qt.m_debugPrint ));
                                  }
                              }
                          }
                          if (qt.m_debugPrint) {
                              System.out.println("    wholeMatch  : " + wholeMatch);
                          }
                      }
                      // Make sure not to swallow up extra ')', in whole match; and
                      // replace symbols like {foo} with the appropriate group values
                      replaceText.append(replaceGroupNameVariables(
                              handleParens(wholeMatch, qt.m_prefix, qt.m_suffix, qt.m_debugPrint),
                              qt.m_groups, groups, qt.m_debugPrint));
                  }
              }
              if (qt.m_debugPrint) {
                  System.out.println("  replaceText : " + replaceText);
              }
              // Extra escaping to make sure that "\" remains as "\" and "$"
              // remains "$", despite appendReplacement's efforts to change them
              matcher.appendReplacement ( modified_query,
                    protectSpecialChars ( replaceText.toString(), qt.m_debugPrint ));
          }
          matcher.appendTail(modified_query);
          if ((DEBUG || qt.m_debugPrint) && !query.equalsIgnoreCase(modified_query.toString())) {
              System.out.println("In NonVoltDBBackend.transformQuery,\n  with query    : " + query);
              System.out.println("  modified_query: " + modified_query);
          }
          return modified_query.toString();
      }