title: Build JPA Specification from tree
body: <p>I have created an API which allows users to build out queries using a tree. The tree is built from the <code>SearchOperationRequest</code> class.</p>
  
  <pre><code>@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 &amp;&amp; right == null;
      }
  
      public boolean isComparator() {
          return comparator != null;
      }
  }
  </code></pre>
  
  <p>So from this example I could create a <code>SearchOperationRequest</code> that asks for all <strong>WHERE hidden = false AND X = 88</strong></p>
  
  <pre><code>"searchOperation": {
      "left": {
          "column": "Hidden",
          "condition": "EQUALS",
          "value": false
      },
      "comparator": "AND",
      "right": {
          "left": {
              "column": "X",
              "condition": "EQUALS",
              "value": 88
          },
          "comparator": "AND"
      }
  }
  </code></pre>
  
  <p>This request is built into a specification using a generic specification builder.</p>
  
  <pre><code>public class GenericSpecificationsBuilder&lt;U&gt; {
  
      public Specification&lt;U&gt; buildSpecificationFromSearchOperationRequest(SearchOperationRequest root, Function&lt;SpecificationSearchCriteria, Specification&lt;U&gt;&gt; converter) {
  
          Stack&lt;SearchOperationRequest&gt; stack = new Stack&lt;&gt;();
  
          Stack&lt;SearchOperationRequest&gt; comparatorStack = new Stack&lt;&gt;();
          Deque&lt;Specification&lt;U&gt;&gt; specStack = new LinkedList&lt;&gt;();
  
          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() &lt;= comparatorStack.size()) {
              comparatorStack.pop();
          }
  
          while (!comparatorStack.empty()) {
  
              SearchOperationRequest searchOperationRequest = comparatorStack.pop();
  
              Specification&lt;U&gt; operand1 = specStack.pop();
              Specification&lt;U&gt; 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();
      }
  }
  </code></pre>
  
  <p>My current works great for RIGHT heavy tree's. Meaning queries such as:</p>
  
  <pre><code>WHERE X = 6 AND X = 9
  WHERE Z = 5 OR T=9
  WHERE Z = 5 OR T=9 OR H=6
  </code></pre>
  
  <p>But it does not work with building more complex trees where the criteria in braces should get precedence and executed first.</p>
  
  <pre><code>WHERE (X = 6 OR Z = 9) AND (T=6 OR H=8)
  </code></pre>
  
  <p>The model for this more complex <code>SearchOperationRequest</code> would be:</p>
  
  <pre><code>"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
          }
      }
  }
  </code></pre>
  
  <p>How do I modify my <code>GenericSpecificationsBuilder</code> to be able to handle more complex <code>SearchOperationRequest</code> trees?</p>