public interface IdentifierEscaper {
    String quote(String id);
} 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 enum Cardinality implements IsSerializable {
    ZERO, ONE, MULTI;
} public class ConstraintReferential extends Constraint {
    private Column[] columns;

    private Table refTable;

    private Column[] refColumns;

    private String revName;

    private boolean hidden;

    private boolean revHidden;

    public boolean isHidden() {
        return hidden;
    }

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

    public boolean isRevHidden() {
        return revHidden;
    }

    public void setRevHidden(boolean p) {
        this.revHidden = p;
    }

    public String getRevName() {
        return revName;
    }

    public void setRevName(String p) {
        this.revName = p;
    }

    // for GWT RPC
    public ConstraintReferential() {
    }

    public ConstraintReferential(String name, Table t) {
        super(name, t);
    }

    public Column[] getColumns() {
        return columns;
    }

    public void setColumns(Column[] p) {
        this.columns = p;
    }

    public Column[] getRefColumns() {
        return refColumns;
    }

    public void setRefColumns(Column[] p) {
        this.refColumns = p;
    }

    public Table getRefTable() {
        return refTable;
    }

    public void setRefTable(Table p) {
        this.refTable = p;
    }

    @Override
    public String toString() {
        return "FK:" + getName() + "(" + getTable() + " to " + getRefTable() + ")";
    }
} public class Editor implements HasStyleName, Serializable, IsSerializable {
    public static class TextEditor extends Editor {
        @Override
        public Object getDefault() {
            return "";
        }
    }

    public static class DateEditor extends Editor {
        public static final String FORMAT = "format";
    }

    public static class BooleanEditor extends Editor {
        @Override
        public Object getDefault() {
            return Boolean.FALSE;
        }
    }

    public static class SelectEditor extends Editor {

    }

    public static class NumberEditor extends Editor {
    }


    // XXX - rubbish, only used by JSON?
    private static final Map<String, Editor> editorByName = new HashMap<String, Editor>();

    private static Editor valueOf2(String name) {
        if ("STRING".equals(name) || "TEXT".equals(name)) {
            return new TextEditor();
        } else if ("DATE".equals(name)) {
            return new DateEditor();
        } else if ("SUGGEST".equals(name)) {
            return new SuggestEditor();
        } else if ("SELECT".equals(name)) {
            return new SelectEditor();
        } else if ("NUMBER".equals(name)) {
            return new NumberEditor();
        } else {
            throw new RuntimeException("No editor for " + name);
        }
    }

    public static Editor valueOf(String name) {
        Editor e = editorByName.get(name);
        if (e == null) {
            e = valueOf2(name);
            editorByName.put(name, e);
        }
        return e;
    }

    // XXX map mush
    private final Map<String, Object> attributes = new HashMap<String, Object>();

    private String styleName;

    public Object getDefault() {
        return null;
    }

    public void setAttribute(String name, Object value) {
        attributes.put(name, value);
    }

    public Object getAttribute(String name) {
        return attributes.get(name);
    }

    public String getStyleName() {
        return styleName;
    }

    @Override
    public void setStyleName(String p) {
        this.styleName = p;
    }
} public class EnumerateRequest {
    private String tableName;

    private String columnName;

    private String columnType;

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String p) {
        this.tableName = p;
    }

    public String getColumnName() {
        return columnName;
    }

    public void setColumnName(String p) {
        this.columnName = p;
    }

    public String getColumnTypeName() {
        return columnType;
    }

    public void setColumnTypeName(String p) {
        this.columnType = p;
    }

} 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();
    }
} public class Type implements HasStyleName, Serializable, IsSerializable {
    private String name;

    private Operator[] operators = new Operator[] {
            new Operator("="),
            new Operator("IS NULL", Cardinality.ZERO),
            new Operator("LIKE") };

    private Editor editor;

    private String styleName;

    // for GWT RPC
    private Type() {
    }

    public Type(String name2) {
        this(name2, new Editor.TextEditor());
    }

    public Type(String name2, Editor editor2) {
        this.name = name2;
        this.editor = editor2;
    }

    public Operator[] getOperators() {
        return operators;
    }
    public void setOperators(Operator[] p) {
        this.operators = p;
    }
    public String getName() {
        return name;
    }

    public Editor getEditor() {
        return editor;
    }

    public void setEditor(Editor p) {
        this.editor = p;
    }

    public Operator getOperatorByName(String opName) {
        for (Operator op : operators) {
            if (opName.equals(op.getName())) {
                return op;
            }
        }
        return null;
    }

    public Operator getOperatorByNameOrFail(String opName) {
        Operator op = getOperatorByName(opName);
        if (op == null) {
            throw new RuntimeException("Could not find operator '" + opName
                    + "' in type '" + name + "'");
        }
        return op;
    }

    public String getStyleName() {
        if (styleName == null) {
            return getEditor().getStyleName();
        }
        return styleName;
    }

    @Override
    public void setStyleName(String p) {
        this.styleName = p;
    }
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Ignore;
import com.google.gwt.dom.client.Element;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle.MultiWordSuggestion;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SuggestOracle.Response;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwt.user.client.ui.Widget;
import com.redspr.redquerybuilder.core.client.conf.IdentifierEscaper;
import com.redspr.redquerybuilder.core.client.engine.Session;
import com.redspr.redquerybuilder.core.client.table.TableFilter;
import com.redspr.redquerybuilder.core.shared.meta.Cardinality;
import com.redspr.redquerybuilder.core.shared.meta.Column;
import com.redspr.redquerybuilder.core.shared.meta.ConstraintReferential;
import com.redspr.redquerybuilder.core.shared.meta.Database;
import com.redspr.redquerybuilder.core.shared.meta.Editor;
import com.redspr.redquerybuilder.core.shared.meta.EnumerateRequest;
import com.redspr.redquerybuilder.core.shared.meta.Operator;
import com.redspr.redquerybuilder.core.shared.meta.Schema;
import com.redspr.redquerybuilder.core.shared.meta.SuggestEditor;
import com.redspr.redquerybuilder.core.shared.meta.SuggestRequest;
import com.redspr.redquerybuilder.core.shared.meta.Table;
import com.redspr.redquerybuilder.core.shared.meta.Type;
package com.redspr.redquerybuilder.core.client;




@Ignore
public class AbstractTest extends GWTTestCase {

    @Override
    public void gwtSetUp() {
        TableFilter.resetAlias();
        RootPanel.get().clear();
    }

    protected void assertEquals(String html, Widget w) {
        assertEquals(html, w.getElement());
    }

    protected void assertEquals(String html, Element elmt) {
        assertEquals(html.replace('\'', '"'), elmt.getInnerHTML());
    }


    protected Session getSession() {
        return createSession(null);
    }

    protected Session createSession(Configuration config) {
        Schema schema = new Schema();
        Database database = new Database(schema);

        Type stringType = new Type("VARCHAR");
        stringType.setOperators(new Operator[] {
                new Operator("="),
                new Operator("IS NULL", Cardinality.ZERO),
                new Operator("LIKE"), new Operator("CUSTOM_OP") });

        // XXX IN done better as hint or style?
        Type refType = new Type("REFS");
        refType.setEditor(new Editor.SelectEditor());
        refType.setOperators(new Operator[]{
                new Operator("IN", Cardinality.MULTI),
                new Operator("NOT IN", Cardinality.MULTI)});

        Type suggestType = new Type("SUGGEST");
        suggestType.setEditor(new SuggestEditor());
        suggestType.setOperators(new Operator[]{new Operator("=")});

        Type singleRefType = new Type("REF");
        singleRefType.setEditor(new Editor.SelectEditor());
        singleRefType.setOperators(new Operator[] {
                new Operator("="),
                new Operator("IS NULL", Cardinality.ZERO),
                new Operator("CUSTOM_OP", Cardinality.ONE)});

        Type dateType = new Type("DATE");
        dateType.setEditor(new Editor.DateEditor());
        Type[] types = new Type[]{stringType, dateType, refType, singleRefType};
        database.setTypes(types);

        Table person = new Table("PERSON"); // XXX want case sensitivity?
        Column personId = new Column("id", stringType);
        person.add(personId);
        person.add(new Column("sex", singleRefType));
        person.add(new Column("owner", stringType));
        person.add(new Column("category", refType));
        person.add(new Column("category2", refType));
        person.add(new Column("county", suggestType));
        schema.add(person);

        {
            Table log = new Table("Log");
            log.add(new Column("id", stringType));
            log.add(new Column("date", dateType));
            Column pc = new Column("parent", stringType);
            ConstraintReferential fk = new ConstraintReferential("parentfk",
                    log);
            fk.setRefTable(person);
            fk.setColumns(new Column[] {pc });
            fk.setRefColumns(new Column[] {personId });
            log.add(fk);
            log.add(pc);

            schema.add(log);
        }

        {
            Table order = new Table("Order");
            order.add(new Column("date", dateType));
            Column pc = new Column("parent", stringType);
            ConstraintReferential fk = new ConstraintReferential("orderparentfk",
                    order);
            fk.setRefTable(person);
            fk.setColumns(new Column[] {pc });
            fk.setRefColumns(new Column[] {personId });
            order.add(fk);
            order.add(pc);

            schema.add(order);
        }

        if (config == null) {
            config = createSimpleConfig();
        }

        config.setDatabase(database);

        Session session = new Session(config);

        session.setIdentifierEscaper(new IdentifierEscaper() {