]> source.dussan.org Git - jackcess.git/commitdiff
make query types interfaces
authorJames Ahlborn <jtahlborn@yahoo.com>
Thu, 30 May 2013 03:34:49 +0000 (03:34 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Thu, 30 May 2013 03:34:49 +0000 (03:34 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/jackcess-2@731 f203690c-595d-4dc9-a70b-905162fa7fd2

27 files changed:
TODO.txt
src/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
src/java/com/healthmarketscience/jackcess/impl/query/AppendQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/BaseSelectQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/CrossTabQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/DataDefinitionQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/DeleteQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/MakeTableQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/PassthroughQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/SelectQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/UnionQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/impl/query/UpdateQueryImpl.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/query/AppendQuery.java
src/java/com/healthmarketscience/jackcess/query/BaseSelectQuery.java
src/java/com/healthmarketscience/jackcess/query/CrossTabQuery.java
src/java/com/healthmarketscience/jackcess/query/DataDefinitionQuery.java
src/java/com/healthmarketscience/jackcess/query/DeleteQuery.java
src/java/com/healthmarketscience/jackcess/query/MakeTableQuery.java
src/java/com/healthmarketscience/jackcess/query/PassthroughQuery.java
src/java/com/healthmarketscience/jackcess/query/Query.java
src/java/com/healthmarketscience/jackcess/query/QueryFormat.java [deleted file]
src/java/com/healthmarketscience/jackcess/query/SelectQuery.java
src/java/com/healthmarketscience/jackcess/query/UnionQuery.java
src/java/com/healthmarketscience/jackcess/query/UpdateQuery.java
test/src/java/com/healthmarketscience/jackcess/query/QueryTest.java

index 4944939430c150c83142c2d2884c0c70628f9240..ed2e2ed36f00b94dfd7db86d766914a4495b058b 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
@@ -38,7 +38,7 @@ Refactor goals:
 * don't use columnimpl for creating tables
   * clean up columnimpl/tableimpl constructors
 * add updateCurrentRow(Map), add updateRow(Row)
-- sort out query types
+* sort out query types
 - clean up javadocs
   - enhance public api classes
   - add @usage tags to util classes
index e93d3dde7cfba4be72dc0b05289b278ab2dd0b83..ee2b5abf4d6838b5f60806f93de7d841336b63d0 100644 (file)
@@ -69,6 +69,7 @@ import com.healthmarketscience.jackcess.Row;
 import com.healthmarketscience.jackcess.RuntimeIOException;
 import com.healthmarketscience.jackcess.Table;
 import com.healthmarketscience.jackcess.query.Query;
+import com.healthmarketscience.jackcess.impl.query.QueryImpl;
 import com.healthmarketscience.jackcess.util.CaseInsensitiveColumnMatcher;
 import com.healthmarketscience.jackcess.util.ErrorHandler;
 import com.healthmarketscience.jackcess.util.LinkResolver;
@@ -1033,8 +1034,8 @@ public class DatabaseImpl implements Database
 
     // find all the queries from the system catalog
     List<Row> queryInfo = new ArrayList<Row>();
-    Map<Integer,List<Query.Row>> queryRowMap = 
-      new HashMap<Integer,List<Query.Row>>();
+    Map<Integer,List<QueryImpl.Row>> queryRowMap = 
+      new HashMap<Integer,List<QueryImpl.Row>>();
     for(Row row :
           CursorImpl.createCursor(_systemCatalog).newIterable().setColumnNames(
               SYSTEM_CATALOG_COLUMNS))
@@ -1043,14 +1044,14 @@ public class DatabaseImpl implements Database
       if (name != null && TYPE_QUERY.equals(row.get(CAT_COL_TYPE))) {
         queryInfo.add(row);
         Integer id = (Integer)row.get(CAT_COL_ID);
-        queryRowMap.put(id, new ArrayList<Query.Row>());
+        queryRowMap.put(id, new ArrayList<QueryImpl.Row>());
       }
     }
 
     // find all the query rows
     for(Row row : CursorImpl.createCursor(_queries)) {
-      Query.Row queryRow = new Query.Row(row);
-      List<Query.Row> queryRows = queryRowMap.get(queryRow.objectId);
+      QueryImpl.Row queryRow = new QueryImpl.Row(row);
+      List<QueryImpl.Row> queryRows = queryRowMap.get(queryRow.objectId);
       if(queryRows == null) {
         LOG.warn("Found rows for query with id " + queryRow.objectId +
                  " missing from system catalog");
@@ -1065,8 +1066,8 @@ public class DatabaseImpl implements Database
       String name = (String) row.get(CAT_COL_NAME);
       Integer id = (Integer)row.get(CAT_COL_ID);
       int flags = (Integer)row.get(CAT_COL_FLAGS);
-      List<Query.Row> queryRows = queryRowMap.get(id);
-      queries.add(Query.create(flags, name, queryRows, id));
+      List<QueryImpl.Row> queryRows = queryRowMap.get(id);
+      queries.add(QueryImpl.create(flags, name, queryRows, id));
     }
 
     return queries;
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/AppendQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/AppendQueryImpl.java
new file mode 100644 (file)
index 0000000..1177d6e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
+import com.healthmarketscience.jackcess.query.AppendQuery;
+
+
+/**
+ * Concrete Query subclass which represents an append query, e.g.:
+ * {@code INSERT INTO <table> VALUES (<values>)}
+ * 
+ * @author James Ahlborn
+ */
+public class AppendQueryImpl extends BaseSelectQueryImpl implements AppendQuery
+{
+
+  public AppendQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.APPEND);
+  }
+
+  public String getTargetTable() {
+    return getTypeRow().name1;
+  }
+
+  public String getRemoteDbPath() {
+    return getTypeRow().name2;
+  }
+
+  public String getRemoteDbType() {
+    return getTypeRow().expression;
+  }
+
+  protected List<Row> getValueRows() {
+    return filterRowsByFlag(super.getColumnRows(), APPEND_VALUE_FLAG);
+  }
+
+  @Override
+  protected List<Row> getColumnRows() {
+    return filterRowsByNotFlag(super.getColumnRows(), APPEND_VALUE_FLAG);
+  }
+
+  public List<String> getValues() {
+    return new RowFormatter(getValueRows()) {
+        @Override protected void format(StringBuilder builder, Row row) {
+          builder.append(row.expression);
+        }
+      }.format();
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    builder.append("INSERT INTO ").append(getTargetTable());
+    toRemoteDb(builder, getRemoteDbPath(), getRemoteDbType());
+    builder.append(NEWLINE);
+    List<String> values = getValues();
+    if(!values.isEmpty()) {
+      builder.append("VALUES (").append(values).append(')');
+    } else {
+      toSQLSelectString(builder, true);
+    }
+  }
+  
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/BaseSelectQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/BaseSelectQueryImpl.java
new file mode 100644 (file)
index 0000000..0fddb59
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
+import com.healthmarketscience.jackcess.query.BaseSelectQuery;
+
+
+/**
+ * Base class for queries which represent some form of SELECT statement.
+ * 
+ * @author James Ahlborn
+ */
+public abstract class BaseSelectQueryImpl extends QueryImpl 
+  implements BaseSelectQuery
+{
+
+  protected BaseSelectQueryImpl(String name, List<Row> rows, int objectId,
+                            Type type) {
+    super(name, rows, objectId, type);
+  }
+
+  protected void toSQLSelectString(StringBuilder builder,
+                                   boolean useSelectPrefix) 
+  {
+    if(useSelectPrefix) {
+      builder.append("SELECT ");
+      String selectType = getSelectType();
+      if(!DEFAULT_TYPE.equals(selectType)) {
+        builder.append(selectType).append(' ');
+      }
+    }
+
+    builder.append(getSelectColumns());
+    toSelectInto(builder);
+
+    List<String> fromTables = getFromTables();
+    if(!fromTables.isEmpty()) {
+      builder.append(NEWLINE).append("FROM ").append(fromTables);
+      toRemoteDb(builder, getFromRemoteDbPath(), getFromRemoteDbType());
+    }
+
+    String whereExpr = getWhereExpression();
+    if(whereExpr != null) {
+      builder.append(NEWLINE).append("WHERE ").append(whereExpr);
+    }
+
+    List<String> groupings = getGroupings();
+    if(!groupings.isEmpty()) {
+      builder.append(NEWLINE).append("GROUP BY ").append(groupings);
+    }
+
+    String havingExpr = getHavingExpression();
+    if(havingExpr != null) {
+      builder.append(NEWLINE).append("HAVING ").append(havingExpr);
+    }
+
+    List<String> orderings = getOrderings();
+    if(!orderings.isEmpty()) {
+      builder.append(NEWLINE).append("ORDER BY ").append(orderings);
+    }
+  }
+
+  public String getSelectType()
+  {
+    if(hasFlag(DISTINCT_SELECT_TYPE)) {
+      return "DISTINCT";
+    }
+
+    if(hasFlag(DISTINCT_ROW_SELECT_TYPE)) {
+      return "DISTINCTROW";
+    }
+
+    if(hasFlag(TOP_SELECT_TYPE)) {
+      StringBuilder builder = new StringBuilder();
+      builder.append("TOP ").append(getFlagRow().name1);
+      if(hasFlag(PERCENT_SELECT_TYPE)) {
+        builder.append(" PERCENT");
+      }
+      return builder.toString();
+    }
+
+    return DEFAULT_TYPE;
+  }
+
+  public List<String> getSelectColumns() 
+  {
+    List<String> result = (new RowFormatter(getColumnRows()) {
+        @Override protected void format(StringBuilder builder, Row row) {
+          // note column expression are always quoted appropriately
+          builder.append(row.expression);
+          toAlias(builder, row.name1);
+        }
+      }).format();
+    if(hasFlag(SELECT_STAR_SELECT_TYPE)) {
+      result.add("*");
+    }
+    return result;
+  }
+
+  protected void toSelectInto(StringBuilder builder)
+  {
+    // base does nothing
+  }
+
+  @Override
+  public List<String> getFromTables() 
+  {
+    return super.getFromTables();
+  }
+
+  @Override
+  public String getFromRemoteDbPath() 
+  {
+    return super.getFromRemoteDbPath();
+  }
+
+  @Override
+  public String getFromRemoteDbType() 
+  {
+    return super.getFromRemoteDbType();
+  }
+
+  @Override
+  public String getWhereExpression()
+  {
+    return super.getWhereExpression();
+  }
+
+  public List<String> getGroupings() 
+  {
+    return (new RowFormatter(getGroupByRows()) {
+        @Override protected void format(StringBuilder builder, Row row) {
+          builder.append(row.expression);
+        }
+      }).format();
+  }
+
+  public String getHavingExpression()
+  {
+    return getHavingRow().expression;
+  }
+
+  @Override
+  public List<String> getOrderings() 
+  {
+    return super.getOrderings();
+  }
+  
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/CrossTabQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/CrossTabQueryImpl.java
new file mode 100644 (file)
index 0000000..d4b7e28
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
+import com.healthmarketscience.jackcess.query.CrossTabQuery;
+
+
+/**
+ * Concrete Query subclass which represents a crosstab/pivot query, e.g.:
+ * {@code TRANSFORM <expr> SELECT <query> PIVOT <expr>}
+ * 
+ * @author James Ahlborn
+ */
+public class CrossTabQueryImpl extends BaseSelectQueryImpl 
+  implements CrossTabQuery
+{
+
+  public CrossTabQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.CROSS_TAB);
+  }
+
+  protected Row getTransformRow() {
+    return getUniqueRow(
+        filterRowsByNotFlag(super.getColumnRows(), 
+                            (short)(CROSSTAB_PIVOT_FLAG | 
+                                    CROSSTAB_NORMAL_FLAG)));
+  }
+
+  @Override
+  protected List<Row> getColumnRows() {
+    return filterRowsByFlag(super.getColumnRows(), CROSSTAB_NORMAL_FLAG);
+  }
+
+  @Override
+  protected List<Row> getGroupByRows() {
+    return filterRowsByFlag(super.getGroupByRows(), CROSSTAB_NORMAL_FLAG);
+  }
+
+  protected Row getPivotRow() {
+    return getUniqueRow(filterRowsByFlag(super.getColumnRows(), 
+                                         CROSSTAB_PIVOT_FLAG));
+  }
+
+  public String getTransformExpression() {
+    Row row = getTransformRow();
+    if(row.expression == null) {
+      return null;
+    }
+    // note column expression are always quoted appropriately
+    StringBuilder builder = new StringBuilder(row.expression);
+    return toAlias(builder, row.name1).toString();
+  }
+
+  public String getPivotExpression() {
+    return getPivotRow().expression;
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    String transformExpr = getTransformExpression();
+    if(transformExpr != null) {
+      builder.append("TRANSFORM ").append(transformExpr).append(NEWLINE);
+    }
+
+    toSQLSelectString(builder, true);
+
+    builder.append(NEWLINE).append("PIVOT ")
+      .append(getPivotExpression());
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/DataDefinitionQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/DataDefinitionQueryImpl.java
new file mode 100644 (file)
index 0000000..27ee5ab
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+import com.healthmarketscience.jackcess.query.DataDefinitionQuery;
+
+
+/**
+ * Concrete Query subclass which represents a DDL query.
+ * 
+ * @author James Ahlborn
+ */
+public class DataDefinitionQueryImpl extends QueryImpl 
+  implements DataDefinitionQuery
+{
+
+  public DataDefinitionQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.DATA_DEFINITION);
+  }
+
+  public String getDDLString() {
+    return getTypeRow().expression;
+  }
+
+  @Override
+  protected boolean supportsStandardClauses() {
+    return false;
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    String ddl = getDDLString();
+    if(ddl != null) {
+      builder.append(ddl);
+    }
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/DeleteQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/DeleteQueryImpl.java
new file mode 100644 (file)
index 0000000..8c96b6d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+import com.healthmarketscience.jackcess.query.DeleteQuery;
+
+
+/**
+ * Concrete Query subclass which represents a delete query, e.g.:
+ * {@code DELETE * FROM <table> WHERE <expression>}
+ *
+ * @author James Ahlborn
+ */
+public class DeleteQueryImpl extends BaseSelectQueryImpl implements DeleteQuery
+{
+
+  public DeleteQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.DELETE);
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    builder.append("DELETE ");
+    toSQLSelectString(builder, false);
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/MakeTableQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/MakeTableQueryImpl.java
new file mode 100644 (file)
index 0000000..29e402b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+import com.healthmarketscience.jackcess.query.MakeTableQuery;
+
+
+/**
+ * Concrete Query subclass which represents an table creation query, e.g.:
+ * {@code SELECT <query> INTO <newTable>}
+ * 
+ * @author James Ahlborn
+ */
+public class MakeTableQueryImpl extends BaseSelectQueryImpl 
+  implements MakeTableQuery
+{
+
+  public MakeTableQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.MAKE_TABLE);
+  }
+
+  public String getTargetTable() {
+    return getTypeRow().name1;
+  }
+
+  public String getRemoteDbPath() {
+    return getTypeRow().name2;
+  }
+
+  public String getRemoteDbType() {
+    return getTypeRow().expression;
+  }
+
+  @Override
+  protected void toSelectInto(StringBuilder builder) 
+  {
+    builder.append(" INTO ").append(getTargetTable());
+    toRemoteDb(builder, getRemoteDbPath(), getRemoteDbType());
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    toSQLSelectString(builder, true);
+  }  
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/PassthroughQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/PassthroughQueryImpl.java
new file mode 100644 (file)
index 0000000..af67e2c
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+import com.healthmarketscience.jackcess.query.PassthroughQuery;
+
+
+/**
+ * Concrete Query subclass which represents a query which will be executed via
+ * ODBC.
+ * 
+ * @author James Ahlborn
+ */
+public class PassthroughQueryImpl extends QueryImpl implements PassthroughQuery
+{
+
+  public PassthroughQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.PASSTHROUGH);
+  }
+
+  public String getConnectionString() {
+    return getTypeRow().name1;
+  }
+
+  public String getPassthroughString() {
+    return getTypeRow().expression;
+  }
+
+  @Override
+  protected boolean supportsStandardClauses() {
+    return false;
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    String pt = getPassthroughString();
+    if(pt != null) {
+      builder.append(pt);
+    }
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java b/src/java/com/healthmarketscience/jackcess/impl/query/QueryFormat.java
new file mode 100644 (file)
index 0000000..83064c0
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import com.healthmarketscience.jackcess.DataType;
+import org.apache.commons.lang.SystemUtils;
+
+/**
+ * Constants used by the query data parsing.
+ * 
+ * @author James Ahlborn
+ */
+public class QueryFormat 
+{
+
+  private QueryFormat() {}
+
+  public static final int SELECT_QUERY_OBJECT_FLAG = 0;
+  public static final int MAKE_TABLE_QUERY_OBJECT_FLAG = 80;
+  public static final int APPEND_QUERY_OBJECT_FLAG = 64;
+  public static final int UPDATE_QUERY_OBJECT_FLAG = 48;
+  public static final int DELETE_QUERY_OBJECT_FLAG = 32;
+  public static final int CROSS_TAB_QUERY_OBJECT_FLAG = 16;
+  public static final int DATA_DEF_QUERY_OBJECT_FLAG = 96;
+  public static final int PASSTHROUGH_QUERY_OBJECT_FLAG = 112;
+  public static final int UNION_QUERY_OBJECT_FLAG = 128;
+  // dbQSPTBulk = 144
+  // dbQCompound = 160
+  // dbQProcedure = 224
+  // dbQAction = 240
+
+  public static final String COL_ATTRIBUTE = "Attribute";
+  public static final String COL_EXPRESSION = "Expression";
+  public static final String COL_FLAG = "Flag";
+  public static final String COL_EXTRA = "LvExtra";
+  public static final String COL_NAME1 = "Name1";
+  public static final String COL_NAME2 = "Name2";
+  public static final String COL_OBJECTID = "ObjectId";
+  public static final String COL_ORDER = "Order";
+
+  public static final Byte START_ATTRIBUTE = 0;
+  public static final Byte TYPE_ATTRIBUTE = 1;
+  public static final Byte PARAMETER_ATTRIBUTE = 2;
+  public static final Byte FLAG_ATTRIBUTE = 3;
+  public static final Byte REMOTEDB_ATTRIBUTE = 4;
+  public static final Byte TABLE_ATTRIBUTE = 5;
+  public static final Byte COLUMN_ATTRIBUTE = 6;
+  public static final Byte JOIN_ATTRIBUTE = 7;
+  public static final Byte WHERE_ATTRIBUTE = 8;
+  public static final Byte GROUPBY_ATTRIBUTE = 9;
+  public static final Byte HAVING_ATTRIBUTE = 10;
+  public static final Byte ORDERBY_ATTRIBUTE = 11;
+  public static final Byte END_ATTRIBUTE = (byte)255;
+
+  public static final short UNION_FLAG = 0x02;
+
+  public static final Short TEXT_FLAG = (short)DataType.TEXT.getValue();
+
+  public static final String DESCENDING_FLAG = "D";
+
+  public static final short SELECT_STAR_SELECT_TYPE = 0x01;
+  public static final short DISTINCT_SELECT_TYPE = 0x02;
+  public static final short OWNER_ACCESS_SELECT_TYPE = 0x04;
+  public static final short DISTINCT_ROW_SELECT_TYPE = 0x08;
+  public static final short TOP_SELECT_TYPE = 0x10;
+  public static final short PERCENT_SELECT_TYPE = 0x20;
+
+  public static final short APPEND_VALUE_FLAG = (short)0x8000;
+
+  public static final short CROSSTAB_PIVOT_FLAG = 0x01;
+  public static final short CROSSTAB_NORMAL_FLAG = 0x02;  
+
+  public static final String UNION_PART1 = "X7YZ_____1";
+  public static final String UNION_PART2 = "X7YZ_____2";
+
+  public static final String DEFAULT_TYPE = "";
+
+  public static final Pattern QUOTABLE_CHAR_PAT = Pattern.compile("\\W");
+
+  public static final Pattern IDENTIFIER_SEP_PAT = Pattern.compile("\\.");
+  public static final char IDENTIFIER_SEP_CHAR = '.';
+
+  public static final String NEWLINE = SystemUtils.LINE_SEPARATOR;
+
+
+  public static final Map<Short,String> PARAM_TYPE_MAP = 
+    new HashMap<Short,String>();
+  static {
+    PARAM_TYPE_MAP.put((short)0, "Value");
+    PARAM_TYPE_MAP.put((short)DataType.BOOLEAN.getValue(), "Bit");
+    PARAM_TYPE_MAP.put((short)DataType.TEXT.getValue(), "Text");
+    PARAM_TYPE_MAP.put((short)DataType.BYTE.getValue(), "Byte");
+    PARAM_TYPE_MAP.put((short)DataType.INT.getValue(), "Short");
+    PARAM_TYPE_MAP.put((short)DataType.LONG.getValue(), "Long");
+    PARAM_TYPE_MAP.put((short)DataType.MONEY.getValue(), "Currency");
+    PARAM_TYPE_MAP.put((short)DataType.FLOAT.getValue(), "IEEESingle");
+    PARAM_TYPE_MAP.put((short)DataType.DOUBLE.getValue(), "IEEEDouble");
+    PARAM_TYPE_MAP.put((short)DataType.SHORT_DATE_TIME.getValue(), "DateTime");
+    PARAM_TYPE_MAP.put((short)DataType.BINARY.getValue(), "Binary");
+    PARAM_TYPE_MAP.put((short)DataType.OLE.getValue(), "LongBinary");
+    PARAM_TYPE_MAP.put((short)DataType.GUID.getValue(), "Guid");
+  }
+
+  public static final Map<Short,String> JOIN_TYPE_MAP = 
+    new HashMap<Short,String>();
+  static {
+    JOIN_TYPE_MAP.put((short)1, " INNER JOIN ");
+    JOIN_TYPE_MAP.put((short)2, " LEFT JOIN ");
+    JOIN_TYPE_MAP.put((short)3, " RIGHT JOIN ");
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java
new file mode 100644 (file)
index 0000000..114933a
--- /dev/null
@@ -0,0 +1,721 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.healthmarketscience.jackcess.RowId;
+import com.healthmarketscience.jackcess.query.Query;
+import com.healthmarketscience.jackcess.impl.RowIdImpl;
+import com.healthmarketscience.jackcess.impl.RowImpl;
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Base class for classes which encapsulate information about an Access query.
+ * The {@link #toSQLString()} method can be used to convert this object into
+ * the actual SQL string which this query data represents.
+ * 
+ * @author James Ahlborn
+ */
+public abstract class QueryImpl implements Query
+{
+  protected static final Log LOG = LogFactory.getLog(QueryImpl.class);  
+
+  private static final Row EMPTY_ROW = new Row();
+
+  private final String _name;
+  private final List<Row> _rows;
+  private final int _objectId;
+  private final Type _type;
+
+  protected QueryImpl(String name, List<Row> rows, int objectId, Type type) {
+    _name = name;
+    _rows = rows;
+    _objectId = objectId;
+    _type = type;
+
+    if(type != Type.UNKNOWN) {
+      short foundType = getShortValue(getQueryType(rows),
+                                      _type.getValue());
+      if(foundType != _type.getValue()) {
+        throw new IllegalStateException("Unexpected query type " + foundType);
+      }
+    }
+  }
+
+  /**
+   * Returns the name of the query.
+   */
+  public String getName() {
+    return _name;
+  }
+
+  /**
+   * Returns the type of the query.
+   */
+  public Type getType() {
+    return _type;
+  }
+
+  /**
+   * Returns the unique object id of the query.
+   */
+  public int getObjectId() {
+    return _objectId;
+  }
+
+  public int getObjectFlag() {
+    return getType().getObjectFlag();
+  }
+
+  /**
+   * Returns the rows from the system query table from which the query
+   * information was derived.
+   */
+  public List<Row> getRows() {
+    return _rows;
+  }
+
+  protected List<Row> getRowsByAttribute(Byte attribute) {
+    return getRowsByAttribute(getRows(), attribute);
+  }
+
+  protected Row getRowByAttribute(Byte attribute) {
+    return getUniqueRow(getRowsByAttribute(getRows(), attribute));
+  }
+
+  public Row getTypeRow() {
+    return getRowByAttribute(TYPE_ATTRIBUTE);
+  }
+
+  protected List<Row> getParameterRows() {
+    return getRowsByAttribute(PARAMETER_ATTRIBUTE);
+  }
+
+  protected Row getFlagRow() {
+    return getRowByAttribute(FLAG_ATTRIBUTE);
+  }
+
+  protected Row getRemoteDatabaseRow() {
+    return getRowByAttribute(REMOTEDB_ATTRIBUTE);
+  }
+
+  protected List<Row> getTableRows() {
+    return getRowsByAttribute(TABLE_ATTRIBUTE);
+  }
+
+  protected List<Row> getColumnRows() {
+    return getRowsByAttribute(COLUMN_ATTRIBUTE);
+  }
+
+  protected List<Row> getJoinRows() {
+    return getRowsByAttribute(JOIN_ATTRIBUTE);
+  }
+
+  protected Row getWhereRow() {
+    return getRowByAttribute(WHERE_ATTRIBUTE);
+  }
+
+  protected List<Row> getGroupByRows() {
+    return getRowsByAttribute(GROUPBY_ATTRIBUTE);
+  }
+
+  protected Row getHavingRow() {
+    return getRowByAttribute(HAVING_ATTRIBUTE);
+  }
+
+  protected List<Row> getOrderByRows() {
+    return getRowsByAttribute(ORDERBY_ATTRIBUTE);
+  }
+
+  protected abstract void toSQLString(StringBuilder builder);
+
+  protected void toSQLParameterString(StringBuilder builder) {
+    // handle any parameters
+    List<String> params = getParameters();
+    if(!params.isEmpty()) {
+      builder.append("PARAMETERS ").append(params)
+        .append(';').append(NEWLINE);
+    }
+  }
+
+  public List<String> getParameters() 
+  {
+    return (new RowFormatter(getParameterRows()) {
+        @Override protected void format(StringBuilder builder, Row row) {
+          String typeName = PARAM_TYPE_MAP.get(row.flag);
+          if(typeName == null) {
+            throw new IllegalStateException("Unknown param type " + row.flag);
+          }
+              
+          builder.append(row.name1).append(' ').append(typeName);
+          if((TEXT_FLAG.equals(row.flag)) && (getIntValue(row.extra, 0) > 0)) {
+            builder.append('(').append(row.extra).append(')');
+          }
+        }
+      }).format();
+  }
+
+  protected List<String> getFromTables() 
+  {
+    List<Join> joinExprs = new ArrayList<Join>();
+    for(Row table : getTableRows()) {
+      StringBuilder builder = new StringBuilder();
+
+      if(table.expression != null) {
+        toQuotedExpr(builder, table.expression).append(IDENTIFIER_SEP_CHAR);
+      }
+      if(table.name1 != null) {
+        toOptionalQuotedExpr(builder, table.name1, true);
+      }
+      toAlias(builder, table.name2);
+
+      String key = ((table.name2 != null) ? table.name2 : table.name1);
+      joinExprs.add(new Join(key, builder.toString()));
+    }
+
+
+    List<Row> joins = getJoinRows();
+    if(!joins.isEmpty()) {
+
+      // combine any multi-column joins
+      Collection<List<Row>> comboJoins = combineJoins(joins);
+      
+      for(List<Row> comboJoin : comboJoins) {
+
+        Row join = comboJoin.get(0);
+        String joinExpr = join.expression;
+
+        if(comboJoin.size() > 1) {
+
+          // combine all the join expressions with "AND"
+          AppendableList<String> comboExprs = new AppendableList<String>() {
+            private static final long serialVersionUID = 0L;
+            @Override
+            protected String getSeparator() {
+              return ") AND (";
+            }
+          };
+          for(Row tmpJoin : comboJoin) {
+            comboExprs.add(tmpJoin.expression);
+          }
+
+          joinExpr = new StringBuilder().append("(")
+            .append(comboExprs).append(")").toString();
+        }
+
+        String fromTable = join.name1;
+        String toTable = join.name2;
+      
+        Join fromExpr = getJoinExpr(fromTable, joinExprs);
+        Join toExpr = getJoinExpr(toTable, joinExprs);
+        String joinType = JOIN_TYPE_MAP.get(join.flag);
+        if(joinType == null) {
+            throw new IllegalStateException("Unknown join type " + join.flag);
+        }
+
+        String expr = new StringBuilder().append(fromExpr)
+          .append(joinType).append(toExpr).append(" ON ")
+          .append(joinExpr).toString();
+
+        fromExpr.join(toExpr, expr);
+        joinExprs.add(fromExpr);
+      }
+    }
+
+    List<String> result = new AppendableList<String>();
+    for(Join joinExpr : joinExprs) {
+      result.add(joinExpr.expression);
+    }
+
+    return result;
+  }
+
+  private static Join getJoinExpr(String table, List<Join> joinExprs)
+  {
+    for(Iterator<Join> iter = joinExprs.iterator(); iter.hasNext(); ) {
+      Join joinExpr = iter.next();
+      if(joinExpr.tables.contains(table)) {
+        iter.remove();
+        return joinExpr;
+      }
+    }
+    throw new IllegalStateException("Cannot find join table " + table);
+  }
+
+  private static Collection<List<Row>> combineJoins(List<Row> joins)
+  {
+    // combine joins with the same to/from tables
+    Map<List<String>,List<Row>> comboJoinMap = 
+      new LinkedHashMap<List<String>,List<Row>>();
+    for(Row join : joins) {
+      List<String> key = Arrays.asList(join.name1, join.name2);
+      List<Row> comboJoins = comboJoinMap.get(key);
+      if(comboJoins == null) {
+        comboJoins = new ArrayList<Row>();
+        comboJoinMap.put(key, comboJoins);
+      } else {
+        if(comboJoins.get(0).flag != join.flag) {
+          throw new IllegalStateException(
+              "Mismatched join flags for combo joins");
+        }
+      }
+      comboJoins.add(join);
+    }
+    return comboJoinMap.values();
+  }
+
+  protected String getFromRemoteDbPath() 
+  {
+    return getRemoteDatabaseRow().name1;
+  }
+
+  protected String getFromRemoteDbType() 
+  {
+    return getRemoteDatabaseRow().expression;
+  }
+
+  protected String getWhereExpression()
+  {
+    return getWhereRow().expression;
+  }
+
+  protected List<String> getOrderings() 
+  {
+    return (new RowFormatter(getOrderByRows()) {
+        @Override protected void format(StringBuilder builder, Row row) {
+          builder.append(row.expression);
+          if(DESCENDING_FLAG.equalsIgnoreCase(row.name1)) {
+            builder.append(" DESC");
+          }
+        }
+      }).format();
+  }
+
+  public String getOwnerAccessType() {
+    return(hasFlag(OWNER_ACCESS_SELECT_TYPE) ?
+           "WITH OWNERACCESS OPTION" : DEFAULT_TYPE);
+  }
+
+  protected boolean hasFlag(int flagMask)
+  {
+    return hasFlag(getFlagRow(), flagMask);
+  }
+
+  protected boolean supportsStandardClauses() {
+    return true;
+  }
+
+  /**
+   * Returns the actual SQL string which this query data represents.
+   */
+  public String toSQLString() 
+  {
+    StringBuilder builder = new StringBuilder();
+    if(supportsStandardClauses()) {
+      toSQLParameterString(builder);
+    }
+
+    toSQLString(builder);
+
+    if(supportsStandardClauses()) {
+
+      String accessType = getOwnerAccessType();
+      if(!DEFAULT_TYPE.equals(accessType)) {
+        builder.append(NEWLINE).append(accessType);
+      }
+      
+      builder.append(';');
+    }
+    return builder.toString();
+  }
+
+  @Override
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this);
+  }
+
+  /**
+   * Creates a concrete Query instance from the given query data.
+   *
+   * @param objectFlag the flag indicating the type of the query
+   * @param name the name of the query
+   * @param rows the rows from the system query table containing the data
+   *             describing this query
+   * @param objectId the unique object id of this query
+   *
+   * @return a Query instance for the given query data
+   */
+  public static QueryImpl create(int objectFlag, String name, List<Row> rows, 
+                                 int objectId)
+  {
+    try {
+      switch(objectFlag) {
+      case SELECT_QUERY_OBJECT_FLAG:
+        return new SelectQueryImpl(name, rows, objectId);
+      case MAKE_TABLE_QUERY_OBJECT_FLAG:
+        return new MakeTableQueryImpl(name, rows, objectId);
+      case APPEND_QUERY_OBJECT_FLAG:
+        return new AppendQueryImpl(name, rows, objectId);
+      case UPDATE_QUERY_OBJECT_FLAG:
+        return new UpdateQueryImpl(name, rows, objectId);
+      case DELETE_QUERY_OBJECT_FLAG:
+        return new DeleteQueryImpl(name, rows, objectId);
+      case CROSS_TAB_QUERY_OBJECT_FLAG:
+        return new CrossTabQueryImpl(name, rows, objectId);
+      case DATA_DEF_QUERY_OBJECT_FLAG:
+        return new DataDefinitionQueryImpl(name, rows, objectId);
+      case PASSTHROUGH_QUERY_OBJECT_FLAG:
+        return new PassthroughQueryImpl(name, rows, objectId);
+      case UNION_QUERY_OBJECT_FLAG:
+        return new UnionQueryImpl(name, rows, objectId);
+      default:
+        // unknown querytype
+        throw new IllegalStateException(
+            "unknown query object flag " + objectFlag);
+      }
+    } catch(IllegalStateException e) {
+      LOG.warn("Failed parsing query", e);
+    }
+
+    // return unknown query
+    return new UnknownQueryImpl(name, rows, objectId, objectFlag);
+  }
+
+  private static Short getQueryType(List<Row> rows)
+  {
+    return getUniqueRow(getRowsByAttribute(rows, TYPE_ATTRIBUTE)).flag;
+  }
+
+  private static List<Row> getRowsByAttribute(List<Row> rows, Byte attribute) {
+    List<Row> result = new ArrayList<Row>();
+    for(Row row : rows) {
+      if(attribute.equals(row.attribute)) {
+        result.add(row);
+      }
+    }
+    return result;
+  }
+
+  protected static Row getUniqueRow(List<Row> rows) {
+    if(rows.size() == 1) {
+      return rows.get(0);
+    }
+    if(rows.isEmpty()) {
+      return EMPTY_ROW;
+    }
+    throw new IllegalStateException("Unexpected number of rows for" + rows);
+  }
+
+  protected static List<Row> filterRowsByFlag(
+      List<Row> rows, final short flag) 
+  {
+    return new RowFilter() {
+        @Override protected boolean keep(Row row) {
+          return hasFlag(row, flag);
+        }
+      }.filter(rows);
+  }
+
+  protected static List<Row> filterRowsByNotFlag(
+      List<Row> rows, final short flag) 
+  {
+    return new RowFilter() {
+        @Override protected boolean keep(Row row) {
+          return !hasFlag(row, flag);
+        }
+      }.filter(rows);
+  }
+
+  protected static boolean hasFlag(Row row, int flagMask)
+  {
+    return((getShortValue(row.flag, 0) & flagMask) != 0);
+  }
+
+  protected static short getShortValue(Short s, int def) {
+    return ((s != null) ? (short)s : (short)def);
+  }
+
+  protected static int getIntValue(Integer i, int def) {
+    return ((i != null) ? (int)i : def);
+  }
+
+  protected static StringBuilder toOptionalQuotedExpr(StringBuilder builder, 
+                                                      String fullExpr,
+                                                      boolean isIdentifier)
+  {
+    String[] exprs = (isIdentifier ? 
+                      IDENTIFIER_SEP_PAT.split(fullExpr) : 
+                      new String[]{fullExpr});
+    for(int i = 0; i < exprs.length; ++i) {
+      String expr = exprs[i];
+      if(QUOTABLE_CHAR_PAT.matcher(expr).find()) {
+        toQuotedExpr(builder, expr);
+      } else {
+        builder.append(expr);
+      }
+      if(i < (exprs.length - 1)) {
+        builder.append(IDENTIFIER_SEP_CHAR);
+      }
+    }
+    return builder;
+  }
+
+  protected static StringBuilder toQuotedExpr(StringBuilder builder, 
+                                              String expr)
+  {
+    return builder.append('[').append(expr).append(']');
+  }
+
+  protected static StringBuilder toRemoteDb(StringBuilder builder,
+                                            String remoteDbPath,
+                                            String remoteDbType) {
+    if((remoteDbPath != null) || (remoteDbType != null)) {
+      // note, always include path string, even if empty
+      builder.append(" IN '");
+      if(remoteDbPath != null) {
+        builder.append(remoteDbPath);
+      }
+      builder.append('\'');
+      if(remoteDbType != null) {
+        builder.append(" [").append(remoteDbType).append(']');
+      }
+    }
+    return builder;
+  }
+
+  protected static StringBuilder toAlias(StringBuilder builder,
+                                         String alias) {
+    if(alias != null) {
+      toOptionalQuotedExpr(builder.append(" AS "), alias, false);
+    }
+    return builder;
+  }
+
+  private static final class UnknownQueryImpl extends QueryImpl
+  {
+    private final int _objectFlag;
+
+    private UnknownQueryImpl(String name, List<Row> rows, int objectId, 
+                             int objectFlag) 
+    {
+      super(name, rows, objectId, Type.UNKNOWN);
+      _objectFlag = objectFlag;
+    }
+
+    @Override
+    public int getObjectFlag() {
+      return _objectFlag;
+    }
+
+    @Override
+    protected void toSQLString(StringBuilder builder) {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  /**
+   * Struct containing the information from a single row of the system query
+   * table.
+   */
+  public static final class Row
+  {
+    private final RowId _id;
+    public final Byte attribute;
+    public final String expression;
+    public final Short flag;
+    public final Integer extra;
+    public final String name1;
+    public final String name2;
+    public final Integer objectId;
+    public final byte[] order;
+
+    private Row() {
+      this._id = null;
+      this.attribute = null;
+      this.expression = null;
+      this.flag = null;
+      this.extra = null;
+      this.name1 = null;
+      this.name2= null;
+      this.objectId = null;
+      this.order = null;
+    }
+
+    public Row(com.healthmarketscience.jackcess.Row tableRow) {
+      this(tableRow.getId(),
+           (Byte)tableRow.get(COL_ATTRIBUTE),
+           (String)tableRow.get(COL_EXPRESSION),
+           (Short)tableRow.get(COL_FLAG),
+           (Integer)tableRow.get(COL_EXTRA),
+           (String)tableRow.get(COL_NAME1),
+           (String)tableRow.get(COL_NAME2),
+           (Integer)tableRow.get(COL_OBJECTID),
+           (byte[])tableRow.get(COL_ORDER));
+    }
+
+    public Row(RowId id, Byte attribute, String expression, Short flag,
+               Integer extra, String name1, String name2,
+               Integer objectId, byte[] order)
+    {
+      this._id = id;
+      this.attribute = attribute;
+      this.expression = expression;
+      this.flag = flag;
+      this.extra = extra;
+      this.name1 = name1;
+      this.name2= name2;
+      this.objectId = objectId;
+      this.order = order;
+    }
+
+    public com.healthmarketscience.jackcess.Row toTableRow()
+    {
+      com.healthmarketscience.jackcess.Row tableRow = new RowImpl((RowIdImpl)_id);
+
+      tableRow.put(COL_ATTRIBUTE, attribute);
+      tableRow.put(COL_EXPRESSION, expression);
+      tableRow.put(COL_FLAG, flag);
+      tableRow.put(COL_EXTRA, extra);
+      tableRow.put(COL_NAME1, name1);
+      tableRow.put(COL_NAME2, name2);
+      tableRow.put(COL_OBJECTID, objectId);
+      tableRow.put(COL_ORDER, order);
+
+      return tableRow;
+    }
+
+    @Override
+    public String toString() {
+      return ToStringBuilder.reflectionToString(this);
+    }
+  }
+
+  protected static abstract class RowFormatter
+  {
+    private final List<Row> _list;
+
+    protected RowFormatter(List<Row> list) {
+      _list = list;
+    }
+
+    public List<String> format() {
+      return format(new AppendableList<String>());
+    }
+
+    public List<String> format(List<String> strs) {
+      for(Row row : _list) {
+        StringBuilder builder = new StringBuilder();
+        format(builder, row);
+        strs.add(builder.toString());
+      }
+      return strs;
+    }
+
+    protected abstract void format(StringBuilder builder, Row row);
+  }
+
+  protected static abstract class RowFilter
+  {
+    protected RowFilter() {
+    }
+
+    public List<Row> filter(List<Row> list) {
+      for(Iterator<Row> iter = list.iterator(); iter.hasNext(); ) {
+        if(!keep(iter.next())) {
+          iter.remove();
+        }
+      }
+      return list;
+    }
+
+    protected abstract boolean keep(Row row);
+  }
+
+  protected static class AppendableList<E> extends ArrayList<E>
+  {
+    private static final long serialVersionUID = 0L;
+
+    protected AppendableList() {
+    }
+
+    protected AppendableList(Collection<? extends E> c) {
+      super(c);
+    }
+
+    protected String getSeparator() {
+      return ", ";
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      for(Iterator<E> iter = iterator(); iter.hasNext(); ) {
+        builder.append(iter.next().toString());
+        if(iter.hasNext()) {
+          builder.append(getSeparator());
+        }
+      }
+      return builder.toString();
+    }
+  }
+
+  private static final class Join
+  {
+    public final List<String> tables = new ArrayList<String>();
+    public boolean isJoin;
+    public String expression;
+
+    private Join(String table, String expr) {
+      tables.add(table);
+      expression = expr;
+    }
+
+    public void join(Join other, String newExpr) {
+      tables.addAll(other.tables);
+      isJoin = true;
+      expression = newExpr;
+    }
+
+    @Override
+    public String toString() {
+      return (isJoin ? ("(" + expression + ")") : expression);
+    }
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/SelectQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/SelectQueryImpl.java
new file mode 100644 (file)
index 0000000..dfe326a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+import com.healthmarketscience.jackcess.query.SelectQuery;
+
+
+/**
+ * Concrete Query subclass which represents a select query, e.g.:
+ * {@code SELECT <columns> FROM <tables> WHERE <expression>}
+ * 
+ * @author James Ahlborn
+ */
+public class SelectQueryImpl extends BaseSelectQueryImpl implements SelectQuery
+{
+
+  public SelectQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.SELECT);
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    toSQLSelectString(builder, true);
+  }  
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/UnionQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/UnionQueryImpl.java
new file mode 100644 (file)
index 0000000..d94efc1
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
+import com.healthmarketscience.jackcess.query.UnionQuery;
+
+
+/**
+ * Concrete Query subclass which represents a UNION query, e.g.:
+ * {@code SELECT <query1> UNION SELECT <query2>}
+ * 
+ * @author James Ahlborn
+ */
+public class UnionQueryImpl extends QueryImpl implements UnionQuery
+{
+  public UnionQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.UNION);
+  }
+
+  public String getUnionType() {
+    return(hasFlag(UNION_FLAG) ? DEFAULT_TYPE : "ALL");
+  }
+
+  public String getUnionString1() {
+    return getUnionString(UNION_PART1);
+  }
+
+  public String getUnionString2() {
+    return getUnionString(UNION_PART2);
+  }
+
+  @Override
+  public List<String> getOrderings() {
+    return super.getOrderings();
+  }
+
+  private String getUnionString(String id) {
+    for(Row row : getTableRows()) {
+      if(id.equals(row.name2)) {
+        return cleanUnionString(row.expression);
+      }
+    }
+    throw new IllegalStateException(
+        "Could not find union query with id " + id);
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    builder.append(getUnionString1()).append(NEWLINE)
+      .append("UNION ");
+    String unionType = getUnionType();
+    if(!DEFAULT_TYPE.equals(unionType)) {
+      builder.append(unionType).append(' ');
+    }
+    builder.append(getUnionString2());
+    List<String> orderings = getOrderings();
+    if(!orderings.isEmpty()) {
+      builder.append(NEWLINE).append("ORDER BY ").append(orderings);
+    }
+  }
+
+  private static String cleanUnionString(String str)
+  {
+    return str.trim().replaceAll("[\r\n]+", NEWLINE);
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/impl/query/UpdateQueryImpl.java b/src/java/com/healthmarketscience/jackcess/impl/query/UpdateQueryImpl.java
new file mode 100644 (file)
index 0000000..093f3ec
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess.impl.query;
+
+import java.util.List;
+
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
+import com.healthmarketscience.jackcess.query.UpdateQuery;
+
+
+/**
+ * Concrete Query subclass which represents a row update query, e.g.:
+ * {@code UPDATE <table> SET <newValues>}
+ * 
+ * @author James Ahlborn
+ */
+public class UpdateQueryImpl extends QueryImpl implements UpdateQuery
+{
+
+  public UpdateQueryImpl(String name, List<Row> rows, int objectId) {
+    super(name, rows, objectId, Type.UPDATE);
+  }
+
+  public List<String> getTargetTables() 
+  {
+    return super.getFromTables();
+  }
+
+  public String getRemoteDbPath() 
+  {
+    return super.getFromRemoteDbPath();
+  }
+
+  public String getRemoteDbType() 
+  {
+    return super.getFromRemoteDbType();
+  }
+
+  public List<String> getNewValues()
+  {
+    return (new RowFormatter(getColumnRows()) {
+        @Override protected void format(StringBuilder builder, Row row) {
+          toOptionalQuotedExpr(builder, row.name2, true)
+            .append(" = ").append(row.expression);
+        }
+      }).format();
+  }
+
+  @Override
+  public String getWhereExpression()
+  {
+    return super.getWhereExpression();
+  }
+
+  @Override
+  protected void toSQLString(StringBuilder builder)
+  {
+    builder.append("UPDATE ").append(getTargetTables());
+    toRemoteDb(builder, getRemoteDbPath(), getRemoteDbType());
+
+    builder.append(NEWLINE).append("SET ").append(getNewValues());
+
+    String whereExpr = getWhereExpression();
+    if(whereExpr != null) {
+      builder.append(NEWLINE).append("WHERE ").append(whereExpr);
+    }
+  }  
+
+}
index 3a216e81a3c9b57aafa1f52533c50dc352c9d9bd..96ae5ad6d9dd8d16344657f36b8d08d79294f288 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,77 +15,27 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
 import java.util.List;
 
-import static com.healthmarketscience.jackcess.query.QueryFormat.*;
-
 
 /**
- * Concrete Query subclass which represents an append query, e.g.:
+ * Query interface which represents an append query, e.g.:
  * {@code INSERT INTO <table> VALUES (<values>)}
  * 
  * @author James Ahlborn
  */
-public class AppendQuery extends BaseSelectQuery 
+public interface AppendQuery extends BaseSelectQuery 
 {
 
-  public AppendQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.APPEND);
-  }
-
-  public String getTargetTable() {
-    return getTypeRow().name1;
-  }
-
-  public String getRemoteDbPath() {
-    return getTypeRow().name2;
-  }
-
-  public String getRemoteDbType() {
-    return getTypeRow().expression;
-  }
-
-  protected List<Row> getValueRows() {
-    return filterRowsByFlag(super.getColumnRows(), APPEND_VALUE_FLAG);
-  }
+  public String getTargetTable();
 
-  @Override
-  protected List<Row> getColumnRows() {
-    return filterRowsByNotFlag(super.getColumnRows(), APPEND_VALUE_FLAG);
-  }
+  public String getRemoteDbPath();
 
-  public List<String> getValues() {
-    return new RowFormatter(getValueRows()) {
-        @Override protected void format(StringBuilder builder, Row row) {
-          builder.append(row.expression);
-        }
-      }.format();
-  }
+  public String getRemoteDbType();
 
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    builder.append("INSERT INTO ").append(getTargetTable());
-    toRemoteDb(builder, getRemoteDbPath(), getRemoteDbType());
-    builder.append(NEWLINE);
-    List<String> values = getValues();
-    if(!values.isEmpty()) {
-      builder.append("VALUES (").append(values).append(')');
-    } else {
-      toSQLSelectString(builder, true);
-    }
-  }
-  
+  public List<String> getValues();
 }
index 272bacab4046a3334c4a5d152e83815460bc6b97..107dbe9e376f0c40774d0dfcbb360abb11a8ace2 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,161 +15,36 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
 import java.util.List;
 
-import static com.healthmarketscience.jackcess.query.QueryFormat.*;
-
 
 /**
- * Base class for queries which represent some form of SELECT statement.
+ * Base interface for queries which represent some form of SELECT statement.
  * 
  * @author James Ahlborn
  */
-public abstract class BaseSelectQuery extends Query 
+public interface BaseSelectQuery extends Query 
 {
 
-  protected BaseSelectQuery(String name, List<Row> rows, int objectId,
-                            Type type) {
-    super(name, rows, objectId, type);
-  }
-
-  protected void toSQLSelectString(StringBuilder builder,
-                                   boolean useSelectPrefix) 
-  {
-    if(useSelectPrefix) {
-      builder.append("SELECT ");
-      String selectType = getSelectType();
-      if(!DEFAULT_TYPE.equals(selectType)) {
-        builder.append(selectType).append(' ');
-      }
-    }
-
-    builder.append(getSelectColumns());
-    toSelectInto(builder);
-
-    List<String> fromTables = getFromTables();
-    if(!fromTables.isEmpty()) {
-      builder.append(NEWLINE).append("FROM ").append(fromTables);
-      toRemoteDb(builder, getFromRemoteDbPath(), getFromRemoteDbType());
-    }
-
-    String whereExpr = getWhereExpression();
-    if(whereExpr != null) {
-      builder.append(NEWLINE).append("WHERE ").append(whereExpr);
-    }
-
-    List<String> groupings = getGroupings();
-    if(!groupings.isEmpty()) {
-      builder.append(NEWLINE).append("GROUP BY ").append(groupings);
-    }
-
-    String havingExpr = getHavingExpression();
-    if(havingExpr != null) {
-      builder.append(NEWLINE).append("HAVING ").append(havingExpr);
-    }
-
-    List<String> orderings = getOrderings();
-    if(!orderings.isEmpty()) {
-      builder.append(NEWLINE).append("ORDER BY ").append(orderings);
-    }
-  }
-
-  public String getSelectType()
-  {
-    if(hasFlag(DISTINCT_SELECT_TYPE)) {
-      return "DISTINCT";
-    }
-
-    if(hasFlag(DISTINCT_ROW_SELECT_TYPE)) {
-      return "DISTINCTROW";
-    }
-
-    if(hasFlag(TOP_SELECT_TYPE)) {
-      StringBuilder builder = new StringBuilder();
-      builder.append("TOP ").append(getFlagRow().name1);
-      if(hasFlag(PERCENT_SELECT_TYPE)) {
-        builder.append(" PERCENT");
-      }
-      return builder.toString();
-    }
-
-    return DEFAULT_TYPE;
-  }
-
-  public List<String> getSelectColumns() 
-  {
-    List<String> result = (new RowFormatter(getColumnRows()) {
-        @Override protected void format(StringBuilder builder, Row row) {
-          // note column expression are always quoted appropriately
-          builder.append(row.expression);
-          toAlias(builder, row.name1);
-        }
-      }).format();
-    if(hasFlag(SELECT_STAR_SELECT_TYPE)) {
-      result.add("*");
-    }
-    return result;
-  }
-
-  protected void toSelectInto(StringBuilder builder)
-  {
-    // base does nothing
-  }
-
-  @Override
-  public List<String> getFromTables() 
-  {
-    return super.getFromTables();
-  }
-
-  @Override
-  public String getFromRemoteDbPath() 
-  {
-    return super.getFromRemoteDbPath();
-  }
-
-  @Override
-  public String getFromRemoteDbType() 
-  {
-    return super.getFromRemoteDbType();
-  }
-
-  @Override
-  public String getWhereExpression()
-  {
-    return super.getWhereExpression();
-  }
-
-  public List<String> getGroupings() 
-  {
-    return (new RowFormatter(getGroupByRows()) {
-        @Override protected void format(StringBuilder builder, Row row) {
-          builder.append(row.expression);
-        }
-      }).format();
-  }
-
-  public String getHavingExpression()
-  {
-    return getHavingRow().expression;
-  }
-
-  @Override
-  public List<String> getOrderings() 
-  {
-    return super.getOrderings();
-  }
-  
+  public String getSelectType();
+
+  public List<String> getSelectColumns();
+
+  public List<String> getFromTables();
+
+  public String getFromRemoteDbPath();
+
+  public String getFromRemoteDbType();
+
+  public String getWhereExpression();
+
+  public List<String> getGroupings();
+
+  public String getHavingExpression();
+
+  public List<String> getOrderings();
 }
index 3fd6bf6ef6ba3a6178d0402c0d7dab477d289d28..474c979f94506aea0122c6fb23934844f6ecae00 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,84 +15,23 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
 import java.util.List;
 
-import static com.healthmarketscience.jackcess.query.QueryFormat.*;
-
 
 /**
- * Concrete Query subclass which represents a crosstab/pivot query, e.g.:
+ * Query interface which represents a crosstab/pivot query, e.g.:
  * {@code TRANSFORM <expr> SELECT <query> PIVOT <expr>}
  * 
  * @author James Ahlborn
  */
-public class CrossTabQuery extends BaseSelectQuery 
+public interface CrossTabQuery extends BaseSelectQuery 
 {
 
-  public CrossTabQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.CROSS_TAB);
-  }
-
-  protected Row getTransformRow() {
-    return getUniqueRow(
-        filterRowsByNotFlag(super.getColumnRows(), 
-                            (short)(CROSSTAB_PIVOT_FLAG | 
-                                    CROSSTAB_NORMAL_FLAG)));
-  }
-
-  @Override
-  protected List<Row> getColumnRows() {
-    return filterRowsByFlag(super.getColumnRows(), CROSSTAB_NORMAL_FLAG);
-  }
-
-  @Override
-  protected List<Row> getGroupByRows() {
-    return filterRowsByFlag(super.getGroupByRows(), CROSSTAB_NORMAL_FLAG);
-  }
-
-  protected Row getPivotRow() {
-    return getUniqueRow(filterRowsByFlag(super.getColumnRows(), 
-                                         CROSSTAB_PIVOT_FLAG));
-  }
-
-  public String getTransformExpression() {
-    Row row = getTransformRow();
-    if(row.expression == null) {
-      return null;
-    }
-    // note column expression are always quoted appropriately
-    StringBuilder builder = new StringBuilder(row.expression);
-    return toAlias(builder, row.name1).toString();
-  }
-
-  public String getPivotExpression() {
-    return getPivotRow().expression;
-  }
-
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    String transformExpr = getTransformExpression();
-    if(transformExpr != null) {
-      builder.append("TRANSFORM ").append(transformExpr).append(NEWLINE);
-    }
-
-    toSQLSelectString(builder, true);
-
-    builder.append(NEWLINE).append("PIVOT ")
-      .append(getPivotExpression());
-  }
+  public String getTransformExpression();
 
