I have created an API which allows users to build out queries using a tree. The tree is built from the SearchOperationRequest class.
@Data
@ApiModel(value = "SearchOperationRequest", description = "Condition for the query")
public class SearchOperationRequest {
    @ApiModelProperty(value = "Conditional statement for the where clause", allowableValues = "EQUALS, NOT_EQUALS, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS")
    private SearchConditionOperation condition;
    @ApiModelProperty(value = "Column name to be searched on")
    private String column;
    @ApiModelProperty(value = "Value of column")
    private Object value;
    @ApiModelProperty(value = "Value of OR / AND")
    private SearchComparator comparator;
    @ApiModelProperty(value = "Left node of comparator condition")
    private SearchOperationRequest left;
    @ApiModelProperty(value = "Right node of comparator condition")
    private SearchOperationRequest right;
    public boolean isTreeLeaf() {
        return left == null && right == null;
    }
    public boolean isComparator() {
        return comparator != null;
    }
}
So from this example I could create a SearchOperationRequest that asks for all WHERE hidden = false AND X = 88
"searchOperation": {
    "left": {
        "column": "Hidden",
        "condition": "EQUALS",
        "value": false
    },
    "comparator": "AND",
    "right": {
        "left": {
            "column": "X",
            "condition": "EQUALS",
            "value": 88
        },
        "comparator": "AND"
    }
}
This request is built into a specification using a generic specification builder.
public class GenericSpecificationsBuilder<U> {
    public Specification<U> buildSpecificationFromSearchOperationRequest(SearchOperationRequest root, Function<SpecificationSearchCriteria, Specification<U>> converter) {
        Stack<SearchOperationRequest> stack = new Stack<>();
        Stack<SearchOperationRequest> comparatorStack = new Stack<>();
        Deque<Specification<U>> specStack = new LinkedList<>();
        SearchOperationRequest pointer = root;
        while (pointer != null || !stack.empty()) {
            if (pointer.isTreeLeaf()) {
                specStack.push(converter.apply(new SpecificationSearchCriteria(pointer.getColumn(), pointer.getCondition(), pointer.getValue())));
            }
            if (pointer.isComparator()) {
                comparatorStack.push(pointer);
            }
            if (pointer.getRight() != null) {
                stack.push(pointer.getRight());
            }
            if (pointer.getLeft() != null) {
                pointer.setRight(pointer.getLeft());
                pointer.setLeft(null);
            } else if (!stack.empty()) {
                SearchOperationRequest temp = stack.pop();
                pointer.setRight(temp);
            }
            pointer = pointer.getRight();
        }
        while (specStack.size() <= comparatorStack.size()) {
            comparatorStack.pop();
        }
        while (!comparatorStack.empty()) {
            SearchOperationRequest searchOperationRequest = comparatorStack.pop();
            Specification<U> operand1 = specStack.pop();
            Specification<U> operand2 = specStack.pop();
            if (searchOperationRequest.getComparator().equals(SearchComparator.AND)) {
                specStack.push(Specification.where(operand1)
                        .and(operand2));
            } else if (searchOperationRequest.getComparator().equals(SearchComparator.OR)) {
                specStack.push(Specification.where(operand1)
                        .or(operand2));
            }
        }
        return specStack.pop();
    }
}
My current works great for RIGHT heavy tree's. Meaning queries such as:
WHERE X = 6 AND X = 9
WHERE Z = 5 OR T=9
WHERE Z = 5 OR T=9 OR H=6
But it does not work with building more complex trees where the criteria in braces should get precedence and executed first.
WHERE (X = 6 OR Z = 9) AND (T=6 OR H=8)
The model for this more complex SearchOperationRequest would be:
"searchOperation": {
    "left": {
        "left": {
            "column": "X",
            "condition": "EQUALS",
            "value": 6
        },
        "comparator": "AND",
        "right": {
            "column": "Z",
            "condition": "EQUALS",
            "value": 9
        }
    },
    "comparator": "AND",
    "right": {
        "left": {
            "column": "T",
            "condition": "EQUALS",
            "value": 6
        },
        "comparator": "AND",
        "right": {
            "column": "H",
            "condition": "EQUALS",
            "value": 8
        }
    }
}
How do I modify my GenericSpecificationsBuilder to be able to handle more complex SearchOperationRequest trees?
