@Override
 public boolean visitDistinct(EJBQLExpression expression) {
   // "distinct" is appended via a marker as sometimes a later match on to-many would
   // require a DISTINCT insertion.
   context.pushMarker(context.makeDistinctMarker(), true);
   context.append(" DISTINCT");
   context.popMarker();
   return true;
 }
  @Override
  public boolean visitFromItem(EJBQLFromItem expression, int finishedChildIndex) {

    String id = expression.getId();

    if (lastId != null) {
      context.append(',');
      context.markCurrentPosition(EJBQLJoinAppender.makeJoinTailMarker(lastId));
    }

    this.lastId = id;
    joinAppender.appendTable(new EJBQLTableId(id));
    return false;
  }
 @Override
 public boolean visitSelect(EJBQLExpression expression) {
   // this ensures that result columns are appeneded only in top-level select, but
   // not subselect (as 'visitSelect' is not called on subselect)
   context.setAppendingResultColumns(true);
   return true;
 }
 @Override
 public boolean visitOuterJoin(EJBQLJoin join) {
   joinAppender.appendOuterJoin(
       null,
       new EJBQLTableId(join.getLeftHandSideId()),
       new EJBQLTableId(join.getRightHandSideId()));
   context.markCurrentPosition(EJBQLJoinAppender.makeJoinTailMarker(join.getRightHandSideId()));
   return false;
 }
  @Override
  public boolean visitFrom(EJBQLExpression expression, int finishedChildIndex) {
    if (finishedChildIndex + 1 == expression.getChildrenCount()) {
      if (lastId != null) {
        context.markCurrentPosition(EJBQLJoinAppender.makeJoinTailMarker(lastId));
      }
    }

    return true;
  }
 @Override
 public boolean visitFrom(EJBQLExpression expression, int finishedChildIndex) {
   context.append(" FROM");
   context.setAppendingResultColumns(false);
   expression.visit(context.getTranslatorFactory().getFromTranslator(context));
   context.markCurrentPosition(context.makeWhereMarker());
   context.markCurrentPosition(context.makeEntityQualifierMarker());
   return false;
 }
  @Override
  public boolean visitInnerJoin(EJBQLJoin join) {
    joinAppender.appendInnerJoin(
        null,
        new EJBQLTableId(join.getLeftHandSideId()),
        new EJBQLTableId(join.getRightHandSideId()));

    // fix 1341-mark current join position for probable future joins to this join
    context.markCurrentPosition(EJBQLJoinAppender.makeJoinTailMarker(join.getRightHandSideId()));
    return false;
  }
  @Override
  public boolean visitWhere(EJBQLExpression expression) {
    // "WHERE" is appended via a marker as it may have been already appended when an
    // entity inheritance qualifier was applied.
    context.pushMarker(context.makeWhereMarker(), true);
    context.append(" WHERE");
    context.popMarker();

    if (context.findOrCreateMarkedBuffer(context.makeEntityQualifierMarker()).length() > 0) {
      context.append(" AND");
    }

    expression.visit(context.getTranslatorFactory().getConditionTranslator(context));
    return false;
  }
 public EJBQLFromTranslator(EJBQLTranslationContext context) {
   super(true);
   this.context = context;
   this.joinAppender = context.getTranslatorFactory().getJoinAppender(context);
 }
 @Override
 public boolean visitSelectExpressions(EJBQLExpression expression) {
   expression.visit(context.getTranslatorFactory().getSelectColumnsTranslator(context));
   return false;
 }
 @Override
 public boolean visitSelectClause(EJBQLExpression expression) {
   context.append("SELECT");
   context.markCurrentPosition(context.makeDistinctMarker());
   return true;
 }
 @Override
 public boolean visitOrderBy(EJBQLExpression expression) {
   context.append(" ORDER BY");
   expression.visit(context.getTranslatorFactory().getOrderByTranslator(context));
   return false;
 }
 @Override
 public boolean visitHaving(EJBQLExpression expression) {
   context.append(" HAVING");
   expression.visit(context.getTranslatorFactory().getConditionTranslator(context));
   return false;
 }
 @Override
 public boolean visitGroupBy(EJBQLExpression expression) {
   context.append(" GROUP BY");
   expression.visit(context.getTranslatorFactory().getGroupByTranslator(context));
   return false;
 }