+  public String getPivotExpression();
 }
index 16937b3026ba660b1e9e9bf6cfb47ac31fe8ff1a..9b6b6fe95c2ed41210a1c8b4a6d5b576dac614e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,14 +15,6 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
@@ -31,33 +23,11 @@ import java.util.List;
 
 
 /**
- * Concrete Query subclass which represents a DDL query.
+ * Query interface which represents a DDL query.
  * 
  * @author James Ahlborn
  */
-public class DataDefinitionQuery extends Query 
+public interface DataDefinitionQuery extends Query 
 {
-
-  public DataDefinitionQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.DATA_DEFINITION);
-  }
-
-  public String getDDLString() {
-    return getTypeRow().expression;
-  }
-
-  @Override
-  protected boolean supportsStandardClauses() {
-    return false;
-  }
-
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    String ddl = getDDLString();
-    if(ddl != null) {
-      builder.append(ddl);
-    }
-  }
-
+  public String getDDLString();
 }
index 4f42b82ea2b863f4cf945dabcf9f0436a2c4c07b..b598c8b7c96af7b8410c1dcd7672f1ff84f2ade7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,39 +15,19 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
-import java.util.List;
 
 
 /**
- * Concrete Query subclass which represents a delete query, e.g.:
+ * Query interface which represents a delete query, e.g.:
  * {@code DELETE * FROM <table> WHERE <expression>}
  *
  * @author James Ahlborn
  */
