XpathForClause executeQuery() {
    if (!stack.isEmptyWhereBuilderStack()) {
      this.query = stack.popWhereBuilderStack().build();
    }

    List<Coverage> coverages = this.query.find();

    return new XpathForClause().setCoverages(coverages);
  }
  @Override
  public XpathForClause visitMain(MainContext ctx) {

    Where<Coverage> whereRoot = query.whereBuilder();
    stack.pushWhereStack(whereRoot);

    XpathForClause visitMain = super.visitMain(ctx);

    stack.popWhereStack();

    return visitMain.aggregate(executeQuery());
  }
  @Override
  public XpathForClause visitOrExpr(OrExprContext ctx) {
    if (isSimpleXpath) {
      return super.visitOrExpr(ctx);
    }

    if (ctx.andExpr().size() > 1) {

      Where<Coverage> orWhere = query.expressionFactory();
      stack.pushWhereStack(orWhere);

      int i = 0;
      for (AndExprContext andExprContext : ctx.andExpr()) {

        visit(andExprContext);

        if (i + 1 < ctx.andExpr().size()) {
          stack.popWhereStack();
          stack.pushWhereStack(stack.popWhereBuilderStack().or());
        }

        ++i;
      }

      stack.popWhereStack();
      stack.pushWhereBuilderStack(stack.peekWhereStack().expression(stack.popWhereBuilderStack()));

      return null;
    } else {
      return super.visitOrExpr(ctx);
    }
  }
  @Override
  public XpathForClause visitEqualityExpr(EqualityExprContext ctx) {
    if (isSimpleXpath) {
      return super.visitEqualityExpr(ctx);
    }

    if (ctx.relationalExpr().size() == 1) {
      return super.visitEqualityExpr(ctx);
    }

    String key = ctx.relationalExpr(0).getText();
    String value = ctx.relationalExpr(1).getText();

    Metadatum metadatum = new DataElementMetadatum();
    metadatum.setName(key.replaceAll("@", ""));
    metadatum.setValue(value);

    stack.pushWhereBuilderStack(stack.peekWhereStack().expression(metadatum));

    // FIXME
    return null;
  }
  @Override
  public XpathForClause visitStep(StepContext ctx) {

    if (isSimpleXpath) {
      return super.visitStep(ctx);
    }

    boolean foundCoverage = false;

    String axisSpecifier = ctx.axisSpecifier().getText();
    String nodeName = ctx.nodeTest().getText();

    if (axisSpecifier.isEmpty()) {
      // is node

      switch (nodeName) {
        case XWCPSReservedWords.COVERAGE:
          foundCoverage = true;

          for (PredicateContext predicate : ctx.predicate()) {
            visit(predicate);
          }

          break;
        case XWCPSReservedWords.SERVER:
          Where<Coverage> serverWhere = query.expressionFactory();
          stack.pushWhereStack(serverWhere);

          for (PredicateContext predicate : ctx.predicate()) {
            visit(predicate);
          }

          stack.popWhereStack();
          WhereBuilder<Coverage> serverWhereBuilder =
              stack.peekWhereStack().isChildOf(stack.popWhereBuilderStack());
          stack.popWhereStack();

          stack.pushWhereStack(serverWhereBuilder.and());

          break;
        default:
          throw new ParseCancellationException(
              "Expecting an xpath with '" + XWCPSReservedWords.COVERAGE + "' element");
      }
    }

    if (foundCoverage) {
      isSimpleXpath = true;
    }

    // FIXME
    return null;
  }