@SuppressWarnings("AssignmentToForLoopParameter")
  public static void transformCriteria(@NotNull String criteria, @NotNull MatchOptions options) {
    final StringBuilder pattern = new StringBuilder();
    int anonymousTypedVarsCount = 0;
    boolean targetFound = false;

    final MatchVariableConstraint context = options.addNewVariableConstraint(Configuration.CONTEXT_VAR_NAME);

    final int length = criteria.length();
    for (int index = 0; index < length; ++index) {
      char ch = criteria.charAt(index);

      if (index == 0 && ch == '[') {
        index = handleTypedVarCondition(0, criteria, context);
        if (index == length) break;
        ch = criteria.charAt(index);
      }
      if (ch == '\\' && index + 1 < length) {
        ch = criteria.charAt(++index);
      }
      else if (ch == '\'') {
        final int newIndex = handleCharacterLiteral(criteria, index, pattern);
        if (newIndex != index) {
          index = newIndex;
          continue;
        }

        // typed variable; eat the name of typed var
        int endIndex = ++index;
        while (endIndex < length && Character.isJavaIdentifierPart(criteria.charAt(endIndex))) endIndex++;
        if (endIndex == index) throw new MalformedPatternException(SSRBundle.message("error.expected.character"));

        boolean target = true;
        final String typedVar;
        if (criteria.charAt(index) == '_') {
          target = false;

          if (endIndex == index + 1) {
            // anonymous var, make it unique for the case of constraints
            anonymousTypedVarsCount++;
            typedVar = "_" + anonymousTypedVarsCount;
          }
          else {
            typedVar = criteria.substring(index + 1, endIndex);
          }
        }
        else {
          typedVar = criteria.substring(index, endIndex);
        }

        pattern.append("$").append(typedVar).append("$");
        index = endIndex;
        MatchVariableConstraint constraint = options.getVariableConstraint(typedVar);
        boolean constraintCreated = false;

        if (constraint == null) {
          constraint = new MatchVariableConstraint(typedVar);
          constraintCreated = true;
        }

        // Check the number of occurrences for typed variable
        final int savedIndex = index;
        int minOccurs = 1;
        int maxOccurs = 1;
        boolean greedy = true;
        if (index < length) {
          ch = criteria.charAt(index);
          if (ch == '+') {
            maxOccurs = Integer.MAX_VALUE;
            ++index;
          }
          else if (ch == '?') {
            minOccurs = 0;
            ++index;
          }
          else if (ch == '*') {
            minOccurs = 0;
            maxOccurs = Integer.MAX_VALUE;
            ++index;
          }
          else if (ch == '{') {
            ++index;
            minOccurs = -1;
            maxOccurs = -1;
            while (index < length && (ch = criteria.charAt(index)) >= '0' && ch <= '9') {
              if (minOccurs < 0) minOccurs = 0;
              minOccurs = (minOccurs * 10) + (ch - '0');
              if (minOccurs < 0) throw new MalformedPatternException(SSRBundle.message("error.overflow"));
              ++index;
            }

            if (ch == ',') {
              ++index;

              while (index < length && (ch = criteria.charAt(index)) >= '0' && ch <= '9') {
                if (maxOccurs < 0) maxOccurs = 0;
                maxOccurs = (maxOccurs * 10) + (ch - '0');
                if (maxOccurs < 0) throw new MalformedPatternException(SSRBundle.message("error.overflow"));
                ++index;
              }
            }
            else {
              maxOccurs = -2;
            }

            if (ch != '}') {
              if (minOccurs < 0 && maxOccurs < 0) throw new MalformedPatternException(SSRBundle.message("error.expected.digit"));
              if (maxOccurs < 0) {
                throw new MalformedPatternException(SSRBundle.message("error.expected.brace1"));
              }
              else {
                throw new MalformedPatternException(SSRBundle.message("error.expected.brace2"));
              }
            }
            if (minOccurs < 0 && maxOccurs < 0) {
              throw new MalformedPatternException(SSRBundle.message("error.empty.quantifier"));
            }
            else if (minOccurs == -1) {
              minOccurs = 0;
            }
            else if (maxOccurs == -1) {
              maxOccurs = Integer.MAX_VALUE;
            }
            else if (maxOccurs == -2) maxOccurs = minOccurs;
            ++index;
          }

          if (index < length) {
            ch = criteria.charAt(index);
            if (ch == '?') {
              greedy = false;
              ++index;
            }
          }
        }

        if (constraintCreated) {
          constraint.setMinCount(minOccurs);
          constraint.setMaxCount(maxOccurs);
          constraint.setGreedy(greedy);
          constraint.setPartOfSearchResults(target);
          if (targetFound && target) {
            throw new MalformedPatternException(SSRBundle.message("error.only.one.target.allowed"));
          }
          targetFound |= target;
        }
        else if (savedIndex != index) {
          throw new MalformedPatternException(SSRBundle.message("error.condition.only.on.first.variable.reference"));
        }

        if (index < length && criteria.charAt(index) == ':') {
          ++index;
          if (index >= length) throw new MalformedPatternException(SSRBundle.message("error.expected.condition", ":"));
          ch = criteria.charAt(index);
          if (ch == ':') {
            // double colon instead of condition
            pattern.append(ch);
          }
          else {
            if (!constraintCreated) {
              throw new MalformedPatternException(SSRBundle.message("error.condition.only.on.first.variable.reference"));
            }
            index = handleTypedVarCondition(index, criteria, constraint);
          }
        }

        if (constraintCreated) {
          options.addVariableConstraint(constraint);
        }

        if (index == length) break;
        // rewind to process white space or unrelated symbol
        index--;
        continue;
      }

      pattern.append(ch);
    }

    options.setSearchPattern(pattern.toString());
  }
@SuppressWarnings("AssignmentToForLoopParameter")
  public static void transformCriteria(@NotNull String criteria, @NotNull MatchOptions options)