-public class DeleteQuery extends BaseSelectQuery 
+public interface DeleteQuery extends BaseSelectQuery 
 {
 
-  public DeleteQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.DELETE);
-  }
-
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    builder.append("DELETE ");
-    toSQLSelectString(builder, false);
-  }
-
 }
index 21441979a64ecefa8f521541029e559452028900..f7798e0bd6ac3fa4f5bb889f445a6540bd9295d1 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,57 +15,24 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
-import java.util.List;
 
 
 /**
- * Concrete Query subclass which represents an table creation query, e.g.:
+ * Query interface which represents an table creation query, e.g.:
  * {@code SELECT <query> INTO <newTable>}
  * 
  * @author James Ahlborn
  */
-public class MakeTableQuery extends BaseSelectQuery 
+public interface MakeTableQuery extends BaseSelectQuery 
 {
 
-  public MakeTableQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.MAKE_TABLE);
-  }
-
-  public String getTargetTable() {
-    return getTypeRow().name1;
-  }
-
-  public String getRemoteDbPath() {
-    return getTypeRow().name2;
-  }
-
-  public String getRemoteDbType() {
-    return getTypeRow().expression;
-  }
-
-  @Override
-  protected void toSelectInto(StringBuilder builder) 
-  {
-    builder.append(" INTO ").append(getTargetTable());
-    toRemoteDb(builder, getRemoteDbPath(), getRemoteDbType());
-  }
+  public String getTargetTable();
 
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    toSQLSelectString(builder, true);
-  }  
+  public String getRemoteDbPath();
 
