docs: private boolean isValidJoinOrder(List<String> tableAliases) {
          assert(m_joinTree != null);
  
          // Split the original tree into the sub-trees
          // having the same join type for all nodes
          List<JoinNode> subTrees = m_joinTree.extractSubTrees();
  
          // For a sub-tree with inner joins only, any join order is valid.
          // The only requirement is that each and every table from that
          // sub-tree constitute an uninterrupted sequence in the specified
          // join order.
          // The outer joins are associative but changing the join order
          // precedence includes moving ON clauses to preserve the initial
          // SQL semantics.
          // For example,
          // T1 right join T2 on T1.C1 = T2.C1 left join T3 on T2.C2=T3.C2
          // can be rewritten as
          // T1 right join (T2 left join T3 on T2.C2=T3.C2) on T1.C1 = T2.C1
          // At the moment, such transformations are not supported.
          // The specified joined order must match the SQL order.
          int tableNameIdx = 0;
          List<JoinNode> finalSubTrees = new ArrayList<>();
          // Ee need to process the sub-trees last one first because
          // the top sub-tree is the first one on the list.
          for (int i = subTrees.size() - 1; i >= 0; --i) {
              JoinNode subTree = subTrees.get(i);
              // Get all tables for the subTree
              List<JoinNode> subTableNodes = subTree.generateLeafNodesJoinOrder();
              JoinNode joinOrderSubTree;
              if ((subTree instanceof BranchNode) &&
                      ((BranchNode)subTree).getJoinType() != JoinType.INNER) {
                  // add the sub-tree as is
                  joinOrderSubTree = subTree;
                  for (JoinNode tableNode : subTableNodes) {
                      if (tableNode.getId() >= 0) {
                          String tableAlias = tableNode.getTableAlias();
                          if ( ! tableAliases.get(tableNameIdx++).equals(tableAlias)) {
                              return false;
                          }
                      }
                  }
              }
              else {
                  // Collect all the "real" tables from the sub-tree
                  // skipping the nodes representing
                  // the sub-trees with the different join type (id < 0)
                  Map<String, JoinNode> nodeNameMap = new HashMap<>();
                  for (JoinNode tableNode : subTableNodes) {
                      if (tableNode.getId() >= 0) {
                          nodeNameMap.put(tableNode.getTableAlias(), tableNode);
                      }
                  }
  
                  // rearrange the sub tree to match the order
                  List<JoinNode> joinOrderSubNodes = new ArrayList<>();
                  for (int j = 0; j < subTableNodes.size(); ++j) {
                      if (subTableNodes.get(j).getId() >= 0) {
                          assert(tableNameIdx < tableAliases.size());
                          String tableAlias = tableAliases.get(tableNameIdx);
                          if (tableAlias == null ||
                                  ! nodeNameMap.containsKey(tableAlias)) {
                              return false;
                          }
                          joinOrderSubNodes.add(nodeNameMap.get(tableAlias));
                          ++tableNameIdx;
                      }
                      else {
                          // It's dummy node
                          joinOrderSubNodes.add(subTableNodes.get(j));
                      }
                  }
                  joinOrderSubTree = JoinNode.reconstructJoinTreeFromTableNodes(
                          joinOrderSubNodes, JoinType.INNER);
                  //Collect all the join/where conditions to reassign them later
                  AbstractExpression combinedWhereExpr = subTree.getAllFilters();
                  if (combinedWhereExpr != null) {
                      joinOrderSubTree.setWhereExpression(
                              combinedWhereExpr.clone());
                  }
                  // The new tree root node id must match the original one
                  // to be able to reconnect the subtrees
                  joinOrderSubTree.setId(subTree.getId());
              }
              finalSubTrees.add(0, joinOrderSubTree);
          }
          // if we got there the join order is OK. Rebuild the whole tree
          JoinNode newNode = JoinNode.reconstructJoinTreeFromSubTrees(finalSubTrees);
          m_joinOrderList.add(newNode);
          return true;
      }