private static OperationType buildQueryModelFromMethodName(String methodName, StringBuilder sb) {
        String queryString;
        OperationType operationType;

        if (methodName.startsWith("findBy")) {
            queryString = methodName.substring("findBy".length(), methodName.length() - (methodName.endsWith("As") ? "As".length() : 0));
            operationType = OperationType.Read;
        } else if (methodName.startsWith("countBy")) {
            sb.append("    queryModel.setProjection(buildProjection(\"\", org.iternine.jeppetto.dao.ProjectionType.RowCount, argsIterator));\n\n");

            queryString = methodName.substring("countBy".length(), methodName.length() - (methodName.endsWith("As") ? "As".length() : 0));
            operationType = OperationType.Read;
        } else if (methodName.startsWith("updateBy")) {
            queryString = methodName.substring("updateBy".length(), methodName.length() - (methodName.endsWith("As") ? "As".length() : 0));
            operationType = OperationType.Update;
            sb.append("    org.iternine.jeppetto.dao.updateobject.UpdateObject updateObject = (org.iternine.jeppetto.dao.updateobject.UpdateObject) argsIterator.next();\n\n");
        } else if (methodName.startsWith("deleteBy")) {
            queryString = methodName.substring("deleteBy".length(), methodName.length() - (methodName.endsWith("As") ? "As".length() : 0));
            operationType = OperationType.Delete;
        } else {
            throw new UnsupportedOperationException("Don't know how to handle '" + methodName + "'");
        }

        int orderByIndex = queryString.indexOf("OrderBy");
        boolean limitResults;
        boolean skipResults;

        if (skipResults = queryString.endsWith("AndSkip")) {
            queryString = queryString.substring(0, queryString.length() - "AndSkip".length());
        }

        if (limitResults = queryString.endsWith("AndLimit")) {
            queryString = queryString.substring(0, queryString.length() - "AndLimit".length());
        }

        String[] queryParts;
        String orderParts;

        if (orderByIndex == -1) {
            queryParts = queryString.split("Having");
            orderParts = null;
        } else {
            queryParts = queryString.substring(0, orderByIndex).split("Having");
            orderParts = queryString.substring(orderByIndex + "OrderBy".length());
        }

        if (queryParts[0] != null) {
            if (queryParts[0].length() > 0) {
                String[] conditionStrings = queryParts[0].split("And");

                for (String conditionString : conditionStrings) {
                    String conditionName = getConditionNameFromString(conditionString);

                    sb.append(String.format("    queryModel.addCondition(buildCondition(\"%s\", org.iternine.jeppetto.dao.ConditionType.%s, argsIterator));\n",
                                            pruneFieldNameFromString(conditionString, conditionName), conditionName));
                }

                sb.append('\n');
            }

            for (int i = 1; i < queryParts.length; i++) {
                String associationString = queryParts[i];
                int withIndex = associationString.indexOf("With");  // If -1, exception.  Okay.
                String[] conditionStrings = associationString.substring(withIndex + 4, associationString.length()).split("And");

                for (String conditionString : conditionStrings) {
                    String conditionName = getConditionNameFromString(conditionString);

                    sb.append(String.format("    queryModel.addAssociationCondition(\"%s\", buildCondition(\"%s\", org.iternine.jeppetto.dao.ConditionType.%s, argsIterator));\n",
                                            Character.toLowerCase(associationString.charAt(0)) + associationString.substring(1, withIndex),
                                            pruneFieldNameFromString(conditionString, conditionName), conditionName));
                }

                sb.append('\n');
            }
        }

        if (orderParts != null && operationType == OperationType.Read) {
            for (String orderPart : orderParts.split("And")) {
                SortDirection sortDirection;
                String fieldName;

                if (orderPart.endsWith("Desc")) {
                    sortDirection = SortDirection.Descending;
                    fieldName = pruneFieldNameFromString(orderPart, "Desc");
                } else {
                    sortDirection = SortDirection.Ascending;
                    fieldName = pruneFieldNameFromString(orderPart, "Asc");
                }

                sb.append(String.format("    queryModel.addSort(org.iternine.jeppetto.dao.SortDirection.%s, \"%s\");\n",
                                        sortDirection.name(), fieldName));
            }

            sb.append('\n');
        }

        if (limitResults) {
            sb.append("    queryModel.setMaxResults(((Integer) argsIterator.next()).intValue());\n\n");
        }

        if (skipResults) {
            sb.append("    queryModel.setFirstResult(((Integer) argsIterator.next()).intValue());\n\n");
        }

        return operationType;
    }
