public class BigDecimal {
    public BigDecimal(BigInteger p) {
    }

    public BigDecimal(String p) {
    }
} public class SQLException extends Exception {
    public SQLException(String m) {
        super(m);
    }

    public SQLException(String m, Throwable t) {
        super(m, t);
    }


    public int getErrorCode() {
        return 0;
    }
} public class Select extends Query implements ColumnResolver {

    private final Button add = new Button("Add condition");

    private final XWidget<Expression> xcondition = new XWidget<Expression>();

    private HashMap<Expression, Object> currentGroup;

    private int currentGroupRowId;

    private ObjectArray<Expression> expressions;
    private final ObjectArray<TableFilter> filters = ObjectArray.newInstance();
    // private ObjectArray<SelectOrderBy> orderList;
    private ObjectArray<Expression> group;
    private boolean[] groupByExpression;
    private int[] groupIndex;
    private Expression having;

    private boolean isQuickAggregateQuery;
    private final Session session;
    private int visibleColumnCount;

    private final VerticalPanel vp = new VerticalPanel();

    private final CommandListBox what;

    public Select(final Session session2) {
        super(session2);
        session = session2;
        session.setSelect(this);

        initWidget(vp);

        what = new CommandListBox(this);
        what.addStyleName("rqbWhat");  // XXX to be removed
        what.addStyleName("rqbFrom");
        what.setVisible(session.getConfig().getFrom().isVisible());
        vp.add(what);

        vp.add(add);
        add.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                addFirstCondition();
                fireDirty();
            }
        });
        List items = new ArrayList();
        CommandWithLabel prompt = new PromptCommand();
        what.setValue(prompt);
        items.add(prompt);

        // XXX 00 What is SQL 92 case sensitivity rule

        for (Table t : session.getDatabase().getMainSchema().getTables()) {
            if (!t.isHidden()) {
                items.add(new TableCommand(t));
            }
        }

        what.setAcceptableValues(items);

        vp.add(xcondition);
    }

    // XXX scope for testing
    public Comparison addFirstCondition() {
        Comparison c = new Comparison(session);
        addCondition(c);
        return c;
    }

    class TableCommand extends CommandWithLabel {
        private final Table table;
        TableCommand(Table t) {
            super(t);
            table = t;
        }
        @Override
        public void execute() {
            updateTable(table);
        }
    }

    class PromptCommand extends CommandWithLabel {
        PromptCommand() {
            super("Please select");
        }

        @Override
        public void execute() {
            updateTable(null);
        }
    }


    /**
     * Add a condition to the list of conditions.
     *
     * @param cond
     *            the condition to add
     */
    public void addCondition(Expression cond) {
        Expression condition = getCondition();
        if (condition == null) {
            condition = cond;
        } else {
            condition = new ConditionAndOr(session, ConditionAndOr.AND, cond, condition);
        }
        setCondition(condition);
    }

    private void setCondition(Expression condition) {
        xcondition.setValue(condition);
    }

    /**
     * Add a table to the query.
     *
     * @param filter
     *            the table to add
     * @param isTop
     *            if the table can be the first table in the query plan
     */
    public void addTableFilter(TableFilter filter, boolean isTop) {
        // Oracle doesn't check on duplicate aliases
        // String alias = filter.getAlias();
        // if(filterNames.contains(alias)) {
        // throw Message.getSQLException(
        // ErrorCode.DUPLICATE_TABLE_ALIAS, alias);
        // }
        // filterNames.add(alias);

        filters.add(filter);

    }

    public int getColumnCount() {
        return visibleColumnCount;
    }

    public Expression getCondition() {
        return xcondition.getValue();
    }

    public HashMap<Expression, Object> getCurrentGroup() {
        return currentGroup;
    }

    public int getCurrentGroupRowId() {
        return currentGroupRowId;
    }

    public ObjectArray<Expression> getExpressions() {
        return expressions;
    }

    public ObjectArray<TableFilter> getFilters() {
        return filters;
    }

    public String getSQL(List args) {

        StatementBuilder buff = new StatementBuilder("SELECT ");
        // if (distinct) {
        // buff.append("DISTINCT ");
        // }
        if (expressions != null) {
         for (int i = 0; i < expressions.size(); i++) {
         buff.appendExceptFirst(", ");
         buff.append(expressions.get(i).getSQL(args));
         }
        }
        buff.append("\nFROM ");
        TableFilter filter = null;
        if (filters.size() > 0) {
            filter = filters.get(0);
        }
        if (filter != null) {
            buff.resetCount();
            int i = 0;
            do {
                buff.appendExceptFirst("\n");
                buff.append(filter.getSQL(i++ > 0, args));
                filter = filter.getJoin();
            } while (filter != null);
        } else {
            buff.resetCount();
            int i = 0;
            for (TableFilter f : filters) {
                buff.appendExceptFirst("\n");
                buff.append(f.getSQL(i++ > 0, args));
            }
        }
        if (xcondition.getValue() != null) {
            buff.append("\nWHERE ").append(xcondition.getValue().getSQL(args));
        }
        // if (groupIndex != null) {
        // buff.append("\nGROUP BY ");
        // buff.resetCount();
        // for (int gi : groupIndex) {
        // Expression g = exprList[gi];
        // g = g.getNonAliasExpression();
        // buff.appendExceptFirst(", ");
        // buff.append(StringUtils.unEnclose(g.getSQL()));
        // }
        // }
        // if (group != null) {
        // buff.append("\nGROUP BY ");
        // buff.resetCount();
        // for (Expression g : group) {
        // buff.appendExceptFirst(", ");
        // buff.append(StringUtils.unEnclose(g.getSQL()));
        // }
        // }
        // if (having != null) {
        // // could be set in addGlobalCondition
        // // in this case the query is not run directly, just getPlanSQL is
        // // called
        // Expression h = having;
        // buff.append("\nHAVING ").append(StringUtils.unEnclose(h.getSQL()));
        // } else if (havingIndex >= 0) {
        // Expression h = exprList[havingIndex];
        // buff.append("\nHAVING ").append(StringUtils.unEnclose(h.getSQL()));
        // }
        // if (sort != null) {
        // buff.append("\nORDER BY ").append(sort.getSQL(exprList,
        // visibleColumnCount));
        // }
        // if (orderList != null) {
        // buff.append("\nORDER BY ");
        // buff.resetCount();
        // for (SelectOrderBy o : orderList) {
        // buff.appendExceptFirst(", ");
        // buff.append(StringUtils.unEnclose(o.getSQL()));
        // }
        // }
        // if (limitExpr != null) {
        // buff.append("\nLIMIT ").append(StringUtils.unEnclose(limitExpr.getSQL()));
        // if (offsetExpr != null) {
        // buff.append(" OFFSET ").append(StringUtils.unEnclose(offsetExpr.getSQL()));
        // }
        // }
        // if (isForUpdate) {
        // buff.append("\nFOR UPDATE");
        // }
        // if (isQuickAggregateQuery) {
        // buff.append("\n/* direct lookup */");
        // }
        // if (isDistinctQuery) {
        // buff.append("\n/* distinct */");
        // }
        // if (sortUsingIndex) {
        // buff.append("\n/* index sorted */");
        // }
        // if (isGroupQuery) {
        // if (isGroupSortedQuery) {
        // buff.append("\n/* group sorted */");
        // }
        // }
        return buff.toString();
    }

   // public ListBox getWhat() {
   //     return what;
   // }

    // public HashSet<Table> getTables() {
    // HashSet<Table> set = New.hashSet();
    // for (TableFilter filter : filters) {
    // set.add(filter.getTable());
    // }
    // return set;
    // }

    /**
     * Check if this is an aggregate query with direct lookup, for example a
     * query of the type SELECT COUNT(*) FROM TEST or SELECT MAX(ID) FROM TEST.
     *
     * @return true if a direct lookup is possible
     */
    public boolean isQuickAggregateQuery() {
        return isQuickAggregateQuery;
    }

    @Override
    public BaseSqlWidget remove(Expression e) {
        if (getCondition() == e) {
            setCondition(null);
            return this;
        }
        throw new IllegalArgumentException();
    }

    @Override
    public void replace(Expression e0, Expression e1) {
        if (getCondition() == e0) {
            setCondition(e1);
        }
    }

    public void setExpressions(ObjectArray<Expression> p) {
        this.expressions = p;
    }

    // public void mapColumns(ColumnResolver resolver, int level) throws
    // SQLException {
    // for (Expression e : expressions) {
    // e.mapColumns(resolver, level);
    // }
    // if (condition != null) {
    // condition.mapColumns(resolver, level);
    // }
    // }
    //
    // public void setEvaluatable(TableFilter tableFilter, boolean b) {
    // for (Expression e : expressions) {
    // e.setEvaluatable(tableFilter, b);
    // }
    // if (condition != null) {
    // condition.setEvaluatable(tableFilter, b);
    // }
    // }

    public void setGroupBy(ObjectArray<Expression> p) {
        this.group = p;
    }

    public void setHaving(Expression p) {
        this.having = p;
    }

    public void updateTable(Table tt) {
        if (tt == null) {
            filters.clear();
        } else if (getFilters().size() == 0) {
            TableFilter filter = new TableFilter(session, tt, TableFilter
                    .newAlias(), Select.this);
            addTableFilter(filter, true); // XXX really is true?
        } else {
            getFilters().get(0).setTable(tt);
        }
        expressions = null; // XXX 03 bit harsh?
        setCondition(null);

        session.getMsgBus().fireEvent(new TableEvent());
    }

    @Override
    public void onDirty() {
        garbageCollectFilters();

        add.setVisible(getCondition() == null && this.getFilters().size() > 0);

        if (getFilters().size() > 0) {
            what.setValue(new TableCommand(getFilters().get(0).getTable()));
        }
    }

    public void garbageCollectFilters() {
        if (filters.size() > 1) {
            // XXX identity?
            final Set<TableFilter> used = new HashSet<TableFilter>();
            used.add(null);
            Visitor counter = new VisitorBase() {
                @Override
                public void handle(BaseSqlWidget w) {
                    if (w instanceof ExpressionColumn) {
                        ExpressionColumn ec = (ExpressionColumn) w;
                        TableFilter tf = ec.getTableFilter();
                        if (used.add(tf)) {
                            if (tf.getJoinCondition() != null) {
                                tf.getJoinCondition().traverse(this);
                            }
                        }
                    }
                }
            };

            if (getCondition() != null) {
                getCondition().traverse(counter);
            }

            boolean removed;
            do {
                removed = false;
                TableFilter prev = filters.get(0);
                TableFilter x = prev.getJoin();
                while (x != null) {
                    if (!used.contains(x)) {
                        // look for used in all other filters?
                        int i = filters.indexOf(x);
                        if (i > 0) {
                            filters.remove(i);
                        }
                            // XXX why is this the case?
                            // just when comma seperated?
                        prev.setJoin(x.getJoin());
                        removed = true;
                    }
                    prev = x;
                    x = x.getJoin();
                }
            } while (removed);

        }
    }

    @Override
    public void acceptChildren(Visitor callback) {
        Expression condition = getCondition();
        if (condition != null) {
            condition.traverse(callback);
        }
    }

    @Override
    public Column resolveColumn(String alias, String columnName) {
        for (TableFilter tf : getFilters()) {
            if (alias == null || alias.equals(tf.getAlias())) {
               Column c = tf.getTable().getColumn(columnName);
               if (c != null) {
                   return c;
               }
               // XXX if alias != null should blowup?
               // XXX should blowup if find more than one?
            }

            //if (alias.equals(tf.getAlias())) {
            //    return tf.getTable().getColumn(columnName);
            //}
        }
        // XXX log? Window.alert("Unable to find " + alias + "." + columnName);
        return null;
    }

    public TableFilter getTableFilter(String alias) {
        if (alias == null) {
            return getFilters().get(0);
        }

        for (TableFilter tf : getFilters()) {
            if (alias.equals(tf.getAlias())) {
                return tf;
            }
        }
        return null;
    }

    // public void addGlobalCondition(Parameter param, int columnId, int
    // comparisonType) throws SQLException {
    // addParameter(param);
    // Expression col = expressions.get(columnId);
    // col = col.getNonAliasExpression();
    // Expression comp = new Comparison(session, comparisonType, col, param);
    // comp = comp.optimize(session);
    // boolean addToCondition = true;
    // if (isGroupQuery) {
    // addToCondition = false;
    // for (int i = 0; groupIndex != null && i < groupIndex.length; i++) {
    // if (groupIndex[i] == columnId) {
    // addToCondition = true;
    // break;
    // }
    // }
    // if (!addToCondition) {
    // if (havingIndex >= 0) {
    // having = expressions.get(havingIndex);
    // }
    // if (having == null) {
    // having = comp;
    // } else {
    // having = new ConditionAndOr(ConditionAndOr.AND, having, comp);
    // }
    // }
    // }
    // if (addToCondition) {
    // if (condition == null) {
    // condition = comp;
    // } else {
    // condition = new ConditionAndOr(ConditionAndOr.AND, condition, comp);
    // }
    // }
    // }

    // public String getFirstColumnAlias(Session s) {
    // if (SysProperties.CHECK) {
    // if (visibleColumnCount > 1) {
    // Message.throwInternalError("" + visibleColumnCount);
    // }
    // }
    // Expression expr = expressions.get(0);
    // if (expr instanceof Alias) {
    // return expr.getAlias();
    // }
    // Mode mode = s.getDatabase().getMode();
    // String name = s.getNextSystemIdentifier(getSQL());
    // expr = new Alias(expr, name, mode.aliasColumnName);
    // expressions.set(0, expr);
    // return expr.getAlias();
    // }
    //

} public class SelectUnion extends Query {

    /**
     * The type of a UNION statement.
     */
    public static final int UNION = 0;

    /**
     * The type of a UNION ALL statement.
     */
    public static final int UNION_ALL = 1;

    /**
     * The type of an EXCEPT statement.
     */
    public static final int EXCEPT = 2;

    /**
     * The type of an INTERSECT statement.
     */
    public static final int INTERSECT = 3;

    private int unionType;
    private final Query left;

    private Query right;
    private ObjectArray<Expression> expressions;
   // private ObjectArray<SelectOrderBy> orderList;
  // SortOrder sort;
    private boolean distinct;
    private boolean isPrepared, checkInit;
    private boolean isForUpdate;

    public SelectUnion(Session session, Query query) {
        super(session);
        this.left = query;
    }

    public void setUnionType(int type) {
        this.unionType = type;
    }

    public void setRight(Query select) {
        right = select;
    }


//    public void setOrder(ObjectArray<SelectOrderBy> order) {
//        orderList = order;
//    }

//    private Value[] convert(Value[] values, int columnCount) throws SQLException {
//        for (int i = 0; i < columnCount; i++) {
//            Expression e = expressions.get(i);
//           // values[i] = values[i].convertTo(e.getType());
//        }
//        return values;
//    }




//    public HashSet<Table> getTables() {
//        HashSet<Table> set = left.getTables();
//        set.addAll(right.getTables());
//        return set;
//    }

    public void setDistinct(boolean b) {
        distinct = b;
    }

    public ObjectArray<Expression> getExpressions() {
        return expressions;
    }

//    public void mapColumns(ColumnResolver resolver, int level) throws SQLException {
//        left.mapColumns(resolver, level);
//        right.mapColumns(resolver, level);
//    }
//
//    public void setEvaluatable(TableFilter tableFilter, boolean b) {
//        left.setEvaluatable(tableFilter, b);
//        right.setEvaluatable(tableFilter, b);
//    }

//    public void addGlobalCondition(Parameter param, int columnId, int comparisonType) throws SQLException {
//        addParameter(param);
//        switch (unionType) {
//        case UNION_ALL:
//        case UNION:
//        case INTERSECT: {
//            left.addGlobalCondition(param, columnId, comparisonType);
//            right.addGlobalCondition(param, columnId, comparisonType);
//            break;
//        }
//        case EXCEPT: {
//            left.addGlobalCondition(param, columnId, comparisonType);
//            break;
//        }
//        default:
//            Message.throwInternalError("type=" + unionType);
//        }
//    }

//    public String getPlanSQL() {
//        StringBuilder buff = new StringBuilder();
//        buff.append('(').append(StringUtils.unEnclose(left.getPlanSQL())).append(')');
//        switch (unionType) {
//        case UNION_ALL:
//            buff.append("UNION ALL ");
//            break;
//        case UNION:
//            buff.append("UNION ");
//            break;
//        case INTERSECT:
//            buff.append("INTERSECT ");
//            break;
//        case EXCEPT:
//            buff.append("EXCEPT ");
//            break;
//        default:
//            Message.throwInternalError("type=" + unionType);
//        }
//        buff.append('(').append(StringUtils.unEnclose(right.getPlanSQL())).append(')');
//        Expression[] exprList = expressions.toArray(new Expression[expressions.size()]);
//        if (sort != null) {
//            buff.append(" ORDER BY ").append(sort.getSQL(exprList, exprList.length));
//        }
//        if (limitExpr != null) {
//            buff.append(" LIMIT ").append(StringUtils.unEnclose(limitExpr.getSQL()));
//            if (offsetExpr != null) {
//                buff.append(" OFFSET ").append(StringUtils.unEnclose(offsetExpr.getSQL()));
//            }
//        }
//        if (isForUpdate) {
//            buff.append(" FOR UPDATE");
//        }
//        return buff.toString();
//    }




    public String getFirstColumnAlias(Session s) {
        return null;
    }

} public class Session {

    private static IdentifierEscaper identifierEscaper = new IdentifierEscaper() {
        @Override
        public String quote(String id) {
            return "\"" + id + "\"";
        }
    };

  public void setIdentifierEscaper(IdentifierEscaper p) {
      identifierEscaper = p;
  }

  /**
   * Add double quotes around an identifier if required.
   *
   * @param s the identifier
   * @return the quoted identifier
   */
  public static String quoteIdentifier(String s) {
      return identifierEscaper.quote(s);
  }

    // XXX need commandBuilder and select?
    private CommandBuilder commandBuilder;

    private Select select;

    private Configuration config;

    private final ValueRegistry valueRegistry = new ValueRegistry();

    private final Database database;

    private final HandlerManager msgbus;


  // XXX 00 remove one of these constructors
  public Session(Configuration config2) {
      this(config2.getDatabase());
      this.config = config2;
  }

  @Deprecated
  public Session(Database database2) {
      database = database2;
      msgbus = new HandlerManager(this);
  }

    public Configuration getConfig() {
        return config;
    }

    public CommandBuilder getCommandBuilder() {
        return commandBuilder;
    }

    public void setCommandBuilder(CommandBuilder p) {
        commandBuilder = p;
    }

    public Database getDatabase() {
        return database;
    }


  public HandlerManager getMsgBus() {
      return msgbus;
  }



  public void setSelect(Select p) {
      select = p;
  }

  public Column resolveColumn(String alias, String columnName) {
      return select.resolveColumn(alias, columnName);
  }

  public ObjectArray<TableFilter> getFilters() {
      return select.getFilters();
  }

  public TableFilter getTableFilter(Table t) {
      for (TableFilter tf : select.getFilters()) {
          if (tf.getTable().equals(t)) {
              return tf;
          }
      }
      return null;
  }

  public TableFilter createTableFilter(Table t) {
      TableFilter tf = new TableFilter(this, t, TableFilter.newAlias(), select);
      select.addTableFilter(tf, true); // XXX really is true?
      return tf;
  }

  public TableFilter getOrCreateTableFilter(Table t) {
      TableFilter tf = getTableFilter(t);
      if (tf == null) {
          tf = createTableFilter(t);
      }
      return tf;
  }

  //public Table getTable(String alias) {
  //    return select.getTable(alias);
  //}

  public Table getRootTable() {
      // XXX maybe if no table then grab default?
      // or should select always have one table?
      // or ui shouldn't offer condition button till table?
      return select.getFilters().get(0).getTable();
  }

    public ValueRegistry getValueRegistry() {
        return valueRegistry;
    }
} public class Comparison extends Condition {

    private String compareType;
    private final XWidget<Expression> xleft = new XWidget<Expression>();
    private final XWidget<Expression> xright = new XWidget<Expression>();

    private final ListBox2<Operator> op = new ListBox2<Operator>();

    private final Button remove = new Button("-");

    public Comparison(Session session2) {
        super(session2);

        // XXX why do this defaulting here? Not just do it in ExpressionColumn?
        TableFilter tf = getSession().getFilters().get(0);
        Column col = null;
        for (Column col2 : tf.getTable().getColumns()) {
            if (!col2.isHidden()) {
                col = col2;
                break;
            }
        }
        if (col == null) {
            throw new RuntimeException("No visible column");
        }

        setLeft(new ExpressionColumn(getSession(), null, tf.getAlias(),
                col.getName()));
        setRight(new Parameter(getSession()));
        compareType = col.getType().getOperators()[0].getName();
        init();
    }

    public Comparison(Session session2, String compareType2,
            final Expression left2, final Expression right2) {
        super(session2);
        setLeft(left2);
        setRight(right2);
        compareType = compareType2;
        init();
    }

    private void init() {
        HorizontalPanel v = new HorizontalPanel();
        v.addStyleName("rqbComparison");
        v.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);

        v.add(xleft);

        v.add(op);
        op.addValueChangeHandler(new ValueChangeHandler<Operator>() {
            @Override
            public void onValueChange(ValueChangeEvent<Operator> event) {
                setOperator(op.getValue());
                fireDirty();
            }
        });

        v.add(xright);

        v.add(remove);
        remove.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                remove();
                fireDirty();
            }
        });

        Button add = new Button("+");

        v.add(add);
        add.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                add();
                fireDirty();
            }
        });

        initWidget(v);
    }

    public void setOperator(Operator p) {
        this.compareType = p.getName();

        Cardinality c = p.getCardinality();
        if (c == Cardinality.ZERO) {
            if (getRight() != null) {
                setRight(null);
            }
        } else {
            if (getRight() == null) {
                setRight(new Parameter(getSession()));
            }
        }
    }

    public String getOperatorName() {
        return compareType;
    }

    public Operator getOperator() {
        ExpressionColumn leftec = ((ExpressionColumn) getLeft());
        Column lc = leftec.getColumn();
        if (lc == null) {
            leftec.getColumn();
        }
        Type lt = lc.getType();
        Operator o = lt.getOperatorByName(compareType);
        if (o == null) {
            o = lt.getOperators()[0];
            compareType = o.getName();
        }
        return o;
    }

    @Override
    public void onDirty() {
        Column col = ((ExpressionColumn) getLeft()).getColumn();

        op.setValue(getOperator());
        op.setAcceptableValues(col.getType().getOperators());
    }

    @Override
    public String getSQL(List<Object> args) {
        Operator op = getOperator();
        switch (op.getCardinality()) {
        case ZERO:
            return "(" + getLeft().getSQL(args) + " " + op.getName() + ")";
        case ONE:
        case MULTI:
            return "(" + getLeft().getSQL(args) + " " + getOperatorName() + " "
                    + getRight().getSQL(args) + ")";
        default:
            throw new RuntimeException("Unknown cardinality "
                    + op.getCardinality());
        }
    }

    public Expression getLeft() {
        return xleft.getValue();
    }

    public void setLeft(Expression left) {
        xleft.setValue(left);
    }

    public Expression getRight() {
        return xright.getValue();
    }

    public void setRight(Expression right) {
        xright.setValue(right);
    }

    // XXX one side ask about the other...
    // XXX expression have getTypeInfo?
    public Expression getOther(Expression me) {
        if (getLeft() == me) {
            return getRight();
        } else if (getRight() == me) {
            return getLeft();
        } else {
            throw new RuntimeException("Unable to find other side");
        }
    }

    public BaseSqlWidget remove() {
        return getParentExpression().remove(this);
    }

    public void add() {
        ConditionAndOr andOr = new ConditionAndOr(getSession(), 0, new Nop(),
                new Nop());
        getParentExpression().replace(this, andOr);
        andOr.setLeft(this);

        andOr.setRight(new Comparison(getSession()));
    }

    @Override
    public void acceptChildren(Visitor callback) {
        if (xleft.getValue() != null) {
            xleft.getValue().traverse(callback);
        }
        if (xright.getValue() != null) {
            xright.getValue().traverse(callback);
        }
    }
} public class ConditionAndOr extends Condition {
    interface MyUiBinder extends UiBinder<Widget, ConditionAndOr> {
    }

    private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

    @UiField
    XWidget<Expression> left;

    @UiField
    XWidget<Expression> right;

    @UiField
    ListBox op;



    /**
     * The AND condition type as in ID=1 AND NAME='Hello'.
     */
    public static final int AND = 0;

    /**
     * The OR condition type as in ID=1 OR NAME='Hello'.
     */
    public static final int OR = 1;

    private int andOrType;

    public ConditionAndOr(Session session, int andOrType2, Expression left2, Expression right2) {
        super(session);
        initWidget(uiBinder.createAndBindUi(this));
        this.andOrType = andOrType2;
        setLeft(left2);
        setRight(right2);

        op.addItem("AND");
        op.addItem("OR");

        op.setSelectedIndex(andOrType2);


    }

    public Expression getLeft() {
        return left.getValue();
    }

    public void setLeft(Expression e) {
        left.setValue(e);
    }

    public Expression getRight() {
        return right.getValue();
    }

    public void setRight(Expression e) {
        right.setValue(e);
    }

    public String getString() {
        switch (andOrType) {
        case AND:
            return "AND";
        case OR:
            return "OR";
        default:
            throw new RuntimeException("andOrType=" + andOrType);
        }
    }

    @Override
    public String getSQL(List args) {
        String sql;
        switch (andOrType) {
        case AND:
            sql = getLeft().getSQL(args) + " AND " + getRight().getSQL(args);
            break;
        case OR:
            sql = getLeft().getSQL(args) + " OR " + getRight().getSQL(args);
            break;
        default:
            throw new RuntimeException("andOrType=" + andOrType);
        }
        return "(" + sql + ")";
    }

    @Override
    public BaseSqlWidget remove(Expression e) {
        BaseSqlWidget parent = getParentExpression();
        if (getLeft() == e) {
            parent.replace(this, getRight());
        } else if (getRight() == e) {
            parent.replace(this, getLeft());
        } else {
            throw new IllegalArgumentException();
        }
        return parent;
    }

    @Override
    public void replace(Expression a, Expression b) {
        if (getLeft() == a) {
            setLeft(b);
        } else if (getRight() == a) {
            setRight(b);
        }
    }

    @Override
    public void acceptChildren(Visitor callback) {
        getLeft().traverse(callback);
        getRight().traverse(callback);
    }

    @UiHandler("op")
    void handleChange(ChangeEvent e) {
        andOrType = op.getSelectedIndex();
        fireDirty();
    }

    @Override
    public int hook() {
        return (getLeft().hook() + getRight().hook()) / 2;
    }

    @Override
    public void onDirty() {
        Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
            @Override
            public boolean execute() {
                Element opOuter = op.getElement().getParentElement();
                Element outerDiv = opOuter.getParentElement();
                Element topDiv = outerDiv.getFirstChildElement();

                int middleOffset = (hook() - getAbsoluteTop()) - opOuter.getOffsetHeight() / 2;

                opOuter.getStyle().setTop(middleOffset, Unit.PX);

                int leftHook = getLeft().hook();
                int rightHook = getRight().hook();

                int borderWidth = 10;

                int h = rightHook - leftHook - borderWidth;
                int top = leftHook - getAbsoluteTop() - borderWidth / 2;

                topDiv.getStyle().setHeight(h, Unit.PX);
                topDiv.getStyle().setTop(top, Unit.PX);

                return false;
        }

        }, 30);
    }
} public class Expression extends BaseSqlWidget {

    public Expression(Session session) {
        super(session);
        // initWidget(new Label("E" + getClass().getName()));
    }

    public String getSQL(List<Object> args) {
        return "blah";
    }

} public class ExpressionColumn extends Expression implements TableEventHandler {
    private Database database;
    private final String schemaName;
    private String tableAlias;
    private String columnName;

    private final HorizontalPanel hp = new HorizontalPanel();

    public ExpressionColumn(Session session2, String schemaName2,
            String tableAlias2, String columnName2) {
        super(session2);
        this.schemaName = schemaName2;
        this.tableAlias = tableAlias2;
        this.columnName = columnName2;

        initWidget(hp);

        reg2 = getSession().getMsgBus().addHandler(TableEvent.TYPE, this);
    }

    public void selectConstraintRef(ConstraintReferential ref) {
        TableFilter targetTf = JoinHelper.getOrCreateFor(getSession(), ref);
        this.tableAlias = targetTf.getAlias();
        this.columnName = targetTf.getTable().getColumns().iterator().next()
                .getName();

    }

    public void updateColumn(String alias, Column col2) {
        this.tableAlias = alias;
        this.columnName = col2.getName();
    }

    private final HandlerRegistration reg2;

    @Override
    public void onUnload() {
        super.onUnload();
        if (reg2 != null) {
            reg2.removeHandler();
        }
    }

    public Column getColumn() {
        return getSession().resolveColumn(tableAlias, getColumnName());
    }

    public String getColumnName() {
        return columnName;
    }

    public String getQualifiedColumnName() {
        if (tableAlias == null) {
            return getColumnName();
        }
        return tableAlias + "." + getColumnName();
    }

    @Override
    public void onDirty() {
        hp.clear();

        TableFilter tf = null;
        for (TableFilter tf2 : getSession().getFilters()) {
            if (StringUtils.equals(tableAlias, tf2.getAlias())) {
                tf = tf2;
                break;
            } else if (tf == null) {
                tf = tf2;
            }
        }

        Object hotValue = getColumn();

        Command hotCommand = null;
        while (tf != null) {
            final CommandListBox ght = new CommandListBox(this);
            hp.insert(ght, 0);
            List<Command> items = new ArrayList();
//           hotCommand = new ClearCommand();
//            items.add(hotCommand);

            for (Constraint c : tf.getTable().getConstraints()) {
                if (c instanceof ConstraintReferential) {
                    ConstraintReferential cr = (ConstraintReferential) c;
                    if (!cr.isHidden()) {
                        Command command = new ConstraintCommand(cr);
                        if (cr == hotValue) {
                            hotCommand = command;
                        }
                        items.add(command);
                    }
                }
            }

            for (Column c : tf.getTable().getColumns()) {
                if (!c.isHidden()) {
                    Command command = new ColumnCommand(tf.getAlias(), c);
                    if (c == hotValue) {
                        hotCommand = command;
                    }
                    items.add(command);
                }
            }

            ght.setValue(hotCommand);
            ght.setAcceptableValues(items);

            JoinHelper thing = JoinHelper.getParent(tf);
            if (thing != null) {
                tf = thing.getParent();
                hotValue = thing.getConstraint();
            } else {
                tf = null;
            }
        }
    }

    @Override
    public String getSQL(List args) {
        String sql;
        // if (column != null) {
        // sql = column.getSQL();
        // } else {
        sql = Session.quoteIdentifier("" + getColumnName());
        // }
        if (tableAlias != null) {
            sql = Session.quoteIdentifier(tableAlias) + "." + sql;
        }
        // if (schemaName != null) {
        // sql = Parser.quoteIdentifier(schemaName) + "." + sql;
        // }
        return sql;
    }

    @Override
    public void onTable(TableEvent e) {
        // Window.alert("Changed table");
    }

    public TableFilter getTableFilter() {
        // return resolver == null ? null : resolver.getTableFilter();
        for (TableFilter tf2 : getSession().getFilters()) {

            if (StringUtils.equals(tableAlias, tf2.getAlias())) {
                return tf2;
            }
        }
        return null; // XXX or blowup?
    }

//    private class ClearCommand extends Command2 {
//        ClearCommand() {
//            super("Please select...");
//        }
//
//        @Override
//        public void execute() {
//            // TOxDO 00 a Select "PLease select" for column and goes bang. in getSql?
//            // really want this at all? Ever selected? Defaults to first column...
//            // A) Default to first column and get rid of this
//            // B) Don't default and make this work ok
//            updateColumn(null, null);
//        }
//    }

    private class ColumnCommand extends CommandWithLabel {
        private final String alias;
        private final Column column;

        ColumnCommand(String a, Column c) {
            super(c);
            assert (c != null);
            this.alias = a;
            this.column = c;
        }

        @Override
        public void execute() {
            updateColumn(alias, column);
        }
    }

    private class ConstraintCommand extends CommandWithLabel {
        private final ConstraintReferential constraintReferential;

        ConstraintCommand(ConstraintReferential constraintReferential2) {
            super(constraintReferential2);
            this.constraintReferential = constraintReferential2;
        }

        @Override
        public void execute() {
            selectConstraintRef(constraintReferential);
        }
    }
} public class Null extends Expression {
    public Null() {
        super(null);
        initWidget(new Label());
    }

    @Override
    public String getSQL(List args) {
        return "NULL";
    }
} public class Wildcard extends Expression {

    private final String table;

    public Wildcard(Session session, String schema, String table) {
        super(session);
        this.table = table;
    }

    @Override
    public String getSQL(List<Object> args) {
        if (table == null) {
            return "*";
        }
        return getSession().quoteIdentifier(table) + ".*";
    }
} public class StatementBuilder {

    private final StringBuilder builder = new StringBuilder();
    private int index;

    /**
     * Create a new builder.
     */
    public StatementBuilder() {
        // nothing to do
    }

    /**
     * Create a new builder.
     *
     * @param string the initial string
     */
    public StatementBuilder(String string) {
        builder.append(string);
    }

    /**
     * Append a text.
     *
     * @param s the text to append
     * @return itself
     */
    public StatementBuilder append(String s) {
        builder.append(s);
        return this;
    }

    /**
     * Append a character.
     *
     * @param c the character to append
     * @return itself
     */
    public StatementBuilder append(char c) {
        builder.append(c);
        return this;
    }

    /**
     * Append a number.
     *
     * @param x the number to append
     * @return itself
     */
    public StatementBuilder append(long x) {
        builder.append(x);
        return this;
    }

    /**
     * Reset the loop counter.
     *
     * @return itself
     */
    public StatementBuilder resetCount() {
        index = 0;
        return this;
    }

    /**
     * Append a text, but only if appendExceptFirst was never called.
     *
     * @param s the text to append
     */
    public void appendOnlyFirst(String s) {
        if (index == 0) {
            builder.append(s);
        }
    }

    /**
     * Append a text, except when this method is called the first time.
     *
     * @param s the text to append
     */
    public void appendExceptFirst(String s) {
        if (index++ > 0) {
            builder.append(s);
        }
    }

    @Override
    public String toString() {
        return builder.toString();
    }

    /**
     * Get the length.
     *
     * @return the length
     */
    public int length() {
        return builder.length();
    }

} public class Operator implements HasLabel, IsSerializable {

    public static final String EQUAL = "=";
    public static final String NOT_EQUAL = "<>";
    public static final String BIGGER = ">";
    public static final String SMALLER = "<";
    public static final String SMALLER_EQUAL = "<=";
    public static final String BIGGER_EQUAL = ">=";
    public static final String IS_NULL = "IS NULL";
    public static final String IS_NOT_NULL = "IS NOT NULL";

    private String name;
    private String label;

    private Cardinality cardinality;

    // For GWT RPC
    private Operator() {
    }

    public Operator(String name2) {
        this(name2, Cardinality.ONE);
    }

    public Operator(String name2, Cardinality c2) {
        this.name = name2;
        this.label = name;
        this.cardinality = c2;
    }

    @Override
    public String getLabel() {
        return label;
    }

    public Cardinality getCardinality() {
        return cardinality;
    }

    public String getName() {
        return name;
    }

    public void setLabel(String p) {
        this.label = p;
    }

    public void setCardinality(Cardinality p) {
        this.cardinality = p;
    }
} public class Schema extends DbObjectBase {
    private final Map<String, Table> tables = new LinkedHashMap<String, Table>();

    public Table findTableOrView(String name) {
        return tables.get(name.toUpperCase());
    }

    public Collection<Table> getTables() {
        return tables.values();
    }

    public void add(Table p) {
        tables.put(p.getName().toUpperCase(), p);
    }

    // XXX better place for this?
    // only internal "optimisation"?
    public void addReverseConstraints() {
        List<ConstraintReferential> crs = new ArrayList<ConstraintReferential>();
        for (Table t : getTables()) {
            for (Constraint c : t.getConstraints()) {
                if (c instanceof ConstraintReferential) {
                    crs.add((ConstraintReferential) c);
                }
            }
        }

        for (ConstraintReferential cr : crs) {
            String title = cr.getRevName();
            if (title == null) {
                title = "Rev " + cr.getName();
            }
            ConstraintReferential cr2 = new ConstraintReferential(title, cr.getRefTable());
            cr2.setRefColumns(cr.getColumns());
            cr2.setColumns(cr.getRefColumns());
            cr2.setRefTable(cr.getTable());
            cr.getRefTable().add(cr2);
            cr2.setHidden(cr.isRevHidden());
        }
    }
} public class Table extends SchemaObjectBase implements HasLabel {

    private final Map<String, Column> columns = new LinkedHashMap<String, Column>();

    private final List<Constraint> constraints = new ArrayList<Constraint>();

    private boolean hidden;


    public Table() {
    }

    public Table(String name) {
        setObjectName(name);
    }

    public void add(Column c) {
        c.setTable(this);
        columns.put(c.getName().toUpperCase(), c);
    }

    public Collection<Column> getColumns() {
        return columns.values();
    }

    public Column getColumn(String name) {
        return columns.get(name.toUpperCase());
    }

    public List<Constraint> getConstraints() {
        return constraints;
    }

    public Constraint getConstraintByName(String name) {
        for (Constraint c : constraints) {
            if (c.getName().equals(name)) {
                return c;
            }
        }
        throw new IllegalArgumentException("Unable to find constraint '" + name + "'");
    }

    public void add(Constraint c) {
        constraints.add(c);
    }

    public boolean isHidden() {
        return hidden;
    }

    public void setHidden(boolean p) {
        this.hidden = p;
    }

    @Override
    public String toString() {
        return "Table:" + getName();
    }
}
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import com.redspr.redquerybuilder.core.client.command.dml.Query;
import com.redspr.redquerybuilder.core.client.command.dml.Select;
import com.redspr.redquerybuilder.core.client.command.dml.SelectUnion;
import com.redspr.redquerybuilder.core.client.constant.ErrorCode;
import com.redspr.redquerybuilder.core.client.engine.Session;
import com.redspr.redquerybuilder.core.client.expression.Comparison;
import com.redspr.redquerybuilder.core.client.expression.ConditionAndOr;
import com.redspr.redquerybuilder.core.client.expression.Expression;
import com.redspr.redquerybuilder.core.client.expression.ExpressionColumn;
import com.redspr.redquerybuilder.core.client.expression.Null;
import com.redspr.redquerybuilder.core.client.expression.Parameter;
import com.redspr.redquerybuilder.core.client.expression.Wildcard;
import com.redspr.redquerybuilder.core.client.table.TableFilter;
import com.redspr.redquerybuilder.core.client.util.ObjectArray;
import com.redspr.redquerybuilder.core.client.util.StatementBuilder;
import com.redspr.redquerybuilder.core.client.value.Value;
import com.redspr.redquerybuilder.core.shared.meta.Column;
import com.redspr.redquerybuilder.core.shared.meta.Database;
import com.redspr.redquerybuilder.core.shared.meta.Operator;
import com.redspr.redquerybuilder.core.shared.meta.Schema;
import com.redspr.redquerybuilder.core.shared.meta.Table;
                }
                break;
            case 'M':
                if (readIf("MERGE")) {
                    c = parseMerge();
                }
                break;
            case 'P':
                if (readIf("PREPARE")) {
                    c = parsePrepare();
                }
                break;
            case 'S':
                if (isToken("SELECT")) {
                    c = parseSelect();
                } else if (readIf("SET")) {
                    c = parseSet();
                } else if (readIf("SAVEPOINT")) {
                    c = parseSavepoint();
                } else if (readIf("SCRIPT")) {
                    c = parseScript();
                } else if (readIf("SHUTDOWN")) {
                    c = parseShutdown();
                } else if (readIf("SHOW")) {
                    c = parseShow();
                }
                break;
            case 'T':
                if (readIf("TRUNCATE")) {
                    c = parseTruncate();
                }
                break;
            case 'U':
                if (readIf("UPDATE")) {
                    c = parseUpdate();
                }
                break;
            case 'V':
                if (readIf("VALUES")) {
                    c = parserCall();
                }
                break;
            case 'W':
                if (readIf("WITH")) {
                    c = parserWith();
                }
                break;
            default:
                throw getSyntaxError();
            }
            if (indexedParameterList != null) {
                for (int i = 0; i < indexedParameterList.size(); i++) {
                    if (indexedParameterList.get(i) == null) {
                        indexedParameterList.set(i, new Parameter(session, i));
                    }
                }
                parameters = indexedParameterList;
            }
            if (readIf("{")) {
                do {
                    int index = (int) readLong() - 1;
                    if (index < 0 || index >= parameters.size()) {
                        throw getSyntaxError();
                    }
                    Parameter p = parameters.get(index);
                    if (p == null) {
                        throw getSyntaxError();
                    }
                    read(":");
                    Expression expr = readExpression();
                  //  expr = expr.optimize(session);
                    //p.setValue(expr.getValue(session));
                } while (readIf(","));
                read("}");
                for (Parameter p : parameters) {
                    //p.checkSet();
                }
                parameters.clear();
            }
        }
        if (c == null) {
            throw getSyntaxError();
        }
        setSQL(c, null, start);
        return c;
    }

    private SQLException getSyntaxError() {
        if (expectedList == null || expectedList.size() == 0) {
            return Message.getSyntaxError(sqlCommand, parseIndex);
        }
        StatementBuilder buff = new StatementBuilder();
        for (String e : expectedList) {
            buff.appendExceptFirst(", ");
            buff.append(e);
        }
        return Message.getSyntaxError(sqlCommand, parseIndex, buff.toString());
    }

    private Prepared parseAnalyze() throws SQLException {
//        Analyze command = new Analyze(session);
//        if (readIf("SAMPLE_SIZE")) {
//            command.setTop(getPositiveInt());
//        }
//        return command;
        return null;
    }

    private Prepared parseShutdown() throws SQLException {
        return null;
    }

    private Prepared parsePrepare() throws SQLException {
        return null;
    }

    private Prepared parseSavepoint() throws SQLException {
        return null;
    }

    private Schema getSchema() throws SQLException {