@Override
    public String combineOutputParams(String sql, IndexedSupplier<String> outputParams) {
        final int length = sql.length();
        final StringBuilder normalized = new StringBuilder(length + 16);
        for (int i = 0; i < length; i++) {
            final char ch = sql.charAt(i);
            switch (ch) {
                // COMMENT start check
                case '/':
                    // comment state
                    int lookAhead1Char = lookAhead1(sql, i);
                    // multi line comment and oracle hint /*+ */
                    if (lookAhead1Char == '*') {
                        normalized.append("/*");
                        i += 2;
                        for (; i < length; i++) {
                            char stateCh = sql.charAt(i);
                            if (stateCh == '*') {
                                if (lookAhead1(sql, i) == '/') {
                                    normalized.append("*/");
                                    i++;
                                    break;
                                }
                            }
                            normalized.append(stateCh);
                        }
                        break;
                        // single line comment
                    } else if (lookAhead1Char == '/') {
                        normalized.append("//");
                        i += 2;
                        i = readLine(sql, normalized, i);
                        break;

                    } else {
                        // unary operator
//                        numberTokenStartEnable = true;
                        normalized.append(ch);
                        break;
                    }
//                case '#'
//                  # is a single line comment in mysql
                case '-':
                    // single line comment state
                    if (lookAhead1(sql, i) == '-') {
                        normalized.append("--");
                        i += 2;
                        i = readLine(sql, normalized, i);
                        break;
                    } else {
                        // unary operator
//                        numberTokenStartEnable = true;
                        normalized.append(ch);
                        break;
                    }

                    // number start check
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    // http://www.h2database.com/html/grammar.html look at the state machine more
                    if (lookAhead1(sql, i) == NEXT_TOKEN_NOT_EXIST) {
                        normalized.append(ch);
                        break;
                    }
                    StringBuilder outputIndex = new StringBuilder();
                    outputIndex.append(ch);
                    // number token start
                    i++;
                    tokenEnd:
                    for (; i < length; i++) {
                        final char stateCh = sql.charAt(i);
                        switch (stateCh) {
                            case '0':
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                            case '8':
                            case '9':
                                if (lookAhead1(sql, i) == NEXT_TOKEN_NOT_EXIST) {
                                    outputIndex.append(stateCh);
                                    normalized.append(outputIndex.toString());
                                    break tokenEnd;
                                }
                                outputIndex.append(stateCh);
                                break;
                            case NUMBER_REPLACE:
                                int numberIndex = 0;
                                try {
                                    numberIndex = Integer.parseInt(outputIndex.toString());
                                } catch (NumberFormatException e) {
                                    // just append for invalid parameters
                                    normalized.append(outputIndex.toString());
                                    normalized.append(NUMBER_REPLACE);
                                    break tokenEnd;
                                }
                                try {
                                    String replaceNumber = outputParams.get(numberIndex);
                                    normalized.append(replaceNumber);
                                } catch (IndexOutOfBoundsException e) {
                                    // just append for invalid parameters
                                    normalized.append(outputIndex.toString());
                                    normalized.append(NUMBER_REPLACE);
                                    break tokenEnd;
                                }
                                break tokenEnd;

                            case SYMBOL_REPLACE:
                                int symbolIndex = 0;
                                try {
                                    symbolIndex = Integer.parseInt(outputIndex.toString());
                                } catch (NumberFormatException e) {
                                    // just append for invalid parameters
                                    normalized.append(outputIndex.toString());
                                    normalized.append(SYMBOL_REPLACE);
                                }
                                try {
                                    String replaceSymbol = outputParams.get(symbolIndex);
                                    normalized.append(replaceSymbol);
                                } catch (IndexOutOfBoundsException e) {
                                    normalized.append(outputIndex.toString());
                                    normalized.append(SYMBOL_REPLACE);
                                }
                                break tokenEnd;

                            default:
                                // should look at the token outside the loop - not here
//                                    outputParam.append(SEPARATOR);
                                normalized.append(outputIndex.toString());
                                i--;
                                break tokenEnd;
                        }
                    }
                    break;

                default:
                    normalized.append(ch);
                    break;
            }
        }

        return normalized.toString();
    }
look up the next character in a string

@param sql
@param index
@return
commons-profiler/src/main/java/com/navercorp/pinpoint/common/profiler/sql/DefaultSqlNormalizer.java