Code review comment: ```suggestion             // if they are found before 'By' ``` ?

Code under review:
public Result parse(MethodInfo methodInfo) {
        String methodName = methodInfo.name();
        ClassInfo repositoryClassInfo = methodInfo.declaringClass();
        String repositoryMethodDescription = "'" + methodName + "' of repository '" + repositoryClassInfo + "'";
        QueryType queryType = getType(methodName);
        if (queryType == null) {
            throw new UnableToParseMethodException("Method " + repositoryMethodDescription
                    + " cannot be parsed. Did you forget to annotate the method with '@Query'?");
        }

        int byIndex = methodName.indexOf("By");
        if ((byIndex == -1) || (byIndex + 2 >= methodName.length())) {
            throw new UnableToParseMethodException("Method " + repositoryMethodDescription
                    + " cannot be parsed as there is no proper 'By' clause in the name.");
        }

        
        Integer topCount = null;
        int firstIndex = methodName.indexOf("First");
        int topIndex = methodName.indexOf("Top");
        if ((firstIndex != -1) || (topIndex != -1)) {
            int maxFirstAndTopIndex = Math.max(firstIndex, topIndex);
            
            
            if (maxFirstAndTopIndex < byIndex) {
                try {
                    String topCountStr = methodName.substring(maxFirstAndTopIndex, byIndex)
                            .replace("Top", "").replace("First", "");
                    if (topCountStr.isEmpty()) {
                        topCount = 1;
                    } else {
                        topCount = Integer.valueOf(topCountStr);
                    }
                } catch (Exception e) {
                    throw new UnableToParseMethodException(
                            "Unable to parse query with limiting results clause. Offending method is "
                                    + repositoryMethodDescription + ".");
                }
            }
        }
        if ((topCount != null) && (queryType != QueryType.SELECT)) {
            throw new UnableToParseMethodException(
                    "When 'Top' or 'First' is specified, the query must be a find query. Offending method is "
                            + repositoryMethodDescription + ".");
        }

        if (methodName.substring(0, byIndex).contains("Distinct")) {
            throw new UnableToParseMethodException(
                    "Distinct is not yet supported. Offending method is " + repositoryMethodDescription + ".");
        }

        
        String afterByPart = methodName.substring(byIndex + 2);
        boolean allIgnoreCase = false;
        if (afterByPart.contains(ALL_IGNORE_CASE)) {
            allIgnoreCase = true;
            afterByPart = afterByPart.replace(ALL_IGNORE_CASE, "");
        }

        
        Sort sort = null;
        if (containsLogicOperator(afterByPart, ORDER_BY)) {
            int orderByIndex = afterByPart.indexOf(ORDER_BY);
            if (orderByIndex + ORDER_BY.length() == afterByPart.length()) {
                throw new UnableToParseMethodException(
                        "A field must by supplied after 'OrderBy' . Offending method is " + repositoryMethodDescription + ".");
            }
            String afterOrderByPart = afterByPart.substring(orderByIndex + ORDER_BY.length());
            afterByPart = afterByPart.substring(0, orderByIndex);
            boolean ascending = true;
            if (afterOrderByPart.endsWith("Asc")) {
                ascending = true;
                afterOrderByPart = afterOrderByPart.replace("Asc", "");
            } else if (afterOrderByPart.endsWith("Desc")) {
                ascending = false;
                afterOrderByPart = afterOrderByPart.replace("Desc", "");
            }
            String orderField = lowerFirstLetter(afterOrderByPart);
            if (!entityContainsField(orderField)) {
                throw new UnableToParseMethodException(
                        "Field " + orderField
                                + " which was configured as the order field does not exist in the entity. Offending method is "
                                + repositoryMethodDescription + ".");
            }

            if (ascending) {
                sort = Sort.ascending(orderField);
            } else {
                sort = Sort.descending(orderField);
            }
        }

        List<String> parts = Collections.singletonList(afterByPart); 
        boolean containsAnd = containsLogicOperator(afterByPart, "And");
        boolean containsOr = containsLogicOperator(afterByPart, "Or");
        if (containsAnd && containsOr) {
            throw new UnableToParseMethodException(
                    "'And' and 'Or' clauses cannot be mixed in a method name - Try specifying the Query with the @Query annotation. Offending method is "
                            + repositoryMethodDescription + ".");
        }
        if (containsAnd) {
            parts = Arrays.asList(afterByPart.split("And"));
        } else if (containsOr) {
            parts = Arrays.asList(afterByPart.split("Or"));
        }

        MutableReference<List<ClassInfo>> mappedSuperClassInfoRef = MutableReference.of(mappedSuperClassInfos);
        StringBuilder where = new StringBuilder();
        int paramsCount = 0;
        for (String part : parts) {
            if (part.isEmpty()) {
                continue;
            }
            String fieldName;
            boolean ignoreCase = false;

            if (part.endsWith(IGNORE_CASE)) {
                ignoreCase = true;
                part = part.replace(IGNORE_CASE, "");
            }

            String operation = getFieldOperation(part);
            if (operation == null) { 
                fieldName = lowerFirstLetter(part);
            } else {
                fieldName = lowerFirstLetter(part.replaceAll(operation, ""));
            }
            FieldInfo fieldInfo = getFieldInfo(fieldName, entityClass, mappedSuperClassInfoRef);
            if (fieldInfo == null) {
                StringBuilder fieldPathBuilder = new StringBuilder(fieldName.length() + 5);
                fieldInfo = resolveNestedField(repositoryMethodDescription, fieldName, fieldPathBuilder);
                fieldName = fieldPathBuilder.toString();
            }
            validateFieldWithOperation(operation, fieldInfo, fieldName, repositoryMethodDescription);
            if ((ignoreCase || allIgnoreCase) && !DotNames.STRING.equals(fieldInfo.type().name())) {
                throw new UnableToParseMethodException(
                        "IgnoreCase cannot be specified for field" + fieldInfo.name() + " because it is not a String type. "
                                + "Offending method is " + repositoryMethodDescription + ".");
            }

            if (where.length() > 0) {
                where.append(containsAnd ? " AND " : " OR ");
            }

            String upperPrefix = (ignoreCase || allIgnoreCase) ? "UPPER(" : "";
            String upperSuffix = (ignoreCase || allIgnoreCase) ? ")" : "";

            where.append(upperPrefix).append(fieldName).append(upperSuffix);
            if ((operation == null) || "Equals".equals(operation) || "Is".equals(operation)) {
                paramsCount++;
                where.append(" = ").append(upperPrefix).append("?").append(paramsCount).append(upperSuffix);
            } else {
                switch (operation) {
                    case "IsNot":
                    case "Not":
                        paramsCount++;
                        where.append(" <> ?").append(paramsCount);
                        break;
                    case "IsNull":
                    case "Null":
                        where.append(" IS null ");
                        break;
                    case "IsNotNull":
                    case "NotNull":
                        where.append(" IS NOT null ");
                        break;
                    case "Between":
                    case "IsBetween":
                        where.append(" BETWEEN ");
                        paramsCount++;
                        where.append("?").append(paramsCount).append(" AND ");
                        paramsCount++;
                        where.append("?").append(paramsCount);
                        break;
                    case "LessThan":
                    case "IsLessThan":
                    case "Before":
                    case "IsBefore":
                        paramsCount++;
                        where.append(" < ?").append(paramsCount);
                        break;
                    case "LessThanEqual":
                    case "IsLessThanEqual":
                        paramsCount++;
                        where.append(" <= ?").append(paramsCount);
                        break;
                    case "GreaterThan":
                    case "IsGreaterThan":
                    case "After":
                    case "IsAfter":
                        paramsCount++;
                        where.append(" > ?").append(paramsCount);
                        break;
                    case "GreaterThanEqual":
                    case "IsGreaterThanEqual":
                        paramsCount++;
                        where.append(" >= ?").append(paramsCount);
                        break;
                    case "Like":
                    case "IsLike":
                        paramsCount++;
                        where.append(" LIKE ?").append(paramsCount);
                        break;
                    case "NotLike":
                    case "IsNotLike":
                        paramsCount++;
                        where.append(" NOT LIKE ?").append(paramsCount);
                        break;
                    case "IsStartingWith":
                    case "StartingWith":
                    case "StartsWith":
                        paramsCount++;
                        where.append(" LIKE CONCAT(").append(upperPrefix).append("?").append(paramsCount).append(upperSuffix)
                                .append(", '%')");
                        break;
                    case "IsEndingWith":
                    case "EndingWith":
                    case "EndsWith":
                        paramsCount++;
                        where.append(" LIKE CONCAT('%', ").append(upperPrefix).append("?").append(paramsCount)
                                .append(upperSuffix).append(")");
                        break;
                    case "IsContaining":
                    case "Containing":
                    case "Contains":
                        paramsCount++;
                        where.append(" LIKE CONCAT('%', ").append(upperPrefix).append("?").append(paramsCount)
                                .append(upperSuffix).append(", '%')");
                        break;
                    case "True":
                    case "False":
                        where.append(" = ").append(operation.toLowerCase());
                        break;
                    case "IsIn":
                    case "In":
                        paramsCount++;
                        where.append(" IN ?").append(paramsCount);
                        break;
                    case "IsNotIn":
                    case "NotIn":
                        paramsCount++;
                        where.append(" NOT IN ?").append(paramsCount);
                        break;
                    case "IsEmpty":
                    case "Empty":
                        where.append(" IS EMPTY");
                        break;
                    case "IsNotEmpty":
                    case "NotEmpty":
                        where.append(" IS NOT EMPTY");
                        break;
                }
            }
        }

        String whereQuery = where.toString().isEmpty() ? "" : " WHERE " + where.toString();
        return new Result(entityClass, "FROM " + getEntityName() + whereQuery, queryType, paramsCount, sort,
                topCount);
    }


