/**
   * Reads the next token name from the attribute syntax definition, skipping over any leading or
   * trailing spaces, and appends it to the provided buffer.
   *
   * @param valueStr The string representation of the attribute syntax definition.
   * @param tokenName The buffer into which the token name will be written.
   * @param startPos The position in the provided string at which to start reading the token name.
   * @return The position of the first character that is not part of the token name or one of the
   *     trailing spaces after it.
   * @throws DirectoryException If a problem is encountered while reading the token name.
   */
  private static int readTokenName(String valueStr, StringBuilder tokenName, int startPos)
      throws DirectoryException {
    // Skip over any spaces at the beginning of the value.
    char c = '\u0000';
    int length = valueStr.length();
    while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' ')) {
      startPos++;
    }

    if (startPos >= length) {
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // Read until we find the next space.
    while ((startPos < length) && ((c = valueStr.charAt(startPos++)) != ' ')) {
      tokenName.append(c);
    }

    // Skip over any trailing spaces after the value.
    while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' ')) {
      startPos++;
    }

    // Return the position of the first non-space character after the token.
    return startPos;
  }
  /**
   * Reads the value of a string enclosed in single quotes, skipping over the quotes and any leading
   * or trailing spaces, and appending the string to the provided buffer.
   *
   * @param valueStr The user-provided representation of the attribute type definition.
   * @param valueBuffer The buffer into which the user-provided representation of the value will be
   *     placed.
   * @param startPos The position in the provided string at which to start reading the quoted
   *     string.
   * @return The position of the first character that is not part of the quoted string or one of the
   *     trailing spaces after it.
   * @throws DirectoryException If a problem is encountered while reading the quoted string.
   */
  private static int readQuotedString(String valueStr, StringBuilder valueBuffer, int startPos)
      throws DirectoryException {
    // Skip over any spaces at the beginning of the value.
    char c = '\u0000';
    int length = valueStr.length();
    while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' ')) {
      startPos++;
    }

    if (startPos >= length) {
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // The next character must be a single quote.
    if (c != '\'') {
      Message message =
          ERR_ATTR_SYNTAX_LDAPSYNTAX_EXPECTED_QUOTE_AT_POS.get(
              valueStr, startPos, String.valueOf(c));
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // Read until we find the closing quote.
    startPos++;
    while ((startPos < length) && ((c = valueStr.charAt(startPos)) != '\'')) {
      valueBuffer.append(c);
      startPos++;
    }

    // Skip over any trailing spaces after the value.
    startPos++;
    while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' ')) {
      startPos++;
    }

    // If we're at the end of the value, then that's illegal.
    if (startPos >= length) {
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // Return the position of the first non-space character after the token.
    return startPos;
  }
  /**
   * Reads the value for an "extra" parameter. It will handle a single unquoted word (which is
   * technically illegal, but we'll allow it), a single quoted string, or an open parenthesis
   * followed by a space-delimited set of quoted strings or unquoted words followed by a close
   * parenthesis.
   *
   * @param valueStr The string containing the information to be read.
   * @param valueList The list of "extra" parameter values read so far.
   * @param startPos The position in the value string at which to start reading.
   * @return The "extra" parameter value that was read.
   * @throws DirectoryException If a problem occurs while attempting to read the value.
   */
  private static int readExtraParameterValues(String valueStr, List<String> valueList, int startPos)
      throws DirectoryException {
    // Skip over any leading spaces.
    int length = valueStr.length();
    char c = '\u0000';
    while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' ')) {
      startPos++;
    }

    if (startPos >= length) {
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    // Look at the next character.  If it is a quote, then parse until the next
    // quote and end.  If it is an open parenthesis, then parse individual
    // values until the close parenthesis and end.  Otherwise, parse until the
    // next space and end.
    if (c == '\'') {
      // Parse until the closing quote.
      StringBuilder valueBuffer = new StringBuilder();
      startPos++;
      while ((startPos < length) && ((c = valueStr.charAt(startPos)) != '\'')) {
        valueBuffer.append(c);
        startPos++;
      }
      startPos++;
      valueList.add(valueBuffer.toString());
    } else if (c == '(') {
      startPos++;
      // We're expecting a list of values. Quoted, space separated.
      while (true) {
        // Skip over any leading spaces;
        while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' ')) {
          startPos++;
        }

        if (startPos >= length) {
          Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }

        if (c == ')') {
          // This is the end of the list.
          startPos++;
          break;
        } else if (c == '(') {
          // This is an illegal character.
          Message message =
              ERR_ATTR_SYNTAX_LDAPSYNTAX_EXTENSION_INVALID_CHARACTER.get(valueStr, startPos);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        } else if (c == '\'') {
          // We have a quoted string
          StringBuilder valueBuffer = new StringBuilder();
          startPos++;
          while ((startPos < length) && ((c = valueStr.charAt(startPos)) != '\'')) {
            valueBuffer.append(c);
            startPos++;
          }

          valueList.add(valueBuffer.toString());
          startPos++;
        } else {
          // Consider unquoted string
          StringBuilder valueBuffer = new StringBuilder();
          while ((startPos < length) && ((c = valueStr.charAt(startPos)) != ' ')) {
            valueBuffer.append(c);
            startPos++;
          }

          valueList.add(valueBuffer.toString());
        }

        if (startPos >= length) {
          Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
          throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
        }
      }
    } else {
      // Parse until the next space.
      StringBuilder valueBuffer = new StringBuilder();
      while ((startPos < length) && ((c = valueStr.charAt(startPos)) != ' ')) {
        valueBuffer.append(c);
        startPos++;
      }

      valueList.add(valueBuffer.toString());
    }

    // Skip over any trailing spaces.
    while ((startPos < length) && (valueStr.charAt(startPos) == ' ')) {
      startPos++;
    }

    if (startPos >= length) {
      Message message = ERR_ATTR_SYNTAX_LDAPSYNTAX_TRUNCATED_VALUE.get(valueStr);
      throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
    }

    return startPos;
  }