+  public String getRemoteDbType();
 }
index cb18090350e98165236fd35bdc1d834c7f6e3261..7004ceecc4cf7fda9ace3231dfb941d65491e554 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,54 +15,20 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
-import java.util.List;
 
 
 /**
- * Concrete Query subclass which represents a query which will be executed via
- * ODBC.
+ * Query interface which represents a query which will be executed via ODBC.
  * 
  * @author James Ahlborn
  */
-public class PassthroughQuery extends Query 
+public interface PassthroughQuery extends Query 
 {
+  public String getConnectionString();
 
-  public PassthroughQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.PASSTHROUGH);
-  }
-
-  public String getConnectionString() {
-    return getTypeRow().name1;
-  }
-
-  public String getPassthroughString() {
-    return getTypeRow().expression;
-  }
-
-  @Override
-  protected boolean supportsStandardClauses() {
-    return false;
-  }
-
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    String pt = getPassthroughString();
-    if(pt != null) {
-      builder.append(pt);
-    }
-  }
-
+  public String getPassthroughString();
 }
index 642cf6e1792bd4b6cce6f98b57a793ddf6050592..f6d6cc3c74726965170da018d3985c25428f6975 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,47 +15,24 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 
-import com.healthmarketscience.jackcess.RowId;
-import com.healthmarketscience.jackcess.impl.RowIdImpl;
-import com.healthmarketscience.jackcess.impl.RowImpl;
-import static com.healthmarketscience.jackcess.query.QueryFormat.*;
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
 
 
 /**
- * Base class for classes which encapsulate information about an Access query.
- * The {@link #toSQLString()} method can be used to convert this object into
- * the actual SQL string which this query data represents.
+ * Base interface for classes which encapsulate information about an Access
+ * query.  The {@link #toSQLString()} method can be used to convert this
+ * object into the actual SQL string which this query data represents.
  * 
  * @author James Ahlborn
  */