Revised code:
public Result parse(MethodInfo methodInfo) {
        String methodName = methodInfo.name();
        ClassInfo repositoryClassInfo = methodInfo.declaringClass();
        String repositoryMethodDescription = "'" + methodName + "' of repository '" + repositoryClassInfo + "'";
        QueryType queryType = getType(methodName);
        if (queryType == null) {
            throw new UnableToParseMethodException("Method " + repositoryMethodDescription
                    + " cannot be parsed. Did you forget to annotate the method with '@Query'?");
        }

        int byIndex = methodName.indexOf("By");
        if ((byIndex == -1) || (byIndex + 2 >= methodName.length())) {
            throw new UnableToParseMethodException("Method " + repositoryMethodDescription
                    + " cannot be parsed as there is no proper 'By' clause in the name.");
        }

        
        Integer topCount = null;
        int minFirstOrTopIndex = Math.min(indexOfOrMaxValue(methodName, "First"), indexOfOrMaxValue(methodName, "Top"));
        
        
        if (minFirstOrTopIndex < byIndex) {
            if (queryType != QueryType.SELECT) {
                throw new UnableToParseMethodException(
                        "When 'Top' or 'First' is specified, the query must be a find query. Offending method is "
                                + repositoryMethodDescription + ".");
            }
            try {
                String topCountStr = methodName.substring(minFirstOrTopIndex, byIndex)
                        .replace("Top", "").replace("First", "");
                if (topCountStr.isEmpty()) {
                    topCount = 1;
                } else {
                    topCount = Integer.valueOf(topCountStr);
                }
            } catch (Exception e) {
                throw new UnableToParseMethodException(
                        "Unable to parse query with limiting results clause. Offending method is "
                                + repositoryMethodDescription + ".");
            }
        }

        if (methodName.substring(0, byIndex).contains("Distinct")) {
            throw new UnableToParseMethodException(
                    "Distinct is not yet supported. Offending method is " + repositoryMethodDescription + ".");
        }

        
        String afterByPart = methodName.substring(byIndex + 2);
        boolean allIgnoreCase = false;
        if (afterByPart.contains(ALL_IGNORE_CASE)) {
            allIgnoreCase = true;
            afterByPart = afterByPart.replace(ALL_IGNORE_CASE, "");
        }

        
        Sort sort = null;
        if (containsLogicOperator(afterByPart, ORDER_BY)) {
            int orderByIndex = afterByPart.indexOf(ORDER_BY);
            if (orderByIndex + ORDER_BY.length() == afterByPart.length()) {
                throw new UnableToParseMethodException(
                        "A field must by supplied after 'OrderBy' . Offending method is " + repositoryMethodDescription + ".");
            }
            String afterOrderByPart = afterByPart.substring(orderByIndex + ORDER_BY.length());
            afterByPart = afterByPart.substring(0, orderByIndex);
            boolean ascending = true;
            if (afterOrderByPart.endsWith("Asc")) {
                ascending = true;
                afterOrderByPart = afterOrderByPart.replace("Asc", "");
            } else if (afterOrderByPart.endsWith("Desc")) {
                ascending = false;
                afterOrderByPart = afterOrderByPart.replace("Desc", "");
            }
            String orderField = lowerFirstLetter(afterOrderByPart);
            if (!entityContainsField(orderField)) {
                throw new UnableToParseMethodException(
                        "Field " + orderField
                                + " which was configured as the order field does not exist in the entity. Offending method is "
                                + repositoryMethodDescription + ".");
            }

            if (ascending) {
                sort = Sort.ascending(orderField);
            } else {
                sort = Sort.descending(orderField);
            }
        }

        List<String> parts = Collections.singletonList(afterByPart); 
        boolean containsAnd = containsLogicOperator(afterByPart, "And");
        boolean containsOr = containsLogicOperator(afterByPart, "Or");
        if (containsAnd && containsOr) {
            throw new UnableToParseMethodException(
                    "'And' and 'Or' clauses cannot be mixed in a method name - Try specifying the Query with the @Query annotation. Offending method is "
                            + repositoryMethodDescription + ".");
        }
        if (containsAnd) {
            parts = Arrays.asList(afterByPart.split("And"));
        } else if (containsOr) {
            parts = Arrays.asList(afterByPart.split("Or"));
        }

        MutableReference<List<ClassInfo>> mappedSuperClassInfoRef = MutableReference.of(mappedSuperClassInfos);
        StringBuilder where = new StringBuilder();
        int paramsCount = 0;
        for (String part : parts) {
            if (part.isEmpty()) {
                continue;
            }
            String fieldName;
            boolean ignoreCase = false;

            if (part.endsWith(IGNORE_CASE)) {
                ignoreCase = true;
                part = part.replace(IGNORE_CASE, "");
            }

            String operation = getFieldOperation(part);
            if (operation == null) { 
                fieldName = lowerFirstLetter(part);
            } else {
                fieldName = lowerFirstLetter(part.replaceAll(operation, ""));
            }
            FieldInfo fieldInfo = getFieldInfo(fieldName, entityClass, mappedSuperClassInfoRef);
            if (fieldInfo == null) {
                StringBuilder fieldPathBuilder = new StringBuilder(fieldName.length() + 5);
                fieldInfo = resolveNestedField(repositoryMethodDescription, fieldName, fieldPathBuilder);
                fieldName = fieldPathBuilder.toString();
            }
            validateFieldWithOperation(operation, fieldInfo, fieldName, repositoryMethodDescription);
            if ((ignoreCase || allIgnoreCase) && !DotNames.STRING.equals(fieldInfo.type().name())) {
                throw new UnableToParseMethodException(
                        "IgnoreCase cannot be specified for field" + fieldInfo.name() + " because it is not a String type. "
                                + "Offending method is " + repositoryMethodDescription + ".");
            }

            if (where.length() > 0) {
                where.append(containsAnd ? " AND " : " OR ");
            }

            String upperPrefix = (ignoreCase || allIgnoreCase) ? "UPPER(" : "";
            String upperSuffix = (ignoreCase || allIgnoreCase) ? ")" : "";

            where.append(upperPrefix).append(fieldName).append(upperSuffix);
            if ((operation == null) || "Equals".equals(operation) || "Is".equals(operation)) {
                paramsCount++;
                where.append(" = ").append(upperPrefix).append("?").append(paramsCount).append(upperSuffix);
            } else {
                switch (operation) {
                    case "IsNot":
                    case "Not":
                        paramsCount++;
                        where.append(" <> ?").append(paramsCount);
                        break;
                    case "IsNull":
                    case "Null":
                        where.append(" IS null ");
                        break;
                    case "IsNotNull":
                    case "NotNull":
                        where.append(" IS NOT null ");
                        break;
                    case "Between":
                    case "IsBetween":
                        where.append(" BETWEEN ");
                        paramsCount++;
                        where.append("?").append(paramsCount).append(" AND ");
                        paramsCount++;
                        where.append("?").append(paramsCount);
                        break;
                    case "LessThan":
                    case "IsLessThan":
                    case "Before":
                    case "IsBefore":
                        paramsCount++;
                        where.append(" < ?").append(paramsCount);
                        break;
                    case "LessThanEqual":
                    case "IsLessThanEqual":
                        paramsCount++;
                        where.append(" <= ?").append(paramsCount);
                        break;
                    case "GreaterThan":
                    case "IsGreaterThan":
                    case "After":
                    case "IsAfter":
                        paramsCount++;
                        where.append(" > ?").append(paramsCount);
                        break;
                    case "GreaterThanEqual":
                    case "IsGreaterThanEqual":
                        paramsCount++;
                        where.append(" >= ?").append(paramsCount);
                        break;
                    case "Like":
                    case "IsLike":
                        paramsCount++;
                        where.append(" LIKE ?").append(paramsCount);
                        break;
                    case "NotLike":
                    case "IsNotLike":
                        paramsCount++;
                        where.append(" NOT LIKE ?").append(paramsCount);
                        break;
                    case "IsStartingWith":
                    case "StartingWith":
                    case "StartsWith":
                        paramsCount++;
                        where.append(" LIKE CONCAT(").append(upperPrefix).append("?").append(paramsCount).append(upperSuffix)
                                .append(", '%')");
                        break;
                    case "IsEndingWith":
                    case "EndingWith":
                    case "EndsWith":
                        paramsCount++;
                        where.append(" LIKE CONCAT('%', ").append(upperPrefix).append("?").append(paramsCount)
                                .append(upperSuffix).append(")");
                        break;
                    case "IsContaining":
                    case "Containing":
                    case "Contains":
                        paramsCount++;
                        where.append(" LIKE CONCAT('%', ").append(upperPrefix).append("?").append(paramsCount)
                                .append(upperSuffix).append(", '%')");
                        break;
                    case "True":
                    case "False":
                        where.append(" = ").append(operation.toLowerCase());
                        break;
                    case "IsIn":
                    case "In":
                        paramsCount++;
                        where.append(" IN ?").append(paramsCount);
                        break;
                    case "IsNotIn":
                    case "NotIn":
                        paramsCount++;
                        where.append(" NOT IN ?").append(paramsCount);
                        break;
                    case "IsEmpty":
                    case "Empty":
                        where.append(" IS EMPTY");
                        break;
                    case "IsNotEmpty":
                    case "NotEmpty":
                        where.append(" IS NOT EMPTY");
                        break;
                }
            }
        }

        String whereQuery = where.toString().isEmpty() ? "" : " WHERE " + where.toString();
        return new Result(entityClass, "FROM " + getEntityName() + whereQuery, queryType, paramsCount, sort,
                topCount);
    }