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