-public abstract class Query 
+public interface Query 
 {
-  protected static final Log LOG = LogFactory.getLog(Query.class);  
-
-  private static final Row EMPTY_ROW = new Row();
 
   public enum Type 
   {
@@ -87,664 +64,35 @@ public abstract class Query
     }
   }
 
-  private final String _name;
-  private final List<Row> _rows;
-  private final int _objectId;
-  private final Type _type;
-
-  protected Query(String name, List<Row> rows, int objectId, Type type) {
-    _name = name;
-    _rows = rows;
-    _objectId = objectId;
-    _type = type;
-
-    if(type != Type.UNKNOWN) {
-      short foundType = getShortValue(getQueryType(rows),
-                                      _type.getValue());
-      if(foundType != _type.getValue()) {
-        throw new IllegalStateException("Unexpected query type " + foundType);
-      }
-    }
-  }
-
   /**
    * Returns the name of the query.
    */
-  public String getName() {
-    return _name;
-  }
+  public String getName();
 
   /**
    * Returns the type of the query.
    */
-  public Type getType() {
-    return _type;
-  }
+  public Type getType();
 
   /**
    * Returns the unique object id of the query.
    */
-  public int getObjectId() {
-    return _objectId;
-  }
+  public int getObjectId();
 
-  public int getObjectFlag() {
-    return getType().getObjectFlag();
-  }
+  public int getObjectFlag();
 
   /**
    * Returns the rows from the system query table from which the query
    * information was derived.
    */
