public static int findEndOfMethodArgsIndex(CharSequence string, int startOfMethodArgsIndex) {
        boolean isDoubleQuoted = false;
        boolean isSingleQuoted = false;
        int nestingLevel = 0;
        for (int charIndex = startOfMethodArgsIndex; charIndex < string.length(); charIndex++) {
            boolean isCurrentCharEscaped = charIndex > 0 && string.charAt(charIndex - 1) == '\\';
            switch (string.charAt(charIndex)) {
                case '(':
                    if (!isDoubleQuoted && !isSingleQuoted) {
                        nestingLevel++;
                    }
                    break;

                case ')':
                    if (!isDoubleQuoted && !isSingleQuoted) {
                        nestingLevel--;
                        if (nestingLevel == 0) {
                            return charIndex;
                        }
                    }
                    break;

                case '"':
                    if (isCurrentCharEscaped || isSingleQuoted) {
                        // ignore escaped double quote and double quote inside single quotes (e.g 'text " text')
                        continue;
                    }
                    isDoubleQuoted = !isDoubleQuoted;
                    break;

                case '\'':
                    if (isCurrentCharEscaped || isDoubleQuoted) {
                        // ignore escaped single quote and single quote inside double quotes (e.g. "text ' text")
                        continue;
                    }
                    isSingleQuoted = !isSingleQuoted;
                    break;

                default:
                    // nothing to do, just continue with next character
            }
        }

        return -1;
    }
@Test
    public void testFindEndOfMethodArgsIndex() {
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"myId\")", 12);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"myId\").call()", 12);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId('myId')", 12);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId('myId').call()", 12);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my'Id\")", 13);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my'Id\").call()", 13);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my'Id'\")", 14);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my'Id'\").call()", 14);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId('my\"Id\"')", 14);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId('my\"Id\"').call()", 14);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId('my\"Id')", 13);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId('my\"Id').call()", 13);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my\\\"Id\")", 14);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my\\\"Id\").call()", 14);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId('myId', 'something')", 25);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"myId\", \"something\")", 25);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my'Id\", \"somet'hing\")", 27);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my'Id'\", \"somet'hing\")", 28);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setId(\"my'(Id\", \"somet'(hing'\")", 30);

        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setObject(new Object())", 22);
        findEndOfMethodArgsIndexAndAssertItEqualsToExpected("setObject(new Object(\"string param\"))", 36);
    }