/**
   * Returns true if the constraint is associated with the given FromElement.
   *
   * @param constraint the constraint in question
   * @param fromElement the FromElement to check
   * @return true if associated
   */
  public static boolean isRelatedTo(Constraint constraint, FromElement fromElement) {
    if (isAssociatedWith(constraint, fromElement)) {
      return true;
    }

    // also want to find out if this is referred to in a Contains or Subquery
    // constraint that is associated with another fromElement.

    if (constraint instanceof ContainsConstraint) {
      if (fromElement == ((ContainsConstraint) constraint).getQueryClass()) {
        return true;
      }
    } else if (constraint instanceof SubqueryConstraint) {
      if (fromElement == ((SubqueryConstraint) constraint).getQuery()) {
        return true;
      }
    } else if (constraint instanceof SimpleConstraint) {
      SimpleConstraint sc = (SimpleConstraint) constraint;
      Set<QueryField> qFields = getQueryFields(sc.getArg1());
      qFields.addAll(getQueryFields(sc.getArg2()));
      for (QueryField qf : qFields) {
        if (fromElement == qf.getFromElement()) {
          return true;
        }
      }
    }
    return false;
  }
  /**
   * Returns true if the constraint is associated with the given FromElement. Associated with means
   * relating directly to a field of the QueryClass but NOT a cross-reference contraint (which
   * compares two arbitrary fields of different QueryClasses.
   *
   * @param constraint the constraint in question
   * @param fromElement the FromElement to check
   * @return true if associated
   */
  public static boolean isAssociatedWith(Constraint constraint, FromElement fromElement) {

    Object left = getLeftArgument(constraint);
    Object right = getRightArgument(constraint);

    // ignore cross-references
    if (left instanceof QueryEvaluable && !(isCrossReference(constraint))) {
      // not a cross-reference -> at most one QueryClass.  find it.
      QueryClass qc = null; // TODO test for a bug here? left not assoc by right is
      if (getQueryFields((QueryEvaluable) left).iterator().hasNext()) {
        QueryField qf = getQueryFields((QueryEvaluable) left).iterator().next();
        qc = (QueryClass) qf.getFromElement();
      } else if (getQueryFields((QueryEvaluable) right).iterator().hasNext()) {
        QueryField qf = getQueryFields((QueryEvaluable) right).iterator().next();
        qc = (QueryClass) qf.getFromElement();
      } else {
        return false; // does not relate to any QueryClass
      }
      return (fromElement == qc);
    } else if (left instanceof QueryClass && right instanceof QueryClass) {
      return (fromElement == left || fromElement == right);
    } else if (left instanceof QueryClass) {
      return (fromElement == left);
    } else if (left instanceof QueryReference) {
      return (fromElement == ((QueryReference) left).getQueryClass());
    } else if (right instanceof Query) {
      return (fromElement == right);
    }
    return false;
  }
 /**
  * Returns true if the Constraint is a cross-reference between two QueryClasses. A constraint is
  * deemed to be a cross-reference if it compares fields of two different QueryClasses, either
  * directly or via QueryExpressions.
  *
  * @param constraint the contraint to test
  * @return true if the contraint is a cross-reference
  */
 public static boolean isCrossReference(Constraint constraint) {
   if (constraint instanceof SimpleConstraint) {
     // if QueryField exposed part of a subquery QueryField.getFromElement()
     // returns a query, does not cause any problem.
     Set<FromElement> qcs = new HashSet<FromElement>();
     for (QueryField qf : getQueryFields(((SimpleConstraint) constraint).getArg1())) {
       qcs.add(qf.getFromElement());
     }
     for (QueryField qf : getQueryFields(((SimpleConstraint) constraint).getArg2())) {
       qcs.add(qf.getFromElement());
     }
     if (qcs.size() > 1) {
       return true;
     }
   }
   return false;
 }
  public String toString(ValueContext vc) {
    valid = false;
    if (queryDefn == null) return "Query Definition is NULL";

    StringBuffer errorMsg = new StringBuffer();
    if (queryDefn.getErrors() != null) {
      List errors = queryDefn.getErrors();
      for (int i = 0; i < errors.size(); i++) errorMsg.append(errors.get(i) + ".\n");
    }
    if (select == null) {
      errorMsg.append("Query select is NULL.");
    } else {
      if (select.getErrors() != null) {
        List errors = select.getErrors();
        for (int i = 0; i < errors.size(); i++) errorMsg.append(errors.get(i) + ".\n");
      }
    }
    if (errorMsg.length() > 0) return errorMsg.toString();

    List showFields = select.getReportFields();
    int showFieldsCount = showFields.size();
    for (int sf = 0; sf < showFieldsCount; sf++) {
      QueryField field = (QueryField) showFields.get(sf);
      String selClauseAndLabel = field.getSelectClauseExprAndLabel();
      if (selClauseAndLabel != null) selectClause.add(field.getSelectClauseExprAndLabel());
      addJoin(field);
    }

    QueryConditions allSelectConditions = select.getConditions();
    QueryConditions usedSelectConditions = allSelectConditions.getUsedConditions(this, vc);

    // add join tables which have the auto-include flag set and their respective conditions to the
    // from and where clause lists. If the join is already in the 'joins' list, no need to add it
    // in.
    List autoIncJoinList = this.queryDefn.getAutoIncJoins();
    for (Iterator it = autoIncJoinList.iterator(); it.hasNext(); ) {
      this.addJoin((QueryJoin) it.next());
    }

    StringBuffer sql = new StringBuffer();

    int selectCount = selectClause.size();
    int selectLast = selectCount - 1;
    sql.append("select ");
    if (select.distinctRowsOnly()) sql.append("distinct \n");
    else sql.append("\n");
    for (int sc = 0; sc < selectCount; sc++) {
      sql.append("  " + selectClause.get(sc));
      if (sc != selectLast) sql.append(", ");
      sql.append("\n");
    }

    int fromCount = fromClause.size();
    int fromLast = fromCount - 1;
    sql.append("from \n");
    for (int fc = 0; fc < fromCount; fc++) {
      sql.append("  " + fromClause.get(fc));
      if (fc != fromLast) sql.append(", ");
      sql.append("\n");
    }

    boolean haveJoinWheres = false;
    int whereCount = whereJoinClause.size();
    int whereLast = whereCount - 1;
    if (whereCount > 0) {
      sql.append("where\n  (\n");
      for (int wc = 0; wc < whereCount; wc++) {
        sql.append("  " + whereJoinClause.get(wc));
        if (wc != whereLast) sql.append(" and ");
        sql.append("\n");
      }
      sql.append("  )");
      haveJoinWheres = true;
    }

    boolean haveCondWheres = false;
    int usedCondsCount = usedSelectConditions.size();
    if (usedCondsCount > 0) {
      if (haveJoinWheres) sql.append(" and (\n");
      else sql.append("where\n  (\n");
      usedSelectConditions.createSql(this, vc, usedSelectConditions, sql);
      sql.append("  )\n");
      haveCondWheres = true;
    }

    List whereExprs = select.getWhereExpressions();
    if (whereExprs != null && whereExprs.size() > 0) {
      int whereExprsLast = whereExprs.size() - 1;
      boolean first = false;
      if (!haveJoinWheres && !haveCondWheres) {
        sql.append("where\n  (\n");
        first = true;
      }

      int whereExprsCount = whereExprs.size();
      for (int we = 0; we < whereExprsCount; we++) {
        SqlWhereExpression expr = (SqlWhereExpression) whereExprs.get(we);
        if (first) first = false;
        else sql.append(expr.getConnectorSql());

        sql.append(" (");
        sql.append(expr.getWhereCondExpr(this));
        sql.append("  )\n");
      }
    }
    List groupBys = select.getGroupBy();
    int groupBysCount = groupBys.size();
    if (groupBysCount > 0) {
      int groupByLast = groupBysCount - 1;
      sql.append("group by\n");
      for (int gb = 0; gb < groupBysCount; gb++) {
        QueryField field = (QueryField) groupBys.get(gb);
        sql.append("  " + field.getQualifiedColName());
        if (gb != groupByLast) {
          sql.append(", ");
        }
        sql.append("\n");
      }
    }

    List orderBys = select.getOrderBy();
    int orderBysCount = orderBys.size();
    int orderBysLast = orderBysCount - 1;
    if (orderBysCount > 0) {
      sql.append("order by\n");
      for (int ob = 0; ob < orderBysCount; ob++) {
        QuerySortFieldRef sortRef = (QuerySortFieldRef) orderBys.get(ob);
        QueryField[] fields = sortRef.getFields(vc);
        if (fields == null) {
          return "Order by field '"
              + sortRef.getFieldName().getId()
              + "' did not evaluate to an appropriate QueryField.\n";
        } else {
          int lastField = fields.length - 1;
          for (int i = 0; i < fields.length; i++) {
            QueryField field = fields[i];
            if (field == null)
              return "Order by field ["
                  + i
                  + "] in '"
                  + sortRef.getFieldName().getId()
                  + "' did not evaluate to an appropriate QueryField.\n";

            sql.append("  " + field.getOrderByClauseExpr());
            if (sortRef.isDescending()) sql.append(" desc");

            if (i != lastField) {
              sql.append(", ");
              sql.append("\n");
            }
          }
        }

        if (ob != orderBysLast) sql.append(", ");
        sql.append("\n");
      }
    }

    valid = true;
    return sql.toString();
  }
  public void addJoin(QueryField field) {
    if (field == null) throw new RuntimeException("Null field");

    QueryJoin join = field.getJoin();
    this.addJoin(join);
  }