-  public List<Row> getRows() {
-    return _rows;
-  }
-
-  protected List<Row> getRowsByAttribute(Byte attribute) {
-    return getRowsByAttribute(getRows(), attribute);
-  }
-
-  protected Row getRowByAttribute(Byte attribute) {
-    return getUniqueRow(getRowsByAttribute(getRows(), attribute));
-  }
-
-  protected Row getTypeRow() {
-    return getRowByAttribute(TYPE_ATTRIBUTE);
-  }
-
-  protected List<Row> getParameterRows() {
-    return getRowsByAttribute(PARAMETER_ATTRIBUTE);
-  }
-
-  protected Row getFlagRow() {
-    return getRowByAttribute(FLAG_ATTRIBUTE);
-  }
-
-  protected Row getRemoteDatabaseRow() {
-    return getRowByAttribute(REMOTEDB_ATTRIBUTE);
-  }
-
-  protected List<Row> getTableRows() {
-    return getRowsByAttribute(TABLE_ATTRIBUTE);
-  }
-
-  protected List<Row> getColumnRows() {
-    return getRowsByAttribute(COLUMN_ATTRIBUTE);
-  }
-
-  protected List<Row> getJoinRows() {
-    return getRowsByAttribute(JOIN_ATTRIBUTE);
-  }
-
-  protected Row getWhereRow() {
-    return getRowByAttribute(WHERE_ATTRIBUTE);
-  }
-
-  protected List<Row> getGroupByRows() {
-    return getRowsByAttribute(GROUPBY_ATTRIBUTE);
-  }
-
-  protected Row getHavingRow() {
-    return getRowByAttribute(HAVING_ATTRIBUTE);
-  }
-
-  protected List<Row> getOrderByRows() {
-    return getRowsByAttribute(ORDERBY_ATTRIBUTE);
-  }
-
-  protected abstract void toSQLString(StringBuilder builder);
-
-  protected void toSQLParameterString(StringBuilder builder) {
-    // handle any parameters
-    List<String> params = getParameters();
-    if(!params.isEmpty()) {
-      builder.append("PARAMETERS ").append(params)
-        .append(';').append(NEWLINE);
-    }
-  }
-
-  public List<String> getParameters() 
-  {
-    return (new RowFormatter(getParameterRows()) {
-        @Override protected void format(StringBuilder builder, Row row) {
-          String typeName = PARAM_TYPE_MAP.get(row.flag);
-          if(typeName == null) {
-            throw new IllegalStateException("Unknown param type " + row.flag);
-          }
-              
-          builder.append(row.name1).append(' ').append(typeName);
-          if((TEXT_FLAG.equals(row.flag)) && (getIntValue(row.extra, 0) > 0)) {
-            builder.append('(').append(row.extra).append(')');
-          }
-        }
-      }).format();
-  }
-
-  protected List<String> getFromTables() 
-  {
-    List<Join> joinExprs = new ArrayList<Join>();
-    for(Row table : getTableRows()) {
-      StringBuilder builder = new StringBuilder();
-
-      if(table.expression != null) {
-        toQuotedExpr(builder, table.expression).append(IDENTIFIER_SEP_CHAR);
-      }
-      if(table.name1 != null) {
-        toOptionalQuotedExpr(builder, table.name1, true);
-      }
-      toAlias(builder, table.name2);
-
-      String key = ((table.name2 != null) ? table.name2 : table.name1);
-      joinExprs.add(new Join(key, builder.toString()));
-    }
-
-
-    List<Row> joins = getJoinRows();
-    if(!joins.isEmpty()) {
+  // public List<Row> getRows();
 
-      // combine any multi-column joins
-      Collection<List<Row>> comboJoins = combineJoins(joins);
-      
-      for(List<Row> comboJoin : comboJoins) {
+  public List<String> getParameters();
 
-        Row join = comboJoin.get(0);
-        String joinExpr = join.expression;
-
-        if(comboJoin.size() > 1) {
-
-          // combine all the join expressions with "AND"
-          AppendableList<String> comboExprs = new AppendableList<String>() {
-            private static final long serialVersionUID = 0L;
-            @Override
-            protected String getSeparator() {
-              return ") AND (";
-            }
-          };
-          for(Row tmpJoin : comboJoin) {
-            comboExprs.add(tmpJoin.expression);
-          }
-
-          joinExpr = new StringBuilder().append("(")
-            .append(comboExprs).append(")").toString();
-        }
-
-        String fromTable = join.name1;
-        String toTable = join.name2;
-      
-        Join fromExpr = getJoinExpr(fromTable, joinExprs);
-        Join toExpr = getJoinExpr(toTable, joinExprs);
-        String joinType = JOIN_TYPE_MAP.get(join.flag);
-        if(joinType == null) {
-            throw new IllegalStateException("Unknown join type " + join.flag);
-        }
-
-        String expr = new StringBuilder().append(fromExpr)
-          .append(joinType).append(toExpr).append(" ON ")
-          .append(joinExpr).toString();
-
-        fromExpr.join(toExpr, expr);
-        joinExprs.add(fromExpr);
-      }
-    }
-
-    List<String> result = new AppendableList<String>();
-    for(Join joinExpr : joinExprs) {
-      result.add(joinExpr.expression);
-    }
-
-    return result;
-  }
-
-  private Join getJoinExpr(String table, List<Join> joinExprs)
-  {
-    for(Iterator<Join> iter = joinExprs.iterator(); iter.hasNext(); ) {
-      Join joinExpr = iter.next();
-      if(joinExpr.tables.contains(table)) {
-        iter.remove();
-        return joinExpr;
-      }
-    }
-    throw new IllegalStateException("Cannot find join table " + table);
-  }
-
-  private Collection<List<Row>> combineJoins(List<Row> joins)
-  {
-    // combine joins with the same to/from tables
-    Map<List<String>,List<Row>> comboJoinMap = 
-      new LinkedHashMap<List<String>,List<Row>>();
-    for(Row join : joins) {
-      List<String> key = Arrays.asList(join.name1, join.name2);
-      List<Row> comboJoins = comboJoinMap.get(key);
-      if(comboJoins == null) {
-        comboJoins = new ArrayList<Row>();
-        comboJoinMap.put(key, comboJoins);
-      } else {
-        if((short)comboJoins.get(0).flag != (short)join.flag) {
-          throw new IllegalStateException(
-              "Mismatched join flags for combo joins");
-        }
-      }
-      comboJoins.add(join);
-    }
-    return comboJoinMap.values();
-  }
-
-  protected String getFromRemoteDbPath() 
-  {
-    return getRemoteDatabaseRow().name1;
-  }
-
-  protected String getFromRemoteDbType() 
-  {
-    return getRemoteDatabaseRow().expression;
-  }
-
-  protected String getWhereExpression()
-  {
-    return getWhereRow().expression;
-  }
-
-  protected List<String> getOrderings() 
-  {
-    return (new RowFormatter(getOrderByRows()) {
-        @Override protected void format(StringBuilder builder, Row row) {
-          builder.append(row.expression);
-          if(DESCENDING_FLAG.equalsIgnoreCase(row.name1)) {
-            builder.append(" DESC");
-          }
-        }
-      }).format();
-  }
-
-  public String getOwnerAccessType() {
-    return(hasFlag(OWNER_ACCESS_SELECT_TYPE) ?
-           "WITH OWNERACCESS OPTION" : DEFAULT_TYPE);
-  }
-
-  protected boolean hasFlag(int flagMask)
-  {
-    return hasFlag(getFlagRow(), flagMask);
-  }
-
-  protected boolean supportsStandardClauses() {
-    return true;
-  }
+  public String getOwnerAccessType();
 
   /**
    * Returns the actual SQL string which this query data represents.
    */
