提交 113996ea authored 作者: Thomas Mueller's avatar Thomas Mueller

Cleanup:

- Javadocs
- Work in progress
上级 daa2b0bf
...@@ -19,7 +19,6 @@ import org.h2.jaqu.bytecode.ClassReader; ...@@ -19,7 +19,6 @@ import org.h2.jaqu.bytecode.ClassReader;
import org.h2.jaqu.util.StatementLogger; import org.h2.jaqu.util.StatementLogger;
import org.h2.jaqu.util.JdbcUtils; import org.h2.jaqu.util.JdbcUtils;
import org.h2.jaqu.util.Utils; import org.h2.jaqu.util.Utils;
//import org.h2.util.JdbcUtils;
//## Java 1.5 end ## //## Java 1.5 end ##
/** /**
...@@ -38,8 +37,8 @@ public class Query<T> { ...@@ -38,8 +37,8 @@ public class Query<T> {
private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap(); private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList(); private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
private Object[] groupByExpressions; private Object[] groupByExpressions;
private long limit = 0; private long limit;
private long offset = 0; private long offset;
Query(Db db) { Query(Db db) {
this.db = db; this.db = db;
...@@ -65,6 +64,7 @@ public class Query<T> { ...@@ -65,6 +64,7 @@ public class Query<T> {
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
int whyTrue;
JdbcUtils.closeSilently(rs, true); JdbcUtils.closeSilently(rs, true);
} }
} }
...@@ -120,24 +120,26 @@ public class Query<T> { ...@@ -120,24 +120,26 @@ public class Query<T> {
StatementLogger.delete(stat.getSQL()); StatementLogger.delete(stat.getSQL());
return stat.executeUpdate(); return stat.executeUpdate();
} }
public <A> SetColumn<T, A> set(A field) { public <A> SetColumn<T, A> set(A field) {
int renameSetColumnClassToUpdateSetColumn;
return new SetColumn<T, A>(this, field); return new SetColumn<T, A>(this, field);
} }
public <A> IncrementColumn<T, A> increment(A field) { public <A> IncrementColumn<T, A> increment(A field) {
int renameIncrementColumnClassToUpdateIncrementColumn;
return new IncrementColumn<T, A>(this, field); return new IncrementColumn<T, A>(this, field);
} }
public int update() { public int update() {
if (declarations.size() == 0) if (declarations.size() == 0)
throw new RuntimeException("Please specify SET or INCREMENT before calling Update!"); throw new RuntimeException("Please specify SET or INCREMENT before calling Update!");
SQLStatement stat = new SQLStatement(db); SQLStatement stat = new SQLStatement(db);
stat.appendSQL("UPDATE "); stat.appendSQL("UPDATE ");
from.appendSQL(stat); from.appendSQL(stat);
stat.appendSQL(" SET "); stat.appendSQL(" SET ");
int i = 0; int i = 0;
for (Declaration declaration:declarations) { for (Declaration declaration : declarations) {
if (i++ > 0) { if (i++ > 0) {
stat.appendSQL(", "); stat.appendSQL(", ");
} }
...@@ -145,7 +147,7 @@ public class Query<T> { ...@@ -145,7 +147,7 @@ public class Query<T> {
} }
appendWhere(stat); appendWhere(stat);
StatementLogger.update(stat.getSQL()); StatementLogger.update(stat.getSQL());
return stat.executeUpdate(); return stat.executeUpdate();
} }
public <X, Z> List<X> selectDistinct(Z x) { public <X, Z> List<X> selectDistinct(Z x) {
...@@ -196,6 +198,7 @@ public class Query<T> { ...@@ -196,6 +198,7 @@ public class Query<T> {
try { try {
X value; X value;
Object o = rs.getObject(1); Object o = rs.getObject(1);
int convertHereIsProbablyWrong;
if (Clob.class.isAssignableFrom(o.getClass())) { if (Clob.class.isAssignableFrom(o.getClass())) {
value = (X) Utils.convert(o, String.class); value = (X) Utils.convert(o, String.class);
} else } else
...@@ -266,7 +269,7 @@ public class Query<T> { ...@@ -266,7 +269,7 @@ public class Query<T> {
return this; return this;
} }
//## Java 1.5 end ## //## Java 1.5 end ##
/** /**
* Order by a number of columns. * Order by a number of columns.
* *
...@@ -323,8 +326,9 @@ public class Query<T> { ...@@ -323,8 +326,9 @@ public class Query<T> {
void addConditionToken(Token condition) { void addConditionToken(Token condition) {
conditions.add(condition); conditions.add(condition);
} }
void addDeclarationToken(Declaration declaration) { void addDeclarationToken(Declaration declaration) {
int todoWhatIsDeclaration;
declarations.add(declaration); declarations.add(declaration);
} }
......
...@@ -33,12 +33,12 @@ public class QueryWhere<T> { ...@@ -33,12 +33,12 @@ public class QueryWhere<T> {
query.addConditionToken(ConditionAndOr.OR); query.addConditionToken(ConditionAndOr.OR);
return new QueryCondition<T, A>(query, x); return new QueryCondition<T, A>(query, x);
} }
public QueryWhere<T> limit(long limit) { public QueryWhere<T> limit(long limit) {
query.limit(limit); query.limit(limit);
return this; return this;
} }
public QueryWhere<T> offset(long offset) { public QueryWhere<T> offset(long offset) {
query.offset(offset); query.offset(offset);
return this; return this;
......
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: James Moger * Initial Developer: James Moger
*/ */
package org.h2.jaqu; package org.h2.jaqu;
import org.h2.jaqu.TableDefinition.IndexDefinition; import org.h2.jaqu.TableDefinition.IndexDefinition;
import org.h2.jaqu.util.StatementBuilder; import org.h2.jaqu.util.StatementBuilder;
import org.h2.jaqu.util.StringUtils; import org.h2.jaqu.util.StringUtils;
/** /**
* This interface defines points where JaQu can build different statements * This interface defines points where JaQu can build different statements
* depending on the database used. * depending on the database used.
*/ */
public interface SQLDialect { public interface SQLDialect {
/** /**
* Get the SQL snippet for the table name. * Get the SQL snippet for the table name.
* *
* @param schema the schema name, or null for no schema * @param schema the schema name, or null for no schema
* @param table the table name * @param table the table name
* @return the SQL snippet * @return the SQL snippet
*/ */
String tableName(String schema, String table); String tableName(String schema, String table);
/** /**
* Get the CREATE INDEX statement. * Get the CREATE INDEX statement.
* *
* @param schema the schema name * @param schema the schema name
* @param table the table name * @param table the table name
* @param index the index definition * @param index the index definition
* @return the SQL statement * @return the SQL statement
*/ */
String createIndex(String schema, String table, IndexDefinition index); String createIndex(String schema, String table, IndexDefinition index);
/** /**
* Append "LIMIT limit" to the SQL statement. * Append "LIMIT limit" to the SQL statement.
* *
* @param stat the statement * @param stat the statement
* @param limit the limit * @param limit the limit
*/ */
void appendLimit(SQLStatement stat, long limit); void appendLimit(SQLStatement stat, long limit);
/** /**
* Append "OFFSET offset" to the SQL statement. * Append "OFFSET offset" to the SQL statement.
* *
* @param stat the statement * @param stat the statement
* @param offset the offset * @param offset the offset
*/ */
void appendOffset(SQLStatement stat, long offset); void appendOffset(SQLStatement stat, long offset);
/** /**
* Default implementation of an SQL dialect. * Default implementation of an SQL dialect.
* Designed for an H2 database. May be suitable for others. * Designed for an H2 database. May be suitable for others.
*/ */
public static class DefaultSQLDialect implements SQLDialect { public static class DefaultSQLDialect implements SQLDialect {
public String tableName(String schema, String table) { public String tableName(String schema, String table) {
if (StringUtils.isNullOrEmpty(schema)) { if (StringUtils.isNullOrEmpty(schema)) {
return table; return table;
} }
return schema + "." + table; return schema + "." + table;
} }
public String createIndex(String schema, String table, IndexDefinition index) { public String createIndex(String schema, String table, IndexDefinition index) {
StatementBuilder buff = new StatementBuilder(); StatementBuilder buff = new StatementBuilder();
buff.append("CREATE "); buff.append("CREATE ");
switch(index.type) { switch(index.type) {
case STANDARD: case STANDARD:
break; break;
case UNIQUE: case UNIQUE:
buff.append("UNIQUE "); buff.append("UNIQUE ");
break; break;
case HASH: case HASH:
buff.append("HASH "); buff.append("HASH ");
break; break;
case UNIQUE_HASH: case UNIQUE_HASH:
buff.append("UNIQUE HASH "); buff.append("UNIQUE HASH ");
break; break;
} }
buff.append("INDEX IF NOT EXISTS "); buff.append("INDEX IF NOT EXISTS ");
buff.append(index.indexName); buff.append(index.indexName);
buff.append(" ON "); buff.append(" ON ");
buff.append(table); buff.append(table);
buff.append("("); buff.append("(");
for (String col:index.columnNames) { for (String col:index.columnNames) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(col); buff.append(col);
} }
buff.append(")"); buff.append(")");
return buff.toString(); return buff.toString();
} }
public void appendLimit(SQLStatement stat, long limit) { public void appendLimit(SQLStatement stat, long limit) {
stat.appendSQL(" LIMIT " + limit); stat.appendSQL(" LIMIT " + limit);
} }
public void appendOffset(SQLStatement stat, long offset) { public void appendOffset(SQLStatement stat, long offset) {
stat.appendSQL(" OFFSET " + offset); stat.appendSQL(" OFFSET " + offset);
} }
} }
} }
...@@ -38,7 +38,7 @@ public class SQLStatement { ...@@ -38,7 +38,7 @@ public class SQLStatement {
sql = null; sql = null;
return this; return this;
} }
public SQLStatement appendTable(String schema, String table) { public SQLStatement appendTable(String schema, String table) {
return appendSQL(db.getDialect().tableName(schema, table)); return appendSQL(db.getDialect().tableName(schema, table));
} }
...@@ -54,7 +54,7 @@ public class SQLStatement { ...@@ -54,7 +54,7 @@ public class SQLStatement {
params.add(o); params.add(o);
return this; return this;
} }
ResultSet executeQuery() { ResultSet executeQuery() {
try { try {
return prepare(false).executeQuery(); return prepare(false).executeQuery();
...@@ -74,7 +74,7 @@ public class SQLStatement { ...@@ -74,7 +74,7 @@ public class SQLStatement {
JdbcUtils.closeSilently(ps); JdbcUtils.closeSilently(ps);
} }
} }
long executeInsert() { long executeInsert() {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
...@@ -82,9 +82,10 @@ public class SQLStatement { ...@@ -82,9 +82,10 @@ public class SQLStatement {
ps.executeUpdate(); ps.executeUpdate();
long identity = -1; long identity = -1;
ResultSet rs = ps.getGeneratedKeys(); ResultSet rs = ps.getGeneratedKeys();
if (rs != null && rs.next()) if (rs != null && rs.next()) {
identity = rs.getLong(1); identity = rs.getLong(1);
JdbcUtils.closeSilently(rs); }
JdbcUtils.closeSilently(rs);
return identity; return identity;
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
...@@ -101,8 +102,8 @@ public class SQLStatement { ...@@ -101,8 +102,8 @@ public class SQLStatement {
} }
} }
private PreparedStatement prepare(boolean returnIdentity) { private PreparedStatement prepare(boolean returnGeneratedKeys) {
PreparedStatement prep = db.prepare(getSQL(), returnIdentity); PreparedStatement prep = db.prepare(getSQL(), returnGeneratedKeys);
for (int i = 0; i < params.size(); i++) { for (int i = 0; i < params.size(); i++) {
Object o = params.get(i); Object o = params.get(i);
setValue(prep, i + 1, o); setValue(prep, i + 1, o);
......
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: James Moger * Initial Developer: James Moger
*/ */
package org.h2.jaqu; package org.h2.jaqu;
/** /**
* This class represents "SET column = value" in an UPDATE statement. * This class represents "SET column = value" in an UPDATE statement.
* *
* @param <T> the query type * @param <T> the query type
* @param <A> the new value data type * @param <A> the new value data type
*/ */
//## Java 1.5 begin ## //## Java 1.5 begin ##
public class SetColumn<T, A> implements Declaration { public class SetColumn<T, A> implements Declaration {
private Query<T> query; private Query<T> query;
private A x; private A x;
private A y; private A y;
SetColumn(Query<T> query, A x) { SetColumn(Query<T> query, A x) {
this.query = query; this.query = query;
this.x = x; this.x = x;
} }
public Query<T> to(A y) { public Query<T> to(A y) {
query.addDeclarationToken(this); query.addDeclarationToken(this);
this.y = y; this.y = y;
return query; return query;
} }
public void appendSQL(SQLStatement stat) { public void appendSQL(SQLStatement stat) {
query.appendSQL(stat, x); query.appendSQL(stat, x);
stat.appendSQL("=?"); stat.appendSQL("=?");
stat.addParameter(y); stat.addParameter(y);
} }
} }
//## Java 1.5 end ## //## Java 1.5 end ##
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/ */
package org.h2.jaqu; package org.h2.jaqu;
...@@ -10,6 +11,9 @@ import java.lang.annotation.Retention; ...@@ -10,6 +11,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/**
* A class that implements this interface can be used as a database table.
*/
/** /**
* A class that implements the JaQu model mapping options. * A class that implements the JaQu model mapping options.
* <p> * <p>
...@@ -112,95 +116,93 @@ import java.lang.annotation.Target; ...@@ -112,95 +116,93 @@ import java.lang.annotation.Target;
* <p> * <p>
* You may automatically generate model classes as strings with the <i>Db</i> * You may automatically generate model classes as strings with the <i>Db</i>
* and <i>DbInspector</i> objects. * and <i>DbInspector</i> objects.
* *
* <pre> * <pre>
* Db db = Db.open(&quot;jdbc:h2:mem:&quot;, &quot;sa&quot;, &quot;sa&quot;); * Db db = Db.open(&quot;jdbc:h2:mem:&quot;, &quot;sa&quot;, &quot;sa&quot;);
* DbInspector inspector = new DbInspector(db); * DbInspector inspector = new DbInspector(db);
* List&lt;String&gt; models = inspector.generateModel(schema, table, * List&lt;String&gt; models =
* packageName, annotateSchema, trimStrings); * inspector.generateModel(schema, table, packageName,
* annotateSchema, trimStrings)
* </pre> * </pre>
* *
* OR you may use the <i>GenerateModels</i> tool to generate and save your * OR you may use the <i>GenerateModels</i> tool to generate and save your
* classes to the filesystem. * classes to the filesystem.
* *
* <pre> * <pre>
* java -cp h2jaqu.jar org.h2.jaqu.util.GenerateModels * java -cp h2jaqu.jar org.h2.jaqu.util.GenerateModels
* -url &quot;jdbc:h2:mem:&quot; * -url &quot;jdbc:h2:mem:&quot;
* -user sa -password sa -schema schemaName -table tableName * -user sa -password sa -schema schemaName -table tableName
* -package packageName -folder destination * -package packageName -folder destination
* -annotateSchema false -trimStrings true * -annotateSchema false -trimStrings true
* </pre> * </pre>
* *
* <b>Model Validation</b> * <b>Model Validation</b>
* <p> * <p>
* You may validate your model class with <i>DbInspector</i> object.<br> * You may validate your model class with <i>DbInspector</i> object.<br>
* The DbInspector will report ERRORS, WARNINGS, and SUGGESTIONS to help you. * The DbInspector will report ERRORS, WARNINGS, and
* * SUGGESTIONS to help you.
*
* <pre> * <pre>
* Db db = Db.open(&quot;jdbc:h2:mem:&quot;, &quot;sa&quot;, &quot;sa&quot;); * Db db = Db.open(&quot;jdbc:h2:mem:&quot;,
* &quot;sa&quot;, &quot;sa&quot;);
* DbInspector inspector = new DbInspector(db); * DbInspector inspector = new DbInspector(db);
* MyModel model = new MyModel(); * List&lt;Validation&gt; remarks =
* List&lt;Validation&gt; remarks = inspector.validateModel(model, throwOnError); * inspector.validateModel(new MyModel(), throwOnError);
* for (Validation remark : remarks) * for (Validation remark : remarks) {
* System.out.println(remark); * System.out.println(remark);
* }
* </pre> * </pre>
*/ */
public interface Table { public interface Table {
static final int reviewWholeClassAndJavadocs = 0;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface JQDatabase { public @interface JQDatabase {
/** /**
* If <b>version</b> is set to a <i>non-zero</i> value, JaQu will * If version is set to a non-zero value, JaQu will
* maintain a "_jq_versions" table within your database. The * maintain a "_jq_versions" table within your database. The
* <i>version</i> number will be used to call to a registered * version number will be used to call to a registered
* <i>DbUpgrader</i> implementation to perform relevant ALTERs or * DbUpgrader implementation to perform relevant ALTER statements.
* whatever. * Default: 0.
* <p> * You must specify a DbUpgrader on your Db object to
* <b>Default: <i>0</i></b>
* <p>
* <b>NOTE:</b><br>
* You must specify a <i>DbUpgrader</i> on your <i>Db</i> object to
* use this parameter. * use this parameter.
*/ */
int version() default 0; int version() default 0;
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface JQSchema { public @interface JQSchema {
/** /**
* <b>schema</b> may be optionally specified. If it is not specified the * The schema may be optionally specified. If it is not specified the
* schema will be ignored. * schema will be ignored. Default: Unspecified.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/ */
String name() default ""; String name() default "";
} }
/** /**
* Enumeration defining the 4 index types. * Enumeration defining the 4 index types.
*
*/ */
public static enum IndexType { public static enum IndexType {
STANDARD, UNIQUE, HASH, UNIQUE_HASH; STANDARD, UNIQUE, HASH, UNIQUE_HASH;
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface JQIndex { public @interface JQIndex {
/** /**
* <b>standard</b> indexes may be optionally specified. If not specified, * Standard indexes may be optionally specified. If not specified,
* these values will be ignored. * these values will be ignored.
* <ul> * <ul>
* <li>standard = "id, name" * <li>standard = "id, name"
* <li>standard = "id name" * <li>standard = "id name"
* <li>standard = { "id name", "date" } * <li>standard = { "id name", "date" }
* </ul> * </ul>
* Standard indexes may still be added in the <i>define()</i> method if * Standard indexes may still be added in the define() method if
* the model class is not annotated with JQTable. * the model class is not annotated with JQTable.
* <p> * Default: Unspecified.
* <b>Default: <i>Unspecified</i></b>
*/ */
String[] standard() default {}; String[] standard() default {};
...@@ -218,7 +220,7 @@ public interface Table { ...@@ -218,7 +220,7 @@ public interface Table {
* <b>Default: <i>Unspecified</i></b> * <b>Default: <i>Unspecified</i></b>
*/ */
String[] unique() default {}; String[] unique() default {};
/** /**
* <b>hash</b> indexes may be optionally specified. If not specified, * <b>hash</b> indexes may be optionally specified. If not specified,
* these values will be ignored. * these values will be ignored.
...@@ -232,7 +234,7 @@ public interface Table { ...@@ -232,7 +234,7 @@ public interface Table {
* <b>Default: <i>Unspecified</i></b> * <b>Default: <i>Unspecified</i></b>
*/ */
String[] hash() default {}; String[] hash() default {};
/** /**
* <b>uniqueHash</b> indexes may be optionally specified. If not specified, * <b>uniqueHash</b> indexes may be optionally specified. If not specified,
* these values will be ignored. * these values will be ignored.
...@@ -337,8 +339,8 @@ public interface Table { ...@@ -337,8 +339,8 @@ public interface Table {
* If <b>version</b> is set to a <i>non-zero</i> value, JaQu will * If <b>version</b> is set to a <i>non-zero</i> value, JaQu will
* maintain a "_jq_versions" table within your database. The * maintain a "_jq_versions" table within your database. The
* <i>version</i> number will be used to call to a registered * <i>version</i> number will be used to call to a registered
* <i>DbUpgrader</i> implementation to perform relevant ALTERs or * <i>DbUpgrader</i> implementation to perform relevant ALTER
* whatever. * statements.
* <p> * <p>
* <b>Default: <i>0</i></b> * <b>Default: <i>0</i></b>
* <p> * <p>
......
...@@ -227,7 +227,7 @@ class TableDefinition<T> { ...@@ -227,7 +227,7 @@ class TableDefinition<T> {
for (Field f : classFields) { for (Field f : classFields) {
// default to field name // default to field name
String columnName = f.getName(); String columnName = f.getName();
boolean isAutoIncrement = false; boolean isAutoIncrement = false;
boolean isPrimaryKey = false; boolean isPrimaryKey = false;
int maxLength = 0; int maxLength = 0;
......
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: James Moger * Initial Developer: James Moger
*/ */
package org.h2.jaqu; package org.h2.jaqu;
import static java.text.MessageFormat.format; import static java.text.MessageFormat.format;
import static org.h2.jaqu.Validation.CONSIDER; import static org.h2.jaqu.Validation.CONSIDER;
import static org.h2.jaqu.Validation.ERROR; import static org.h2.jaqu.Validation.ERROR;
import static org.h2.jaqu.Validation.WARN; import static org.h2.jaqu.Validation.WARN;
import static org.h2.jaqu.util.JdbcUtils.closeSilently; import static org.h2.jaqu.util.JdbcUtils.closeSilently;
import static org.h2.jaqu.util.StringUtils.isNullOrEmpty; import static org.h2.jaqu.util.StringUtils.isNullOrEmpty;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.h2.jaqu.Table.IndexType; import org.h2.jaqu.Table.IndexType;
import org.h2.jaqu.Table.JQColumn; import org.h2.jaqu.Table.JQColumn;
import org.h2.jaqu.Table.JQIndex; import org.h2.jaqu.Table.JQIndex;
import org.h2.jaqu.Table.JQSchema; import org.h2.jaqu.Table.JQSchema;
import org.h2.jaqu.Table.JQTable; import org.h2.jaqu.Table.JQTable;
import org.h2.jaqu.TableDefinition.FieldDefinition; import org.h2.jaqu.TableDefinition.FieldDefinition;
import org.h2.jaqu.TableDefinition.IndexDefinition; import org.h2.jaqu.TableDefinition.IndexDefinition;
import org.h2.jaqu.util.StatementBuilder; import org.h2.jaqu.util.StatementBuilder;
import org.h2.jaqu.util.Utils; import org.h2.jaqu.util.Utils;
/** /**
* Class to inspect the contents of a particular table including its indexes. * Class to inspect the contents of a particular table including its indexes.
* This class does the bulk of the work in terms of model generation and model * This class does the bulk of the work in terms of model generation and model
* validation. * validation.
*/ */
public class TableInspector { public class TableInspector {
private String schema; private final static int todoReviewClass = 0;
private String table;
private boolean forceUpperCase; private String schema;
private Class<? extends java.util.Date> dateClazz; private String table;
private List<String> primaryKeys = Utils.newArrayList(); private boolean forceUpperCase;
private Map<String, IndexInspector> indexes; private Class<? extends java.util.Date> dateClazz;
private Map<String, ColumnInspector> columns; private List<String> primaryKeys = Utils.newArrayList();
private final String eol = "\n"; private Map<String, IndexInspector> indexes;
private Map<String, ColumnInspector> columns;
private int todoReviewWholeClass; private final String eol = "\n";
TableInspector(String schema, String table, boolean forceUpperCase, TableInspector(String schema, String table, boolean forceUpperCase,
Class<? extends java.util.Date> dateClazz) { Class<? extends java.util.Date> dateClazz) {
this.schema = schema; this.schema = schema;
this.table = table; this.table = table;
this.forceUpperCase = forceUpperCase; this.forceUpperCase = forceUpperCase;
this.dateClazz = dateClazz; this.dateClazz = dateClazz;
} }
/** /**
* Tests to see if this TableInspector represents schema.table. * Tests to see if this TableInspector represents schema.table.
* <p> * <p>
* @param schema the schema name * @param schema the schema name
* @param table the table name * @param table the table name
* @return true if the table matches * @return true if the table matches
*/ */
boolean matches(String schema, String table) { boolean matches(String schema, String table) {
if (isNullOrEmpty(schema)) { if (isNullOrEmpty(schema)) {
// table name matching // table name matching
return this.table.equalsIgnoreCase(table); return this.table.equalsIgnoreCase(table);
} else if (isNullOrEmpty(table)) { } else if (isNullOrEmpty(table)) {
// schema name matching // schema name matching
return this.schema.equalsIgnoreCase(schema); return this.schema.equalsIgnoreCase(schema);
} else { } else {
// exact table matching // exact table matching
return this.schema.equalsIgnoreCase(schema) return this.schema.equalsIgnoreCase(schema)
&& this.table.equalsIgnoreCase(table); && this.table.equalsIgnoreCase(table);
} }
} }
/** /**
* Reads the DatabaseMetaData for the details of this table including * Reads the DatabaseMetaData for the details of this table including
* primary keys and indexes. * primary keys and indexes.
* *
* @param metaData the database meta data * @param metaData the database meta data
*/ */
void read(DatabaseMetaData metaData) throws SQLException { void read(DatabaseMetaData metaData) throws SQLException {
ResultSet rs = null; ResultSet rs = null;
// Primary Keys // primary keys
try { try {
rs = metaData.getPrimaryKeys(null, schema, table); rs = metaData.getPrimaryKeys(null, schema, table);
while (rs.next()) { while (rs.next()) {
String c = rs.getString("COLUMN_NAME"); String c = rs.getString("COLUMN_NAME");
primaryKeys.add(c); primaryKeys.add(c);
} }
closeSilently(rs); closeSilently(rs);
// Indexes // indexes
rs = metaData.getIndexInfo(null, schema, table, false, true); rs = metaData.getIndexInfo(null, schema, table, false, true);
indexes = Utils.newHashMap(); indexes = Utils.newHashMap();
while (rs.next()) { while (rs.next()) {
IndexInspector info = new IndexInspector(rs); IndexInspector info = new IndexInspector(rs);
if (info.type.equals(IndexType.UNIQUE) if (info.type.equals(IndexType.UNIQUE)
&& info.name.toLowerCase().startsWith("primary")) { && info.name.toLowerCase().startsWith("primary")) {
// Skip PrimaryKey indexes // skip primary key indexes
continue; continue;
} }
if (indexes.containsKey(info.name)) { if (indexes.containsKey(info.name)) {
indexes.get(info.name).addColumn(rs); indexes.get(info.name).addColumn(rs);
} else { } else {
indexes.put(info.name, info); indexes.put(info.name, info);
} }
} }
closeSilently(rs); closeSilently(rs);
// Columns // columns
rs = metaData.getColumns(null, schema, table, null); rs = metaData.getColumns(null, schema, table, null);
columns = Utils.newHashMap(); columns = Utils.newHashMap();
while (rs.next()) { while (rs.next()) {
ColumnInspector col = new ColumnInspector(); ColumnInspector col = new ColumnInspector();
col.name = rs.getString("COLUMN_NAME"); col.name = rs.getString("COLUMN_NAME");
col.type = rs.getString("TYPE_NAME"); col.type = rs.getString("TYPE_NAME");
col.clazz = ModelUtils.getClassType(col.type, dateClazz); col.clazz = ModelUtils.getClassType(col.type, dateClazz);
col.size = rs.getInt("COLUMN_SIZE"); col.size = rs.getInt("COLUMN_SIZE");
col.allowNull = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable;
// Allow Null col.isAutoIncrement = rs.getBoolean("IS_AUTOINCREMENT");
try { if (primaryKeys.size() == 1) {
col.allowNull = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable; if (col.name.equalsIgnoreCase(primaryKeys.get(0))) {
} catch (SQLException x) { col.isPrimaryKey = true;
} }
}
// AutoIncrement if (!col.isAutoIncrement) {
try { col.defaultValue = rs.getString("COLUMN_DEF");
col.isAutoIncrement = rs.getBoolean("IS_AUTOINCREMENT"); }
} catch (SQLException x) { columns.put(col.name, col);
} }
} finally {
// Primary Key closeSilently(rs);
if (primaryKeys.size() == 1) { }
if (col.name.equalsIgnoreCase(primaryKeys.get(0))) }
col.isPrimaryKey = true;
} /**
* Generates a model (class definition) from this table.
// Default Value * The model includes indexes, primary keys, default values, maxLengths,
if (!col.isAutoIncrement) { * and allowNull information.
try { * <p>
col.defaultValue = rs.getString("COLUMN_DEFAULT"); * The caller may optionally set a destination package name, whether or not
} catch (SQLException t) { * ot include the schema name (setting schema can be a problem when using
try { * the model between databases), and if to automatically trim strings for
col.defaultValue = rs.getString("COLUMN_DEF"); * those that have a maximum length.
} catch (SQLException x) { * <p>
} * @param packageName
} * @param annotateSchema
} * @param trimStrings
columns.put(col.name, col); * @return a complete model (class definition) for this table as a string
} */
} finally { String generateModel(String packageName, boolean annotateSchema,
closeSilently(rs); boolean trimStrings) {
}
} // import statements
Set<String> imports = Utils.newHashSet();
/** imports.add(JQSchema.class.getCanonicalName());
* Generates a model (class definition) from this table. imports.add(JQTable.class.getCanonicalName());
* The model includes indexes, primary keys, default values, maxLengths, imports.add(JQIndex.class.getCanonicalName());
* and allowNull information. imports.add(JQColumn.class.getCanonicalName());
* <p>
* The caller may optionally set a destination package name, whether or not // fields
* ot include the schema name (setting schema can be a problem when using StringBuilder fields = new StringBuilder();
* the model between databases), and if to automatically trim strings for List<ColumnInspector> sortedColumns = Utils.newArrayList(columns.values());
* those that have a maximum length. Collections.sort(sortedColumns);
* <p> for (ColumnInspector col : sortedColumns) {
* @param packageName fields.append(generateColumn(imports, col, trimStrings));
* @param annotateSchema }
* @param trimStrings
* @return a complete model (class definition) for this table as a string // build complete class definition
*/ StringBuilder model = new StringBuilder();
String generateModel(String packageName, boolean annotateSchema, if (!isNullOrEmpty(packageName)) {
boolean trimStrings) { // package
model.append("package " + packageName + ";");
// Set of imports model.append(eol).append(eol);
Set<String> imports = Utils.newHashSet(); }
imports.add(JQSchema.class.getCanonicalName());
imports.add(JQTable.class.getCanonicalName()); // imports
imports.add(JQIndex.class.getCanonicalName()); List<String> sortedImports = new ArrayList<String>(imports);
imports.add(JQColumn.class.getCanonicalName()); Collections.sort(sortedImports);
for (String imp : sortedImports) {
// Table Fields model.append("import ").append(imp).append(';').append(eol);
StringBuilder fields = new StringBuilder(); }
List<ColumnInspector> sortedColumns = Utils.newArrayList(columns.values()); model.append(eol);
Collections.sort(sortedColumns);
for (ColumnInspector col : sortedColumns) { // @JQSchema
fields.append(generateColumn(imports, col, trimStrings)); if (annotateSchema && !isNullOrEmpty(schema)) {
} model.append('@').append(JQSchema.class.getSimpleName());
model.append('(');
// Build Complete Class Definition AnnotationBuilder ap = new AnnotationBuilder();
StringBuilder model = new StringBuilder(); ap.addParameter("name", schema);
if (!isNullOrEmpty(packageName)) { model.append(ap);
// Package model.append(')').append(eol);
model.append("package " + packageName + ";"); }
model.append(eol).append(eol);
} // @JQTable
model.append('@').append(JQTable.class.getSimpleName());
// Imports model.append('(');
List<String> sortedImports = new ArrayList<String>(imports);
Collections.sort(sortedImports); // JQTable annotation parameters
for (String imp : sortedImports) { AnnotationBuilder ap = new AnnotationBuilder();
model.append("import ").append(imp).append(';').append(eol); ap.addParameter("name", table);
}
model.append(eol); if (primaryKeys.size() > 1) {
StringBuilder pk = new StringBuilder();
// @JQSchema for (String key : primaryKeys) {
if (annotateSchema && !isNullOrEmpty(schema)) { pk.append(key).append(' ');
model.append('@').append(JQSchema.class.getSimpleName()); }
model.append('('); pk.trimToSize();
AnnotationBuilder ap = new AnnotationBuilder(); ap.addParameter("primaryKey", pk.toString());
ap.addParameter("name", schema); }
model.append(ap);
model.append(')').append(eol); // finish @JQTable annotation
} model.append(ap);
model.append(')').append(eol);
// @JQTable
model.append('@').append(JQTable.class.getSimpleName()); // @JQIndex
model.append('('); ap = new AnnotationBuilder();
generateIndexAnnotations(ap, "standard", IndexType.STANDARD);
// JQTable Annotation Parameters generateIndexAnnotations(ap, "unique", IndexType.UNIQUE);
AnnotationBuilder ap = new AnnotationBuilder(); generateIndexAnnotations(ap, "hash", IndexType.HASH);
ap.addParameter("name", table); generateIndexAnnotations(ap, "uniqueHash", IndexType.UNIQUE_HASH);
if (ap.length() > 0) {
if (primaryKeys.size() > 1) { model.append('@').append(JQIndex.class.getSimpleName());
StringBuilder pk = new StringBuilder(); model.append('(');
for (String key : primaryKeys) { model.append(ap);
pk.append(key).append(' '); model.append(')').append(eol);
} }
pk.trimToSize();
ap.addParameter("primaryKey", pk.toString()); // class declaration
} String clazzName = ModelUtils.createClassName(table);
model.append(format("public class {0} '{'", clazzName)).append(eol);
// Finish @JQTable annotation model.append(eol);
model.append(ap);
model.append(')').append(eol); // field declarations
model.append(fields);
// @JQIndex
ap = new AnnotationBuilder(); // default constructor
generateIndexAnnotations(ap, "standard", IndexType.STANDARD); model.append("\t" + "public ").append(clazzName).append("() {").append(eol);
generateIndexAnnotations(ap, "unique", IndexType.UNIQUE); model.append("\t}").append(eol);
generateIndexAnnotations(ap, "hash", IndexType.HASH);
generateIndexAnnotations(ap, "uniqueHash", IndexType.UNIQUE_HASH); // end of class body
if (ap.length() > 0) { model.append('}');
model.append('@').append(JQIndex.class.getSimpleName()); model.trimToSize();
model.append('('); return model.toString();
model.append(ap); }
model.append(')').append(eol);
} /**
* Generates the specified index annotation.
// Class Declaration * @param ap
String clazzName = ModelUtils.createClassName(table); */
model.append(format("public class {0} '{'", clazzName)).append(eol); void generateIndexAnnotations(AnnotationBuilder ap, String parameter, IndexType type) {
model.append(eol); List<IndexInspector> list = getIndexes(type);
if (list.size() == 0) {
// Field Declarations // no matching indexes
model.append(fields); return;
}
// Default Constructor if (list.size() == 1) {
model.append("\tpublic ").append(clazzName).append("() {").append(eol); ap.addParameter(parameter, list.get(0).getColumnsString());
model.append("\t}").append(eol); } else {
List<String> parameters = Utils.newArrayList();
// End of Class Body for (IndexInspector index:list) {
model.append('}'); parameters.add(index.getColumnsString());
model.trimToSize(); }
return model.toString(); ap.addParameter(parameter, parameters);
} }
/** }
* Generates the specified index annotation.
* @param ap /**
*/ * Returns indexes of a specific type from the map.
void generateIndexAnnotations(AnnotationBuilder ap, String parameter, IndexType type) { * <p>
List<IndexInspector> list = getIndexes(type); * @param type
if (list.size() == 0) { * @return
// No matching indexes */
return; List<IndexInspector> getIndexes(IndexType type) {
} List<IndexInspector> list = Utils.newArrayList();
if (list.size() == 1) { for (IndexInspector index:indexes.values()) {
ap.addParameter(parameter, list.get(0).getColumnsString()); if (index.type.equals(type)) {
} else { list.add(index);
List<String> parameters = Utils.newArrayList(); }
for (IndexInspector index:list) { }
parameters.add(index.getColumnsString()); return list;
} }
ap.addParameter(parameter, parameters);
}
/**
} * Generates a column field definition with annotations.
* <p>
/** * @param imports
* Returns indexes of a specific type from the map. * @param col
* <p> * @param trimStrings
* @param type * @return
* @return */
*/ StatementBuilder generateColumn(Set<String> imports, ColumnInspector col,
List<IndexInspector> getIndexes(IndexType type) { boolean trimStrings) {
List<IndexInspector> list = Utils.newArrayList(); StatementBuilder sb = new StatementBuilder();
for (IndexInspector index:indexes.values()) { Class<?> clazz = col.clazz;
if (index.type.equals(type)) { String column = ModelUtils.createFieldName(col.name.toLowerCase());
list.add(index); sb.append('\t');
} if (clazz == null) {
} // unsupported type
return list; clazz = Object.class;
} sb.append("// unsupported type " + col.type);
} else {
// @JQColumn
/** imports.add(clazz.getCanonicalName());
* Generates a column field definition with annotations. sb.append('@').append(JQColumn.class.getSimpleName());
* <p>
* @param imports // JQColumn annotation parameters
* @param col AnnotationBuilder ap = new AnnotationBuilder();
* @param trimStrings
* @return // JQColumn.name
*/ if (!col.name.equalsIgnoreCase(column)) {
StatementBuilder generateColumn(Set<String> imports, ColumnInspector col, ap.addParameter("name", col.name);
boolean trimStrings) { }
StatementBuilder sb = new StatementBuilder();
Class<?> clazz = col.clazz; // JQColumn.primaryKey
String cname = ModelUtils.createFieldName(col.name.toLowerCase()); // composite primary keys are annotated on the table
sb.append('\t'); if (col.isPrimaryKey && primaryKeys.size() == 1) {
if (clazz == null) { ap.addParameter("primaryKey=true");
// Unsupported Type }
clazz = Object.class;
sb.append("// Unsupported type " + col.type); // JQColumn.maxLength
} else { if ((clazz == String.class) && (col.size > 0)
// @JQColumn && (col.size < Integer.MAX_VALUE)) {
imports.add(clazz.getCanonicalName()); ap.addParameter("maxLength", col.size);
sb.append('@').append(JQColumn.class.getSimpleName());
// JQColumn.trimStrings
// JQColumn Annotation Parameters if (trimStrings) {
AnnotationBuilder ap = new AnnotationBuilder(); ap.addParameter("trimString=true");
}
// JQColumn.name } else {
if (!col.name.equalsIgnoreCase(cname)) { // JQColumn.AutoIncrement
ap.addParameter("name", col.name); if (col.isAutoIncrement) {
} ap.addParameter("autoIncrement=true");
}
// JQColumn.primaryKey }
// Composite Primary Keys are annotated on the Table
if (col.isPrimaryKey && primaryKeys.size() == 1) { // JQColumn.allowNull
ap.addParameter("primaryKey=true"); if (!col.allowNull) {
} ap.addParameter("allowNull=false");
}
// JQColumn.maxLength
if ((clazz == String.class) && (col.size > 0) // JQColumn.defaultValue
&& (col.size < Integer.MAX_VALUE)) { if (!isNullOrEmpty(col.defaultValue)) {
ap.addParameter("maxLength", col.size); ap.addParameter("defaultValue=\"" + col.defaultValue + "\"");
}
// JQColumn.trimStrings
if (trimStrings) { // add leading and trailing ()
ap.addParameter("trimString=true"); if (ap.length() > 0) {
} ap.insert(0, '(');
} else { ap.append(')');
// JQColumn.AutoIncrement }
if (col.isAutoIncrement) { sb.append(ap);
ap.addParameter("autoIncrement=true"); }
} sb.append(eol);
}
// variable declaration
// JQColumn.allowNull sb.append("\tpublic ");
if (!col.allowNull) { sb.append(clazz.getSimpleName());
ap.addParameter("allowNull=false"); sb.append(' ');
} sb.append(column);
sb.append(';');
// JQColumn.defaultValue sb.append(eol).append(eol);
if (!isNullOrEmpty(col.defaultValue)) { return sb;
ap.addParameter("defaultValue=\"" + col.defaultValue + "\""); }
}
/**
// Add leading and trailing () * Validates that a table definition (annotated, interface, or both) matches
if (ap.length() > 0) { * the current state of the table and indexes in the database.
ap.insert(0, '('); * <p>
ap.append(')'); * Results are returned as a List&lt;Validation&gt; which includes
} * recommendations, warnings, and errors about the model.
sb.append(ap); * <p>
} * The caller may choose to have validate throw an exception on any
sb.append(eol); * validation ERROR.
* <p>
// Variable Declaration *
sb.append("\tpublic "); * @param <T>
sb.append(clazz.getSimpleName()); * @param def
sb.append(' '); * @param throwError
sb.append(cname); * @return List&lt;Validation&gt;
sb.append(';'); */
sb.append(eol).append(eol); <T> List<Validation> validate(TableDefinition<T> def,
return sb; boolean throwError) {
} List<Validation> remarks = Utils.newArrayList();
/** // model class definition validation
* Validates that a table definition (annotated, interface, or both) matches if (!Modifier.isPublic(def.getModelClass().getModifiers())) {
* the current state of the table and indexes in the database. remarks.add(ERROR(table, "SCHEMA",
* <p> format("Class {0} MUST BE PUBLIC!",
* Results are returned as a List&lt;Validation&gt; which includes def.getModelClass().getCanonicalName())).throwError(throwError));
* recommendations, warnings, and errors about the model. }
* <p>
* The caller may choose to have validate throw an exception on any validation // Schema Validation
* ERROR. if (!isNullOrEmpty(schema)) {
* <p> if (isNullOrEmpty(def.schemaName)) {
* @param <T> remarks.add(CONSIDER(table, "SCHEMA",
* @param def format("@{0}(name={1})",
* @param throwError JQSchema.class.getSimpleName(), schema)));
* @return List&lt;Validation&gt; } else if (!schema.equalsIgnoreCase(def.schemaName)) {
*/ remarks.add(ERROR(table, "SCHEMA",
<T> List<Validation> validate(TableDefinition<T> def, format("@{0}(name={1}) != {2}",
boolean throwError) { JQSchema.class.getSimpleName(), def.schemaName,
List<Validation> remarks = Utils.newArrayList(); schema)).throwError(throwError));
}
// Model Class Definition Validation }
if (!Modifier.isPublic(def.getModelClass().getModifiers())) {
remarks.add(ERROR(table, "SCHEMA", // index validation
format("Class {0} MUST BE PUBLIC!", for (IndexInspector index : indexes.values()) {
def.getModelClass().getCanonicalName())).throwError(throwError)); validate(remarks, def, index, throwError);
} }
// Schema Validation // field column validation
if (!isNullOrEmpty(schema)) { for (FieldDefinition fieldDef : def.getFields()) {
if (isNullOrEmpty(def.schemaName)) { validate(remarks, fieldDef, throwError);
remarks.add(CONSIDER(table, "SCHEMA", }
format("@{0}(name={1})", return remarks;
JQSchema.class.getSimpleName(), schema))); }
} else if (!schema.equalsIgnoreCase(def.schemaName)) {
remarks.add(ERROR(table, "SCHEMA", /**
format("@{0}(name={1}) != {2}", * Validates an inspected index from the database against the IndexDefinition
JQSchema.class.getSimpleName(), def.schemaName, * within the TableDefinition.
schema)).throwError(throwError)); * <p>
} * <b>TODO</b>: Complete index validation
} * <p>
* @param <T>
// Index Validation * @param remarks
for (IndexInspector index:indexes.values()) { * @param def
validate(remarks, def, index, throwError); * @param index
} * @param throwError
*/
// Field Column Validation <T> void validate(List<Validation> remarks, TableDefinition<T> def,
List<FieldDefinition> fieldDefs = def.getFields(); IndexInspector index, boolean throwError) {
for (FieldDefinition fieldDef : fieldDefs) { List<IndexDefinition> defIndexes = def.getIndexes(IndexType.STANDARD);
validate(remarks, fieldDef, throwError); List<IndexInspector> dbIndexes = getIndexes(IndexType.STANDARD);
} if (defIndexes.size() > dbIndexes.size()) {
return remarks; remarks.add(WARN(table, IndexType.STANDARD.name(), "# of Model Indexes > DB Indexes!"));
} } else if (defIndexes.size() < dbIndexes.size()) {
remarks.add(WARN(table, IndexType.STANDARD.name(), "Model class is missing indexes!"));
/** }
* Validates an inspected index from the database against the IndexDefinition // TODO complete index validation.
* within the TableDefinition. // Need to actually compare index types and columns within each index.
* <p> // At this point my head was starting to hurt.
* <b>TODO</b>: Complete index validation }
* <p>
* @param <T> /**
* @param remarks * Validates a column against the model's field definition. Checks for
* @param def * existence, supported type, type mapping, default value, defined lengths,
* @param index * primary key, autoincrement.
* @param throwError * <p>
*/ * @param remarks
<T> void validate(List<Validation> remarks, TableDefinition<T> def, * @param fieldDef
IndexInspector index, boolean throwError) { * @param throwError
List<IndexDefinition> defIndexes = def.getIndexes(IndexType.STANDARD); */
List<IndexInspector> dbIndexes = getIndexes(IndexType.STANDARD); void validate(List<Validation> remarks, FieldDefinition fieldDef,
if (defIndexes.size() > dbIndexes.size()) { boolean throwError) {
remarks.add(WARN(table, IndexType.STANDARD.name(), "# of Model Indexes > DB Indexes!")); // unknown field
} else if (defIndexes.size() < dbIndexes.size()) { String field = forceUpperCase ?
remarks.add(WARN(table, IndexType.STANDARD.name(), "Model class is missing indexes!")); fieldDef.columnName.toUpperCase() : fieldDef.columnName;
} if (!columns.containsKey(field)) {
// TODO Complete index validation. // unknown column mapping
// Need to actually compare index types and columns within each index. remarks.add(ERROR(table, fieldDef,
// At this point my head was starting to hurt. "Does not exist in database!").throwError(throwError));
} return;
}
/** ColumnInspector col = columns.get(field);
* Validates a column against the model's field definition. Checks for Class<?> fieldClazz = fieldDef.field.getType();
* existence, supported type, type mapping, default value, defined lengths, Class<?> jdbcClazz = ModelUtils.getClassType(col.type, dateClazz);
* primary key, autoincrement.
* <p> // supported type check
* @param remarks // JaQu maps to VARCHAR for unsupported types.
* @param fieldDef if (fieldDef.dataType.equals("VARCHAR")
* @param throwError && (fieldClazz != String.class)) {
*/ remarks.add(ERROR(table, fieldDef,
void validate(List<Validation> remarks, FieldDefinition fieldDef, "JaQu does not currently implement support for "
boolean throwError) { + fieldClazz.getName()).throwError(throwError));
// Unknown Field }
String fname = forceUpperCase ? // number types
fieldDef.columnName.toUpperCase() : fieldDef.columnName; if (!fieldClazz.equals(jdbcClazz)) {
if (!columns.containsKey(fname)) { if (Number.class.isAssignableFrom(fieldClazz)) {
// Unknown column mapping! remarks.add(WARN(table, col,
remarks.add(ERROR(table, fieldDef, format("Precision Mismatch: ModelObject={0}, ColumnObject={1}",
"Does not exist in database!").throwError(throwError)); fieldClazz.getSimpleName(), jdbcClazz.getSimpleName())));
return; } else {
} if (!Date.class.isAssignableFrom(jdbcClazz)) {
ColumnInspector col = columns.get(fname); remarks.add(WARN(table, col,
Class<?> fieldClazz = fieldDef.field.getType(); format("Object Mismatch: ModelObject={0}, ColumnObject={1}",
Class<?> jdbcClazz = ModelUtils.getClassType(col.type, dateClazz); fieldClazz.getSimpleName(), jdbcClazz.getSimpleName())));
}
// Supported Type Check }
// JaQu maps to VARCHAR for unsupported types. }
if (fieldDef.dataType.equals("VARCHAR")
&& (fieldClazz != String.class)) { // string types
remarks.add(ERROR(table, fieldDef, if (fieldClazz == String.class) {
"JaQu does not currently implement support for " if ((fieldDef.maxLength != col.size)
+ fieldClazz.getName()).throwError(throwError)); && (col.size < Integer.MAX_VALUE)) {
} remarks.add(WARN(table, col,
// Number Types format("{0}.maxLength={1}, ColumnMaxLength={2}",
if (!fieldClazz.equals(jdbcClazz)) { JQColumn.class.getSimpleName(),
if (Number.class.isAssignableFrom(fieldClazz)) { fieldDef.maxLength, col.size)));
remarks.add(WARN(table, col, }
format("Precision Mismatch: ModelObject={0}, ColumnObject={1}", if (fieldDef.maxLength > 0 && !fieldDef.trimString) {
fieldClazz.getSimpleName(), jdbcClazz.getSimpleName()))); remarks.add(CONSIDER(table, col,
} else { format("{0}.truncateToMaxLength=true"
if (!Date.class.isAssignableFrom(jdbcClazz)) { + " will prevent RuntimeExceptions on"
remarks.add(WARN(table, col, + " INSERT or UPDATE, but will clip data!",
format("Object Mismatch: ModelObject={0}, ColumnObject={1}", JQColumn.class.getSimpleName())));
fieldClazz.getSimpleName(), jdbcClazz.getSimpleName()))); }
} }
}
} // numeric autoIncrement
if (fieldDef.isAutoIncrement != col.isAutoIncrement) {
// String Types remarks.add(WARN(table, col, format("{0}.isAutoIncrement={1}"
if (fieldClazz == String.class) { + " while Column autoIncrement={2}",
if ((fieldDef.maxLength != col.size) JQColumn.class.getSimpleName(), fieldDef.isAutoIncrement,
&& (col.size < Integer.MAX_VALUE)) { col.isAutoIncrement)));
remarks.add(WARN(table, col, }
format("{0}.maxLength={1}, ColumnMaxLength={2}", // last check
JQColumn.class.getSimpleName(), // default value...
fieldDef.maxLength, col.size))); if (!col.isAutoIncrement && !col.isPrimaryKey) {
} // check Model.defaultValue format
if (fieldDef.maxLength > 0 && !fieldDef.trimString) { if (!ModelUtils.isProperlyFormattedDefaultValue(fieldDef.defaultValue)) {
remarks.add(CONSIDER(table, col, remarks.add(ERROR(table, col, format("{0}.defaultValue=\"{1}\""
format("{0}.truncateToMaxLength=true" + " is improperly formatted!",
+ " will prevent RuntimeExceptions on" JQColumn.class.getSimpleName(),
+ " INSERTs or UPDATEs, but will clip data!", fieldDef.defaultValue)).throwError(throwError));
JQColumn.class.getSimpleName()))); // next field
} return;
} }
// compare Model.defaultValue to Column.defaultValue
// Numeric AutoIncrement if (isNullOrEmpty(fieldDef.defaultValue)
if (fieldDef.isAutoIncrement != col.isAutoIncrement) { && !isNullOrEmpty(col.defaultValue)) {
remarks.add(WARN(table, col, format("{0}.isAutoIncrement={1}" // Model.defaultValue is NULL, Column.defaultValue is NOT NULL
+ " while Column autoIncrement={2}", remarks.add(WARN(table, col, format("{0}.defaultValue=\"\""
JQColumn.class.getSimpleName(), fieldDef.isAutoIncrement, + " while Column default=\"{1}\"",
col.isAutoIncrement))); JQColumn.class.getSimpleName(), col.defaultValue)));
} } else if (!isNullOrEmpty(fieldDef.defaultValue)
// Last Check && isNullOrEmpty(col.defaultValue)) {
// Default Value... // Column.defaultValue is NULL, Model.defaultValue is NOT NULL
if (!col.isAutoIncrement && !col.isPrimaryKey) { remarks.add(WARN(table, col, format("{0}.defaultValue=\"{1}\""
// Check Model.defaultValue Format + " while Column default=\"\"",
if (!ModelUtils.isProperlyFormattedDefaultValue(fieldDef.defaultValue)) { JQColumn.class.getSimpleName(), fieldDef.defaultValue)));
remarks.add(ERROR(table, col, format("{0}.defaultValue=\"{1}\"" } else if (!isNullOrEmpty(fieldDef.defaultValue)
+ " is improperly formatted!", && !isNullOrEmpty(col.defaultValue)) {
JQColumn.class.getSimpleName(), if (!fieldDef.defaultValue.equals(col.defaultValue)) {
fieldDef.defaultValue)).throwError(throwError)); // Model.defaultValue != Column.defaultValue
// Next field remarks.add(WARN(table, col, format("{0}.defaultValue=\"{1}\""
return; + " while Column default=\"{2}\"",
} JQColumn.class.getSimpleName(), fieldDef.defaultValue,
// Compare Model.defaultValue to Column.defaultValue col.defaultValue)));
if (isNullOrEmpty(fieldDef.defaultValue) }
&& !isNullOrEmpty(col.defaultValue)) { }
// Model.defaultValue is NULL, Column.defaultValue is NOT NULL
remarks.add(WARN(table, col, format("{0}.defaultValue=\"\"" // sanity check Model.defaultValue literal value
+ " while Column default=\"{1}\"", if (!ModelUtils.isValidDefaultValue(fieldDef.field.getType(),
JQColumn.class.getSimpleName(), col.defaultValue))); fieldDef.defaultValue)) {
} else if (!isNullOrEmpty(fieldDef.defaultValue) remarks.add(ERROR(table, col,
&& isNullOrEmpty(col.defaultValue)) { format("{0}.defaultValue=\"{1}\" is invalid!!",
// Column.defaultValue is NULL, Model.defaultValue is NOT NULL JQColumn.class.getSimpleName(),
remarks.add(WARN(table, col, format("{0}.defaultValue=\"{1}\"" fieldDef.defaultValue)));
+ " while Column default=\"\"", }
JQColumn.class.getSimpleName(), fieldDef.defaultValue))); }
} else if (!isNullOrEmpty(fieldDef.defaultValue) }
&& !isNullOrEmpty(col.defaultValue)) {
if (!fieldDef.defaultValue.equals(col.defaultValue)) { /**
// Model.defaultValue != Column.defaultValue * Represents an index as it exists in the database.
remarks.add(WARN(table, col, format("{0}.defaultValue=\"{1}\"" */
+ " while Column default=\"{2}\"", public static class IndexInspector {
JQColumn.class.getSimpleName(), fieldDef.defaultValue, String name;
col.defaultValue)));
} IndexType type;
}
private List<String> columns = new ArrayList<String>();
// Sanity Check Model.defaultValue Literal Value
if (!ModelUtils.isValidDefaultValue(fieldDef.field.getType(), public IndexInspector(ResultSet rs) throws SQLException {
fieldDef.defaultValue)) { name = rs.getString("INDEX_NAME");
remarks.add(ERROR(table, col,
format("{0}.defaultValue=\"{1}\" is invalid!!", // determine index type
JQColumn.class.getSimpleName(), boolean hash = rs.getInt("TYPE") == DatabaseMetaData.tableIndexHashed;
fieldDef.defaultValue))); boolean unique = !rs.getBoolean("NON_UNIQUE");
}
} if (!hash && !unique) {
} type = IndexType.STANDARD;
} else if (hash && unique) {
/** type = IndexType.UNIQUE_HASH;
* Represents an index as it exists in the database. } else if (unique) {
* type = IndexType.UNIQUE;
*/ } else if (hash) {
public static class IndexInspector { type = IndexType.HASH;
String name; }
columns.add(rs.getString("COLUMN_NAME"));
IndexType type; }
private List<String> columns = new ArrayList<String>(); public void addColumn(ResultSet rs) throws SQLException {
columns.add(rs.getString("COLUMN_NAME"));
public IndexInspector(ResultSet rs) throws SQLException { }
name = rs.getString("INDEX_NAME");
public String getColumnsString() {
// Determine Index Type StatementBuilder sb = new StatementBuilder();
boolean hash = rs.getInt("TYPE") == DatabaseMetaData.tableIndexHashed; for (String col : columns) {
boolean unique = !rs.getBoolean("NON_UNIQUE"); sb.appendExceptFirst(", ");
sb.append(col);
if (!hash && !unique) { }
type = IndexType.STANDARD; return sb.toString().trim();
} else if (hash && unique) { }
type = IndexType.UNIQUE_HASH; }
} else if (unique) {
type = IndexType.UNIQUE; /**
} else if (hash) { * Represents a column as it exists in the database.
type = IndexType.HASH; *
} */
columns.add(rs.getString("COLUMN_NAME")); public static class ColumnInspector implements Comparable<ColumnInspector> {
} String name;
String type;
public void addColumn(ResultSet rs) throws SQLException { int size;
columns.add(rs.getString("COLUMN_NAME")); boolean allowNull;
} Class<?> clazz;
boolean isPrimaryKey;
public String getColumnsString() { boolean isAutoIncrement;
StatementBuilder sb = new StatementBuilder(); String defaultValue;
for (String col : columns) {
sb.appendExceptFirst(", "); public int compareTo(ColumnInspector o) {
sb.append(col); if (isPrimaryKey && o.isPrimaryKey) {
} // both primary sort by name
return sb.toString().trim(); return name.compareTo(o.name);
} } else if (isPrimaryKey && !o.isPrimaryKey) {
} // primary first
return -1;
/** } else if (!isPrimaryKey && o.isPrimaryKey) {
* Represents a column as it exists in the database. // primary first
* return 1;
*/ } else {
public static class ColumnInspector implements Comparable<ColumnInspector> { // neither primary, sort by name
String name; return name.compareTo(o.name);
String type; }
int size; }
boolean allowNull; }
Class<?> clazz;
boolean isPrimaryKey; /**
boolean isAutoIncrement; * Convenience class based on StatementBuilder for creating the
String defaultValue; * annotation parameter list.
*
public int compareTo(ColumnInspector o) { */
if (isPrimaryKey && o.isPrimaryKey) { private static class AnnotationBuilder extends StatementBuilder {
// both primary sort by name AnnotationBuilder() {
return name.compareTo(o.name); super();
} else if (isPrimaryKey && !o.isPrimaryKey) { }
// primary first
return -1; void addParameter(String parameter) {
} else if (!isPrimaryKey && o.isPrimaryKey) { appendExceptFirst(", ");
// primary first append(parameter);
return 1; }
} else {
// Neither primary, sort by name <T> void addParameter(String parameter, T value) {
return name.compareTo(o.name); appendExceptFirst(", ");
} append(parameter);
} append('=');
} if (value instanceof List) {
append("{ ");
/** List list = (List) value;
* Convenience class based on StatementBuilder for creating the StatementBuilder flat = new StatementBuilder();
* annotation parameter list. for (Object o:list) {
* flat.appendExceptFirst(", ");
*/ if (o instanceof String) {
private static class AnnotationBuilder extends StatementBuilder { flat.append('\"');
AnnotationBuilder() { }
super(); int todoEscape;
} flat.append(o.toString().trim());
if (o instanceof String) {
void addParameter(String parameter) { flat.append('\"');
appendExceptFirst(", "); }
append(parameter); }
} append(flat);
append(" }");
<T> void addParameter(String parameter, T value) { } else {
appendExceptFirst(", "); if (value instanceof String) {
append(parameter); append('\"');
append('='); }
if (value instanceof List) { int todoEscape;
append("{ "); append(value.toString().trim());
List list = (List) value; if (value instanceof String) {
StatementBuilder flat = new StatementBuilder(); append('\"');
for (Object o:list) { }
flat.appendExceptFirst(", "); }
if (o instanceof String) { }
flat.append('\"'); }
}
int todoEscape;
flat.append(o.toString().trim());
if (o instanceof String) {
flat.append('\"');
}
}
append(flat);
append(" }");
} else {
if (value instanceof String) {
append('\"');
}
int todoEscape;
append(value.toString().trim());
if (value instanceof String) {
append('\"');
}
}
}
}
} }
\ No newline at end of file
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: James Moger * Initial Developer: James Moger
*/ */
package org.h2.jaqu; package org.h2.jaqu;
import org.h2.jaqu.TableDefinition.FieldDefinition; import org.h2.jaqu.TableDefinition.FieldDefinition;
import org.h2.jaqu.TableInspector.ColumnInspector; import org.h2.jaqu.TableInspector.ColumnInspector;
import org.h2.jaqu.util.StringUtils; import org.h2.jaqu.util.StringUtils;
/** /**
* A Validation Remark is a result of running a model validation. * A Validation Remark is a result of running a model validation.
* <p> * <p>
* Each remark has a level, associated component (schema, table, column, index), * Each remark has a level, associated component (schema, table, column, index),
* and a message. * and a message.
* *
*/ */
public class Validation { public class Validation {
public static Validation CONSIDER(String table, String type, String message) { private int todoReviewWholeClass;
return new Validation(Level.CONSIDER, table, type, message);
} public static Validation CONSIDER(String table, String type, String message) {
return new Validation(Level.CONSIDER, table, type, message);
public static Validation CONSIDER(String table, ColumnInspector col, String message) { }
return new Validation(Level.CONSIDER, table, col, message);
} public static Validation CONSIDER(String table, ColumnInspector col, String message) {
return new Validation(Level.CONSIDER, table, col, message);
public static Validation WARN(String table, ColumnInspector col, String message) { }
return new Validation(Level.WARN, table, col, message);
} public static Validation WARN(String table, ColumnInspector col, String message) {
return new Validation(Level.WARN, table, col, message);
public static Validation WARN(String table, String type, String message) { }
return new Validation(Level.WARN, table, type, message);
} public static Validation WARN(String table, String type, String message) {
return new Validation(Level.WARN, table, type, message);
public static Validation ERROR(String table, ColumnInspector col, String message) { }
return new Validation(Level.ERROR, table, col, message);
} public static Validation ERROR(String table, ColumnInspector col, String message) {
return new Validation(Level.ERROR, table, col, message);
public static Validation ERROR(String table, String type, String message) { }
return new Validation(Level.ERROR, table, type, message);
} public static Validation ERROR(String table, String type, String message) {
return new Validation(Level.ERROR, table, type, message);
public static Validation ERROR(String table, FieldDefinition field, String message) { }
return new Validation(Level.ERROR, table, field, message);
} public static Validation ERROR(String table, FieldDefinition field, String message) {
return new Validation(Level.ERROR, table, field, message);
public static enum Level { }
CONSIDER, WARN, ERROR;
} public static enum Level {
CONSIDER, WARN, ERROR;
Level level; }
String table;
String fieldType; Level level;
String fieldName; String table;
String message; String fieldType;
String fieldName;
private Validation(Level level, String table, String type, String message) { String message;
this.level = level;
this.table = table; private Validation(Level level, String table, String type, String message) {
this.fieldType = type; this.level = level;
this.fieldName = ""; this.table = table;
this.message = message; this.fieldType = type;
} this.fieldName = "";
this.message = message;
private Validation(Level level, String table, FieldDefinition field, String message) { }
this.level = level;
this.table = table; private Validation(Level level, String table, FieldDefinition field, String message) {
this.fieldType = field.dataType; this.level = level;
this.fieldName = field.columnName; this.table = table;
this.message = message; this.fieldType = field.dataType;
} this.fieldName = field.columnName;
this.message = message;
private Validation(Level level, String table, ColumnInspector col, String message) { }
this.level = level;
this.table = table; private Validation(Level level, String table, ColumnInspector col, String message) {
this.fieldType = col.type; this.level = level;
this.fieldName = col.name; this.table = table;
this.message = message; this.fieldType = col.type;
} this.fieldName = col.name;
this.message = message;
public Validation throwError(boolean throwOnError) { }
if (throwOnError && isError())
throw new RuntimeException(toString()); public Validation throwError(boolean throwOnError) {
return this; if (throwOnError && isError())
} throw new RuntimeException(toString());
return this;
public boolean isError() { }
return level.equals(Level.ERROR);
} public boolean isError() {
return level.equals(Level.ERROR);
public String toString() { }
StringBuilder sb = new StringBuilder();
sb.append(StringUtils.pad(level.name(), 9, " ", true)); public String toString() {
sb.append(StringUtils.pad(table, 25, " ", true)); StringBuilder sb = new StringBuilder();
sb.append(StringUtils.pad(fieldName, 20, " ", true)); sb.append(StringUtils.pad(level.name(), 9, " ", true));
sb.append(' '); sb.append(StringUtils.pad(table, 25, " ", true));
sb.append(message); sb.append(StringUtils.pad(fieldName, 20, " ", true));
return sb.toString(); sb.append(' ');
} sb.append(message);
return sb.toString();
public String toCSVString() { }
StringBuilder sb = new StringBuilder();
sb.append(level.name()).append(','); public String toCSVString() {
sb.append(table).append(','); StringBuilder sb = new StringBuilder();
sb.append(fieldType).append(','); sb.append(level.name()).append(',');
sb.append(fieldName).append(','); sb.append(table).append(',');
sb.append(message); sb.append(fieldType).append(',');
return sb.toString(); sb.append(fieldName).append(',');
} sb.append(message);
} return sb.toString();
}
}
...@@ -13,6 +13,8 @@ package org.h2.jaqu.util; ...@@ -13,6 +13,8 @@ package org.h2.jaqu.util;
*/ */
public class ClassUtils { public class ClassUtils {
int todoDelete;
private ClassUtils() { private ClassUtils() {
// utility class // utility class
} }
...@@ -31,5 +33,7 @@ public class ClassUtils { ...@@ -31,5 +33,7 @@ public class ClassUtils {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
//## Java 1.5 end ## //## Java 1.5 end ##
} }
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: James Moger * Initial Developer: James Moger
*/ */
package org.h2.jaqu.util; package org.h2.jaqu.util;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Writer; import java.io.Writer;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.h2.jaqu.Db; import org.h2.jaqu.Db;
import org.h2.jaqu.DbInspector; import org.h2.jaqu.DbInspector;
import org.h2.message.DbException; import org.h2.message.DbException;
/** /**
* Generates JaQu models. * Generates JaQu models.
*/ */
public class GenerateModels { public class GenerateModels {
/** private static final int todoReview = 0;
* The output stream where this tool writes to.
*/ /**
protected PrintStream out = System.out; * The output stream where this tool writes to.
*/
public static void main(String... args) throws SQLException { protected PrintStream out = System.out;
new GenerateModels().runTool(args);
} public static void main(String... args) throws SQLException {
new GenerateModels().runTool(args);
public void runTool(String... args) throws SQLException { }
String url = null;
String user = "sa"; public void runTool(String... args) throws SQLException {
String password = ""; String url = null;
String schema = null; String user = "sa";
String table = null; String password = "";
String packageName = ""; String schema = null;
String folder = null; String table = null;
boolean annotateSchema = true; String packageName = "";
boolean trimStrings = false; String folder = null;
for (int i = 0; args != null && i < args.length; i++) { boolean annotateSchema = true;
String arg = args[i]; boolean trimStrings = false;
if (arg.equals("-url")) { for (int i = 0; args != null && i < args.length; i++) {
url = args[++i]; String arg = args[i];
} else if (arg.equals("-user")) { if (arg.equals("-url")) {
user = args[++i]; url = args[++i];
} else if (arg.equals("-password")) { } else if (arg.equals("-user")) {
password = args[++i]; user = args[++i];
} else if (arg.equals("-schema")) { } else if (arg.equals("-password")) {
schema = args[++i]; password = args[++i];
} else if (arg.equals("-table")) { } else if (arg.equals("-schema")) {
table = args[++i]; schema = args[++i];
} else if (arg.equals("-package")) { } else if (arg.equals("-table")) {
packageName = args[++i]; table = args[++i];
} else if (arg.equals("-folder")) { } else if (arg.equals("-package")) {
folder = args[++i]; packageName = args[++i];
} else if (arg.equals("-annotateSchema")) { } else if (arg.equals("-folder")) {
try { folder = args[++i];
annotateSchema = Boolean.parseBoolean(args[++i]); } else if (arg.equals("-annotateSchema")) {
} catch (Throwable t) { try {
throw new SQLException("Can not parse -annotateSchema value"); annotateSchema = Boolean.parseBoolean(args[++i]);
} } catch (Throwable t) {
} else if (arg.equals("-trimStrings")) { throw new SQLException("Can not parse -annotateSchema value");
try { }
trimStrings = Boolean.parseBoolean(args[++i]); } else if (arg.equals("-trimStrings")) {
} catch (Throwable t) { try {
throw new SQLException("Can not parse -trimStrings value"); trimStrings = Boolean.parseBoolean(args[++i]);
} } catch (Throwable t) {
} else { throw new SQLException("Can not parse -trimStrings value");
throwUnsupportedOption(arg); }
} } else {
} throwUnsupportedOption(arg);
if (url == null) { }
throw new SQLException("URL not set"); }
} if (url == null) {
execute(url, user, password, schema, table, packageName, folder, throw new SQLException("URL not set");
annotateSchema, trimStrings); }
} execute(url, user, password, schema, table, packageName, folder,
annotateSchema, trimStrings);
/** }
* Generates models from the database.
* /**
* @param url the database URL * Generates models from the database.
* @param user the user name *
* @param password the password * @param url the database URL
* @param schema the schema to read from. null for all schemas. * @param user the user name
* @param table the table to model. null for all tables within schema. * @param password the password
* @param packageName the package name of the model classes. * @param schema the schema to read from. null for all schemas.
* @param folder destination folder for model classes (package path not included) * @param table the table to model. null for all tables within schema.
* @param annotateSchema includes the schema in the table model annotations * @param packageName the package name of the model classes.
* @param trimStrings automatically trim strings that exceed maxLength * @param folder destination folder for model classes (package path not included)
*/ * @param annotateSchema includes the schema in the table model annotations
public static void execute(String url, String user, String password, * @param trimStrings automatically trim strings that exceed maxLength
String schema, String table, String packageName, String folder, */
boolean annotateSchema, boolean trimStrings) public static void execute(String url, String user, String password,
throws SQLException { String schema, String table, String packageName, String folder,
Connection conn = null; boolean annotateSchema, boolean trimStrings)
try { throws SQLException {
org.h2.Driver.load(); Connection conn = null;
conn = DriverManager.getConnection(url, user, password); try {
Db db = Db.open(url, user, password.toCharArray()); org.h2.Driver.load();
DbInspector inspector = new DbInspector(db); conn = DriverManager.getConnection(url, user, password);
List<String> models = inspector.generateModel(schema, table, Db db = Db.open(url, user, password.toCharArray());
packageName, annotateSchema, trimStrings); DbInspector inspector = new DbInspector(db);
File parentFile; List<String> models = inspector.generateModel(schema, table,
if (StringUtils.isNullOrEmpty(folder)) packageName, annotateSchema, trimStrings);
parentFile = new File(System.getProperty("user.dir")); File parentFile;
else if (StringUtils.isNullOrEmpty(folder)) {
parentFile = new File(folder); parentFile = new File(System.getProperty("user.dir"));
parentFile.mkdirs(); } else {
Pattern p = Pattern.compile("class ([a-zA-Z0-9]+)"); parentFile = new File(folder);
for (String model : models) { }
Matcher m = p.matcher(model); parentFile.mkdirs();
if (m.find()) { Pattern p = Pattern.compile("class ([a-zA-Z0-9]+)");
String className = m.group().substring("class".length()).trim(); for (String model : models) {
File classFile = new File(parentFile, className + ".java"); Matcher m = p.matcher(model);
Writer o = new FileWriter(classFile, false); if (m.find()) {
PrintWriter writer = new PrintWriter(new BufferedWriter(o)); String className = m.group().substring("class".length()).trim();
writer.write(model); File classFile = new File(parentFile, className + ".java");
writer.close(); Writer o = new FileWriter(classFile, false);
System.out.println("Generated " + classFile.getAbsolutePath()); PrintWriter writer = new PrintWriter(new BufferedWriter(o));
} writer.write(model);
} writer.close();
} catch (IOException io) { System.out.println("Generated " + classFile.getAbsolutePath());
throw DbException.convertIOException(io, "could not generate model").getSQLException(); }
} finally { }
JdbcUtils.closeSilently(conn); } catch (IOException io) {
} throw DbException.convertIOException(io, "could not generate model").getSQLException();
} } finally {
JdbcUtils.closeSilently(conn);
/** }
* Throw a SQLException saying this command line option is not supported. }
*
* @param option the unsupported option /**
* @return this method never returns normally * Throw a SQLException saying this command line option is not supported.
*/ *
protected SQLException throwUnsupportedOption(String option) throws SQLException { * @param option the unsupported option
showUsage(); * @return this method never returns normally
throw new SQLException("Unsupported option: " + option); */
} protected SQLException throwUnsupportedOption(String option) throws SQLException {
showUsage();
protected void showUsage() { throw new SQLException("Unsupported option: " + option);
out.println("GenerateModels"); }
out.println("Usage: java "+getClass().getName());
out.println(); protected void showUsage() {
out.println("(*) -url jdbc:h2:~test"); out.println("GenerateModels");
out.println(" -user <string>"); out.println("Usage: java "+getClass().getName());
out.println(" -password <string>"); out.println();
out.println(" -schema <string>"); out.println("(*) -url jdbc:h2:~test");
out.println(" -table <string>"); out.println(" -user <string>");
out.println(" -package <string>"); out.println(" -password <string>");
out.println(" -folder <string>"); out.println(" -schema <string>");
out.println(" -annotateSchema <boolean>"); out.println(" -table <string>");
out.println(" -trimStrings <boolean>"); out.println(" -package <string>");
} out.println(" -folder <string>");
} out.println(" -annotateSchema <boolean>");
out.println(" -trimStrings <boolean>");
}
}
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.jaqu.util; package org.h2.jaqu.util;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Properties; import java.util.Properties;
import javax.naming.Context; import javax.naming.Context;
import javax.sql.DataSource; import javax.sql.DataSource;
import javax.sql.XAConnection; import javax.sql.XAConnection;
/** /**
* This is a utility class with JDBC helper functions. * This is a utility class with JDBC helper functions.
*/ */
public class JdbcUtils { public class JdbcUtils {
private static final String[] DRIVERS = { private static final int todoDeleteClass = 0;
"h2:", "org.h2.Driver",
"Cache:", "com.intersys.jdbc.CacheDriver", private static final String[] DRIVERS = {
"daffodilDB://", "in.co.daffodil.db.rmi.RmiDaffodilDBDriver", "h2:", "org.h2.Driver",
"daffodil", "in.co.daffodil.db.jdbc.DaffodilDBDriver", "Cache:", "com.intersys.jdbc.CacheDriver",
"db2:", "COM.ibm.db2.jdbc.net.DB2Driver", "daffodilDB://", "in.co.daffodil.db.rmi.RmiDaffodilDBDriver",
"derby:net:", "org.apache.derby.jdbc.ClientDriver", "daffodil", "in.co.daffodil.db.jdbc.DaffodilDBDriver",
"derby://", "org.apache.derby.jdbc.ClientDriver", "db2:", "COM.ibm.db2.jdbc.net.DB2Driver",
"derby:", "org.apache.derby.jdbc.EmbeddedDriver", "derby:net:", "org.apache.derby.jdbc.ClientDriver",
"FrontBase:", "com.frontbase.jdbc.FBJDriver", "derby://", "org.apache.derby.jdbc.ClientDriver",
"firebirdsql:", "org.firebirdsql.jdbc.FBDriver", "derby:", "org.apache.derby.jdbc.EmbeddedDriver",
"hsqldb:", "org.hsqldb.jdbcDriver", "FrontBase:", "com.frontbase.jdbc.FBJDriver",
"informix-sqli:", "com.informix.jdbc.IfxDriver", "firebirdsql:", "org.firebirdsql.jdbc.FBDriver",
"jtds:", "net.sourceforge.jtds.jdbc.Driver", "hsqldb:", "org.hsqldb.jdbcDriver",
"microsoft:", "com.microsoft.jdbc.sqlserver.SQLServerDriver", "informix-sqli:", "com.informix.jdbc.IfxDriver",
"mimer:", "com.mimer.jdbc.Driver", "jtds:", "net.sourceforge.jtds.jdbc.Driver",
"mysql:", "com.mysql.jdbc.Driver", "microsoft:", "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"odbc:", "sun.jdbc.odbc.JdbcOdbcDriver", "mimer:", "com.mimer.jdbc.Driver",
"oracle:", "oracle.jdbc.driver.OracleDriver", "mysql:", "com.mysql.jdbc.Driver",
"pervasive:", "com.pervasive.jdbc.v2.Driver", "odbc:", "sun.jdbc.odbc.JdbcOdbcDriver",
"pointbase:micro:", "com.pointbase.me.jdbc.jdbcDriver", "oracle:", "oracle.jdbc.driver.OracleDriver",
"pointbase:", "com.pointbase.jdbc.jdbcUniversalDriver", "pervasive:", "com.pervasive.jdbc.v2.Driver",
"postgresql:", "org.postgresql.Driver", "pointbase:micro:", "com.pointbase.me.jdbc.jdbcDriver",
"sybase:", "com.sybase.jdbc3.jdbc.SybDriver", "pointbase:", "com.pointbase.jdbc.jdbcUniversalDriver",
"sqlserver:", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "postgresql:", "org.postgresql.Driver",
"teradata:", "com.ncr.teradata.TeraDriver", "sybase:", "com.sybase.jdbc3.jdbc.SybDriver",
}; "sqlserver:", "com.microsoft.sqlserver.jdbc.SQLServerDriver",
"teradata:", "com.ncr.teradata.TeraDriver",
private JdbcUtils() { };
// utility class
} private JdbcUtils() {
// utility class
/** }
* Close a statement without throwing an exception.
* /**
* @param stat the statement or null * Close a statement without throwing an exception.
*/ *
public static void closeSilently(Statement stat) { * @param stat the statement or null
if (stat != null) { */
try { public static void closeSilently(Statement stat) {
stat.close(); if (stat != null) {
} catch (SQLException e) { try {
// ignore stat.close();
} } catch (SQLException e) {
} // ignore
} }
}
/** }
* Close a connection without throwing an exception.
* /**
* @param conn the connection or null * Close a connection without throwing an exception.
*/ *
public static void closeSilently(Connection conn) { * @param conn the connection or null
if (conn != null) { */
try { public static void closeSilently(Connection conn) {
conn.close(); if (conn != null) {
} catch (SQLException e) { try {
// ignore conn.close();
} } catch (SQLException e) {
} // ignore
} }
}
/** }
* Close a result set without throwing an exception.
* /**
* @param rs the result set or null * Close a result set without throwing an exception.
*/ *
public static void closeSilently(ResultSet rs) { * @param rs the result set or null
closeSilently(rs, false); */
} public static void closeSilently(ResultSet rs) {
closeSilently(rs, false);
/** }
* Close a result set, and optionally its statement without throwing an
* exception. /**
* * Close a result set, and optionally its statement without throwing an
* @param rs the result set or null * exception.
*/ *
public static void closeSilently(ResultSet rs, boolean closeStatement) { * @param rs the result set or null
if (rs != null) { */
Statement stat = null; public static void closeSilently(ResultSet rs, boolean closeStatement) {
if (closeStatement) { if (rs != null) {
try { Statement stat = null;
stat = rs.getStatement(); if (closeStatement) {
} catch (SQLException e) { try {
//ignore stat = rs.getStatement();
} } catch (SQLException e) {
} //ignore
try { }
rs.close(); }
} catch (SQLException e) { try {
// ignore rs.close();
} } catch (SQLException e) {
closeSilently(stat); // ignore
} }
} closeSilently(stat);
}
/** }
* Close an XA connection set without throwing an exception.
* /**
* @param conn the XA connection or null * Close an XA connection set without throwing an exception.
*/ *
//## Java 1.4 begin ## * @param conn the XA connection or null
public static void closeSilently(XAConnection conn) { */
if (conn != null) { //## Java 1.4 begin ##
try { public static void closeSilently(XAConnection conn) {
conn.close(); if (conn != null) {
} catch (SQLException e) { try {
// ignore conn.close();
} } catch (SQLException e) {
} // ignore
} }
//## Java 1.4 end ## }
}
/** //## Java 1.4 end ##
* Open a new database connection with the given settings.
* /**
* @param driver the driver class name * Open a new database connection with the given settings.
* @param url the database URL *
* @param user the user name * @param driver the driver class name
* @param password the password * @param url the database URL
* @return the database connection * @param user the user name
*/ * @param password the password
public static Connection getConnection(String driver, String url, String user, String password) throws SQLException { * @return the database connection
Properties prop = new Properties(); */
if (user != null) { public static Connection getConnection(String driver, String url, String user, String password) throws SQLException {
prop.setProperty("user", user); Properties prop = new Properties();
} if (user != null) {
if (password != null) { prop.setProperty("user", user);
prop.setProperty("password", password); }
} if (password != null) {
return getConnection(driver, url, prop); prop.setProperty("password", password);
} }
return getConnection(driver, url, prop);
/** }
* Escape table or schema patterns used for DatabaseMetaData functions.
* /**
* @param pattern the pattern * Escape table or schema patterns used for DatabaseMetaData functions.
* @return the escaped pattern *
*/ * @param pattern the pattern
public static String escapeMetaDataPattern(String pattern) { * @return the escaped pattern
if (pattern == null || pattern.length() == 0) { */
return pattern; public static String escapeMetaDataPattern(String pattern) {
} if (pattern == null || pattern.length() == 0) {
return StringUtils.replaceAll(pattern, "\\", "\\\\"); return pattern;
} }
return StringUtils.replaceAll(pattern, "\\", "\\\\");
/** }
* Open a new database connection with the given settings.
* /**
* @param driver the driver class name * Open a new database connection with the given settings.
* @param url the database URL *
* @param prop the properties containing at least the user name and password * @param driver the driver class name
* @return the database connection * @param url the database URL
*/ * @param prop the properties containing at least the user name and password
public static Connection getConnection(String driver, String url, Properties prop) throws SQLException { * @return the database connection
if (StringUtils.isNullOrEmpty(driver)) { */
JdbcUtils.load(url); public static Connection getConnection(String driver, String url, Properties prop) throws SQLException {
} else { if (StringUtils.isNullOrEmpty(driver)) {
Class<?> d = ClassUtils.loadClass(driver); JdbcUtils.load(url);
if (java.sql.Driver.class.isAssignableFrom(d)) { } else {
return DriverManager.getConnection(url, prop); Class<?> d = ClassUtils.loadClass(driver);
//## Java 1.4 begin ## if (java.sql.Driver.class.isAssignableFrom(d)) {
} else if (javax.naming.Context.class.isAssignableFrom(d)) { return DriverManager.getConnection(url, prop);
// JNDI context //## Java 1.4 begin ##
try { } else if (javax.naming.Context.class.isAssignableFrom(d)) {
Context context = (Context) d.newInstance(); // JNDI context
DataSource ds = (DataSource) context.lookup(url); try {
String user = prop.getProperty("user"); Context context = (Context) d.newInstance();
String password = prop.getProperty("password"); DataSource ds = (DataSource) context.lookup(url);
if (StringUtils.isNullOrEmpty(user) && StringUtils.isNullOrEmpty(password)) { String user = prop.getProperty("user");
return ds.getConnection(); String password = prop.getProperty("password");
} if (StringUtils.isNullOrEmpty(user) && StringUtils.isNullOrEmpty(password)) {
return ds.getConnection(user, password); return ds.getConnection();
} catch (Exception e) { }
throw Message.convert(e); return ds.getConnection(user, password);
} } catch (Exception e) {
//## Java 1.4 end ## throw Message.convert(e);
} else { }
// Don't know, but maybe it loaded a JDBC Driver //## Java 1.4 end ##
return DriverManager.getConnection(url, prop); } else {
} // Don't know, but maybe it loaded a JDBC Driver
} return DriverManager.getConnection(url, prop);
return DriverManager.getConnection(url, prop); }
} }
return DriverManager.getConnection(url, prop);
/** }
* Get the driver class name for the given URL, or null if the URL is
* unknown. /**
* * Get the driver class name for the given URL, or null if the URL is
* @param url the database URL * unknown.
* @return the driver class name *
*/ * @param url the database URL
public static String getDriver(String url) { * @return the driver class name
if (url.startsWith("jdbc:")) { */
url = url.substring("jdbc:".length()); public static String getDriver(String url) {
for (int i = 0; i < DRIVERS.length; i += 2) { if (url.startsWith("jdbc:")) {
String prefix = DRIVERS[i]; url = url.substring("jdbc:".length());
if (url.startsWith(prefix)) { for (int i = 0; i < DRIVERS.length; i += 2) {
return DRIVERS[i + 1]; String prefix = DRIVERS[i];
} if (url.startsWith(prefix)) {
} return DRIVERS[i + 1];
} }
return null; }
} }
return null;
/** }
* Load the driver class for the given URL, if the database URL is known.
* /**
* @param url the database URL * Load the driver class for the given URL, if the database URL is known.
*/ *
public static void load(String url) { * @param url the database URL
String driver = getDriver(url); */
if (driver != null) { public static void load(String url) {
ClassUtils.loadClass(driver); String driver = getDriver(url);
} if (driver != null) {
} ClassUtils.loadClass(driver);
}
} }
}
...@@ -18,6 +18,8 @@ import java.sql.SQLException; ...@@ -18,6 +18,8 @@ import java.sql.SQLException;
*/ */
public class Message { public class Message {
private int todoDelete;
private Message() { private Message() {
// utility class // utility class
} }
......
...@@ -30,6 +30,8 @@ package org.h2.jaqu.util; ...@@ -30,6 +30,8 @@ package org.h2.jaqu.util;
*/ */
public class StatementBuilder { public class StatementBuilder {
private int todoDelete;
private final StringBuilder builder = new StringBuilder(); private final StringBuilder builder = new StringBuilder();
private int index; private int index;
...@@ -113,11 +115,11 @@ public class StatementBuilder { ...@@ -113,11 +115,11 @@ public class StatementBuilder {
builder.append(s); builder.append(s);
} }
} }
public void append(StatementBuilder sb) { public void append(StatementBuilder sb) {
builder.append(sb); builder.append(sb);
} }
public void insert(int offset, char c) { public void insert(int offset, char c) {
builder.insert(offset, c); builder.insert(offset, c);
} }
......
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: James Moger * Initial Developer: James Moger
*/ */
package org.h2.jaqu.util; package org.h2.jaqu.util;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
/** /**
* Utility class to optionally log generated statements to an output stream.<br> * Utility class to optionally log generated statements to an output stream.<br>
* Default output stream is System.out.<br> * Default output stream is System.out.<br>
* Statement logging is disabled by default. * Statement logging is disabled by default.
* <p> * <p>
* This class also tracks the counts for generated statements by major type. * This class also tracks the counts for generated statements by major type.
* *
*/ */
public class StatementLogger { public class StatementLogger {
public static boolean logStatements = false; public static boolean logStatements;
private static PrintWriter out = new PrintWriter(System.out);
public static PrintWriter out = new PrintWriter(System.out); private static final AtomicLong SELECT_COUNT = new AtomicLong();
private static final AtomicLong CREATE_COUNT = new AtomicLong();
public final static AtomicLong selectCount = new AtomicLong(0); private static final AtomicLong INSERT_COUNT = new AtomicLong();
private static final AtomicLong UPDATE_COUNT = new AtomicLong();
public final static AtomicLong createCount = new AtomicLong(0); private static final AtomicLong MERGE_COUNT = new AtomicLong();
private static final AtomicLong DELETE_COUNT = new AtomicLong();
public final static AtomicLong insertCount = new AtomicLong(0);
public static void create(String statement) {
public final static AtomicLong updateCount = new AtomicLong(0); CREATE_COUNT.incrementAndGet();
log(statement);
public final static AtomicLong mergeCount = new AtomicLong(0); }
public final static AtomicLong deleteCount = new AtomicLong(0); public static void insert(String statement) {
INSERT_COUNT.incrementAndGet();
public static void create(String statement) { log(statement);
createCount.incrementAndGet(); }
log(statement);
} public static void update(String statement) {
UPDATE_COUNT.incrementAndGet();
public static void insert(String statement) { log(statement);
insertCount.incrementAndGet(); }
log(statement);
} public static void merge(String statement) {
MERGE_COUNT.incrementAndGet();
public static void update(String statement) { log(statement);
updateCount.incrementAndGet(); }
log(statement);
} public static void delete(String statement) {
DELETE_COUNT.incrementAndGet();
public static void merge(String statement) { log(statement);
mergeCount.incrementAndGet(); }
log(statement);
} public static void select(String statement) {
SELECT_COUNT.incrementAndGet();
public static void delete(String statement) { log(statement);
deleteCount.incrementAndGet(); }
log(statement);
} private static void log(String statement) {
if (logStatements) {
public static void select(String statement) { out.println(statement);
selectCount.incrementAndGet(); }
log(statement); }
}
public static void printStats() {
private static void log(String statement) { out.println("JaQu Runtime Statistics");
if (logStatements) out.println("=======================");
out.println(statement); printStat("CREATE", CREATE_COUNT);
} printStat("INSERT", INSERT_COUNT);
printStat("UPDATE", UPDATE_COUNT);
public static void printStats() { printStat("MERGE", MERGE_COUNT);
out.println("JaQu Runtime Stats"); printStat("DELETE", DELETE_COUNT);
out.println("======================="); printStat("SELECT", SELECT_COUNT);
printStat("CREATE", createCount); }
printStat("INSERT", insertCount);
printStat("UPDATE", updateCount); private static void printStat(String name, AtomicLong value) {
printStat("MERGE", mergeCount); if (value.get() > 0) {
printStat("DELETE", deleteCount); DecimalFormat df = new DecimalFormat("###,###,###,###");
printStat("SELECT", selectCount); out.println(name + "=" + df.format(CREATE_COUNT.get()));
} }
}
private static void printStat(String name, AtomicLong value) {
if (value.get() > 0) {
DecimalFormat df = new DecimalFormat("###,###,###,###");
out.println(name + "=" + df.format(createCount.get()));
}
}
} }
\ No newline at end of file
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.jaqu.util; package org.h2.jaqu.util;
/** /**
* Common string utilities. I expect that this class will be removed. * Common string utilities. I expect that this class will be removed.
* *
*/ */
public class StringUtils { public class StringUtils {
/** private int todoDelete;
* Replace all occurrences of the before string with the after string.
* /**
* @param s the string * Replace all occurrences of the before string with the after string.
* @param before the old text *
* @param after the new text * @param s the string
* @return the string with the before string replaced * @param before the old text
*/ * @param after the new text
public static String replaceAll(String s, String before, String after) { * @return the string with the before string replaced
int next = s.indexOf(before); */
if (next < 0) { public static String replaceAll(String s, String before, String after) {
return s; int next = s.indexOf(before);
} if (next < 0) {
StringBuilder buff = new StringBuilder(s.length() - before.length() + after.length()); return s;
int index = 0; }
while (true) { StringBuilder buff = new StringBuilder(s.length() - before.length() + after.length());
buff.append(s.substring(index, next)).append(after); int index = 0;
index = next + before.length(); while (true) {
next = s.indexOf(before, index); buff.append(s.substring(index, next)).append(after);
if (next < 0) { index = next + before.length();
buff.append(s.substring(index)); next = s.indexOf(before, index);
break; if (next < 0) {
} buff.append(s.substring(index));
} break;
return buff.toString(); }
} }
return buff.toString();
/** }
* Check if a String is null or empty (the length is null).
* /**
* @param s the string to check * Check if a String is null or empty (the length is null).
* @return true if it is null or empty *
*/ * @param s the string to check
public static boolean isNullOrEmpty(String s) { * @return true if it is null or empty
return s == null || s.length() == 0; */
} public static boolean isNullOrEmpty(String s) {
return s == null || s.length() == 0;
/** }
* Convert a string to a Java literal using the correct escape sequences.
* The literal is not enclosed in double quotes. The result can be used in /**
* properties files or in Java source code. * Convert a string to a Java literal using the correct escape sequences.
* * The literal is not enclosed in double quotes. The result can be used in
* @param s the text to convert * properties files or in Java source code.
* @return the Java representation *
*/ * @param s the text to convert
public static String javaEncode(String s) { * @return the Java representation
int length = s.length(); */
StringBuilder buff = new StringBuilder(length); public static String javaEncode(String s) {
for (int i = 0; i < length; i++) { int length = s.length();
char c = s.charAt(i); StringBuilder buff = new StringBuilder(length);
switch (c) { for (int i = 0; i < length; i++) {
// case '\b': char c = s.charAt(i);
// // BS backspace switch (c) {
// // not supported in properties files // case '\b':
// buff.append("\\b"); // // BS backspace
// break; // // not supported in properties files
case '\t': // buff.append("\\b");
// HT horizontal tab // break;
buff.append("\\t"); case '\t':
break; // HT horizontal tab
case '\n': buff.append("\\t");
// LF linefeed break;
buff.append("\\n"); case '\n':
break; // LF linefeed
case '\f': buff.append("\\n");
// FF form feed break;
buff.append("\\f"); case '\f':
break; // FF form feed
case '\r': buff.append("\\f");
// CR carriage return break;
buff.append("\\r"); case '\r':
break; // CR carriage return
case '"': buff.append("\\r");
// double quote break;
buff.append("\\\""); case '"':
break; // double quote
case '\\': buff.append("\\\"");
// backslash break;
buff.append("\\\\"); case '\\':
break; // backslash
default: buff.append("\\\\");
int ch = c & 0xffff; break;
if (ch >= ' ' && (ch < 0x80)) { default:
buff.append(c); int ch = c & 0xffff;
// not supported in properties files if (ch >= ' ' && (ch < 0x80)) {
// } else if(ch < 0xff) { buff.append(c);
// buff.append("\\"); // not supported in properties files
// // make sure it's three characters (0x200 is octal 1000) // } else if(ch < 0xff) {
// buff.append(Integer.toOctalString(0x200 | // buff.append("\\");
// ch).substring(1)); // // make sure it's three characters (0x200 is octal 1000)
} else { // buff.append(Integer.toOctalString(0x200 |
buff.append("\\u"); // ch).substring(1));
// make sure it's four characters } else {
buff.append(Integer.toHexString(0x10000 | ch).substring(1)); buff.append("\\u");
} // make sure it's four characters
} buff.append(Integer.toHexString(0x10000 | ch).substring(1));
} }
return buff.toString(); }
} }
return buff.toString();
/** }
* Pad a string. This method is used for the SQL function RPAD and LPAD.
* /**
* @param string the original string * Pad a string. This method is used for the SQL function RPAD and LPAD.
* @param n the target length *
* @param padding the padding string * @param string the original string
* @param right true if the padding should be appended at the end * @param n the target length
* @return the padded string * @param padding the padding string
*/ * @param right true if the padding should be appended at the end
public static String pad(String string, int n, String padding, boolean right) { * @return the padded string
if (n < 0) { */
n = 0; public static String pad(String string, int n, String padding, boolean right) {
} if (n < 0) {
if (n < string.length()) { n = 0;
return string.substring(0, n); }
} else if (n == string.length()) { if (n < string.length()) {
return string; return string.substring(0, n);
} } else if (n == string.length()) {
char paddingChar; return string;
if (padding == null || padding.length() == 0) { }
paddingChar = ' '; char paddingChar;
} else { if (padding == null || padding.length() == 0) {
paddingChar = padding.charAt(0); paddingChar = ' ';
} } else {
StringBuilder buff = new StringBuilder(n); paddingChar = padding.charAt(0);
n -= string.length(); }
if (right) { StringBuilder buff = new StringBuilder(n);
buff.append(string); n -= string.length();
} if (right) {
for (int i = 0; i < n; i++) { buff.append(string);
buff.append(paddingChar); }
} for (int i = 0; i < n; i++) {
if (!right) { buff.append(paddingChar);
buff.append(string); }
} if (!right) {
return buff.toString(); buff.append(string);
} }
return buff.toString();
/** }
* Convert a string to a SQL literal. Null is converted to NULL. The text is
* enclosed in single quotes. If there are any special characters, the method /**
* STRINGDECODE is used. * Convert a string to a SQL literal. Null is converted to NULL. The text is
* * enclosed in single quotes. If there are any special characters, the method
* @param s the text to convert. * STRINGDECODE is used.
* @return the SQL literal *
*/ * @param s the text to convert.
public static String quoteStringSQL(String s) { * @return the SQL literal
if (s == null) { */
return "NULL"; public static String quoteStringSQL(String s) {
} if (s == null) {
int length = s.length(); return "NULL";
StringBuilder buff = new StringBuilder(length + 2); }
buff.append('\''); int length = s.length();
for (int i = 0; i < length; i++) { StringBuilder buff = new StringBuilder(length + 2);
char c = s.charAt(i); buff.append('\'');
if (c == '\'') { for (int i = 0; i < length; i++) {
buff.append(c); char c = s.charAt(i);
} else if (c < ' ' || c > 127) { if (c == '\'') {
// need to start from the beginning because maybe there was a \ buff.append(c);
// that was not quoted } else if (c < ' ' || c > 127) {
return "STRINGDECODE(" + quoteStringSQL(javaEncode(s)) + ")"; // need to start from the beginning because maybe there was a \
} // that was not quoted
buff.append(c); return "STRINGDECODE(" + quoteStringSQL(javaEncode(s)) + ")";
} }
buff.append('\''); buff.append(c);
return buff.toString(); }
} buff.append('\'');
return buff.toString();
} }
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论