/**
   * This method parses a string and build a set of sorting instructions. The parsing may only be
   * partial on the case the rules are to be merged sometime later.
   *
   * @param stop_on_reset If this parameter is true then the parser stops when it encounters a reset
   *     instruction. In the other case, it tries to parse the subrules and merged it in the same
   *     repository.
   * @param v Output vector for the set of instructions.
   * @param base_offset Offset in the string to begin parsing.
   * @param rules Rules to be parsed.
   * @return -1 if the parser reached the end of the string, an integer representing the offset in
   *     the string at which it stopped parsing.
   * @throws ParseException if something turned wrong during the parsing. To get details decode the
   *     message.
   */
  private int subParseString(boolean stop_on_reset, ArrayList v, int base_offset, String rules)
      throws ParseException {
    boolean ignoreChars = (base_offset == 0);
    int operator = -1;
    StringBuffer sb = new StringBuffer();
    boolean doubleQuote = false;
    boolean eatingChars = false;
    boolean nextIsModifier = false;
    boolean isModifier = false;
    int i;

    main_parse_loop:
    for (i = 0; i < rules.length(); i++) {
      char c = rules.charAt(i);
      int type = -1;

      if (!eatingChars && ((c >= 0x09 && c <= 0x0D) || (c == 0x20))) continue;

      isModifier = nextIsModifier;
      nextIsModifier = false;

      if (eatingChars && c != '\'') {
        doubleQuote = false;
        sb.append(c);
        continue;
      }
      if (doubleQuote && eatingChars) {
        sb.append(c);
        doubleQuote = false;
        continue;
      }

      switch (c) {
        case '!':
          throw new ParseException(
              "Modifier '!' is not yet supported by Classpath", i + base_offset);
        case '<':
          type = CollationSorter.GREATERP;
          break;
        case ';':
          type = CollationSorter.GREATERS;
          break;
        case ',':
          type = CollationSorter.GREATERT;
          break;
        case '=':
          type = CollationSorter.EQUAL;
          break;
        case '\'':
          eatingChars = !eatingChars;
          doubleQuote = true;
          break;
        case '@':
          if (ignoreChars)
            throw new ParseException(
                "comparison list has not yet been started. You may only use" + "(<,;=&)",
                i + base_offset);
          // Inverse the order of secondaries from now on.
          nextIsModifier = true;
          type = CollationSorter.INVERSE_SECONDARY;
          break;
        case '&':
          type = CollationSorter.RESET;
          if (stop_on_reset) break main_parse_loop;
          break;
        default:
          if (operator < 0)
            throw new ParseException("operator missing at " + (i + base_offset), i + base_offset);
          if (!eatingChars
              && ((c >= 0x21 && c <= 0x2F)
                  || (c >= 0x3A && c <= 0x40)
                  || (c >= 0x5B && c <= 0x60)
                  || (c >= 0x7B && c <= 0x7E)))
            throw new ParseException("unquoted punctuation character '" + c + "'", i + base_offset);

          // type = ignoreChars ? CollationSorter.IGNORE : -1;
          sb.append(c);
          break;
      }

      if (type < 0) continue;

      if (operator < 0) {
        operator = type;
        continue;
      }

      if (sb.length() == 0 && !isModifier)
        throw new ParseException("text element empty at " + (i + base_offset), i + base_offset);

      if (operator == CollationSorter.RESET) {
        /* Reposition in the sorting list at the position
         * indicated by the text element.
         */
        String subrules = rules.substring(i);
        ArrayList sorted_rules = new ArrayList();
        int idx;

        // Parse the subrules but do not iterate through all
        // sublist. This is the priviledge of the first call.
        idx = subParseString(true, sorted_rules, base_offset + i, subrules);

        // Merge new parsed rules into the list.
        mergeRules(base_offset + i, sb.toString(), v, sorted_rules);
        sb.setLength(0);

        // Reset state to none.
        operator = -1;
        type = -1;
        // We have found a new subrule at 'idx' but it has not been parsed.
        if (idx >= 0) {
          i += idx - 1;
          continue main_parse_loop;
        } else
          // No more rules.
          break main_parse_loop;
      }

      CollationSorter sorter = new CollationSorter();

      if (operator == CollationSorter.GREATERP) ignoreChars = false;

      sorter.comparisonType = operator;
      sorter.textElement = sb.toString();
      sorter.hashText = sorter.textElement.hashCode();
      sorter.offset = base_offset + rules.length();
      sorter.ignore = ignoreChars;
      sb.setLength(0);

      v.add(sorter);
      operator = type;
    }

    if (operator >= 0) {
      CollationSorter sorter = new CollationSorter();
      int pos = rules.length() + base_offset;

      if ((sb.length() != 0 && nextIsModifier)
          || (sb.length() == 0 && !nextIsModifier && !eatingChars))
        throw new ParseException("text element empty at " + pos, pos);

      if (operator == CollationSorter.GREATERP) ignoreChars = false;

      sorter.comparisonType = operator;
      sorter.textElement = sb.toString();
      sorter.hashText = sorter.textElement.hashCode();
      sorter.offset = base_offset + pos;
      sorter.ignore = ignoreChars;
      v.add(sorter);
    }

    if (i == rules.length()) return -1;
    else return i;
  }