-  public String toSQLString() 
-  {
-    StringBuilder builder = new StringBuilder();
-    if(supportsStandardClauses()) {
-      toSQLParameterString(builder);
-    }
-
-    toSQLString(builder);
-
-    if(supportsStandardClauses()) {
-
-      String accessType = getOwnerAccessType();
-      if(!DEFAULT_TYPE.equals(accessType)) {
-        builder.append(NEWLINE).append(accessType);
-      }
-      
-      builder.append(';');
-    }
-    return builder.toString();
-  }
-
-  @Override
-  public String toString() {
-    return ToStringBuilder.reflectionToString(this);
-  }
-
-  /**
-   * Creates a concrete Query instance from the given query data.
-   *
-   * @param objectFlag the flag indicating the type of the query
-   * @param name the name of the query
-   * @param rows the rows from the system query table containing the data
-   *             describing this query
-   * @param objectId the unique object id of this query
-   *
-   * @return a Query instance for the given query data
-   */
-  public static Query create(int objectFlag, String name, List<Row> rows, 
-                             int objectId)
-  {
-    try {
-      switch(objectFlag) {
-      case SELECT_QUERY_OBJECT_FLAG:
-        return new SelectQuery(name, rows, objectId);
-      case MAKE_TABLE_QUERY_OBJECT_FLAG:
-        return new MakeTableQuery(name, rows, objectId);
-      case APPEND_QUERY_OBJECT_FLAG:
-        return new AppendQuery(name, rows, objectId);
-      case UPDATE_QUERY_OBJECT_FLAG:
-        return new UpdateQuery(name, rows, objectId);
-      case DELETE_QUERY_OBJECT_FLAG:
-        return new DeleteQuery(name, rows, objectId);
-      case CROSS_TAB_QUERY_OBJECT_FLAG:
-        return new CrossTabQuery(name, rows, objectId);
-      case DATA_DEF_QUERY_OBJECT_FLAG:
-        return new DataDefinitionQuery(name, rows, objectId);
-      case PASSTHROUGH_QUERY_OBJECT_FLAG:
-        return new PassthroughQuery(name, rows, objectId);
-      case UNION_QUERY_OBJECT_FLAG:
-        return new UnionQuery(name, rows, objectId);
-      default:
-        // unknown querytype
-        throw new IllegalStateException(
-            "unknown query object flag " + objectFlag);
-      }
-    } catch(IllegalStateException e) {
-      LOG.warn("Failed parsing query", e);
-    }
-
-    // return unknown query
-    return new UnknownQuery(name, rows, objectId, objectFlag);
-  }
-
-  private static Short getQueryType(List<Row> rows)
-  {
-    return getUniqueRow(getRowsByAttribute(rows, TYPE_ATTRIBUTE)).flag;
-  }
-
-  private static List<Row> getRowsByAttribute(List<Row> rows, Byte attribute) {
-    List<Row> result = new ArrayList<Row>();
-    for(Row row : rows) {
-      if(attribute.equals(row.attribute)) {
-        result.add(row);
-      }
-    }
-    return result;
-  }
-
-  protected static Row getUniqueRow(List<Row> rows) {
-    if(rows.size() == 1) {
-      return rows.get(0);
-    }
-    if(rows.isEmpty()) {
-      return EMPTY_ROW;
-    }
-    throw new IllegalStateException("Unexpected number of rows for" + rows);
-  }
-
-  protected static List<Row> filterRowsByFlag(
-      List<Row> rows, final short flag) 
-  {
-    return new RowFilter() {
-        @Override protected boolean keep(Row row) {
-          return hasFlag(row, flag);
-        }
-      }.filter(rows);
-  }
-
-  protected static List<Row> filterRowsByNotFlag(
-      List<Row> rows, final short flag) 
-  {
-    return new RowFilter() {
-        @Override protected boolean keep(Row row) {
-          return !hasFlag(row, flag);
-        }
-      }.filter(rows);
-  }
-
-  protected static boolean hasFlag(Row row, int flagMask)
-  {
-    return((getShortValue(row.flag, 0) & flagMask) != 0);
-  }
-
-  protected static short getShortValue(Short s, int def) {
-    return ((s != null) ? (short)s : (short)def);
-  }
-
-  protected static int getIntValue(Integer i, int def) {
-    return ((i != null) ? (int)i : def);
-  }
-
-  protected static StringBuilder toOptionalQuotedExpr(StringBuilder builder, 
-                                                      String fullExpr,
-                                                      boolean isIdentifier)
-  {
-    String[] exprs = (isIdentifier ? 
-                      IDENTIFIER_SEP_PAT.split(fullExpr) : 
-                      new String[]{fullExpr});
-    for(int i = 0; i < exprs.length; ++i) {
-      String expr = exprs[i];
-      if(QUOTABLE_CHAR_PAT.matcher(expr).find()) {
-        toQuotedExpr(builder, expr);
-      } else {
-        builder.append(expr);
-      }
-      if(i < (exprs.length - 1)) {
-        builder.append(IDENTIFIER_SEP_CHAR);
-      }
-    }
-    return builder;
-  }
-
-  protected static StringBuilder toQuotedExpr(StringBuilder builder, 
-                                              String expr)
-  {
-    return builder.append('[').append(expr).append(']');
-  }
-
-  protected static StringBuilder toRemoteDb(StringBuilder builder,
-                                            String remoteDbPath,
-                                            String remoteDbType) {
-    if((remoteDbPath != null) || (remoteDbType != null)) {
-      // note, always include path string, even if empty
-      builder.append(" IN '");
-      if(remoteDbPath != null) {
-        builder.append(remoteDbPath);
-      }
-      builder.append('\'');
-      if(remoteDbType != null) {
-        builder.append(" [").append(remoteDbType).append(']');
-      }
-    }
-    return builder;
-  }
-
-  protected static StringBuilder toAlias(StringBuilder builder,
-                                         String alias) {
-    if(alias != null) {
-      toOptionalQuotedExpr(builder.append(" AS "), alias, false);
-    }
-    return builder;
-  }
-
-  private static final class UnknownQuery extends Query
-  {
-    private final int _objectFlag;
-
-    private UnknownQuery(String name, List<Row> rows, int objectId, 
-                         int objectFlag) 
-    {
-      super(name, rows, objectId, Type.UNKNOWN);
-      _objectFlag = objectFlag;
-    }
-
-    @Override
-    public int getObjectFlag() {
-      return _objectFlag;
-    }
-
-    @Override
-    protected void toSQLString(StringBuilder builder) {
-      throw new UnsupportedOperationException();
-    }
-  }
-
-  /**
-   * Struct containing the information from a single row of the system query
-   * table.
-   */
-  public static final class Row
-  {
-    private final RowId _id;
-    public final Byte attribute;
-    public final String expression;
-    public final Short flag;
-    public final Integer extra;
-    public final String name1;
-    public final String name2;
-    public final Integer objectId;
-    public final byte[] order;
-
-    private Row() {
-      this._id = null;
-      this.attribute = null;
-      this.expression = null;
-      this.flag = null;
-      this.extra = null;
-      this.name1 = null;
-      this.name2= null;
-      this.objectId = null;
-      this.order = null;
-    }
-
-    public Row(com.healthmarketscience.jackcess.Row tableRow) {
-      this(tableRow.getId(),
-           (Byte)tableRow.get(COL_ATTRIBUTE),
-           (String)tableRow.get(COL_EXPRESSION),
-           (Short)tableRow.get(COL_FLAG),
-           (Integer)tableRow.get(COL_EXTRA),
-           (String)tableRow.get(COL_NAME1),
-           (String)tableRow.get(COL_NAME2),
-           (Integer)tableRow.get(COL_OBJECTID),
-           (byte[])tableRow.get(COL_ORDER));
-    }
-
-    public Row(RowId id, Byte attribute, String expression, Short flag,
-               Integer extra, String name1, String name2,
-               Integer objectId, byte[] order)
-    {
-      this._id = id;
-      this.attribute = attribute;
-      this.expression = expression;
-      this.flag = flag;
-      this.extra = extra;
-      this.name1 = name1;
-      this.name2= name2;
-      this.objectId = objectId;
-      this.order = order;
-    }
-
-    public com.healthmarketscience.jackcess.Row toTableRow()
-    {
-      com.healthmarketscience.jackcess.Row tableRow = new RowImpl((RowIdImpl)_id);
-
-      tableRow.put(COL_ATTRIBUTE, attribute);
-      tableRow.put(COL_EXPRESSION, expression);
-      tableRow.put(COL_FLAG, flag);
-      tableRow.put(COL_EXTRA, extra);
-      tableRow.put(COL_NAME1, name1);
-      tableRow.put(COL_NAME2, name2);
-      tableRow.put(COL_OBJECTID, objectId);
-      tableRow.put(COL_ORDER, order);
-
-      return tableRow;
-    }
-
-    @Override
-    public String toString() {
-      return ToStringBuilder.reflectionToString(this);
-    }
-  }
-
-  protected static abstract class RowFormatter
-  {
-    private final List<Row> _list;
-
-    protected RowFormatter(List<Row> list) {
-      _list = list;
-    }
-
-    public List<String> format() {
-      return format(new AppendableList<String>());
-    }
-
-    public List<String> format(List<String> strs) {
-      for(Row row : _list) {
-        StringBuilder builder = new StringBuilder();
-        format(builder, row);
-        strs.add(builder.toString());
-      }
-      return strs;
-    }
-
-    protected abstract void format(StringBuilder builder, Row row);
-  }
-
-  protected static abstract class RowFilter
-  {
-    protected RowFilter() {
-    }
-
-    public List<Row> filter(List<Row> list) {
-      for(Iterator<Row> iter = list.iterator(); iter.hasNext(); ) {
-        if(!keep(iter.next())) {
-          iter.remove();
-        }
-      }
-      return list;
-    }
-
-    protected abstract boolean keep(Row row);
-  }
-
-  protected static class AppendableList<E> extends ArrayList<E>
-  {
-    private static final long serialVersionUID = 0L;
-
-    protected AppendableList() {
-    }
-
-    protected AppendableList(Collection<? extends E> c) {
-      super(c);
-    }
-
-    protected String getSeparator() {
-      return ", ";
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder builder = new StringBuilder();
-      for(Iterator<E> iter = iterator(); iter.hasNext(); ) {
-        builder.append(iter.next().toString());
-        if(iter.hasNext()) {
-          builder.append(getSeparator());
-        }
-      }
-      return builder.toString();
-    }
-  }
-
-  private static final class Join
-  {
-    public final List<String> tables = new ArrayList<String>();
-    public boolean isJoin;
-    public String expression;
-
-    private Join(String table, String expr) {
-      tables.add(table);
-      expression = expr;
-    }
-
-    public void join(Join other, String newExpr) {
-      tables.addAll(other.tables);
-      isJoin = true;
-      expression = newExpr;
-    }
-
-    @Override
-    public String toString() {
-      return (isJoin ? ("(" + expression + ")") : expression);
-    }
-  }
-
+  public String toSQLString();
 }
