Index: trunk/src/groovy/HqlBuilder.groovy
===================================================================
--- trunk/src/groovy/HqlBuilder.groovy	(revision 642)
+++ 	(revision )
@@ -1,480 +1,0 @@
-/* Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Provides a DSL for building and managing HQL strings.
- * For more usage examples see the HqlBuilderTests.
- * HQL reference see http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html
- *
- * DML reference see http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/batch.html#batch-direct
- * DML-style clauses: "update, delete, insert into" may not be well tested, the "set" clause has basic implementation.
- * So double check the q.query output for these cases.
- *
- * Primary goals:
- * 1. Easy to read and understand in code.
- * 2. Easy to read and understand when printed (e.g when displayed in a report).
- * 3. Easy to execute with correct paginateParams and namedParams.
- * 4. Easy to change a clause and execute again.
- *
- * Basic usage:
- *    def q = new HqlBuilder().query {
- *        select 'count(distinct book)'
- *        from 'Book as book'
- *        where 'book.id > 100'
- *            and 'book.inStock = true'
- *    }
- *    def totalCount = Book.executeQuery(q.query, q.namedParams, q.paginateParams)[0].toInteger()
- *    q.select = 'distinct book'
- *    def list = Book.executeQuery(q.query, q.namedParams, q.paginateParams)
- *
- *    def bookList = new PagedResultList(list, totalCount)
- *    log.debug '\n' + q.printFormattedQuery
- *
- * @author Gavin Kromhout
- * @version DraftB
- *
- */
-class HqlBuilder {
-
-    // HQL clauses.
-    // Each clause is a map key with a list of terms.
-    def clauses = [:]
-
-    // HQL namedParams.
-    // HQL requires the namedParams to match exactly with the clause expressions.
-    def namedParams = [:]
-
-    // HQL paginateParams.
-    def paginateParams = [max: 1000, offset: 0]
-
-    // The where clause terms are handled separately from other clauses
-    // and are a list of logicalTerms.
-    // The where clause is built by buildWhereClause.
-    def whereClauseTerms = []
-
-    // LogicalIndexStack holds indexes of the current whereClauseTerm nesting.
-    def logicalIndexStack = []
-
-   def logicalBuilders = [AND: 'and',
-                                        OR: 'or']
-
-    def nestingStack = []
-
-    // Sort and Order.
-    // It is easier and more flexible to simply add order as a clause, e.g: order 'by name desc, id asc'
-    // def sort = "" // e.g. instanceName.id
-    // def order = "" // e.g. asc or desc
-
-    /**
-     * Constructor.
-     * Any property that exists (or responds) in the class may be supplied as an argument.
-     * E.g: max:20, offset:10, debug:true
-     * The debug property does not really exist, but if true and no external log property
-     * has been setup then the internal mockLogger will be configured in debug mode.
-     *
-     * @param args A map of arguments, defaults to an empty map.
-     *
-     */
-    def HqlBuilder(Map args = [:]) {
-        args.each { arg ->
-            def argKey = arg.key.toLowerCase()
-            if(super.hasProperty(argKey))
-                this[argKey] = arg.value
-        }
-        if(!super.metaClass.hasMetaProperty('log'))
-            mockLogging(args.debug)
-        log.debug "HqlBuilder()"
-    }
-
-    /**
-     * Call with no args.
-     *  Has no real use other than to prevent obscure errors.
-     */
-    def call() {
-        log.debug "call()"
-    }
-
-    /**
-     * Call with closure as last arg.
-     * A typically used build call, e.g: q { } is equivalent to q.call() { }
-     */
-    def call(Closure cl) {
-        log.debug "call(Closure cl)"
-        handleClosure(cl)
-    }
-
-    /**
-     * Domain specific build method.
-     *  Has no real use other than to prevent obscure errors
-     * when user makes a call to query() and Groovy calls query(Closure cl)
-     *
-     * @returns This object.
-     *
-     */
-    def query() {
-        log.debug "query()"
-        return this // Must return this object to q.
-    }
-
-    /**
-     * Domain specific build method.
-     * The recommended build call, e.g: def q = new HqlBuilder().query { }
-     *
-     * @param cl The closure that will be used to build the query.
-     * @returns This object.
-     *
-     */
-    def query(Closure cl) {
-        log.debug "query(Closure cl)"
-        handleClosure(cl)
-        return this // Must return this object to q.
-    }
-
-    /**
-     * InvokeMethod resolves all undefined methods.
-     * Which include the clause methods, e.g: select 'book' is equivalent to select('book').
-     * Note that defined methods will be called directly since this class does not implement GroovyInterceptable.
-     * If class was "HqlBuilder implements GroovyInterceptable" then even println would be intercepted and
-     * several exlusions might be needed. e.g: if(methodName != 'call' && methodName != 'println')
-     */
-    def invokeMethod(String methodName, args) {
-
-        log.debug "invokeMethod(${methodName}, ${args})"
-
-        // Call any closures first, that way the nesting is handled and we just keep a reference.
-        if(args[-1] instanceof Closure) {
-            handleClosure(args[-1], methodName)
-            args = args.minus(args[-1])
-        }
-
-        if(!clauses.containsKey(methodName) && !isLogicalBuilder(methodName))
-            clauses[methodName] = []
-
-        if(args) {
-            if(isWhereClauseBuilder(methodName)) {
-                logicalBuilder(methodName, args)
-                return
-            }
-        }
-
-        for(arg in args) {
-            if(arg instanceof String || arg instanceof GString)
-                clauses[methodName] << arg
-        }
-
-    } // invokeMethod()
-
-    /**
-     * PropertyMissing.
-     * Allows clauses to be added after build, e.g: q.order = 'by book.name asc'
-     * and clauses to be removed, e.g: q.order = null
-     */
-    def propertyMissing(String propertyName, value) {
-        log.debug "propertyMissing(${propertyName}, ${value})"
-
-        if(value == null) {
-            removeClause(propertyName)
-            if(propertyName.toLowerCase() == 'where')
-                whereClauseTerms.clear()
-            return
-        }
-
-        if(!clauses.containsKey(propertyName))
-            clauses[propertyName] = []
-
-        // Occurs when user assigns to where clause, e.g: q.where = 'book.id > 100'
-        if(propertyName.toLowerCase() == 'where') {
-            whereClauseTerms.clear()
-            logicalBuilder(propertyName, [value])
-            return
-        }
-
-        if(value instanceof String || value instanceof GString)
-            clauses[propertyName] = [value]
-    } // propertyMissing(String propertyName, value)
-
-    /**
-     * PropertyMissing.
-     * Allow clauses to be accessed directly by name, e.g: println q.order.
-     * Since clauses is a Map null is simply returned for a non-existant clause.
-     */
-    def propertyMissing(String propertyName) {
-        log.debug "propertyMissing(${propertyName})"
-
-        if(!clauses.containsKey(propertyName))
-            clauses[propertyName] = []
-
-        // Occurs when user performs an operation on where clause.
-        // E.g: q.where << "book.id = 100" which is actually NOT a supported operation since
-        // calling the method provides the correct function e.g: q.where "book.id > 100".
-        // Also allows `println q.where` to be short hand for `println q.whereClauseTerms`
-        if(propertyName.toLowerCase() == 'where') {
-            return whereClauseTerms
-        }
-
-        clauses[propertyName]
-    } // propertyMissing(String propertyName)
-
-    def setMax(Integer value) {
-        paginateParams.max = value
-    }
-
-    def getMax() {
-        paginateParams.max
-    }
-
-    def setOffset(Integer value) {
-        paginateParams.offset = value
-    }
-
-    def getOffset() {
-        paginateParams.offset
-    }
-
-    /**
-     * RemoveClause.
-     * Allows clauses to be removed, e.g: q.removeClause('order')
-     *
-     * @param clauseName The clause to remove.
-     *
-     */
-    def removeClause(String clauseName) {
-            clauses.remove(clauseName)
-    }
-
-    /**
-     * BuildWhereClause.
-     * Build the where clause from whereClauseTerms.
-     */
-    def buildWhereClause(printFormat = false) {
-        //log.debug "buildWhereClause()"
-
-        if(!whereClauseTerms)
-            return ''
-
-        def whereClause = 'where '
-
-        def buildExpression // declared separately to allow recurrsion.
-        buildExpression = { term ->
-            def result = ''
-            def termCount = term.expressions.size()
-            if(termCount > 1) {
-                term.expressions.eachWithIndex { t, index ->
-                    if(index == 0)
-                        result += buildExpression(t)
-                    else if(printFormat)
-                        result += " \n\t${t.logic} ${buildExpression(t)}"
-                    else
-                        result += " ${t.logic} ${buildExpression(t)}"
-
-                }
-                result = "( "+result+" )"
-            }
-            else {
-                if(term.expressions[0] instanceof Map)
-                    result += "${term.expressions[0].expressions[0]}"
-                else
-                    result += "${term.expressions[0]}"
-            }
-            return result
-        }
-
-        whereClauseTerms.eachWithIndex { tm, index ->
-            if(index == 0)
-                whereClause += buildExpression(tm)
-            else if(printFormat)
-                whereClause += " \n\t${tm.logic} ${buildExpression(tm)}"
-            else
-                whereClause += " ${tm.logic} ${buildExpression(tm)}"
-        }
-
-        return whereClause
-    } // buildWhereClause(printFormat = false)
-
-    /**
-     * LogicalBuilder.
-     * Build the whereClauseTerms
-     * by appending logicalTerms to the appropriate expressions.
-     */
-    def logicalBuilder(logicalName, args) {
-        log.debug "logicalBuilder(${logicalName}, ${args})"
-        log.debug "logicalIndexStack: ${logicalIndexStack}"
-
-        def logic = getLogicalString(logicalName)
-
-        for(arg in args) {
-            if(arg instanceof String || arg instanceof GString) {
-                arg = arg.trim()
-                if(arg) { // prevent empty strings being added.
-                    if(logicalIndexStack.size() > 0) {
-                        // Append to current index position.
-                        whereClauseTerms[logicalIndexStack[-1]].expressions << logicalTerm(logic, arg)
-                    }
-                    else {
-                        // Append to 'root'.
-                        whereClauseTerms << logicalTerm(logic, null) // empty expression logicalTerm.
-                        whereClauseTerms[-1].expressions << logicalTerm(logic, arg) // append logicalTerm to expressions
-                    }
-                } // if(arg)
-            } // if(arg instanceof)
-        } // for
-
-    } // logicalBuilder(logicalName, args)
-
-    /**
-     * LogicalTerm.
-     * A logicalTerm is a map object that holds the logic and list of expressions of a whereClauseTerm.
-     */
-    def logicalTerm = { logic, expression ->
-        expression = expression ? [expression] : []
-        ['logic': getLogicalString(logic), 'expressions': expression]
-    }
-
-    /**
-     * GetLogicalString.
-     *
-     * @param logicalName The name to get the matching logicalBuilder string for.
-     */
-    private getLogicalString(logicalName) {
-
-        switch(logicalName.toLowerCase()) {
-            case 'where':
-                logicalBuilders.AND
-                break
-            case logicalBuilders.AND:
-                logicalBuilders.AND
-                break
-            case logicalBuilders.OR:
-                logicalBuilders.OR
-                break
-        }
-
-    }
-
-    /**
-     * HandleClosure.
-     * Setting delegate and DELEGATE_FIRST allows closure to access this object's properties first.
-     */
-    private handleClosure(Closure cl, String methodName = 'root') {
-        log.debug "handleClosure(${cl.toString()}, ${methodName})"
-        if(isWhereClauseBuilder(methodName)) {
-            whereClauseTerms << logicalTerm(getLogicalString(methodName), null)
-            logicalIndexStack << whereClauseTerms.size()-1
-        }
-        nestingStack.push(methodName)
-        cl.delegate = this
-        cl.resolveStrategy = Closure.DELEGATE_FIRST
-        cl.call()
-        //log.debug "nestingStack: $nestingStack"
-        nestingStack.pop()
-        if(isWhereClauseBuilder(methodName)) {
-            logicalIndexStack.pop()
-        }
-    }
-
-    /**
-     * MockLogging.
-     * This class has super cow powers and can mock out it's own debug logging.
-     */
-    private mockLogging(debug = false) {
-        def mockLogger = {}
-        if(debug) {
-            mockLogger = {msg ->
-                    println "${super.getClass()} - DEBUG: $msg"
-            }
-        }
-        super.metaClass.log = [debug: mockLogger]
-        log.debug "Internal mockLogger configured."
-    }
-
-    /**
-     * IsLogicalBuilder.
-     * Determine if a method is a logicalBuilder.
-     */
-    private isLogicalBuilder(String methodName) {
-        logicalBuilders.find{ it.value == methodName.toLowerCase()} ? true:false
-    }
-
-    /**
-     * IsWhereClauseBuilder.
-     * Determine if a method is a where clause builder.
-     */
-    private isWhereClauseBuilder(String methodName) {
-        methodName = methodName.toLowerCase()
-        if(methodName == 'where' || isLogicalBuilder(methodName))
-            return true
-        else
-            return false
-    }
-
-    /**
-     * GetQuery.
-     * Assemble and return the query in a format that can be directly executed.
-     * E.g: executeQuery(q.query, q.namedParams, q.paginateParams).
-     */
-    def getQuery() {
-        clauses.collect { clause ->
-            switch (clause.key.toLowerCase()) {
-                case 'select':
-                    clause.key + ' ' + clause.value.join(', ')
-                    break
-                case 'set':
-                    clause.key + ' ' + clause.value.join(', ')
-                    break
-                case 'where':
-                    buildWhereClause()
-                    break
-                case 'order':
-                    clause.key + ' ' + clause.value.join(', ')
-                    break
-                case 'group':
-                    clause.key + ' ' + clause.value.join(', ')
-                    break
-                default:
-                    clause.key + ' ' + clause.value.join(' ')
-            }
-        }.join(' ')
-    } // getQuery()
-
-    /**
-     * GetPrintFormattedQuery.
-     * Assemble and return the query in a format that can be more easily printed and read by a person.
-     * E.g: println q.printFormattedQuery or when displayed in a report.
-     */
-    def getPrintFormattedQuery() {
-        clauses.collect { clause ->
-            switch (clause.key.toLowerCase()) {
-                case 'select':
-                    clause.key + ' ' + clause.value.join(', \n\t')
-                    break
-                case 'set':
-                    clause.key + ' ' + clause.value.join(', \n\t')
-                    break
-                case 'where':
-                    buildWhereClause(true)
-                    break
-                case 'order':
-                    clause.key + ' ' + clause.value.join(', \n\t')
-                    break
-                case 'group':
-                    clause.key + ' ' + clause.value.join(', \n\t')
-                    break
-                default:
-                    clause.key + ' ' + clause.value.join(' \n\t')
-            }
-        }.join(' \n')
-    } // getPrintFormattedQuery()
-
-} // end class
Index: trunk/src/groovy/net/kromhouts/HqlBuilder.groovy
===================================================================
--- trunk/src/groovy/net/kromhouts/HqlBuilder.groovy	(revision 643)
+++ trunk/src/groovy/net/kromhouts/HqlBuilder.groovy	(revision 643)
@@ -0,0 +1,482 @@
+/* Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.kromhouts
+
+/**
+ * Provides a DSL for building and managing HQL strings.
+ * For more usage examples see the HqlBuilderTests.
+ * HQL reference see http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html
+ *
+ * DML reference see http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/batch.html#batch-direct
+ * DML-style clauses: "update, delete, insert into" may not be well tested, the "set" clause has basic implementation.
+ * So double check the q.query output for these cases.
+ *
+ * Primary goals:
+ * 1. Easy to read and understand in code.
+ * 2. Easy to read and understand when printed (e.g when displayed in a report).
+ * 3. Easy to execute with correct paginateParams and namedParams.
+ * 4. Easy to change a clause and execute again.
+ *
+ * Basic usage:
+ *    def q = new HqlBuilder().query {
+ *        select 'count(distinct book)'
+ *        from 'Book as book'
+ *        where 'book.id > 100'
+ *            and 'book.inStock = true'
+ *    }
+ *    def totalCount = Book.executeQuery(q.query, q.namedParams, q.paginateParams)[0].toInteger()
+ *    q.select = 'distinct book'
+ *    def list = Book.executeQuery(q.query, q.namedParams, q.paginateParams)
+ *
+ *    def bookList = new PagedResultList(list, totalCount)
+ *    log.debug '\n' + q.printFormattedQuery
+ *
+ * @author Gavin Kromhout
+ * @version DraftB
+ *
+ */
+class HqlBuilder {
+
+    // HQL clauses.
+    // Each clause is a map key with a list of terms.
+    def clauses = [:]
+
+    // HQL namedParams.
+    // HQL requires the namedParams to match exactly with the clause expressions.
+    def namedParams = [:]
+
+    // HQL paginateParams.
+    def paginateParams = [max: 1000, offset: 0]
+
+    // The where clause terms are handled separately from other clauses
+    // and are a list of logicalTerms.
+    // The where clause is built by buildWhereClause.
+    def whereClauseTerms = []
+
+    // LogicalIndexStack holds indexes of the current whereClauseTerm nesting.
+    def logicalIndexStack = []
+
+   def logicalBuilders = [AND: 'and',
+                                        OR: 'or']
+
+    def nestingStack = []
+
+    // Sort and Order.
+    // It is easier and more flexible to simply add order as a clause, e.g: order 'by name desc, id asc'
+    // def sort = "" // e.g. instanceName.id
+    // def order = "" // e.g. asc or desc
+
+    /**
+     * Constructor.
+     * Any property that exists (or responds) in the class may be supplied as an argument.
+     * E.g: max:20, offset:10, debug:true
+     * The debug property does not really exist, but if true and no external log property
+     * has been setup then the internal mockLogger will be configured in debug mode.
+     *
+     * @param args A map of arguments, defaults to an empty map.
+     *
+     */
+    def HqlBuilder(Map args = [:]) {
+        args.each { arg ->
+            def argKey = arg.key.toLowerCase()
+            if(super.hasProperty(argKey))
+                this[argKey] = arg.value
+        }
+        if(!super.metaClass.hasMetaProperty('log'))
+            mockLogging(args.debug)
+        log.debug "HqlBuilder()"
+    }
+
+    /**
+     * Call with no args.
+     *  Has no real use other than to prevent obscure errors.
+     */
+    def call() {
+        log.debug "call()"
+    }
+
+    /**
+     * Call with closure as last arg.
+     * A typically used build call, e.g: q { } is equivalent to q.call() { }
+     */
+    def call(Closure cl) {
+        log.debug "call(Closure cl)"
+        handleClosure(cl)
+    }
+
+    /**
+     * Domain specific build method.
+     *  Has no real use other than to prevent obscure errors
+     * when user makes a call to query() and Groovy calls query(Closure cl)
+     *
+     * @returns This object.
+     *
+     */
+    def query() {
+        log.debug "query()"
+        return this // Must return this object to q.
+    }
+
+    /**
+     * Domain specific build method.
+     * The recommended build call, e.g: def q = new HqlBuilder().query { }
+     *
+     * @param cl The closure that will be used to build the query.
+     * @returns This object.
+     *
+     */
+    def query(Closure cl) {
+        log.debug "query(Closure cl)"
+        handleClosure(cl)
+        return this // Must return this object to q.
+    }
+
+    /**
+     * InvokeMethod resolves all undefined methods.
+     * Which include the clause methods, e.g: select 'book' is equivalent to select('book').
+     * Note that defined methods will be called directly since this class does not implement GroovyInterceptable.
+     * If class was "HqlBuilder implements GroovyInterceptable" then even println would be intercepted and
+     * several exlusions might be needed. e.g: if(methodName != 'call' && methodName != 'println')
+     */
+    def invokeMethod(String methodName, args) {
+
+        log.debug "invokeMethod(${methodName}, ${args})"
+
+        // Call any closures first, that way the nesting is handled and we just keep a reference.
+        if(args[-1] instanceof Closure) {
+            handleClosure(args[-1], methodName)
+            args = args.minus(args[-1])
+        }
+
+        if(!clauses.containsKey(methodName) && !isLogicalBuilder(methodName))
+            clauses[methodName] = []
+
+        if(args) {
+            if(isWhereClauseBuilder(methodName)) {
+                logicalBuilder(methodName, args)
+                return
+            }
+        }
+
+        for(arg in args) {
+            if(arg instanceof String || arg instanceof GString)
+                clauses[methodName] << arg
+        }
+
+    } // invokeMethod()
+
+    /**
+     * PropertyMissing.
+     * Allows clauses to be added after build, e.g: q.order = 'by book.name asc'
+     * and clauses to be removed, e.g: q.order = null
+     */
+    def propertyMissing(String propertyName, value) {
+        log.debug "propertyMissing(${propertyName}, ${value})"
+
+        if(value == null) {
+            removeClause(propertyName)
+            if(propertyName.toLowerCase() == 'where')
+                whereClauseTerms.clear()
+            return
+        }
+
+        if(!clauses.containsKey(propertyName))
+            clauses[propertyName] = []
+
+        // Occurs when user assigns to where clause, e.g: q.where = 'book.id > 100'
+        if(propertyName.toLowerCase() == 'where') {
+            whereClauseTerms.clear()
+            logicalBuilder(propertyName, [value])
+            return
+        }
+
+        if(value instanceof String || value instanceof GString)
+            clauses[propertyName] = [value]
+    } // propertyMissing(String propertyName, value)
+
+    /**
+     * PropertyMissing.
+     * Allow clauses to be accessed directly by name, e.g: println q.order.
+     * Since clauses is a Map null is simply returned for a non-existant clause.
+     */
+    def propertyMissing(String propertyName) {
+        log.debug "propertyMissing(${propertyName})"
+
+        if(!clauses.containsKey(propertyName))
+            clauses[propertyName] = []
+
+        // Occurs when user performs an operation on where clause.
+        // E.g: q.where << "book.id = 100" which is actually NOT a supported operation since
+        // calling the method provides the correct function e.g: q.where "book.id > 100".
+        // Also allows `println q.where` to be short hand for `println q.whereClauseTerms`
+        if(propertyName.toLowerCase() == 'where') {
+            return whereClauseTerms
+        }
+
+        clauses[propertyName]
+    } // propertyMissing(String propertyName)
+
+    def setMax(Integer value) {
+        paginateParams.max = value
+    }
+
+    def getMax() {
+        paginateParams.max
+    }
+
+    def setOffset(Integer value) {
+        paginateParams.offset = value
+    }
+
+    def getOffset() {
+        paginateParams.offset
+    }
+
+    /**
+     * RemoveClause.
+     * Allows clauses to be removed, e.g: q.removeClause('order')
+     *
+     * @param clauseName The clause to remove.
+     *
+     */
+    def removeClause(String clauseName) {
+            clauses.remove(clauseName)
+    }
+
+    /**
+     * BuildWhereClause.
+     * Build the where clause from whereClauseTerms.
+     */
+    def buildWhereClause(printFormat = false) {
+        //log.debug "buildWhereClause()"
+
+        if(!whereClauseTerms)
+            return ''
+
+        def whereClause = 'where '
+
+        def buildExpression // declared separately to allow recurrsion.
+        buildExpression = { term ->
+            def result = ''
+            def termCount = term.expressions.size()
+            if(termCount > 1) {
+                term.expressions.eachWithIndex { t, index ->
+                    if(index == 0)
+                        result += buildExpression(t)
+                    else if(printFormat)
+                        result += " \n\t${t.logic} ${buildExpression(t)}"
+                    else
+                        result += " ${t.logic} ${buildExpression(t)}"
+
+                }
+                result = "( "+result+" )"
+            }
+            else {
+                if(term.expressions[0] instanceof Map)
+                    result += "${term.expressions[0].expressions[0]}"
+                else
+                    result += "${term.expressions[0]}"
+            }
+            return result
+        }
+
+        whereClauseTerms.eachWithIndex { tm, index ->
+            if(index == 0)
+                whereClause += buildExpression(tm)
+            else if(printFormat)
+                whereClause += " \n\t${tm.logic} ${buildExpression(tm)}"
+            else
+                whereClause += " ${tm.logic} ${buildExpression(tm)}"
+        }
+
+        return whereClause
+    } // buildWhereClause(printFormat = false)
+
+    /**
+     * LogicalBuilder.
+     * Build the whereClauseTerms
+     * by appending logicalTerms to the appropriate expressions.
+     */
+    def logicalBuilder(logicalName, args) {
+        log.debug "logicalBuilder(${logicalName}, ${args})"
+        log.debug "logicalIndexStack: ${logicalIndexStack}"
+
+        def logic = getLogicalString(logicalName)
+
+        for(arg in args) {
+            if(arg instanceof String || arg instanceof GString) {
+                arg = arg.trim()
+                if(arg) { // prevent empty strings being added.
+                    if(logicalIndexStack.size() > 0) {
+                        // Append to current index position.
+                        whereClauseTerms[logicalIndexStack[-1]].expressions << logicalTerm(logic, arg)
+                    }
+                    else {
+                        // Append to 'root'.
+                        whereClauseTerms << logicalTerm(logic, null) // empty expression logicalTerm.
+                        whereClauseTerms[-1].expressions << logicalTerm(logic, arg) // append logicalTerm to expressions
+                    }
+                } // if(arg)
+            } // if(arg instanceof)
+        } // for
+
+    } // logicalBuilder(logicalName, args)
+
+    /**
+     * LogicalTerm.
+     * A logicalTerm is a map object that holds the logic and list of expressions of a whereClauseTerm.
+     */
+    def logicalTerm = { logic, expression ->
+        expression = expression ? [expression] : []
+        ['logic': getLogicalString(logic), 'expressions': expression]
+    }
+
+    /**
+     * GetLogicalString.
+     *
+     * @param logicalName The name to get the matching logicalBuilder string for.
+     */
+    private getLogicalString(logicalName) {
+
+        switch(logicalName.toLowerCase()) {
+            case 'where':
+                logicalBuilders.AND
+                break
+            case logicalBuilders.AND:
+                logicalBuilders.AND
+                break
+            case logicalBuilders.OR:
+                logicalBuilders.OR
+                break
+        }
+
+    }
+
+    /**
+     * HandleClosure.
+     * Setting delegate and DELEGATE_FIRST allows closure to access this object's properties first.
+     */
+    private handleClosure(Closure cl, String methodName = 'root') {
+        log.debug "handleClosure(${cl.toString()}, ${methodName})"
+        if(isWhereClauseBuilder(methodName)) {
+            whereClauseTerms << logicalTerm(getLogicalString(methodName), null)
+            logicalIndexStack << whereClauseTerms.size()-1
+        }
+        nestingStack.push(methodName)
+        cl.delegate = this
+        cl.resolveStrategy = Closure.DELEGATE_FIRST
+        cl.call()
+        //log.debug "nestingStack: $nestingStack"
+        nestingStack.pop()
+        if(isWhereClauseBuilder(methodName)) {
+            logicalIndexStack.pop()
+        }
+    }
+
+    /**
+     * MockLogging.
+     * This class has super cow powers and can mock out it's own debug logging.
+     */
+    private mockLogging(debug = false) {
+        def mockLogger = {}
+        if(debug) {
+            mockLogger = {msg ->
+                    println "${super.getClass()} - DEBUG: $msg"
+            }
+        }
+        super.metaClass.log = [debug: mockLogger]
+        log.debug "Internal mockLogger configured."
+    }
+
+    /**
+     * IsLogicalBuilder.
+     * Determine if a method is a logicalBuilder.
+     */
+    private isLogicalBuilder(String methodName) {
+        logicalBuilders.find{ it.value == methodName.toLowerCase()} ? true:false
+    }
+
+    /**
+     * IsWhereClauseBuilder.
+     * Determine if a method is a where clause builder.
+     */
+    private isWhereClauseBuilder(String methodName) {
+        methodName = methodName.toLowerCase()
+        if(methodName == 'where' || isLogicalBuilder(methodName))
+            return true
+        else
+            return false
+    }
+
+    /**
+     * GetQuery.
+     * Assemble and return the query in a format that can be directly executed.
+     * E.g: executeQuery(q.query, q.namedParams, q.paginateParams).
+     */
+    def getQuery() {
+        clauses.collect { clause ->
+            switch (clause.key.toLowerCase()) {
+                case 'select':
+                    clause.key + ' ' + clause.value.join(', ')
+                    break
+                case 'set':
+                    clause.key + ' ' + clause.value.join(', ')
+                    break
+                case 'where':
+                    buildWhereClause()
+                    break
+                case 'order':
+                    clause.key + ' ' + clause.value.join(', ')
+                    break
+                case 'group':
+                    clause.key + ' ' + clause.value.join(', ')
+                    break
+                default:
+                    clause.key + ' ' + clause.value.join(' ')
+            }
+        }.join(' ')
+    } // getQuery()
+
+    /**
+     * GetPrintFormattedQuery.
+     * Assemble and return the query in a format that can be more easily printed and read by a person.
+     * E.g: println q.printFormattedQuery or when displayed in a report.
+     */
+    def getPrintFormattedQuery() {
+        clauses.collect { clause ->
+            switch (clause.key.toLowerCase()) {
+                case 'select':
+                    clause.key + ' ' + clause.value.join(', \n\t')
+                    break
+                case 'set':
+                    clause.key + ' ' + clause.value.join(', \n\t')
+                    break
+                case 'where':
+                    buildWhereClause(true)
+                    break
+                case 'order':
+                    clause.key + ' ' + clause.value.join(', \n\t')
+                    break
+                case 'group':
+                    clause.key + ' ' + clause.value.join(', \n\t')
+                    break
+                default:
+                    clause.key + ' ' + clause.value.join(' \n\t')
+            }
+        }.join(' \n')
+    } // getPrintFormattedQuery()
+
+} // end class