private static OperationType buildQueryModelFromMethodName ( String methodName , StringBuilder sb ) { String queryString ; OperationType operationType ; if ( methodName . startsWith ( "findBy" ) ) { queryString = methodName . substring ( "findBy" . length ( ) , methodName . length ( ) - ( methodName . endsWith ( "As" ) ? "As" . length ( ) : 0 ) ) ; operationType = OperationType . Read ; } else if ( methodName . startsWith ( "countBy" ) ) { sb . append ( "    queryModel.setProjection(buildProjection(\"\", org.iternine.jeppetto.dao.ProjectionType.RowCount, argsIterator));\n\n" ) ; queryString = methodName . substring ( "countBy" . length ( ) , methodName . length ( ) - ( methodName . endsWith ( "As" ) ? "As" . length ( ) : 0 ) ) ; operationType = OperationType . Read ; } else if ( methodName . startsWith ( "updateBy" ) ) { queryString = methodName . substring ( "updateBy" . length ( ) , methodName . length ( ) - ( methodName . endsWith ( "As" ) ? "As" . length ( ) : 0 ) ) ; operationType = OperationType . Update ; sb . append ( "    org.iternine.jeppetto.dao.updateobject.UpdateObject updateObject = (org.iternine.jeppetto.dao.updateobject.UpdateObject) argsIterator.next();\n\n" ) ; } else if ( methodName . startsWith ( "deleteBy" ) ) { queryString = methodName . substring ( "deleteBy" . length ( ) , methodName . length ( ) - ( methodName . endsWith ( "As" ) ? "As" . length ( ) : 0 ) ) ; operationType = OperationType . Delete ; } else { throw new UnsupportedOperationException ( "Don't know how to handle '" + methodName + "'" ) ; } int orderByIndex = queryString . indexOf ( "OrderBy" ) ; boolean limitResults ; boolean skipResults ; if ( skipResults = queryString . endsWith ( "AndSkip" ) ) { queryString = queryString . substring ( 0 , queryString . length ( ) - "AndSkip" . length ( ) ) ; } if ( limitResults = queryString . endsWith ( "AndLimit" ) ) { queryString = queryString . substring ( 0 , queryString . length ( ) - "AndLimit" . length ( ) ) ; } String [ ] queryParts ; String orderParts ; if ( orderByIndex == - 1 ) { queryParts = queryString . split ( "Having" ) ; orderParts = null ; } else { queryParts = queryString . substring ( 0 , orderByIndex ) . split ( "Having" ) ; orderParts = queryString . substring ( orderByIndex + "OrderBy" . length ( ) ) ; } if ( queryParts [ 0 ] != null ) { if ( queryParts [ 0 ] . length ( ) > 0 ) { String [ ] conditionStrings = queryParts [ 0 ] . split ( "And" ) ; for ( String conditionString : conditionStrings ) { String conditionName = getConditionNameFromString ( conditionString ) ; sb . append ( String . format ( "    queryModel.addCondition(buildCondition(\"%s\", org.iternine.jeppetto.dao.ConditionType.%s, argsIterator));\n" , pruneFieldNameFromString ( conditionString , conditionName ) , conditionName ) ) ; } sb . append ( '\n' ) ; } for ( int i = 1 ; i < queryParts . length ; i ++ ) { String associationString = queryParts [ i ] ; int withIndex = associationString . indexOf ( "With" ) ; String [ ] conditionStrings = associationString . substring ( withIndex + 4 , associationString . length ( ) ) . split ( "And" ) ; for ( String conditionString : conditionStrings ) { String conditionName = getConditionNameFromString ( conditionString ) ; sb . append ( String . format ( "    queryModel.addAssociationCondition(\"%s\", buildCondition(\"%s\", org.iternine.jeppetto.dao.ConditionType.%s, argsIterator));\n" , Character . toLowerCase ( associationString . charAt ( 0 ) ) + associationString . substring ( 1 , withIndex ) , pruneFieldNameFromString ( conditionString , conditionName ) , conditionName ) ) ; } sb . append ( '\n' ) ; } } if ( orderParts != null && operationType == OperationType . Read ) { for ( String orderPart : orderParts . split ( "And" ) ) { SortDirection sortDirection ; String fieldName ; if ( orderPart . endsWith ( "Desc" ) ) { sortDirection = SortDirection . Descending ; fieldName = pruneFieldNameFromString ( orderPart , "Desc" ) ; } else { sortDirection = SortDirection . Ascending ; fieldName = pruneFieldNameFromString ( orderPart , "Asc" ) ; } sb . append ( String . format ( "    queryModel.addSort(org.iternine.jeppetto.dao.SortDirection.%s, \"%s\");\n" , sortDirection . name ( ) , fieldName ) ) ; } sb . append ( '\n' ) ; } if ( limitResults ) { sb . append ( "    queryModel.setMaxResults(((Integer) argsIterator.next()).intValue());\n\n" ) ; } if ( skipResults ) { sb . append ( "    queryModel.setFirstResult(((Integer) argsIterator.next()).intValue());\n\n" ) ; } return operationType ; }
We build 'findBy', 'countBy', 'updateBy', and 'deleteBy' QueryModels in the following way:
<p/>
findBy<query part>*[OrderBy<order part>*][AndLimit][AndSkip]
countBy<query part>*[OrderBy<order part>*][AndLimit][AndSkip]
updateBy<query part>*
deleteBy<query part>*
<p/>
Query parts are of the following forms:
<p/>
<PropertyName> :                   column value must equal positional argument value
<PropertyName>Equal :              column value must equal positional argument value
<PropertyName>NotEqual :           column value must not equal positional argument value
<PropertyName>GreaterThan :        column value must be greater than positional argument value
<PropertyName>GreaterThanEqual :   column value must be greater than or equal to positional argument value
<PropertyName>LessThan :           column value must be less than positional argument value
<PropertyName>LessThanEqual :      column value must be less than or equal to positional argument value
<PropertyName>Between :            column value must be between the next two positional argument values
<PropertyName>Within :             column value must be in a java.util.Collection of values
<PropertyName>NotWithin :          column value must not be in a java.util.Collection of values
<PropertyName>IsNull :             column value must be null
<PropertyName>IsNotNull :          column value must not be null
<p/>
Additionally, strings of the following form:
<p/>
Having<AssociationName>With<association query part>*
<p/>
Can be specified to find results that have associations to other objects with the interpreted query parts.
Multiple associations can be specified, but note that each hangs off the root object, not each other
(e.g. List<Word> findByHavingFontWithColorHavingFormattingWithJustification() would assume an association
between Word and both Font and Formatting, not Word to Font to Formatting).
<p/>
Order parts are of the following forms:
<p/>
<PropertyName>Asc :          order by the column value ascending
<PropertyName>Desc :         order by the column value descending
<PropertyName> :             order by the column value ascending
<p/>
Limiting the result size and pagination are indicated by the AndLimit and AndSkip phrases.  These must be
at the end of the DAO method name, and be in that order.  It is acceptable to omit one or the other if it
isn't needed.  Both clauses expect to find an integer value in the parameter list after all the other
parameters are specified.  For example, to paginate through a potentially long list of people with the same last
name, one could declare a method findBySurnameAndLimitAndSkip(String surname, int limitCount, int skipCount)

@param methodName of the method to construct a QueryModel from
@param sb the StringBuilder to place the resulting logic into

@return the OperationType that the methodName refers to.