diff --git a/src/java/com/healthmarketscience/jackcess/query/QueryFormat.java b/src/java/com/healthmarketscience/jackcess/query/QueryFormat.java
deleted file mode 100644 (file)
index 89d586e..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-Copyright (c) 2008 Health Market Science, Inc.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
-USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
-*/
-
-package com.healthmarketscience.jackcess.query;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-import com.healthmarketscience.jackcess.DataType;
-import org.apache.commons.lang.SystemUtils;
-
-/**
- * Constants used by the query data parsing.
- * 
- * @author James Ahlborn
- */
-public class QueryFormat 
-{
-
-  private QueryFormat() {}
-
-  public static final int SELECT_QUERY_OBJECT_FLAG = 0;
-  public static final int MAKE_TABLE_QUERY_OBJECT_FLAG = 80;
-  public static final int APPEND_QUERY_OBJECT_FLAG = 64;
-  public static final int UPDATE_QUERY_OBJECT_FLAG = 48;
-  public static final int DELETE_QUERY_OBJECT_FLAG = 32;
-  public static final int CROSS_TAB_QUERY_OBJECT_FLAG = 16;
-  public static final int DATA_DEF_QUERY_OBJECT_FLAG = 96;
-  public static final int PASSTHROUGH_QUERY_OBJECT_FLAG = 112;
-  public static final int UNION_QUERY_OBJECT_FLAG = 128;
-  // dbQSPTBulk = 144
-  // dbQCompound = 160
-  // dbQProcedure = 224
-  // dbQAction = 240
-
-  public static final String COL_ATTRIBUTE = "Attribute";
-  public static final String COL_EXPRESSION = "Expression";
-  public static final String COL_FLAG = "Flag";
-  public static final String COL_EXTRA = "LvExtra";
-  public static final String COL_NAME1 = "Name1";
-  public static final String COL_NAME2 = "Name2";
-  public static final String COL_OBJECTID = "ObjectId";
-  public static final String COL_ORDER = "Order";
-
-  public static final Byte START_ATTRIBUTE = 0;
-  public static final Byte TYPE_ATTRIBUTE = 1;
-  public static final Byte PARAMETER_ATTRIBUTE = 2;
-  public static final Byte FLAG_ATTRIBUTE = 3;
-  public static final Byte REMOTEDB_ATTRIBUTE = 4;
-  public static final Byte TABLE_ATTRIBUTE = 5;
-  public static final Byte COLUMN_ATTRIBUTE = 6;
-  public static final Byte JOIN_ATTRIBUTE = 7;
-  public static final Byte WHERE_ATTRIBUTE = 8;
-  public static final Byte GROUPBY_ATTRIBUTE = 9;
-  public static final Byte HAVING_ATTRIBUTE = 10;
-  public static final Byte ORDERBY_ATTRIBUTE = 11;
-  public static final Byte END_ATTRIBUTE = (byte)255;
-
-  public static final short UNION_FLAG = 0x02;
-
-  public static final Short TEXT_FLAG = (short)DataType.TEXT.getValue();
-
-  public static final String DESCENDING_FLAG = "D";
-
-  public static final short SELECT_STAR_SELECT_TYPE = 0x01;
-  public static final short DISTINCT_SELECT_TYPE = 0x02;
-  public static final short OWNER_ACCESS_SELECT_TYPE = 0x04;
-  public static final short DISTINCT_ROW_SELECT_TYPE = 0x08;
-  public static final short TOP_SELECT_TYPE = 0x10;
-  public static final short PERCENT_SELECT_TYPE = 0x20;
-
-  public static final short APPEND_VALUE_FLAG = (short)0x8000;
-
-  public static final short CROSSTAB_PIVOT_FLAG = 0x01;
-  public static final short CROSSTAB_NORMAL_FLAG = 0x02;  
-
-  public static final String UNION_PART1 = "X7YZ_____1";
-  public static final String UNION_PART2 = "X7YZ_____2";
-
-  public static final String DEFAULT_TYPE = "";
-
-  public static final Pattern QUOTABLE_CHAR_PAT = Pattern.compile("\\W");
-
-  public static final Pattern IDENTIFIER_SEP_PAT = Pattern.compile("\\.");
-  public static final char IDENTIFIER_SEP_CHAR = '.';
-
-  public static final String NEWLINE = SystemUtils.LINE_SEPARATOR;
-
-
-  public static final Map<Short,String> PARAM_TYPE_MAP = 
-    new HashMap<Short,String>();
-  static {
-    PARAM_TYPE_MAP.put((short)0, "Value");
-    PARAM_TYPE_MAP.put((short)DataType.BOOLEAN.getValue(), "Bit");
-    PARAM_TYPE_MAP.put((short)DataType.TEXT.getValue(), "Text");
-    PARAM_TYPE_MAP.put((short)DataType.BYTE.getValue(), "Byte");
-    PARAM_TYPE_MAP.put((short)DataType.INT.getValue(), "Short");
-    PARAM_TYPE_MAP.put((short)DataType.LONG.getValue(), "Long");
-    PARAM_TYPE_MAP.put((short)DataType.MONEY.getValue(), "Currency");
-    PARAM_TYPE_MAP.put((short)DataType.FLOAT.getValue(), "IEEESingle");
-    PARAM_TYPE_MAP.put((short)DataType.DOUBLE.getValue(), "IEEEDouble");
-    PARAM_TYPE_MAP.put((short)DataType.SHORT_DATE_TIME.getValue(), "DateTime");
-    PARAM_TYPE_MAP.put((short)DataType.BINARY.getValue(), "Binary");
-    PARAM_TYPE_MAP.put((short)DataType.OLE.getValue(), "LongBinary");
-    PARAM_TYPE_MAP.put((short)DataType.GUID.getValue(), "Guid");
-  }
-
-  public static final Map<Short,String> JOIN_TYPE_MAP = 
-    new HashMap<Short,String>();
-  static {
-    JOIN_TYPE_MAP.put((short)1, " INNER JOIN ");
-    JOIN_TYPE_MAP.put((short)2, " LEFT JOIN ");
-    JOIN_TYPE_MAP.put((short)3, " RIGHT JOIN ");
-  }
-
-}
index 3efd029f11cc7c22d07ad301749cce8d5afcd072..7ada9b2c83821aa128a40525b0a253c4d57ca16e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,38 +15,18 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
-import java.util.List;
 
 
 /**
- * Concrete Query subclass which represents a select query, e.g.:
+ * Query interface which represents a select query, e.g.:
  * {@code SELECT <columns> FROM <tables> WHERE <expression>}
  * 
  * @author James Ahlborn
  */
-public class SelectQuery extends BaseSelectQuery 
+public interface SelectQuery extends BaseSelectQuery 
 {
-
-  public SelectQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.SELECT);
-  }
-
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    toSQLSelectString(builder, true);
-  }  
-
 }
index cd75906854864ed1ba2c58aee9ae5128755ad600..6b8a1fbf5d051b06b44f62d4fe1b25b362b55ec3 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,81 +15,26 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
 import java.util.List;
 
-import static com.healthmarketscience.jackcess.query.QueryFormat.*;
-
 
 /**
- * Concrete Query subclass which represents a UNION query, e.g.:
+ * Query interface which represents a UNION query, e.g.:
  * {@code SELECT <query1> UNION SELECT <query2>}
  * 
  * @author James Ahlborn
  */
-public class UnionQuery extends Query 
+public interface UnionQuery extends Query 
 {
-  public UnionQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.UNION);
-  }
-
-  public String getUnionType() {
-    return(hasFlag(UNION_FLAG) ? DEFAULT_TYPE : "ALL");
-  }
-
-  public String getUnionString1() {
-    return getUnionString(UNION_PART1);
-  }
-
-  public String getUnionString2() {
-    return getUnionString(UNION_PART2);
-  }
-
-  @Override
-  public List<String> getOrderings() {
-    return super.getOrderings();
-  }
-
-  private String getUnionString(String id) {
-    for(Row row : getTableRows()) {
-      if(id.equals(row.name2)) {
-        return cleanUnionString(row.expression);
-      }
-    }
-    throw new IllegalStateException(
-        "Could not find union query with id " + id);
-  }
+  public String getUnionType();
 
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    builder.append(getUnionString1()).append(NEWLINE)
-      .append("UNION ");
-    String unionType = getUnionType();
-    if(!DEFAULT_TYPE.equals(unionType)) {
-      builder.append(unionType).append(' ');
-    }
-    builder.append(getUnionString2());
-    List<String> orderings = getOrderings();
-    if(!orderings.isEmpty()) {
-      builder.append(NEWLINE).append("ORDER BY ").append(orderings);
-    }
-  }
+  public String getUnionString1();
 
-  private static String cleanUnionString(String str)
-  {
-    return str.trim().replaceAll("[\r\n]+", NEWLINE);
-  }
+  public String getUnionString2();
 
+  public List<String> getOrderings();
 }
index 747a9b3363e6d7cfeb180fb7c3ed24a618b87d36..f2990a14aaee729387862efa11f2f84b00296e82 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008 Health Market Science, Inc.
+Copyright (c) 2013 James Ahlborn
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -15,79 +15,29 @@ You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
 */
 
 package com.healthmarketscience.jackcess.query;
 
 import java.util.List;
 
-import static com.healthmarketscience.jackcess.query.QueryFormat.*;
-
 
 /**
- * Concrete Query subclass which represents a row update query, e.g.:
+ * Query interface which represents a row update query, e.g.:
  * {@code UPDATE <table> SET <newValues>}
  * 
  * @author James Ahlborn
  */
-public class UpdateQuery extends Query 
+public interface UpdateQuery extends Query 
 {
 
-  public UpdateQuery(String name, List<Row> rows, int objectId) {
-    super(name, rows, objectId, Type.UPDATE);
-  }
-
-  public List<String> getTargetTables() 
-  {
-    return super.getFromTables();
-  }
-
-  public String getRemoteDbPath() 
-  {
-    return super.getFromRemoteDbPath();
-  }
-
-  public String getRemoteDbType() 
-  {
-    return super.getFromRemoteDbType();
-  }
-
-  public List<String> getNewValues()
-  {
-    return (new RowFormatter(getColumnRows()) {
-        @Override protected void format(StringBuilder builder, Row row) {
-          toOptionalQuotedExpr(builder, row.name2, true)
-            .append(" = ").append(row.expression);
-        }
-      }).format();
-  }
-
-  @Override
-  public String getWhereExpression()
-  {
-    return super.getWhereExpression();
-  }
+  public List<String> getTargetTables();
 
-  @Override
-  protected void toSQLString(StringBuilder builder)
-  {
-    builder.append("UPDATE ").append(getTargetTables());
-    toRemoteDb(builder, getRemoteDbPath(), getRemoteDbType());
+  public String getRemoteDbPath();
 
-    builder.append(NEWLINE).append("SET ").append(getNewValues());
+  public String getRemoteDbType();
 
-    String whereExpr = getWhereExpression();
-    if(whereExpr != null) {
-      builder.append(NEWLINE).append("WHERE ").append(whereExpr);
-    }
-  }  
+  public List<String> getNewValues();
 
+  public String getWhereExpression();
 }
index a28ffcf1d241df4ee3aa4bc28d961fb8704107eb..015f2fc57567b3ed1c44ea3a5d2dd83308373330 100644 (file)
@@ -37,12 +37,13 @@ import java.util.Map;
 import com.healthmarketscience.jackcess.DataType;
 import com.healthmarketscience.jackcess.Database;
 import com.healthmarketscience.jackcess.DatabaseTest;
-import com.healthmarketscience.jackcess.query.Query.Row;
+import com.healthmarketscience.jackcess.impl.query.QueryImpl;
+import com.healthmarketscience.jackcess.impl.query.QueryImpl.Row;
 import junit.framework.TestCase;
 import org.apache.commons.lang.StringUtils;
 
 import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR;
-import static com.healthmarketscience.jackcess.query.QueryFormat.*;
+import static com.healthmarketscience.jackcess.impl.query.QueryFormat.*;
 
 import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
 
@@ -169,7 +170,7 @@ public class QueryTest extends TestCase
   {
     List<Row> rowList = new ArrayList<Row>();
     rowList.add(newRow(TYPE_ATTRIBUTE, null, -1, null, null));
-    Query query = Query.create(-1, "TestQuery", rowList, 13);
+    QueryImpl query = QueryImpl.create(-1, "TestQuery", rowList, 13);
     try {
       query.toSQLString();
       fail("UnsupportedOperationException should have been thrown");
@@ -187,7 +188,7 @@ public class QueryTest extends TestCase
     }
 
     try {
-      new Query("TestQuery", rowList, 13, Query.Type.UNION) {
+      new QueryImpl("TestQuery", rowList, 13, Query.Type.UNION) {
         @Override protected void toSQLString(StringBuilder builder) {
           throw new UnsupportedOperationException();
         }};
@@ -468,7 +469,7 @@ public class QueryTest extends TestCase
     rowList.add(newRow(TYPE_ATTRIBUTE, typeExpr, type.getValue(),
                        null, typeName1, null));
     rowList.addAll(Arrays.asList(rows));
-    return Query.create(type.getObjectFlag(), "TestQuery", rowList, 13);
+    return QueryImpl.create(type.getObjectFlag(), "TestQuery", rowList, 13);
   }
 
   private static Row newRow(Byte attr, String expr, String name1, String name2)
@@ -498,7 +499,7 @@ public class QueryTest extends TestCase
 
   private static void addRows(Query query, Row... rows)
   {
-    query.getRows().addAll(Arrays.asList(rows));
+    ((QueryImpl)query).getRows().addAll(Arrays.asList(rows));
   }
 
   private static void replaceRows(Query query, Row... rows)
@@ -509,7 +510,7 @@ public class QueryTest extends TestCase
 
   private static void removeRows(Query query, Byte attr)
   {
-    for(Iterator<Row> iter = query.getRows().iterator(); iter.hasNext(); ) {
+    for(Iterator<Row> iter = ((QueryImpl)query).getRows().iterator(); iter.hasNext(); ) {
       if(attr.equals(iter.next().attribute)) {
         iter.remove();
       }
@@ -518,7 +519,7 @@ public class QueryTest extends TestCase
 
   private static void removeLastRows(Query query, int num)
   {
-    List<Row> rows = query.getRows();
+    List<Row> rows = ((QueryImpl)query).getRows();
     int size = rows.size();
     rows.subList(size - num, size).clear();
   }