Initial version of 'lightweight' GWT ORM
This is a "lightweight" (I use the term loosely since its actually
close to 3000 lines of Java code) ORM implementation designed for
use against both JDBC and a local "offline" browser database such
as that offered by Google Gears or HTML 5.
The following 3rd party libraries are also included:
* ObjectWeb ASM 3.1
* ANTLR 3.1.1
* H2 2008-11-07
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..28ff882
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="test"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/GWT"/>
+ <classpathentry kind="lib" path="lib/jdbc-h2.jar"/>
+ <classpathentry kind="lib" path="lib/antlr.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+ <classpathentry kind="lib" path="lib/asm.jar" sourcepath="lib/asm_src.zip"/>
+ <classpathentry kind="output" path="classes"/>
+</classpath>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bbe8a66
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+/.bin
+/classes
+/lib/*_src.zip
+/lib/gwtorm.jar
+/config.mak
+/src/com/google/gwtorm/schema/Query.tokens
+/src/com/google/gwtorm/schema/QueryLexer.java
+/src/com/google/gwtorm/schema/QueryParser.java
+/src/com/google/gwtorm/schema/Query__.g
diff --git a/.project b/.project
new file mode 100644
index 0000000..7c95189
--- /dev/null
+++ b/.project
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<projectDescription>
+ <name>GwtOrm</name>
+ <comment>GWT-ORM</comment>
+ <projects/>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments/>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..82eb859
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..8667cfd
--- /dev/null
+++ b/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..059dd5a
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Fri Nov 14 18:42:47 PST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..f37f6f0
--- /dev/null
+++ b/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,9 @@
+#Tue Sep 02 17:00:18 PDT 2008
+eclipse.preferences.version=1
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/GwtOrm-AllTests.launch b/GwtOrm-AllTests.launch
new file mode 100644
index 0000000..7c5ef6d
--- /dev/null
+++ b/GwtOrm-AllTests.launch
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/GwtOrm"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=GwtOrm"/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="GwtOrm"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgwtorm.debugCodeGen=true"/>
+</launchConfiguration>
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..80c0b97
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,78 @@
+# Lightweight ORM for Google Web Toolkit
+#
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Define GWT_SDK to the location of the Google Web Toolkit SDK.
+#
+# Define GWT_OS to your operating system, e.g. 'linux', 'mac'.
+#
+
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+
+JAVA = java
+JAVAC = javac
+GWT_OS = unknown
+
+ifeq ($(uname_S),Darwin)
+ GWT_OS = mac
+endif
+ifeq ($(uname_S),Linux)
+ GWT_OS = linux
+endif
+ifeq ($(uname_S),Cygwin)
+ GWT_OS = win
+endif
+
+-include config.mak
+
+GWT_CP = $(GWT_SDK)/gwt-user.jar:$(GWT_SDK)/gwt-dev-$(GWT_OS).jar
+OUR_CP = ../lib/antlr.jar:../lib/asm.jar
+MY_JAR = lib/gwtorm.jar
+MY_JAVA = $(shell find src -name \*.java)
+MY_GWT_XML = com/google/gwtorm/GWTORM.gwt.xml
+
+QUERY_G = src/com/google/gwtorm/schema/Query.g
+QUERY_JAVA = src/com/google/gwtorm/schema/QueryParser.java
+
+all: $(MY_JAR)
+
+clean:
+ rm -f $(MY_JAR)
+ rm -f $(QUERY_JAVA)
+ rm -f src/com/google/gwtorm/schema/Query.tokens
+ rm -f src/com/google/gwtorm/schema/QueryLexer.java
+ rm -f src/com/google/gwtorm/schema/Query__.g
+ rm -rf classes .bin
+
+$(QUERY_JAVA): $(QUERY_G)
+ $(JAVA) -cp lib/antlr.jar org.antlr.Tool $(QUERY_G)
+
+$(MY_JAR): $(MY_JAVA) src/$(MY_GWT_XML) $(QUERY_JAVA)
+ rm -rf .bin
+ mkdir .bin
+ cd src && $(JAVAC) \
+ -encoding utf-8 \
+ -source 1.5 \
+ -target 1.5 \
+ -g \
+ -cp $(GWT_CP):$(OUR_CP) \
+ -d ../.bin \
+ $(patsubst src/%,%,$(MY_JAVA))
+ cd .bin && jar cf ../$(MY_JAR) .
+ cd src && jar uf ../$(MY_JAR) .
+ rm -rf .bin
+
+.PHONY: all
+.PHONY: clean
diff --git a/README_ECLIPSE b/README_ECLIPSE
new file mode 100644
index 0000000..a0da056
--- /dev/null
+++ b/README_ECLIPSE
@@ -0,0 +1,21 @@
+Eclipse Setup
+=============
+
+Compile once using "make" to ensure the ANTLR parser is generated.
+
+
+User Library
+------------
+
+ Window > Preferences
+ Java > Build Path > User Libraries
+
+Create a User Library called "GWT":
+
+ New
+
+ Name: GWT
+ Add JARs...
+
+ * Select gwt-user.jar from the $(GWT_SDK) directory.
+ * Select gwt-dev-$(GWT_OS).jar from the $(GWT_SDK) directory.
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..f852cd5
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,2 @@
+/gwtorm.jar
+/*_src.zip
diff --git a/lib/antlr.jar b/lib/antlr.jar
new file mode 100644
index 0000000..da03a67
--- /dev/null
+++ b/lib/antlr.jar
Binary files differ
diff --git a/lib/antlr_LICENSE b/lib/antlr_LICENSE
new file mode 100644
index 0000000..6cfe9b0
--- /dev/null
+++ b/lib/antlr_LICENSE
@@ -0,0 +1,29 @@
+ANTLR 3.1.1
+-----------
+
+[The "BSD licence"]
+Copyright (c) 2003-2008 Terence Parr
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/asm.jar b/lib/asm.jar
new file mode 100644
index 0000000..b3baf3f
--- /dev/null
+++ b/lib/asm.jar
Binary files differ
diff --git a/lib/asm_LICENSE b/lib/asm_LICENSE
new file mode 100644
index 0000000..2b89056
--- /dev/null
+++ b/lib/asm_LICENSE
@@ -0,0 +1,30 @@
+ ObjectWeb ASM 3.1
+ -----------------
+
+ ASM: a very small and fast Java bytecode manipulation framework
+ Copyright (c) 2000-2005 INRIA, France Telecom
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/jdbc-h2.jar b/lib/jdbc-h2.jar
new file mode 100644
index 0000000..e9f2d40
--- /dev/null
+++ b/lib/jdbc-h2.jar
Binary files differ
diff --git a/lib/jdbc-h2_LICENSE.html b/lib/jdbc-h2_LICENSE.html
new file mode 100644
index 0000000..a6c443e
--- /dev/null
+++ b/lib/jdbc-h2_LICENSE.html
@@ -0,0 +1,703 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!-- H2 Database 2008-11-07 -->
+<!--
+Copyright 2004-2008 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
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
+License
+</title><link rel="stylesheet" type="text/css" href="stylesheet.css" />
+<!-- [search] { -->
+<script type="text/javascript" src="navigation.js"></script>
+</head><body onload="frameMe();">
+<table class="content"><tr class="content"><td class="content"><div class="contentDiv">
+<!-- } -->
+
+<h1>License</h1>
+
+<h2>Summary and License FAQ</h2>
+<p>
+H2 is dual licensed and available under a modified version of the
+MPL 1.1 (<a href="http://www.mozilla.org/MPL">Mozilla Public License</a>)
+or EPL 1.0 (<a href="http://opensource.org/licenses/eclipse-1.0.php">Eclipse Public License</a>).
+The changes are <em class="u">underlined</em>.
+There is a License FAQ for both the MPL and the EPL, most of that is applicable to the H2 License as well.
+</p>
+<ul>
+<li>You can use H2 for free. You can integrate it into your application (including commercial applications),
+ and you can distribute it.
+</li><li>Files containing only your code are not covered by this license (it is 'commercial friendly').
+</li><li>Modifications to the H2 source code must be published.
+</li><li>You don't need to provide the source code of H2 if you did not modify anything.
+</li></ul>
+
+<p>
+However, nobody is allowed to rename H2, modify it a little, and sell it as a database engine without telling the customers it is in fact H2.
+This happened to HSQLDB, when a company called 'bungisoft' copied HSQLDB, renamed it to 'RedBase', and tried to sell it,
+hiding the fact that it was, in fact, just HSQLDB. At this time, it seems 'bungisoft' does not exist any more, but you can use the
+Wayback Machine of http://www.archive.org and look for old web pages of http://www.bungisoft.com .
+</p><p>
+About porting the source code to another language (for example C# or C++): Converted source code (even if done manually) stays under the same
+copyright and license as the original code. The copyright of the ported source code does not (automatically) go to the person who ported the code.
+</p>
+
+<h2>H2 License, Version 1.0</h2>
+
+ <h3 id="section-1">1. Definitions</h3>
+ <p id="section-1.0.1"><b>1.0.1. "Commercial Use"</b>
+ means distribution or otherwise making the Covered Code available to a third party.
+ </p>
+ <p id="section-1.1"><b>1.1. "Contributor"</b>
+ means each entity that creates or contributes to the creation of Modifications.
+ </p>
+ <p id="section-1.2"><b>1.2. "Contributor Version"</b>
+ means the combination of the Original Code, prior Modifications used by a Contributor,
+ and the Modifications made by that particular Contributor.
+ </p>
+ <p id="section-1.3"><b>1.3. "Covered Code"</b>
+ means the Original Code or Modifications or the combination of the Original Code and
+ Modifications, in each case including portions thereof.
+ </p>
+ <p id="section-1.4"><b>1.4. "Electronic Distribution Mechanism"</b>
+ means a mechanism generally accepted in the software development community for the
+ electronic transfer of data.
+ </p>
+ <p id="section-1.5"><b>1.5. "Executable"</b>
+ means Covered Code in any form other than Source Code.
+ </p>
+ <p id="section-1.6"><b>1.6. "Initial Developer"</b>
+ means the individual or entity identified as the Initial Developer in the Source Code
+ notice required by <a href="#exhibit-a">Exhibit A</a>.
+ </p>
+ <p id="section-1.7"><b>1.7. "Larger Work"</b>
+ means a work which combines Covered Code or portions thereof with code not governed
+ by the terms of this License.
+ </p>
+ <p id="section-1.8"><b>1.8. "License"</b>
+ means this document.
+ </p>
+ <p id="section-1.8.1"><b>1.8.1. "Licensable"</b>
+ means having the right to grant, to the maximum extent possible, whether at the
+ time of the initial grant or subsequently acquired, any and all of the rights
+ conveyed herein.
+ </p>
+ <p id="section-1.9"><b>1.9. "Modifications"</b>
+ means any addition to or deletion from the substance or structure of either the
+ Original Code or any previous Modifications. When Covered Code is released as a
+ series of files, a Modification is:
+ </p>
+ <p id="section-1.9-a">1.9.a. Any addition to or deletion from the contents of a file
+ containing Original Code or previous Modifications.
+ </p>
+ <p id="section-1.9-b">1.9.b. Any new file that contains any part of the Original Code or
+ previous Modifications.
+ </p>
+ <p id="section-1.10"><b>1.10. "Original Code"</b>
+ means Source Code of computer software code which is described in the Source Code
+ notice required by <a href="#exhibit-a">Exhibit A</a> as Original Code, and which,
+ at the time of its release under this License is not already Covered Code governed
+ by this License.
+ </p>
+ <p id="section-1.10.1"><b>1.10.1. "Patent Claims"</b>
+ means any patent claim(s), now owned or hereafter acquired, including without
+ limitation, method, process, and apparatus claims, in any patent Licensable by
+ grantor.
+ </p>
+ <p id="section-1.11"><b>1.11. "Source Code"</b>
+ means the preferred form of the Covered Code for making modifications to it,
+ including all modules it contains, plus any associated interface definition files,
+ scripts used to control compilation and installation of an Executable, or source
+ code differential comparisons against either the Original Code or another well known,
+ available Covered Code of the Contributor's choice. The Source Code can be in a
+ compressed or archival form, provided the appropriate decompression or de-archiving
+ software is widely available for no charge.
+ </p>
+ <p id="section-1.12"><b>1.12. "You" (or "Your")</b>
+ means an individual or a legal entity exercising rights under, and complying with
+ all of the terms of, this License or a future version of this License issued under
+ <a href="#section-6.1">Section 6.1.</a> For legal entities, "You" includes any entity
+ which controls, is controlled by, or is under common control with You. For purposes of
+ this definition, "control" means (a) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or otherwise, or (b)
+ ownership of more than fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+ </p>
+
+ <h3 id="section-2">2. Source Code License</h3>
+ <h4 id="section-2.1">2.1. The Initial Developer Grant</h4>
+ <p>
+ The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive
+ license, subject to third party intellectual property claims:
+ </p>
+ <p id="section-2.1-a">2.1.a. under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform,
+ sublicense and distribute the Original Code (or portions thereof) with or without
+ Modifications, and/or as part of a Larger Work; and
+ </p>
+ <p id="section-2.1-b">2.1.b. under Patents Claims infringed by the making, using or selling
+ of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or
+ otherwise dispose of the Original Code (or portions thereof).
+ </p>
+ <p id="section-2.1-c">2.1.c. the licenses granted in this Section 2.1
+ (<a href="#section-2.1-a">a</a>) and (<a href="#section-2.1-b">b</a>) are effective on
+ the date Initial Developer first distributes Original Code under the terms of this
+ License.
+ </p>
+ <p id="section-2.1-d">2.1.d. Notwithstanding Section 2.1 (<a href="#section-2.1-b">b</a>)
+ above, no patent license is granted: 1) for code that You delete from the Original Code;
+ 2) separate from the Original Code; or 3) for infringements caused by: i) the
+ modification of the Original Code or ii) the combination of the Original Code with other
+ software or devices.
+ </p>
+
+ <h4 id="section-2.2">2.2. Contributor Grant</h4>
+ <p>
+ Subject to third party intellectual property claims, each Contributor hereby grants You
+ a world-wide, royalty-free, non-exclusive license
+ </p>
+ <p id="section-2.2-a">2.2.a. under intellectual property rights (other than patent or trademark)
+ Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and
+ distribute the Modifications created by such Contributor (or portions thereof) either on
+ an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger
+ Work; and
+ </p>
+ <p id="section-2.2-b">2.2.b. under Patent Claims infringed by the making, using, or selling of
+ Modifications made by that Contributor either alone and/or in combination with its
+ Contributor Version (or portions of such combination), to make, use, sell, offer for
+ sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor
+ (or portions thereof); and 2) the combination of Modifications made by that Contributor
+ with its Contributor Version (or portions of such combination).
+ </p>
+ <p id="section-2.2-c">2.2.c. the licenses granted in Sections 2.2
+ (<a href="#section-2.2-a">a</a>) and 2.2 (<a href="#section-2.2-b">b</a>) are effective
+ on the date Contributor first makes Commercial Use of the Covered Code.
+ </p>
+ <p id="section-2.2-d">2.2.c. Notwithstanding Section 2.2 (<a href="#section-2.2-b">b</a>)
+ above, no patent license is granted: 1) for any code that Contributor has deleted from
+ the Contributor Version; 2) separate from the Contributor Version; 3) for infringements
+ caused by: i) third party modifications of Contributor Version or ii) the combination of
+ Modifications made by that Contributor with other software (except as part of the
+ Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code
+ in the absence of Modifications made by that Contributor.
+ </p>
+
+ <h3 id="section-3">3. Distribution Obligations</h3>
+ <h4 id="section-3.1">3.1. Application of License</h4>
+ <p>
+ The Modifications which You create or to which You contribute are governed by the terms
+ of this License, including without limitation Section <a href="#section-2.2">2.2</a>. The
+ Source Code version of Covered Code may be distributed only under the terms of this License
+ or a future version of this License released under Section <a href="#section-6.1">6.1</a>,
+ and You must include a copy of this License with every copy of the Source Code You
+ distribute. You may not offer or impose any terms on any Source Code version that alters or
+ restricts the applicable version of this License or the recipients' rights hereunder.
+ However, You may include an additional document offering the additional rights described in
+ Section <a href="#section-3.5">3.5</a>.
+</p>
+
+ <h4 id="section-3.2">3.2. Availability of Source Code</h4>
+ <p>
+ Any Modification which You create or to which You contribute must be made available in
+ Source Code form under the terms of this License either on the same media as an Executable
+ version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an
+ Executable version available; and if made available via Electronic Distribution Mechanism,
+ must remain available for at least twelve (12) months after the date it initially became
+ available, or at least six (6) months after a subsequent version of that particular
+ Modification has been made available to such recipients. You are responsible for ensuring
+ that the Source Code version remains available even if the Electronic Distribution
+ Mechanism is maintained by a third party.
+ </p>
+
+ <h4 id="section-3.3">3.3. Description of Modifications</h4>
+<p>
+ You must cause all Covered Code to which You contribute to contain a file documenting the
+ changes You made to create that Covered Code and the date of any change. You must include a
+ prominent statement that the Modification is derived, directly or indirectly, from Original
+ Code provided by the Initial Developer and including the name of the Initial Developer in
+ (a) the Source Code, and (b) in any notice in an Executable version or related documentation
+ in which You describe the origin or ownership of the Covered Code.
+ </p>
+
+ <h4 id="section-3.4">3.4. Intellectual Property Matters</h4>
+ <p id="section-3.4-a"><b>3.4.a. Third Party Claims:</b>
+ If Contributor has knowledge that a license under a third party's intellectual property
+ rights is required to exercise the rights granted by such Contributor under Sections
+ <a href="#section-2.1">2.1</a> or <a href="#section-2.2">2.2</a>, Contributor must include a
+ text file with the Source Code distribution titled "LEGAL" which describes the claim and the
+ party making the claim in sufficient detail that a recipient will know whom to contact. If
+ Contributor obtains such knowledge after the Modification is made available as described in
+ Section <a href="#section-3.2">3.2</a>, Contributor shall promptly modify the LEGAL file in
+ all copies Contributor makes available thereafter and shall take other steps (such as
+ notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who
+ received the Covered Code that new knowledge has been obtained.
+ </p>
+ <p id="section-3.4-b"><b>3.4.b. Contributor APIs:</b>
+ If Contributor's Modifications include an application programming interface and Contributor
+ has knowledge of patent licenses which are reasonably necessary to implement that
+ API, Contributor must also include this information in the legal file.
+ </p>
+ <p id="section-3.4-c"><b>3.4.c. Representations:</b>
+ Contributor represents that, except as disclosed pursuant to Section 3.4
+ (<a href="#section-3.4-a">a</a>) above, Contributor believes that Contributor's Modifications
+ are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the
+ rights conveyed by this License.
+ </p>
+
+ <h4 id="section-3.5">3.5. Required Notices</h4>
+ <p>
+ You must duplicate the notice in <a href="#exhibit-a">Exhibit A</a> in each file of the
+ Source Code. If it is not possible to put such notice in a particular Source Code file due to
+ its structure, then You must include such notice in a location (such as a relevant directory)
+ where a user would be likely to look for such a notice. If You created one or more
+ Modification(s) You may add your name as a Contributor to the notice described in
+ <a href="#exhibit-a">Exhibit A</a>. You must also duplicate this License in any documentation
+ for the Source Code where You describe recipients' rights or ownership rights relating to
+ Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity
+ or liability obligations to one or more recipients of Covered Code. However, You may do so
+ only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You
+ must make it absolutely clear than any such warranty, support, indemnity or liability
+ obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer
+ and every Contributor for any liability incurred by the Initial Developer or such Contributor
+ as a result of warranty, support, indemnity or liability terms You offer.
+ </p>
+
+ <h4 id="section-3.6">3.6. Distribution of Executable Versions</h4>
+ <p>
+ You may distribute Covered Code in Executable form only if the requirements of Sections
+ <a href="#section-3.1">3.1</a>, <a href="#section-3.2">3.2</a>,
+ <a href="#section-3.3">3.3</a>, <a href="#section-3.4">3.4</a> and
+ <a href="#section-3.5">3.5</a> have been met for that Covered Code, and if You include a
+ notice stating that the Source Code version of the Covered Code is available under the terms
+ of this License, including a description of how and where You have fulfilled the obligations
+ of Section <a href="#section-3.2">3.2</a>. The notice must be conspicuously included in any
+ notice in an Executable version, related documentation or collateral in which You describe
+ recipients' rights relating to the Covered Code. You may distribute the Executable version of
+ Covered Code or ownership rights under a license of Your choice, which may contain terms
+ different from this License, provided that You are in compliance with the terms of this
+ License and that the license for the Executable version does not attempt to limit or alter the
+ recipient's rights in the Source Code version from the rights set forth in this License. If
+ You distribute the Executable version under a different license You must make it absolutely
+ clear that any terms which differ from this License are offered by You alone, not by the
+ Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and
+ every Contributor for any liability incurred by the Initial Developer or such Contributor as
+ a result of any such terms You offer.
+</p>
+
+ <h4 id="section-3.7">3.7. Larger Works</h4>
+ <p>
+ You may create a Larger Work by combining Covered Code with other code not governed by the
+ terms of this License and distribute the Larger Work as a single product. In such a case,
+ You must make sure the requirements of this License are fulfilled for the Covered Code.
+ </p>
+
+ <h3 id="section-4">4. Inability to Comply Due to Statute or Regulation.</h3>
+ <p>
+ If it is impossible for You to comply with any of the terms of this License with respect to
+ some or all of the Covered Code due to statute, judicial order, or regulation then You must:
+ (a) comply with the terms of this License to the maximum extent possible; and (b) describe
+ the limitations and the code they affect. Such description must be included in the
+ <b>legal</b> file described in Section
+ <a href="#section-3.4">3.4</a> and must be included with all distributions of the Source Code.
+ Except to the extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to understand it.
+ </p>
+
+ <h3 id="section-5">5. Application of this License.</h3>
+ <p>
+ This License applies to code to which the Initial Developer has attached the notice in
+ <a href="#exhibit-a">Exhibit A</a> and to related Covered Code.
+ </p>
+
+ <h3 id="section-6">6. Versions of the License.</h3>
+
+ <h4 id="section-6.1">6.1. New Versions</h4>
+<p>
+ The <em class="u">H2 Group</em> may publish revised and/or new versions
+ of the License from time to time. Each version will be given a distinguishing version number.
+</p>
+
+ <h4 id="section-6.2">6.2. Effect of New Versions</h4>
+ <p>
+ Once Covered Code has been published under a particular version of the License, You may
+ always continue to use it under the terms of that version. You may also choose to use such
+ Covered Code under the terms of any subsequent version of the License published by the <em class="u">H2 Group</em>.
+ No one other than the <em class="u">H2 Group</em> has the right to modify the terms applicable to Covered Code
+ created under this License.
+</p>
+
+ <h4 id="section-6.3">6.3. Derivative Works</h4>
+ <p>
+ If You create or use a modified version of this License (which you may only do in order to
+ apply it to code which is not already Covered Code governed by this License), You must (a)
+ rename Your license so that the phrases <em class="u">"H2 Group", "H2"</em>
+ or any confusingly similar phrase do not appear in your license (except to note that
+ your license differs from this License) and (b) otherwise make it clear that Your version of
+ the license contains terms which differ from the <em class="u">H2 License</em>.
+ (Filling in the name of the Initial Developer, Original Code or Contributor in the
+ notice described in <a href="#exhibit-a">Exhibit A</a> shall not of themselves be deemed to
+ be modifications of this License.)
+</p>
+
+ <h3 id="section-7">7. Disclaimer of Warranty</h3>
+<p>
+Covered code is provided under this license on an "as is"
+ basis, without warranty of any kind, either expressed or implied, including, without
+ limitation, warranties that the covered code is free of defects, merchantable, fit for a
+ particular purpose or non-infringing. The entire risk as to the quality and performance of
+ the covered code is with you. Should any covered code prove defective in any respect, you
+ (not the initial developer or any other contributor) assume the cost of any necessary
+ servicing, repair or correction. This disclaimer of warranty constitutes an essential part
+ of this license. No use of any covered code is authorized hereunder except under this
+ disclaimer.
+ </p>
+
+ <h3 id="section-8">8. Termination</h3>
+ <p id="section-8.1">8.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure such breach
+ within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which
+ are properly granted shall survive any termination of this License. Provisions which, by
+ their nature, must remain in effect beyond the termination of this License shall survive.
+ </p>
+
+ <p id="section-8.2">8.2. If You initiate litigation by asserting a patent infringement
+ claim (excluding declaratory judgment actions) against Initial Developer or a Contributor
+ (the Initial Developer or Contributor against whom You file such action is referred to
+ as "Participant") alleging that:
+</p>
+ <p id="section-8.2-a">8.2.a. such Participant's Contributor Version directly or indirectly
+ infringes any patent, then any and all rights granted by such Participant to You under
+ Sections <a href="#section-2.1">2.1</a> and/or <a href="#section-2.2">2.2</a> of this
+ License shall, upon 60 days notice from Participant terminate prospectively, unless if
+ within 60 days after receipt of notice You either: (i) agree in writing to pay
+ Participant a mutually agreeable reasonable royalty for Your past and future use of
+ Modifications made by such Participant, or (ii) withdraw Your litigation claim with
+ respect to the Contributor Version against such Participant. If within 60 days of
+ notice, a reasonable royalty and payment arrangement are not mutually agreed upon in
+ writing by the parties or the litigation claim is not withdrawn, the rights granted by
+ Participant to You under Sections <a href="#section-2.1">2.1</a> and/or
+ <a href="#section-2.2">2.2</a> automatically terminate at the expiration of the 60 day
+ notice period specified above.
+ </p>
+ <p id="section-8.2-b">8.2.b. any software, hardware, or device, other than such Participant's
+ Contributor Version, directly or indirectly infringes any patent, then any rights
+ granted to You by such Participant under Sections 2.1(<a href="#section-2.1-b">b</a>)
+ and 2.2(<a href="#section-2.2-b">b</a>) are revoked effective as of the date You first
+ made, used, sold, distributed, or had made, Modifications made by that Participant.
+ </p>
+
+ <p id="section-8.3">8.3. If You assert a patent infringement claim against Participant
+ alleging that such Participant's Contributor Version directly or indirectly infringes
+ any patent where such claim is resolved (such as by license or settlement) prior to the
+ initiation of patent infringement litigation, then the reasonable value of the licenses
+ granted by such Participant under Sections <a href="#section-2.1">2.1</a> or
+ <a href="#section-2.2">2.2</a> shall be taken into account in determining the amount or
+ value of any payment or license.
+</p>
+
+ <p id="section-8.4">8.4. In the event of termination under Sections
+ <a href="#section-8.1">8.1</a> or <a href="#section-8.2">8.2</a> above, all end user
+ license agreements (excluding distributors and resellers) which have been validly
+ granted by You or any distributor hereunder prior to termination shall survive
+ termination.
+ </p>
+
+<h3 id="section-9">9. Limitation of Liability</h3>
+<p>
+Under no circumstances and under no legal theory, whether
+ tort (including negligence), contract, or otherwise, shall you, the initial developer,
+ any other contributor, or any distributor of covered code, or any supplier of any of
+ such parties, be liable to any person for any indirect, special, incidental, or
+ consequential damages of any character including, without limitation, damages for loss
+ of goodwill, work stoppage, computer failure or malfunction, or any and all other
+ commercial damages or losses, even if such party shall have been informed of the
+ possibility of such damages. This limitation of liability shall not apply to liability
+ for death or personal injury resulting from such party's negligence to the extent
+ applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion
+ or limitation of incidental or consequential damages, so this exclusion and limitation
+ may not apply to you.
+</p>
+
+<h3 id="section-10">10. United States Government End Users</h3>
+<p>
+ The Covered Code is a "commercial item", as that term is defined in 48
+ C.F.R. 2.101 (October 1995), consisting of
+ "commercial computer software" and "commercial computer software documentation", as such
+ terms are used in 48 C.F.R. 12.212 (September 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R.
+ 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users
+ acquire Covered Code with only those rights set forth herein.
+</p>
+
+<h3 id="section-11">11. Miscellaneous</h3>
+<p>
+This License represents the complete agreement concerning subject matter hereof. If
+ any provision of this License is held to be unenforceable, such provision shall be
+ reformed only to the extent necessary to make it enforceable. This License shall be
+ governed by <em class="u">Swiss</em> law provisions (except to the extent applicable law, if any,
+ provides otherwise), excluding its conflict-of-law provisions. With respect to
+ disputes in which at least one party is a citizen of, or an entity chartered or
+ registered to do business in <em class="u">Switzerland</em>, any litigation relating to
+ this License shall be subject to the jurisdiction of <em class="u">Switzerland</em>,
+ with the losing party responsible for costs, including without limitation, court
+ costs and reasonable attorneys' fees and expenses. The application of the United
+ Nations Convention on Contracts for the International Sale of Goods is expressly
+ excluded. Any law or regulation which provides that the language of a contract
+ shall be construed against the drafter shall not apply to this License.
+</p>
+
+<h3 id="section-12">12. Responsibility for Claims</h3>
+<p>
+As between Initial Developer and the Contributors, each party is responsible for
+claims and damages arising, directly or indirectly, out of its utilization of rights
+under this License and You agree to work with Initial Developer and Contributors to
+distribute such responsibility on an equitable basis. Nothing herein is intended or
+shall be deemed to constitute any admission of liability.
+</p>
+
+<h3 id="section-13">13. Multiple-Licensed Code</h3>
+<p>
+Initial Developer may designate portions of the Covered Code as
+"Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits
+you to utilize portions of the Covered Code under Your choice of this
+or the alternative licenses, if any, specified by the Initial Developer in the file
+described in <a href="#exhibit-a">Exhibit A</a>.
+</p>
+
+<h3 id="exhibit-a">Exhibit A</h3>
+<pre>
+Multiple-Multiple-Licensed under the H2 License, Version 1.0,
+and under the Eclipse Public License, Version 1.0
+, and under the
+Eclipse Public License, Version 1.0 (http://h2database.com/html/license.html)
+Initial Developer: H2 Group
+</pre>
+
+<h2>Eclipse Public License - Version 1.0</h2>
+<p>
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+</p>
+
+<h3>1. DEFINITIONS</h3>
+<p>
+"Contribution" means:
+</p><p>
+a) in the case of the initial Contributor, the initial code and documentation
+distributed under this Agreement, and
+</p><p>
+b) in the case of each subsequent Contributor:
+</p><p>
+i) changes to the Program, and
+</p><p>
+ii) additions to the Program;
+</p><p>
+where such changes and/or additions to the Program originate from and are
+distributed by that particular Contributor. A Contribution 'originates' from a
+Contributor if it was added to the Program by such Contributor itself or anyone
+acting on such Contributor's behalf. Contributions do not include additions to
+the Program which: (i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are not derivative
+works of the Program.
+</p><p>
+"Contributor" means any person or entity that distributes the Program.
+</p><p>
+"Licensed Patents " mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when combined
+with the Program.
+</p><p>
+"Program" means the Contributions distributed in accordance with this Agreement.
+</p><p>
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+</p>
+
+<h3>2. GRANT OF RIGHTS</h3>
+<p>
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient
+a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare
+derivative works of, publicly display, publicly perform, distribute and sublicense the
+Contribution of such Contributor, if any, and such derivative works, in source code
+and object code form.
+</p><p>
+b) Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed
+Patents to make, use, sell, offer to sell, import and otherwise transfer the
+Contribution of such Contributor, if any, in source code and object code form.
+This patent license shall apply to the combination of the Contribution and the
+Program if, at the time the Contribution is added by the Contributor, such addition
+of the Contribution causes such combination to be covered by the Licensed
+Patents. The patent license shall not apply to any other combinations which
+include the Contribution. No hardware per se is licensed hereunder.
+</p><p>
+c) Recipient understands that although each Contributor grants the licenses to
+its Contributions set forth herein, no assurances are provided by any Contributor
+that the Program does not infringe the patent or other intellectual property
+rights of any other entity. Each Contributor disclaims any liability to Recipient
+for claims brought by any other entity based on infringement of intellectual
+property rights or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to secure
+any other intellectual property rights needed, if any. For example, if a third party
+patent license is required to allow Recipient to distribute the Program, it is
+Recipient's responsibility to acquire that license before distributing the Program.
+</p><p>
+d) Each Contributor represents that to its knowledge it has sufficient copyright
+rights in its Contribution, if any, to grant the copyright license set forth in this
+Agreement.
+</p>
+
+<h3>3. REQUIREMENTS</h3>
+<p>
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+</p><p>
+a) it complies with the terms and conditions of this Agreement; and
+</p><p>
+b) its license agreement:
+</p><p>
+i) effectively disclaims on behalf of all Contributors all warranties and conditions,
+express and implied, including warranties or conditions of title and
+non-infringement, and implied warranties or conditions of merchantability and
+fitness for a particular purpose;
+</p><p>
+ii) effectively excludes on behalf of all Contributors all liability for damages,
+including direct, indirect, special, incidental and consequential damages,
+such as lost profits;
+</p><p>
+iii) states that any provisions which differ from this Agreement are offered by
+that Contributor alone and not by any other party; and
+</p><p>
+iv) states that source code for the Program is available from such Contributor,
+and informs licensees how to obtain it in a reasonable manner on or through
+a medium customarily used for software exchange.
+</p><p>
+When the Program is made available in source code form:
+</p><p>
+a) it must be made available under this Agreement; and
+</p><p>
+b) a copy of this Agreement must be included with each copy of the Program.
+</p><p>
+Contributors may not remove or alter any copyright notices contained within
+the Program.
+</p><p>
+Each Contributor must identify itself as the originator of its Contribution,
+if any, in a manner that reasonably allows subsequent Recipients to identify
+the originator of the Contribution.
+</p>
+
+<h3>4. COMMERCIAL DISTRIBUTION</h3>
+<p>
+Commercial distributors of software may accept certain responsibilities with
+respect to end users, business partners and the like. While this license is
+intended to facilitate the commercial use of the Program, the Contributor
+who includes the Program in a commercial product offering should do so
+in a manner which does not create potential liability for other Contributors.
+Therefore, if a Contributor includes the Program in a commercial product
+offering, such Contributor ("Commercial Contributor") hereby agrees to
+defend and indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses") arising from
+claims, lawsuits and other legal actions brought by a third party against the
+Indemnified Contributor to the extent caused by the acts or omissions of
+such Commercial Contributor in connection with its distribution of the
+Program in a commercial product offering. The obligations in this section
+do not apply to any claims or Losses relating to any actual or alleged
+intellectual property infringement. In order to qualify, an Indemnified
+Contributor must: a) promptly notify the Commercial Contributor in writing
+of such claim, and b) allow the Commercial Contributor to control, and
+cooperate with the Commercial Contributor in, the defense and any related
+settlement negotiations. The Indemnified Contributor may participate in
+any such claim at its own expense.
+</p><p>
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those performance
+claims and warranties, and if a court requires any other Contributor to pay
+any damages as a result, the Commercial Contributor must pay those damages.
+</p>
+
+<h3>5. NO WARRANTY</h3>
+<p>
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM
+IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING,
+WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+PURPOSE. Each Recipient is solely responsible for determining the
+appropriateness of using and distributing the Program and assumes all
+risks associated with its exercise of rights under this Agreement, including
+but not limited to the risks and costs of program errors, compliance with
+applicable laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+</p>
+
+<h3>6. DISCLAIMER OF LIABILITY</h3>
+<p>
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER
+RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY
+RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+</p>
+
+<h3>7. GENERAL</h3>
+<p>
+If any provision of this Agreement is invalid or unenforceable under applicable
+law, it shall not affect the validity or enforceability of the remainder of the
+terms of this Agreement, and without further action by the parties hereto,
+such provision shall be reformed to the minimum extent necessary to make
+such provision valid and enforceable.
+</p><p>
+If Recipient institutes patent litigation against any entity (including a cross-claim
+or counterclaim in a lawsuit) alleging that the Program itself (excluding
+combinations of the Program with other software or hardware) infringes such
+Recipient's patent(s), then such Recipient's rights granted under Section
+2(b) shall terminate as of the date such litigation is filed.
+</p><p>
+All Recipient's rights under this Agreement shall terminate if it fails to comply
+with any of the material terms or conditions of this Agreement and does
+not cure such failure in a reasonable period of time after becoming aware
+of such noncompliance. If all Recipient's rights under this Agreement
+terminate, Recipient agrees to cease use and distribution of the Program
+as soon as reasonably practicable. However, Recipient's obligations under
+this Agreement and any licenses granted by Recipient relating to the
+Program shall continue and survive.
+</p><p>
+Everyone is permitted to copy and distribute copies of this Agreement, but
+in order to avoid inconsistency the Agreement is copyrighted and may only
+be modified in the following manner. The Agreement Steward reserves the
+right to publish new versions (including revisions) of this Agreement from
+time to time. No one other than the Agreement Steward has the right to
+modify this Agreement. The Eclipse Foundation is the initial Agreement
+Steward. The Eclipse Foundation may assign the responsibility to serve as
+the Agreement Steward to a suitable separate entity. Each new version of
+the Agreement will be given a distinguishing version number. The Program
+(including Contributions) may always be distributed subject to the version
+of the Agreement under which it was received. In addition, after a new
+version of the Agreement is published, Contributor may elect to distribute
+the Program (including its Contributions) under the new version. Except as
+expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights
+or licenses to the intellectual property of any Contributor under this
+Agreement, whether expressly, by implication, estoppel or otherwise.
+All rights in the Program not expressly granted under this Agreement are
+reserved.
+</p><p>
+This Agreement is governed by the laws of <em class="u">Switzerland</em>
+and the intellectual property laws of <em class="u">Switzerland</em>.
+No party to this Agreement will bring a legal action under this Agreement more
+than one year after the cause of action arose. Each party waives its rights
+to a jury trial in any resulting litigation.
+</p>
+
+<!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html>
+
diff --git a/src/com/google/gwtorm/GWTORM.gwt.xml b/src/com/google/gwtorm/GWTORM.gwt.xml
new file mode 100644
index 0000000..358174e
--- /dev/null
+++ b/src/com/google/gwtorm/GWTORM.gwt.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<module>
+ <inherits name="com.google.gwt.core.Core"/>
+
+ <generate-with class="com.google.gwtorm.rebind.DataAccessGenerator">
+ <when-type-assignable class="com.google.gwtorm.client.DataAccess" />
+ </generate-with>
+</module>
diff --git a/src/com/google/gwtorm/client/Access.java b/src/com/google/gwtorm/client/Access.java
new file mode 100644
index 0000000..ff8058e
--- /dev/null
+++ b/src/com/google/gwtorm/client/Access.java
@@ -0,0 +1,150 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+/**
+ * Data access interface for an entity type.
+ * <p>
+ * Applications should extend this interface for each entity type they need to
+ * access. At runtime the application extension will be automatically
+ * implemented with a generated class, providing concrete implementations for
+ * all methods.
+ * <p>
+ * Instances should be acquired through the application's extension interface of
+ * {@link Schema}.
+ * <p>
+ * Applications should implement a query method using the {@link PrimaryKey}
+ * annotation, for example:
+ *
+ * <pre>
+ * public interface FooAccess extends Access<Foo, Foo.Key> {
+ * @PrimaryKey("key")
+ * Foo byKey(Foo.Key k) throws OrmException;
+ * }
+ *</pre>
+ *<p>
+ * otherwise the primaryKey, get, update and delete operations declared by this
+ * interface will be unsupported.
+ *
+ * @param <T> type of the entity. Any object type is suitable, so long as at
+ * least one field uses a {@link Column} annotation.
+ * @param <K> type of the primary key of entity. If the primary key is a
+ * primitive type then use Key directly, otherwise use a Key
+ * implementation. Entity specific key subclasses are recommended.
+ */
+public interface Access<T extends Object, K extends Key<?>> {
+ /**
+ * Obtain the primary key of an entity instance.
+ *
+ * @param entity the entity to get the key of; must not be null.
+ * @return the primary key. Null if this entity has no primary key declared,
+ * or if the primary key does not implement the Key interface.
+ */
+ K primaryKey(T entity);
+
+ /**
+ * Lookup a single entity via its primary key.
+ * <p>
+ * This method is only implemented if the entity's primary key is defined to
+ * be an implementation of the {@link Key} interface. Otherwise the method
+ * throws {@link UnsupportedOperationException}.
+ *
+ * @param key the primary key instance; must not be null.
+ * @return the entity; null if no entity has this key.
+ * @throws OrmException the data lookup failed.
+ * @throws UnsupportedOperationException the key type doesn't implement Key.
+ */
+ T get(K key) throws OrmException;
+
+ /**
+ * Lookup multiple entities via their primary key.
+ * <p>
+ * This method is only implemented if the entity's primary key is defined to
+ * be an implementation of the {@link Key} interface. Otherwise the method
+ * throws {@link UnsupportedOperationException}.
+ * <p>
+ * This method is a batch form of {@link #get(Key)} and may be optimized to
+ * reduce round-trips to the data store.
+ *
+ * @param keys collection of zero or more keys to perform lookup with.
+ * @return collection of all matching entities; this may be a smaller result
+ * than the keys supplied if one or more of the keys does not match an
+ * existing entity.
+ * @throws OrmException the data lookup failed.
+ * @throws UnsupportedOperationException the key type doesn't implement Key.
+ */
+ ResultSet<T> get(Iterable<K> keys) throws OrmException;
+
+ /**
+ * Immediately insert new entities into the data store.
+ *
+ * @param instances the instances to insert. The iteration occurs only once.
+ * @throws OrmException data insertion failed.
+ */
+ void insert(Iterable<T> instances) throws OrmException;
+
+ /**
+ * Insert new entities into the data store.
+ *
+ * @param instances the instances to insert. The iteration occurs only once.
+ * @param txn transaction to batch the operation into. If not null the data
+ * store changes will be delayed to {@link Transaction#commit()} is
+ * invoked; if null the operation occurs immediately.
+ * @throws OrmException data insertion failed.
+ */
+ void insert(Iterable<T> instances, Transaction txn) throws OrmException;
+
+ /**
+ * Immediately update existing entities in the data store.
+ *
+ * @param instances the instances to update. The iteration occurs only once.
+ * @throws OrmException data modification failed.
+ * @throws UnsupportedOperationException no PrimaryKey was declared.
+ */
+ void update(Iterable<T> instances) throws OrmException;
+
+ /**
+ * Update existing entities in the data store.
+ *
+ * @param instances the instances to update. The iteration occurs only once.
+ * @param txn transaction to batch the operation into. If not null the data
+ * store changes will be delayed to {@link Transaction#commit()} is
+ * invoked; if null the operation occurs immediately.
+ * @throws OrmException data modification failed.
+ * @throws UnsupportedOperationException no PrimaryKey was declared.
+ */
+ void update(Iterable<T> instances, Transaction txn) throws OrmException;
+
+ /**
+ * Immediately delete existing entities from the data store.
+ *
+ * @param instances the instances to delete. The iteration occurs only once.
+ * @throws OrmException data removal failed.
+ * @throws UnsupportedOperationException no PrimaryKey was declared.
+ */
+ void delete(Iterable<T> instances) throws OrmException;
+
+ /**
+ * Delete existing entities from the data store.
+ *
+ * @param instances the instances to delete. The iteration occurs only once.
+ * @param txn transaction to batch the operation into. If not null the data
+ * store changes will be delayed to {@link Transaction#commit()} is
+ * invoked; if null the operation occurs immediately.
+ * @throws OrmException data removal failed.
+ * @throws UnsupportedOperationException no PrimaryKey was declared.
+ */
+ void delete(Iterable<T> instances, Transaction txn) throws OrmException;
+}
diff --git a/src/com/google/gwtorm/client/Column.java b/src/com/google/gwtorm/client/Column.java
new file mode 100644
index 0000000..a46f26c
--- /dev/null
+++ b/src/com/google/gwtorm/client/Column.java
@@ -0,0 +1,47 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation marking an entity field for persistence in the data store.
+ * <p>
+ * Fields marked with <code>Column</code> must not be final and must not be
+ * private. Fields which might be accessed cross-packages (such as those
+ * declared in a common Key type like {@link StringKey}) must be declared with
+ * public access so generated code can access them directly.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Column {
+ /**
+ * @return name of the column in the data store. Defaults to the field name.
+ */
+ String name() default "";
+
+ /**
+ * @return maximum length (in characters). Only valid for String.
+ */
+ int length() default 0;
+
+ /**
+ * @return is a value required. Defaults to true (NOT NULL).
+ */
+ boolean notNull() default true;
+}
diff --git a/src/com/google/gwtorm/client/IntKey.java b/src/com/google/gwtorm/client/IntKey.java
new file mode 100644
index 0000000..7dcfbf7
--- /dev/null
+++ b/src/com/google/gwtorm/client/IntKey.java
@@ -0,0 +1,85 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.io.Serializable;
+
+/**
+ * Abstract key type using a single integer value.
+ * <p>
+ * Applications should subclass this type to create their own entity-specific
+ * key classes.
+ *
+ * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
+ */
+public abstract class IntKey<P extends Key<?>> implements Key<P>, Serializable {
+ /**
+ * @return id of the entity instance.
+ */
+ public abstract int get();
+
+ /**
+ * @return the parent key instance; null if this is a root level key.
+ */
+ public P getParentKey() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = get();
+ if (getParentKey() != null) {
+ hc *= 31;
+ hc += getParentKey().hashCode();
+ }
+ return hc;
+ }
+
+ @Override
+ public boolean equals(final Object b) {
+ if (b == null || b.getClass() != getClass()) {
+ return false;
+ }
+
+ final IntKey<P> q = cast(b);
+ if (get() == q.get()) {
+ if (getParentKey() != null) {
+ return getParentKey().equals(q.getParentKey());
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer r = new StringBuffer();
+ r.append(getClass().getName());
+ r.append('[');
+ if (getParentKey() != null) {
+ r.append(getParentKey().toString());
+ r.append(", ");
+ }
+ r.append(get());
+ r.append(']');
+ return r.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <A extends Key<?>> IntKey<A> cast(final Object b) {
+ return (IntKey<A>) b;
+ }
+}
diff --git a/src/com/google/gwtorm/client/Key.java b/src/com/google/gwtorm/client/Key.java
new file mode 100644
index 0000000..0505075
--- /dev/null
+++ b/src/com/google/gwtorm/client/Key.java
@@ -0,0 +1,33 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+/**
+ * Generic type for an entity key.
+ * <p>
+ * Although not required, entities should make their primary key type implement
+ * this interface, permitting traversal up through the containment hierarchy of
+ * the entity keys.
+ *
+ * @param <P> type of the parent key. If no parent, use {@link Key} itself.
+ */
+public interface Key<P extends Key<?>> {
+ /**
+ * Get the parent key instance.
+ *
+ * @return the parent key; null if this entity key is a root-level key.
+ */
+ public P getParentKey();
+}
diff --git a/src/com/google/gwtorm/client/LongKey.java b/src/com/google/gwtorm/client/LongKey.java
new file mode 100644
index 0000000..2fc5d6b
--- /dev/null
+++ b/src/com/google/gwtorm/client/LongKey.java
@@ -0,0 +1,85 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.io.Serializable;
+
+/**
+ * Abstract key type using a single long value.
+ * <p>
+ * Applications should subclass this type to create their own entity-specific
+ * key classes.
+ *
+ * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
+ */
+public abstract class LongKey<P extends Key<?>> implements Key<P>, Serializable {
+ /**
+ * @return id of the entity instance.
+ */
+ public abstract long get();
+
+ /**
+ * @return the parent key instance; null if this is a root level key.
+ */
+ public P getParentKey() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = (int) get();
+ if (getParentKey() != null) {
+ hc *= 31;
+ hc += getParentKey().hashCode();
+ }
+ return hc;
+ }
+
+ @Override
+ public boolean equals(final Object b) {
+ if (b == null || b.getClass() != getClass()) {
+ return false;
+ }
+
+ final LongKey<P> q = cast(b);
+ if (get() == q.get()) {
+ if (getParentKey() != null) {
+ return getParentKey().equals(q.getParentKey());
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer r = new StringBuffer();
+ r.append(getClass().getName());
+ r.append('[');
+ if (getParentKey() != null) {
+ r.append(getParentKey().toString());
+ r.append(", ");
+ }
+ r.append(get());
+ r.append(']');
+ return r.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <A extends Key<?>> LongKey<A> cast(final Object b) {
+ return (LongKey<A>) b;
+ }
+}
diff --git a/src/com/google/gwtorm/client/OrmException.java b/src/com/google/gwtorm/client/OrmException.java
new file mode 100644
index 0000000..72cb144
--- /dev/null
+++ b/src/com/google/gwtorm/client/OrmException.java
@@ -0,0 +1,28 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+/**
+ * Any data store read or write error.
+ */
+public class OrmException extends Exception {
+ public OrmException(final String message) {
+ super(message);
+ }
+
+ public OrmException(final String message, final Throwable why) {
+ super(message, why);
+ }
+}
diff --git a/src/com/google/gwtorm/client/PrimaryKey.java b/src/com/google/gwtorm/client/PrimaryKey.java
new file mode 100644
index 0000000..b1308f4
--- /dev/null
+++ b/src/com/google/gwtorm/client/PrimaryKey.java
@@ -0,0 +1,34 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation marking a query function in a {@link Access} interface as the key.
+ *
+ * @see Access
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface PrimaryKey {
+ /**
+ * @return name of the field in the entity which contains the primary key.
+ */
+ String value();
+}
diff --git a/src/com/google/gwtorm/client/Query.java b/src/com/google/gwtorm/client/Query.java
new file mode 100644
index 0000000..19359f6
--- /dev/null
+++ b/src/com/google/gwtorm/client/Query.java
@@ -0,0 +1,56 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation marking a method in an {@link Access} interface as a query.
+ * <p>
+ * Query methods must return a parameterized {@link ResultSet}, for example:
+ *
+ * <pre>
+ * public interface FooAccess extends Access<Foo, Foo.Key> {
+ * @Query("WHERE a=?")
+ * ResultSet<Foo> find(int a) throws OrmException;
+ * }
+ *</pre>
+ *<p>
+ * Query strings must conform to the following grammar:
+ *
+ * <pre>
+ * [WHERE <condition> [AND <condition> ...]]
+ * [ORDER BY <property> [ASC | DESC] [, <property> [ASC | DESC] ...]]
+ * [LIMIT { <count> | ? }]
+ *
+ * <condition> := <property> { < | <= | > | >= | = | != } <value>
+ * <value> := { ? | true | false | <int> | <string> }
+ * </pre>
+ * <p>
+ * Method parameters are bound in order to the placeholders (?) declared in the
+ * query conditions. The type of the limit placeholder parameter (if used in the
+ * query) must be <code>int</code>.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Query {
+ /**
+ * @return the query clause. Defaults to "", matching all entities, no order.
+ */
+ String value() default "";
+}
diff --git a/src/com/google/gwtorm/client/Relation.java b/src/com/google/gwtorm/client/Relation.java
new file mode 100644
index 0000000..a43a033
--- /dev/null
+++ b/src/com/google/gwtorm/client/Relation.java
@@ -0,0 +1,48 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation marking a method in a {@link Schema} interface as entity access.
+ * <p>
+ * Access methods must return an interface extending {@link Access}, for
+ * example:
+ *
+ * <pre>
+ * public interface FooAccess extends Access<Foo, Foo.Key> {
+ * }
+ *
+ * public interface BarSchema extends Schema {
+ * @Relation
+ * FooAccess foos();
+ * }
+ * </pre>
+ * <p>
+ * The table name within the data store will be derived from the relation
+ * annotation, or the method name.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Relation {
+ /**
+ * @return the name of the data store table. Defaults to the method name.
+ */
+ String name() default "";
+}
diff --git a/src/com/google/gwtorm/client/ResultSet.java b/src/com/google/gwtorm/client/ResultSet.java
new file mode 100644
index 0000000..c8c392b
--- /dev/null
+++ b/src/com/google/gwtorm/client/ResultSet.java
@@ -0,0 +1,52 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Result from any data store query function.
+ *
+ * @param <T> type of entity being returned by the query.
+ */
+public interface ResultSet<T> extends Iterable<T> {
+ /**
+ * Obtain an iterator to loop through the results.
+ * <p>
+ * The iterator can be obtained only once. When the iterator completes (
+ * <code>hasNext()</code> returns false) {@link #close()} will be
+ * automatically called.
+ */
+ Iterator<T> iterator();
+
+ /**
+ * Materialize all results as a single list.
+ * <p>
+ * Prior to returning {@link #close()} is invoked. This method must not be
+ * combined with {@link #iterator()} on the same instance.
+ *
+ * @return list of the complete results.
+ */
+ List<T> toList();
+
+ /**
+ * Close the result, discarding any further results.
+ * <p>
+ * This method may be invoked more than once. Its main use is to stop
+ * obtaining results before the iterator has finished.
+ */
+ void close();
+}
diff --git a/src/com/google/gwtorm/client/Schema.java b/src/com/google/gwtorm/client/Schema.java
new file mode 100644
index 0000000..b28da3f
--- /dev/null
+++ b/src/com/google/gwtorm/client/Schema.java
@@ -0,0 +1,83 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+/**
+ * Application database definition and top-level schema access.
+ * <p>
+ * Applications should extend this interface and declare relation methods for
+ * each entity/table used. Relation methods must be marked with the
+ * {@link Relation} annotation and return an interface extending {@link Access}.
+ * At runtime the application extension of Schema will be automatically
+ * implemented with a generated class, providing implementations of the Access
+ * extensions from each of the declared relation methods.
+ * <p>
+ * Instances of a schema should be obtained through the
+ * {@link com.google.gwtorm.jdbc.Database} class on a pure-JDBC implementation
+ * and through <code>GWT.create()</code> on the GWT client side.
+ * <p>
+ * In the JDBC implementation each Schema instance wraps around a single JDBC
+ * Connection object. Therefore a Schema instance has a 1:1 relationship with an
+ * active database handle.
+ * <p>
+ * A Schema instance (as well as its returned Access instances) is not thread
+ * safe. Applications must provide their own synchronization, or ensure that at
+ * most 1 thread access a Schema instance (or any returned Access instance) at a
+ * time. The safest mapping is 1 schema instance per thread, never shared.
+ * <p>
+ * For example the OurDb schema creates two tables (identical structure) named
+ * <code>someFoos</code> and <code>otherFoos</code>:
+ *
+ * <pre>
+ * public interface FooAccess extends Access<Foo, Foo.Key> {
+ * @PrimaryKey("key")
+ * Foo byKey(Foo.Key k) throws OrmException;
+ * }
+ *
+ * public interface OurDb extends Schema {
+ * @Relation
+ * FooAccess someFoos();
+ *
+ * @Relation
+ * FooAccess otherFoos();
+ * }
+ * </pre>
+ */
+public interface Schema {
+ /**
+ * Automatically create the database tables.
+ *
+ * @throws OrmException tables already exist or create permission is denied.
+ */
+ void createSchema() throws OrmException;
+
+ /**
+ * Begin a new transaction.
+ * <p>
+ * Only one transaction can be in-flight at a time on any given Schema
+ * instance. Applications must commit or rollback a previously created
+ * transaction before beginning another transaction on the same Schema.
+ *
+ * @return the new transaction.
+ * @throws OrmException the schema has been closed or another transaction has
+ * already been begun on this schema instance.
+ */
+ Transaction beginTransaction() throws OrmException;
+
+ /**
+ * Close the schema and release all resources.
+ */
+ void close();
+}
diff --git a/src/com/google/gwtorm/client/Sequence.java b/src/com/google/gwtorm/client/Sequence.java
new file mode 100644
index 0000000..c70cc90
--- /dev/null
+++ b/src/com/google/gwtorm/client/Sequence.java
@@ -0,0 +1,61 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation marking a method in {@link Schema} interface as number generator.
+ * <p>
+ * Sequence methods must return a primitive <code>int</code> or
+ * <code>long</code> type.
+ *
+ * <pre>
+ * public interface BarSchema extends Schema {
+ * @Sequence
+ * int nextId();
+ * }
+ * </pre>
+ * <p>
+ * The sequence name will be taken from the method name, after removing the
+ * optional prefix "next".
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Sequence {
+ /**
+ * @return the name of the sequence. Defaults to the method name.
+ */
+ String name() default "";
+
+ /**
+ * @return the initial value of the sequence. Defaults to 1, or whatever the
+ * database dialect defaults to if the sequence starting value is not
+ * supplied in the sequence declaration.
+ */
+ long startsWith() default 0;
+
+ /**
+ * @return maximum number of values to cache in memory from the sequence.
+ * Defaults to -1, indicating a default caching level should be
+ * determined by the database. Cached values may be lost (never
+ * returned by the sequence, creating gaps) if the application or the
+ * database is shutdown and restarted.
+ */
+ int cache() default -1;
+}
diff --git a/src/com/google/gwtorm/client/StringKey.java b/src/com/google/gwtorm/client/StringKey.java
new file mode 100644
index 0000000..427c8ad
--- /dev/null
+++ b/src/com/google/gwtorm/client/StringKey.java
@@ -0,0 +1,86 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import java.io.Serializable;
+
+/**
+ * Abstract key type using a single string value.
+ * <p>
+ * Applications should subclass this type to create their own entity-specific
+ * key classes.
+ *
+ * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
+ */
+public abstract class StringKey<P extends Key<?>> implements Key<P>,
+ Serializable {
+ /**
+ * @return name of the entity instance.
+ */
+ public abstract String get();
+
+ /**
+ * @return the parent key instance; null if this is a root level key.
+ */
+ public P getParentKey() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = get() != null ? get().hashCode() : 0;
+ if (getParentKey() != null) {
+ hc *= 31;
+ hc += getParentKey().hashCode();
+ }
+ return hc;
+ }
+
+ @Override
+ public boolean equals(final Object b) {
+ if (b == null || get() == null || b.getClass() != getClass()) {
+ return false;
+ }
+
+ final StringKey<P> q = cast(b);
+ if (get().equals(q.get())) {
+ if (getParentKey() != null) {
+ return getParentKey().equals(q.getParentKey());
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer r = new StringBuffer();
+ r.append(getClass().getName());
+ r.append('[');
+ if (getParentKey() != null) {
+ r.append(getParentKey().toString());
+ r.append(", ");
+ }
+ r.append(get());
+ r.append(']');
+ return r.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <A extends Key<?>> StringKey<A> cast(final Object b) {
+ return (StringKey<A>) b;
+ }
+}
diff --git a/src/com/google/gwtorm/client/Transaction.java b/src/com/google/gwtorm/client/Transaction.java
new file mode 100644
index 0000000..8f2963b
--- /dev/null
+++ b/src/com/google/gwtorm/client/Transaction.java
@@ -0,0 +1,49 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+/**
+ * An active transaction running on a {@link Schema}.
+ * <p>
+ * Applications must invoke {@link #commit()} to finish a transaction.
+ * <p>
+ * Use method on one or more {@link Access} instances to schedule changes into
+ * an open transaction:
+ * <ul>
+ * <li>{@link Access#insert(Iterable, Transaction)}</li>
+ * <li>{@link Access#update(Iterable, Transaction)}</li>
+ * <li>{@link Access#delete(Iterable, Transaction)}</li>
+ * <ul>
+ *
+ * @see Schema#beginTransaction()
+ */
+public interface Transaction {
+ /**
+ * Commit this transaction, finishing all actions.
+ *
+ * @throws OrmException data store refused/rejected one or more actions.
+ */
+ void commit() throws OrmException;
+
+ /**
+ * Rollback (abort) this transaction, performing none of the actions.
+ * <p>
+ * This method has no affect if the transaction has not made any changes.
+ *
+ * @throws OrmException data store couldn't undo the transaction, as it is
+ * already committed.
+ */
+ void rollback() throws OrmException;
+}
diff --git a/src/com/google/gwtorm/client/impl/AbstractAccess.java b/src/com/google/gwtorm/client/impl/AbstractAccess.java
new file mode 100644
index 0000000..237feef
--- /dev/null
+++ b/src/com/google/gwtorm/client/impl/AbstractAccess.java
@@ -0,0 +1,90 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client.impl;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.Key;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.client.Transaction;
+
+import java.util.ArrayList;
+
+public abstract class AbstractAccess<E, K extends Key<?>, T extends AbstractTransaction>
+ implements Access<E, K> {
+ public ResultSet<E> get(final Iterable<K> keys) throws OrmException {
+ final ArrayList<E> r = new ArrayList<E>();
+ for (final K key : keys) {
+ final E o = get(key);
+ if (o != null) {
+ r.add(o);
+ }
+ }
+ return new ListResultSet<E>(r);
+ }
+
+ public final void insert(final Iterable<E> instances) throws OrmException {
+ doInsert(instances, null);
+ }
+
+ public final void insert(final Iterable<E> instances, final Transaction txn)
+ throws OrmException {
+ if (txn != null) {
+ cast(txn).queueInsert(this, instances);
+ } else {
+ insert(instances);
+ }
+ }
+
+ public final void update(final Iterable<E> instances) throws OrmException {
+ doUpdate(instances, null);
+ }
+
+ public final void update(final Iterable<E> instances, final Transaction txn)
+ throws OrmException {
+ if (txn != null) {
+ cast(txn).queueUpdate(this, instances);
+ } else {
+ update(instances);
+ }
+ }
+
+ public final void delete(final Iterable<E> instances) throws OrmException {
+ doDelete(instances, null);
+ }
+
+ public final void delete(final Iterable<E> instances, final Transaction txn)
+ throws OrmException {
+ if (txn != null) {
+ cast(txn).queueDelete(this, instances);
+ } else {
+ delete(instances);
+ }
+ }
+
+ protected abstract void doInsert(Iterable<E> instances, T txn)
+ throws OrmException;
+
+ protected abstract void doUpdate(Iterable<E> instances, T txn)
+ throws OrmException;
+
+ protected abstract void doDelete(Iterable<E> instances, T txn)
+ throws OrmException;
+
+ @SuppressWarnings("unchecked")
+ private T cast(final Transaction txn) {
+ return ((T) txn);
+ }
+}
diff --git a/src/com/google/gwtorm/client/impl/AbstractTransaction.java b/src/com/google/gwtorm/client/impl/AbstractTransaction.java
new file mode 100644
index 0000000..a424f8f
--- /dev/null
+++ b/src/com/google/gwtorm/client/impl/AbstractTransaction.java
@@ -0,0 +1,124 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client.impl;
+
+import com.google.gwtorm.client.Key;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class AbstractTransaction implements Transaction {
+ private static LinkedHashMap<Object, Action<?, Key<?>, AbstractTransaction>> newMap() {
+ return new LinkedHashMap<Object, Action<?, Key<?>, AbstractTransaction>>();
+ }
+
+ protected final Map<Object, Action<?, Key<?>, AbstractTransaction>> pendingInsert;
+ protected final Map<Object, Action<?, Key<?>, AbstractTransaction>> pendingUpdate;
+ protected final Map<Object, Action<?, Key<?>, AbstractTransaction>> pendingDelete;
+
+ protected AbstractTransaction() {
+ pendingInsert = newMap();
+ pendingUpdate = newMap();
+ pendingDelete = newMap();
+ }
+
+ public void commit() throws OrmException {
+ for (Action<?, Key<?>, AbstractTransaction> a : pendingDelete.values()) {
+ a.doDelete(this);
+ }
+ for (Action<?, Key<?>, AbstractTransaction> a : pendingInsert.values()) {
+ a.doInsert(this);
+ }
+ for (Action<?, Key<?>, AbstractTransaction> a : pendingUpdate.values()) {
+ a.doUpdate(this);
+ }
+ }
+
+ <E, K extends Key<?>, T extends AbstractTransaction> void queueInsert(
+ final AbstractAccess<E, ?, T> access, final Iterable<E> list) {
+ queue(pendingInsert, access, list);
+ }
+
+ <E, K extends Key<?>, T extends AbstractTransaction> void queueUpdate(
+ final AbstractAccess<E, ?, T> access, final Iterable<E> list) {
+ queue(pendingUpdate, access, list);
+ }
+
+ <E, K extends Key<?>, T extends AbstractTransaction> void queueDelete(
+ final AbstractAccess<E, ?, T> access, final Iterable<E> list) {
+ queue(pendingDelete, access, list);
+ }
+
+ private static <E, K extends Key<?>, T extends AbstractTransaction> void queue(
+ final Map<Object, Action<?, Key<?>, AbstractTransaction>> queue,
+ final AbstractAccess<E, K, T> access, final Iterable<E> list) {
+ Action<E, K, T> c = get(queue, access);
+ if (c == null) {
+ c = new Action<E, K, T>(access);
+ put(queue, c);
+ }
+ c.addAll(list);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <E, K extends Key<?>, T extends AbstractTransaction> Action<E, K, T> get(
+ final Map<Object, Action<?, Key<?>, AbstractTransaction>> q,
+ final AbstractAccess<E, K, T> access) {
+ return (Action<E, K, T>) q.get(access);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <E, K extends Key<?>, T extends AbstractTransaction> void put(
+ final Map queue, Action<E, K, T> c) {
+ // This silly little method was needed to defeat the Java compiler's
+ // generic type checking. Somehow we got lost in the anonymous types
+ // from all the ? in our Map definition and the compiler just won't let
+ // us do a put into the map.
+ //
+ queue.put(c.access, c);
+ }
+
+ private static class Action<E, K extends Key<?>, T extends AbstractTransaction> {
+ private final AbstractAccess<E, K, T> access;
+ private final Set<E> instances;
+
+ Action(final AbstractAccess<E, K, T> a) {
+ access = a;
+ instances = new LinkedHashSet<E>();
+ }
+
+ void addAll(final Iterable<E> list) {
+ for (final E o : list) {
+ instances.add(o);
+ }
+ }
+
+ void doInsert(final T t) throws OrmException {
+ access.doInsert(instances, t);
+ }
+
+ void doUpdate(final T t) throws OrmException {
+ access.doUpdate(instances, t);
+ }
+
+ void doDelete(final T t) throws OrmException {
+ access.doDelete(instances, t);
+ }
+ }
+}
diff --git a/src/com/google/gwtorm/client/impl/ListResultSet.java b/src/com/google/gwtorm/client/impl/ListResultSet.java
new file mode 100644
index 0000000..6f48d28
--- /dev/null
+++ b/src/com/google/gwtorm/client/impl/ListResultSet.java
@@ -0,0 +1,45 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client.impl;
+
+import com.google.gwtorm.client.ResultSet;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class ListResultSet<T> implements ResultSet<T> {
+ private List<T> items;
+
+ public ListResultSet(final List<T> r) {
+ items = r;
+ }
+
+ public Iterator<T> iterator() {
+ return toList().iterator();
+ }
+
+ public List<T> toList() {
+ final List<T> r = items;
+ if (r == null) {
+ throw new IllegalStateException("Results already obtained");
+ }
+ items = null;
+ return r;
+ }
+
+ public void close() {
+ items = null;
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/AbstractSchemaFactory.java b/src/com/google/gwtorm/jdbc/AbstractSchemaFactory.java
new file mode 100644
index 0000000..d087a72
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/AbstractSchemaFactory.java
@@ -0,0 +1,39 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc;
+
+import com.google.gwtorm.client.Schema;
+
+import java.sql.Connection;
+
+/**
+ * Internal interface to quickly create Schema instances.
+ * <p>
+ * Applications should not use this interface. It is automatically implemented
+ * at runtime to provide fast construction for new Schema instances within
+ * {@link Database#open()}.
+ *
+ * @param <T> type of the application schema.
+ */
+public abstract class AbstractSchemaFactory<T extends Schema> {
+ /**
+ * Create a new schema instance.
+ *
+ * @param db the database instance which created the connection.
+ * @param c the JDBC connection the instance will talk to the database on.
+ * @return the new schema instance, wrapping the connection.
+ */
+ public abstract T create(Database<T> db, Connection c);
+}
diff --git a/src/com/google/gwtorm/jdbc/Database.java b/src/com/google/gwtorm/jdbc/Database.java
new file mode 100644
index 0000000..d45566d
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/Database.java
@@ -0,0 +1,209 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.jdbc.gen.GeneratedClassLoader;
+import com.google.gwtorm.jdbc.gen.SchemaFactoryGen;
+import com.google.gwtorm.jdbc.gen.SchemaGen;
+import com.google.gwtorm.schema.SchemaModel;
+import com.google.gwtorm.schema.java.JavaSchemaModel;
+import com.google.gwtorm.schema.sql.DialectH2;
+import com.google.gwtorm.schema.sql.DialectPostgreSQL;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+import java.util.WeakHashMap;
+
+/**
+ * Constructor for application {@link Schema} extensions.
+ * <p>
+ * Applications should use the Database class to create instances of their
+ * Schema extension interface, and thus open and connect to the JDBC data store.
+ * <p>
+ * Creating a new Database instance is expensive, due to the type analysis and
+ * code generation performed to implement the Schema and Access interfaces.
+ * Applications should create and cache their Database instance for the live of
+ * the application.
+ * <p>
+ * Database instances are thread-safe, but returned Schema instances are not.
+ *
+ * @param <T>
+ */
+public class Database<T extends Schema> {
+ private static final Map<Class<?>, String> schemaFactoryNames =
+ Collections.synchronizedMap(new WeakHashMap<Class<?>, String>());
+
+ private final Properties connectionInfo;
+ private final String url;
+ private final JavaSchemaModel schemaModel;
+ private final AbstractSchemaFactory<T> implFactory;
+ private final SqlDialect implDialect;
+
+ /**
+ * Create a new database interface, generating the interface implementations.
+ * <p>
+ * The JDBC properties information must define at least <code>url</code> and
+ * <code>driver</code>, but may also include driver specific properties such
+ * as <code>username</code> and <code>password</code>.
+ *
+ * @param dbInfo JDBC connection information. The property table is copied.
+ * @param schema application extension of the Schema interface to implement.
+ * @throws OrmException the schema interface is incorrectly defined, or the
+ * driver class is not available through the current class loader.
+ */
+ public Database(final Properties dbInfo, final Class<T> schema)
+ throws OrmException {
+ connectionInfo = new Properties();
+ connectionInfo.putAll(dbInfo);
+
+ url = (String) connectionInfo.remove("url");
+ if (url == null) {
+ throw new OrmException("Required property 'url' not defined");
+ }
+
+ final String driver = (String) connectionInfo.remove("driver");
+ if (driver != null) {
+ loadDriver(driver);
+ }
+
+ final SqlDialect dialect;
+ String dialectName = (String) connectionInfo.remove("dialect");
+ if (dialectName != null) {
+ if (!dialectName.contains(".")) {
+ final String n = SqlDialect.class.getName();
+ dialectName = n.substring(0, n.lastIndexOf('.') + 1) + dialectName;
+ }
+ try {
+ dialect = (SqlDialect) Class.forName(dialectName).newInstance();
+ } catch (InstantiationException e) {
+ throw new OrmException("Dialect " + dialectName + " not available", e);
+ } catch (IllegalAccessException e) {
+ throw new OrmException("Dialect " + dialectName + " not available", e);
+ } catch (ClassNotFoundException e) {
+ throw new OrmException("Dialect " + dialectName + " not found", e);
+ }
+ } else if (url.startsWith("jdbc:postgresql:")) {
+ dialect = new DialectPostgreSQL();
+ } else if (url.startsWith("jdbc:h2:")) {
+ dialect = new DialectH2();
+ } else {
+ throw new OrmException("No dialect known for " + url);
+ }
+
+ schemaModel = new JavaSchemaModel(schema);
+ final GeneratedClassLoader loader = newLoader(schema);
+ final String cachedName = schemaFactoryNames.get(schema);
+ AbstractSchemaFactory<T> factory = null;
+ if (cachedName != null) {
+ factory = newFactory(loader, cachedName);
+ }
+ if (factory == null) {
+ final SchemaGen gen = new SchemaGen(loader, schemaModel, dialect);
+ gen.defineClass();
+ factory = new SchemaFactoryGen<T>(loader, gen).create();
+ schemaFactoryNames.put(schema, factory.getClass().getName());
+ }
+ implFactory = factory;
+ implDialect = dialect;
+ }
+
+ @SuppressWarnings("unchecked")
+ private AbstractSchemaFactory<T> newFactory(final ClassLoader cl,
+ final String name) {
+ try {
+ final Class<?> ft = Class.forName(name, true, cl);
+ return (AbstractSchemaFactory<T>) ft.newInstance();
+ } catch (InstantiationException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ SqlDialect getDialect() {
+ return implDialect;
+ }
+
+ SchemaModel getSchemaModel() {
+ return schemaModel;
+ }
+
+ /**
+ * Open a new connection to the database and get a Schema wrapper.
+ *
+ * @return a new JDBC connection, wrapped up in the application's Schema.
+ * @throws OrmException the connection could not be opened to the database.
+ * The JDBC exception detail should be examined to determine the root
+ * cause of the connection failure.
+ */
+ public T open() throws OrmException {
+ final Connection conn;
+ try {
+ conn = DriverManager.getConnection(url, connectionInfo);
+ } catch (SQLException e) {
+ throw new OrmException("Cannot open database connection", e);
+ }
+
+ try {
+ if (!conn.getAutoCommit()) {
+ conn.setAutoCommit(true);
+ }
+ } catch (SQLException e) {
+ try {
+ conn.close();
+ } catch (SQLException e2) {
+ }
+ throw new OrmException("Cannot force auto-commit on connection", e);
+ }
+
+ return implFactory.create(this, conn);
+ }
+
+ private static <T> GeneratedClassLoader newLoader(final Class<T> schema) {
+ return new GeneratedClassLoader(schema.getClassLoader());
+ }
+
+ private static synchronized void loadDriver(final String driver)
+ throws OrmException {
+ // I've seen some drivers (*cough* Informix *cough*) which won't load
+ // on multiple threads at the same time. Forcing our code to synchronize
+ // around loading the driver ensures we won't ever ask for the same driver
+ // to initialize from different threads. Of course that could still happen
+ // in other parts of the same JVM, but its quite unlikely.
+ //
+ try {
+ Class.forName(driver, true, threadCL());
+ } catch (ClassNotFoundException err) {
+ throw new OrmException("Driver class " + driver + " not available", err);
+ }
+ }
+
+ private static ClassLoader threadCL() {
+ try {
+ return Thread.currentThread().getContextClassLoader();
+ } catch (SecurityException e) {
+ return Database.class.getClassLoader();
+ }
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/JdbcAccess.java b/src/com/google/gwtorm/jdbc/JdbcAccess.java
new file mode 100644
index 0000000..c651f37
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/JdbcAccess.java
@@ -0,0 +1,206 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.Key;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.impl.AbstractAccess;
+import com.google.gwtorm.client.impl.ListResultSet;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+/** Internal base class for implementations of {@link Access}. */
+public abstract class JdbcAccess<T, K extends Key<?>> extends
+ AbstractAccess<T, K, JdbcTransaction> {
+ private final JdbcSchema schema;
+
+ protected JdbcAccess(final JdbcSchema s) {
+ schema = s;
+ }
+
+ protected PreparedStatement prepareStatement(final String sql)
+ throws OrmException {
+ try {
+ return schema.getConnection().prepareStatement(sql);
+ } catch (SQLException e) {
+ throw new OrmException("Prepare failure\n" + sql, e);
+ }
+ }
+
+ protected T queryOne(final PreparedStatement ps) throws OrmException {
+ try {
+ try {
+ final ResultSet rs = ps.executeQuery();
+ try {
+ T r = null;
+ if (rs.next()) {
+ r = newEntityInstance();
+ bindOneFetch(rs, r);
+ if (rs.next()) {
+ throw new OrmException("Multiple results");
+ }
+ }
+ return r;
+ } finally {
+ rs.close();
+ }
+ } finally {
+ ps.close();
+ }
+ } catch (SQLException e) {
+ throw new OrmException("Fetch failure: " + getRelationName(), e);
+ }
+ }
+
+ protected ListResultSet<T> queryList(final PreparedStatement ps)
+ throws OrmException {
+ try {
+ try {
+ final ResultSet rs = ps.executeQuery();
+ try {
+ final ArrayList<T> r = new ArrayList<T>();
+ while (rs.next()) {
+ final T o = newEntityInstance();
+ bindOneFetch(rs, o);
+ r.add(o);
+ }
+ return new ListResultSet<T>(r);
+ } finally {
+ rs.close();
+ }
+ } finally {
+ ps.close();
+ }
+ } catch (SQLException e) {
+ throw new OrmException("Fetch failure: " + getRelationName(), e);
+ }
+ }
+
+ @Override
+ protected void doInsert(final Iterable<T> instances, final JdbcTransaction txn)
+ throws OrmException {
+ try {
+ final PreparedStatement ps;
+
+ ps = schema.getConnection().prepareStatement(getInsertOneSql());
+ try {
+ int cnt = 0;
+ for (final T o : instances) {
+ bindOneInsert(ps, o);
+ ps.addBatch();
+ cnt++;
+ }
+ execute(ps, cnt);
+ } finally {
+ ps.close();
+ }
+ } catch (SQLException e) {
+ throw new OrmException("Insert failure: " + getRelationName(), e);
+ }
+ }
+
+ @Override
+ protected void doUpdate(final Iterable<T> instances, final JdbcTransaction txn)
+ throws OrmException {
+ try {
+ final PreparedStatement ps;
+
+ ps = schema.getConnection().prepareStatement(getUpdateOneSql());
+ try {
+ int cnt = 0;
+ for (final T o : instances) {
+ bindOneUpdate(ps, o);
+ ps.addBatch();
+ cnt++;
+ }
+ execute(ps, cnt);
+ } finally {
+ ps.close();
+ }
+ } catch (SQLException e) {
+ throw new OrmException("Update failure: " + getRelationName(), e);
+ }
+ }
+
+ @Override
+ protected void doDelete(final Iterable<T> instances, final JdbcTransaction txn)
+ throws OrmException {
+ try {
+ final PreparedStatement ps;
+
+ ps = schema.getConnection().prepareStatement(getDeleteOneSql());
+ try {
+ int cnt = 0;
+ for (final T o : instances) {
+ bindOneDelete(ps, o);
+ ps.addBatch();
+ cnt++;
+ }
+ execute(ps, cnt);
+ } finally {
+ ps.close();
+ }
+ } catch (SQLException e) {
+ throw new OrmException("Delete failure: " + getRelationName(), e);
+ }
+ }
+
+ private static void execute(final PreparedStatement ps, final int cnt)
+ throws SQLException {
+ if (cnt == 0) {
+ return;
+ }
+
+ final int[] states = ps.executeBatch();
+ if (states == null) {
+ throw new SQLException("No rows affected; expected " + cnt + " rows");
+ }
+ if (states.length != cnt) {
+ throw new SQLException("Expected " + cnt + " rows affected, received "
+ + states.length + " instead");
+ }
+ for (int i = 0; i < cnt; i++) {
+ if (states[i] != 1) {
+ throw new SQLException("Entity " + (i + 1) + " not affected by update");
+ }
+ }
+ }
+
+ protected abstract T newEntityInstance();
+
+ protected abstract String getRelationName();
+
+ protected abstract String getInsertOneSql();
+
+ protected abstract String getUpdateOneSql();
+
+ protected abstract String getDeleteOneSql();
+
+ protected abstract void bindOneInsert(PreparedStatement ps, T entity)
+ throws SQLException;
+
+ protected abstract void bindOneUpdate(PreparedStatement ps, T entity)
+ throws SQLException;
+
+ protected abstract void bindOneDelete(PreparedStatement ps, T entity)
+ throws SQLException;
+
+ protected abstract void bindOneFetch(ResultSet rs, T entity)
+ throws SQLException;
+}
diff --git a/src/com/google/gwtorm/jdbc/JdbcSchema.java b/src/com/google/gwtorm/jdbc/JdbcSchema.java
new file mode 100644
index 0000000..4327560
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/JdbcSchema.java
@@ -0,0 +1,105 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.client.Transaction;
+import com.google.gwtorm.schema.RelationModel;
+import com.google.gwtorm.schema.SchemaModel;
+import com.google.gwtorm.schema.SequenceModel;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/** Internal base class for implementations of {@link Schema}. */
+public abstract class JdbcSchema implements Schema {
+ private final Database<?> dbDef;
+ private Connection conn;
+
+ protected JdbcSchema(final Database<?> d, final Connection c) {
+ dbDef = d;
+ conn = c;
+ }
+
+ public final Connection getConnection() {
+ return conn;
+ }
+
+ public void createSchema() throws OrmException {
+ final SqlDialect dialect = dbDef.getDialect();
+ final SchemaModel model = dbDef.getSchemaModel();
+ try {
+ final Statement stmt;
+
+ stmt = getConnection().createStatement();
+ try {
+ for (final SequenceModel s : model.getSequences()) {
+ stmt.execute(s.getCreateSequenceSql(dialect));
+ }
+ for (final RelationModel r : model.getRelations()) {
+ stmt.execute(r.getCreateTableSql(dialect));
+ }
+ } finally {
+ stmt.close();
+ }
+ } catch (SQLException e) {
+ throw new OrmException("Schema creation failure", e);
+ }
+ }
+
+ protected long nextLong(final String query) throws OrmException {
+ try {
+ final Statement st = getConnection().createStatement();
+ try {
+ final ResultSet rs = st.executeQuery(query);
+ try {
+ if (!rs.next()) {
+ throw new SQLException("No result row for sequence query");
+ }
+ final long r = rs.getLong(1);
+ if (rs.next()) {
+ throw new SQLException("Too many results from sequence query");
+ }
+ return r;
+ } finally {
+ rs.close();
+ }
+ } finally {
+ st.close();
+ }
+ } catch (SQLException e) {
+ throw new OrmException("Sequence query failed", e);
+ }
+ }
+
+ public Transaction beginTransaction() {
+ return new JdbcTransaction(this);
+ }
+
+ public void close() {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (SQLException err) {
+ // TODO Handle an exception while closing a connection
+ }
+ conn = null;
+ }
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/JdbcTransaction.java b/src/com/google/gwtorm/jdbc/JdbcTransaction.java
new file mode 100644
index 0000000..80cf804
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/JdbcTransaction.java
@@ -0,0 +1,102 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.gwtorm.client.impl.AbstractTransaction;
+
+import java.sql.SQLException;
+
+/** Implementation of the {@link Transaction} interface, on JDBC. */
+class JdbcTransaction extends AbstractTransaction {
+ private final JdbcSchema schema;
+ private boolean inProgress;
+ private boolean committed;
+
+ JdbcTransaction(final JdbcSchema s) {
+ schema = s;
+ }
+
+ @Override
+ public void commit() throws OrmException {
+ notCommitted();
+
+ if (!inProgress) {
+ try {
+ schema.getConnection().setAutoCommit(false);
+ } catch (SQLException e) {
+ throw new OrmException("Cannot start transaction", e);
+ }
+ inProgress = true;
+ }
+
+ try {
+ super.commit();
+ } catch (OrmException e) {
+ try {
+ rollback();
+ } catch (OrmException e2) {
+ // Ignore the cascaded rollback error.
+ }
+ throw e;
+ } catch (RuntimeException e) {
+ try {
+ rollback();
+ } catch (OrmException e2) {
+ // Ignore the cascaded rollback error.
+ }
+ throw e;
+ }
+
+ try {
+ schema.getConnection().commit();
+ committed = true;
+ } catch (SQLException e) {
+ throw new OrmException("Transaction failed", e);
+ } finally {
+ exitTransaction();
+ }
+ }
+
+ public void rollback() throws OrmException {
+ notCommitted();
+
+ if (inProgress) {
+ try {
+ schema.getConnection().rollback();
+ } catch (SQLException e) {
+ throw new OrmException("Rollback failed", e);
+ } finally {
+ exitTransaction();
+ }
+ }
+ }
+
+ private void notCommitted() throws OrmException {
+ if (committed) {
+ throw new OrmException("Transaction already committed");
+ }
+ }
+
+ private void exitTransaction() {
+ try {
+ schema.getConnection().setAutoCommit(true);
+ } catch (SQLException e) {
+ } finally {
+ inProgress = false;
+ }
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/gen/AccessGen.java b/src/com/google/gwtorm/jdbc/gen/AccessGen.java
new file mode 100644
index 0000000..4eba97d
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/gen/AccessGen.java
@@ -0,0 +1,541 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc.gen;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.Key;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.impl.ListResultSet;
+import com.google.gwtorm.jdbc.JdbcAccess;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.schema.ColumnModel;
+import com.google.gwtorm.schema.KeyModel;
+import com.google.gwtorm.schema.QueryModel;
+import com.google.gwtorm.schema.RelationModel;
+import com.google.gwtorm.schema.Util;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.List;
+
+/** Generates a concrete implementation of an {@link Access} extension. */
+public class AccessGen implements Opcodes {
+ private static final String REL_ALIAS = "T";
+
+ private static enum DmlType {
+ INSERT("bindOneInsert"),
+
+ UPDATE("bindOneUpdate"),
+
+ DELETE("bindOneDelete");
+
+ final String methodName;
+
+ DmlType(final String m) {
+ methodName = m;
+ }
+ }
+
+ private final GeneratedClassLoader classLoader;
+ private final SchemaGen.RelationGen info;
+ private final RelationModel model;
+ private final SqlDialect dialect;
+
+ private ClassWriter cw;
+ private String superTypeName;
+ private String implClassName;
+ private String implTypeName;
+ private Type entityType;
+
+
+ public AccessGen(final GeneratedClassLoader loader,
+ final SchemaGen.RelationGen ri) {
+ classLoader = loader;
+ info = ri;
+ model = info.model;
+ dialect = ri.getDialect();
+ entityType =
+ Type.getObjectType(model.getEntityTypeClassName().replace('.', '/'));
+ }
+
+ public void defineClass() throws OrmException {
+ init();
+ implementConstructor();
+ implementGetString("getRelationName", model.getRelationName());
+ implementGetString("getInsertOneSql", model.getInsertOneSql(dialect));
+
+ if (model.getPrimaryKey() != null) {
+ if (model.getDependentColumns().isEmpty()) {
+ implementMissingGetString("getUpdateOneSql", "update");
+ } else {
+ implementGetString("getUpdateOneSql", model.getUpdateOneSql(dialect));
+ }
+ implementGetString("getDeleteOneSql", model.getDeleteOneSql(dialect));
+ } else {
+ implementMissingGetString("getUpdateOneSql", "update");
+ implementMissingGetString("getDeleteOneSql", "delete");
+ }
+
+ implementPrimaryKey();
+ implementGetOne();
+ implementNewEntityInstance();
+ implementBindOne(DmlType.INSERT);
+ implementBindOne(DmlType.UPDATE);
+ implementBindOne(DmlType.DELETE);
+ implementBindOneFetch();
+
+ if (model.getPrimaryKey() != null) {
+ implementKeyQuery(model.getPrimaryKey());
+ }
+
+ for (final QueryModel q : model.getQueries()) {
+ implementQuery(q);
+ }
+
+ cw.visitEnd();
+ classLoader.defineClass(implClassName, cw.toByteArray());
+ info.accessClassName = implClassName;
+ }
+
+
+ private void init() {
+ superTypeName = Type.getInternalName(JdbcAccess.class);
+ implClassName =
+ model.getEntityTypeClassName() + "_Access_" + model.getMethodName()
+ + "_" + Util.createRandomName();
+ implTypeName = implClassName.replace('.', '/');
+
+ cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ cw.visit(V1_3, ACC_PUBLIC | ACC_FINAL | ACC_SUPER, implTypeName, null,
+ superTypeName, new String[] {model.getAccessInterfaceName().replace(
+ '.', '/')});
+ }
+
+ private void implementConstructor() {
+ final String consName = "<init>";
+ final String consDesc =
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type
+ .getType(JdbcSchema.class)});
+ final MethodVisitor mv;
+ mv = cw.visitMethod(ACC_PUBLIC, consName, consDesc, null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, superTypeName, consName, consDesc);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementGetString(final String methodName,
+ final String returnValue) {
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, methodName, Type
+ .getMethodDescriptor(Type.getType(String.class), new Type[] {}),
+ null, null);
+ mv.visitCode();
+ mv.visitLdcInsn(returnValue);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementMissingGetString(final String methodName,
+ final String why) {
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, methodName, Type
+ .getMethodDescriptor(Type.getType(String.class), new Type[] {}),
+ null, null);
+ mv.visitCode();
+ throwUnsupported(mv, model.getMethodName() + " does not support " + why);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void throwUnsupported(final MethodVisitor mv, final String message) {
+ final Type eType = Type.getType(UnsupportedOperationException.class);
+ mv.visitTypeInsn(NEW, eType.getInternalName());
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn(message);
+ mv.visitMethodInsn(INVOKESPECIAL, eType.getInternalName(), "<init>", Type
+ .getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type
+ .getType(String.class)}));
+ mv.visitInsn(ATHROW);
+ }
+
+ private void implementPrimaryKey() {
+ final KeyModel pk = model.getPrimaryKey();
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "primaryKey", Type
+ .getMethodDescriptor(Type.getType(Key.class), new Type[] {Type
+ .getType(Object.class)}), null, null);
+ mv.visitCode();
+ if (pk != null && pk.getField().isNested()) {
+ final ColumnModel pkf = pk.getField();
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
+ mv.visitFieldInsn(GETFIELD, entityType.getInternalName(), pkf
+ .getFieldName(), CodeGenSupport.toType(pkf).getDescriptor());
+ } else {
+ mv.visitInsn(ACONST_NULL);
+ }
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementGetOne() {
+ final KeyModel pk = model.getPrimaryKey();
+
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "get", Type.getMethodDescriptor(
+ Type.getType(Object.class), new Type[] {Type.getType(Key.class)}),
+ null, new String[] {Type.getType(OrmException.class)
+ .getInternalName()});
+ mv.visitCode();
+ if (pk != null && pk.getField().isNested()) {
+ final Type keyType = CodeGenSupport.toType(pk.getField());
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitTypeInsn(CHECKCAST, keyType.getInternalName());
+ mv.visitMethodInsn(INVOKEVIRTUAL, implTypeName, pk.getName(), Type
+ .getMethodDescriptor(entityType, new Type[] {keyType}));
+ mv.visitInsn(ARETURN);
+ } else {
+ throwUnsupported(mv, model.getMethodName() + " does not support get(Key)");
+ }
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementNewEntityInstance() {
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "newEntityInstance", Type
+ .getMethodDescriptor(Type.getType(Object.class), new Type[] {}),
+ null, null);
+ mv.visitCode();
+ mv.visitTypeInsn(NEW, entityType.getInternalName());
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, entityType.getInternalName(), "<init>",
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementBindOne(final DmlType type) {
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, type.methodName, Type
+ .getMethodDescriptor(Type.VOID_TYPE, new Type[] {
+ Type.getType(PreparedStatement.class),
+ Type.getType(Object.class)}), null, new String[] {Type.getType(
+ SQLException.class).getInternalName()});
+ mv.visitCode();
+
+ if (type != DmlType.INSERT && model.getPrimaryKey() == null) {
+ throwUnsupported(mv, model.getMethodName() + " has no primary key");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
+ mv.visitVarInsn(ASTORE, 2);
+
+ final CodeGenSupport cgs = new CodeGenSupport(mv);
+ cgs.setEntityType(entityType);
+ if (type != DmlType.DELETE) {
+ for (final ColumnModel field : model.getDependentFields()) {
+ if (field.isNested()) {
+ final Label isnull = new Label();
+ final Label end = new Label();
+
+ cgs.setFieldReference(field, false);
+ cgs.pushFieldValue();
+ mv.visitJumpInsn(IFNULL, isnull);
+ for (final ColumnModel c : field.getAllLeafColumns()) {
+ cgs.setFieldReference(c);
+ dialect.getSqlTypeInfo(c).generatePreparedStatementSet(cgs);
+ }
+ mv.visitJumpInsn(GOTO, end);
+
+ mv.visitLabel(isnull);
+ for (final ColumnModel c : field.getAllLeafColumns()) {
+ cgs.setFieldReference(c);
+ dialect.getSqlTypeInfo(c).generatePreparedStatementNull(cgs);
+ }
+
+ mv.visitLabel(end);
+ } else {
+ cgs.setFieldReference(field);
+ dialect.getSqlTypeInfo(field).generatePreparedStatementSet(cgs);
+ }
+ }
+ }
+
+ for (final ColumnModel col : model.getPrimaryKeyColumns()) {
+ cgs.setFieldReference(col);
+ dialect.getSqlTypeInfo(col).generatePreparedStatementSet(cgs);
+ }
+
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementBindOneFetch() {
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "bindOneFetch", Type
+ .getMethodDescriptor(Type.VOID_TYPE, new Type[] {
+ Type.getType(ResultSet.class), Type.getType(Object.class)}),
+ null, new String[] {Type.getType(SQLException.class)
+ .getInternalName()});
+ mv.visitCode();
+
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
+ mv.visitVarInsn(ASTORE, 2);
+
+ final CodeGenSupport cgs = new CodeGenSupport(mv);
+ cgs.setEntityType(entityType);
+
+ if (model.getPrimaryKey() != null
+ && model.getPrimaryKey().getField().isNested()) {
+ final ColumnModel pkf = model.getPrimaryKey().getField();
+ final Type vType = CodeGenSupport.toType(pkf);
+ cgs.setFieldReference(pkf, false);
+ cgs.fieldSetBegin();
+ mv.visitTypeInsn(NEW, vType.getInternalName());
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, vType.getInternalName(), "<init>", Type
+ .getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
+ cgs.fieldSetEnd();
+ }
+
+ for (final ColumnModel field : model.getDependentFields()) {
+ if (field.isNested()) {
+ final Type vType = CodeGenSupport.toType(field);
+ final Label islive = new Label();
+
+ cgs.setFieldReference(field, false);
+ cgs.fieldSetBegin();
+ mv.visitTypeInsn(NEW, vType.getInternalName());
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, vType.getInternalName(), "<init>",
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
+ cgs.fieldSetEnd();
+
+ for (final ColumnModel c : field.getAllLeafColumns()) {
+ cgs.setFieldReference(c);
+ dialect.getSqlTypeInfo(c).generateResultSetGet(cgs);
+ }
+
+ cgs.pushSqlHandle();
+ mv.visitMethodInsn(INVOKEINTERFACE, Type.getType(ResultSet.class)
+ .getInternalName(), "wasNull", Type.getMethodDescriptor(
+ Type.BOOLEAN_TYPE, new Type[] {}));
+ mv.visitJumpInsn(IFEQ, islive);
+ cgs.setFieldReference(field, false);
+ cgs.fieldSetBegin();
+ mv.visitInsn(ACONST_NULL);
+ cgs.fieldSetEnd();
+ mv.visitLabel(islive);
+ } else {
+ cgs.setFieldReference(field);
+ dialect.getSqlTypeInfo(field).generateResultSetGet(cgs);
+ }
+ }
+ for (final ColumnModel col : model.getPrimaryKeyColumns()) {
+ cgs.setFieldReference(col);
+ dialect.getSqlTypeInfo(col).generateResultSetGet(cgs);
+ }
+
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementKeyQuery(final KeyModel info) {
+ final Type keyType = CodeGenSupport.toType(info.getField());
+ final StringBuilder query = new StringBuilder();
+ query.append(model.getSelectSql(dialect, REL_ALIAS));
+ query.append(" WHERE ");
+ int nth = 1;
+ for (final Iterator<ColumnModel> i = info.getAllLeafColumns().iterator(); i
+ .hasNext();) {
+ final ColumnModel c = i.next();
+ query.append(REL_ALIAS);
+ query.append('.');
+ query.append(c.getColumnName());
+ query.append('=');
+ query.append(dialect.getParameterPlaceHolder(nth++));
+ if (i.hasNext()) {
+ query.append(" AND ");
+ }
+ }
+
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, info.getName(), Type
+ .getMethodDescriptor(entityType, new Type[] {keyType}), null,
+ new String[] {Type.getType(OrmException.class).getInternalName()});
+ mv.visitCode();
+
+ final int keyvar = 1, psvar = keyvar + keyType.getSize();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitLdcInsn(query.toString());
+ mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "prepareStatement", Type
+ .getMethodDescriptor(Type.getType(PreparedStatement.class),
+ new Type[] {Type.getType(String.class)}));
+ mv.visitVarInsn(ASTORE, psvar);
+
+ final CodeGenSupport cgs = new CodeGenSupport(mv) {
+ @Override
+ public void pushSqlHandle() {
+ mv.visitVarInsn(ALOAD, psvar);
+ }
+
+ @Override
+ public void pushFieldValue() {
+ appendGetField(getFieldReference());
+ }
+
+ @Override
+ protected void appendGetField(final ColumnModel c) {
+ if (c.getParent() == null) {
+ loadVar(keyType, keyvar);
+ } else {
+ super.appendGetField(c);
+ }
+ }
+ };
+ for (final ColumnModel c : info.getAllLeafColumns()) {
+ cgs.setFieldReference(c);
+ dialect.getSqlTypeInfo(c).generatePreparedStatementSet(cgs);
+ }
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, psvar);
+ mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "queryOne", Type
+ .getMethodDescriptor(Type.getType(Object.class), new Type[] {Type
+ .getType(PreparedStatement.class)}));
+ mv.visitTypeInsn(CHECKCAST, entityType.getInternalName());
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementQuery(final QueryModel info) {
+ final List<ColumnModel> pCols = info.getParameters();
+ final boolean hasLimitParam = info.hasLimitParameter();
+ final Type[] pTypes = new Type[pCols.size() + (hasLimitParam ? 1 : 0)];
+ final int[] pVars = new int[pTypes.length];
+ int nextVar = 1;
+ for (int i = 0; i < pCols.size(); i++) {
+ pTypes[i] = CodeGenSupport.toType(pCols.get(i));
+ pVars[i] = nextVar;
+ nextVar += pTypes[i].getSize();
+ }
+ if (hasLimitParam) {
+ pTypes[pTypes.length - 1] = Type.INT_TYPE;
+ pVars[pTypes.length - 1] = nextVar;
+ nextVar += Type.INT_TYPE.getSize();
+ }
+
+ final int psvar = nextVar++;
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, info.getName(), Type
+ .getMethodDescriptor(Type
+ .getType(com.google.gwtorm.client.ResultSet.class), pTypes),
+ null, new String[] {Type.getType(OrmException.class)
+ .getInternalName()});
+ mv.visitCode();
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitLdcInsn(info.getSelectSql(dialect, REL_ALIAS));
+ mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "prepareStatement", Type
+ .getMethodDescriptor(Type.getType(PreparedStatement.class),
+ new Type[] {Type.getType(String.class)}));
+ mv.visitVarInsn(ASTORE, psvar);
+
+ final int argIdx[] = new int[] {0};
+ final CodeGenSupport cgs = new CodeGenSupport(mv) {
+ @Override
+ public void pushSqlHandle() {
+ mv.visitVarInsn(ALOAD, psvar);
+ }
+
+ @Override
+ public void pushFieldValue() {
+ appendGetField(getFieldReference());
+ }
+
+ @Override
+ protected void appendGetField(final ColumnModel c) {
+ final int n = argIdx[0];
+ if (c == pCols.get(n)) {
+ loadVar(pTypes[n], pVars[n]);
+ } else {
+ super.appendGetField(c);
+ }
+ }
+ };
+ for (final ColumnModel c : pCols) {
+ if (c.isNested()) {
+ for (final ColumnModel n : c.getAllLeafColumns()) {
+ cgs.setFieldReference(n);
+ dialect.getSqlTypeInfo(n).generatePreparedStatementSet(cgs);
+ }
+ } else {
+ cgs.setFieldReference(c);
+ dialect.getSqlTypeInfo(c).generatePreparedStatementSet(cgs);
+ }
+ argIdx[0]++;
+ }
+
+ if (info.hasLimit()) {
+ if (hasLimitParam || !dialect.selectHasLimit()) {
+ mv.visitVarInsn(ALOAD, psvar);
+ if (hasLimitParam) {
+ mv.visitVarInsn(ILOAD, pVars[pTypes.length - 1]);
+ } else {
+ cgs.push(info.getStaticLimit());
+ }
+ mv.visitMethodInsn(INVOKEINTERFACE, Type.getType(
+ PreparedStatement.class).getInternalName(), "setMaxRows", Type
+ .getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.INT_TYPE}));
+ }
+ }
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, psvar);
+ mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "queryList", Type
+ .getMethodDescriptor(Type.getType(ListResultSet.class),
+ new Type[] {Type.getType(PreparedStatement.class)}));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/gen/CodeGenSupport.java b/src/com/google/gwtorm/jdbc/gen/CodeGenSupport.java
new file mode 100644
index 0000000..97bf2f1
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/gen/CodeGenSupport.java
@@ -0,0 +1,178 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc.gen;
+
+import com.google.gwtorm.schema.ColumnModel;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+public class CodeGenSupport implements Opcodes {
+ public final MethodVisitor mv;
+ private ColumnModel col;
+ private int columnIdx;
+ private Type entityType;
+
+ public CodeGenSupport(final MethodVisitor method) {
+ mv = method;
+ }
+
+ public void push(final int val) {
+ switch (val) {
+ case -1:
+ mv.visitInsn(ICONST_M1);
+ break;
+ case 0:
+ mv.visitInsn(ICONST_0);
+ break;
+ case 1:
+ mv.visitInsn(ICONST_1);
+ break;
+ case 2:
+ mv.visitInsn(ICONST_2);
+ break;
+ case 3:
+ mv.visitInsn(ICONST_3);
+ break;
+ case 4:
+ mv.visitInsn(ICONST_4);
+ break;
+ case 5:
+ mv.visitInsn(ICONST_5);
+ break;
+ default:
+ if (Byte.MIN_VALUE >= val && val < Byte.MAX_VALUE) {
+ mv.visitIntInsn(BIPUSH, val);
+ } else if (Short.MIN_VALUE >= val && val < Short.MAX_VALUE) {
+ mv.visitIntInsn(SIPUSH, val);
+ } else {
+ mv.visitLdcInsn(Integer.valueOf(val));
+ }
+ break;
+ }
+ }
+
+ public void loadVar(final Type type, final int index) {
+ mv.visitVarInsn(type.getOpcode(ILOAD), index);
+ }
+
+ public void setEntityType(final Type et) {
+ entityType = et;
+ }
+
+ public void setFieldReference(final ColumnModel cm) {
+ setFieldReference(cm, true);
+ }
+
+ public void setFieldReference(final ColumnModel cm, boolean incr) {
+ col = cm;
+ if (incr) {
+ columnIdx++;
+ }
+ }
+
+ public ColumnModel getFieldReference() {
+ return col;
+ }
+
+ public void pushSqlHandle() {
+ mv.visitVarInsn(ALOAD, 1);
+ }
+
+ public void pushEntity() {
+ mv.visitVarInsn(ALOAD, 2);
+ }
+
+ public void pushColumnIndex() {
+ push(columnIdx);
+ }
+
+ public void invokePreparedStatementSet(final String sqlTypeName) {
+ final Method m;
+ try {
+ m =
+ PreparedStatement.class.getMethod("set" + sqlTypeName, Integer.TYPE,
+ ResultSet.class.getMethod("get" + sqlTypeName, Integer.TYPE)
+ .getReturnType());
+ } catch (SecurityException e) {
+ throw new RuntimeException("java.sql has no " + sqlTypeName);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("java.sql has no " + sqlTypeName, e);
+ }
+ mv.visitMethodInsn(INVOKEINTERFACE, Type
+ .getInternalName(PreparedStatement.class), m.getName(), Type
+ .getMethodDescriptor(m));
+ }
+
+ public void invokeResultSetGet(final String sqlTypeName) {
+ final Method m;
+ try {
+ m = ResultSet.class.getMethod("get" + sqlTypeName, Integer.TYPE);
+ } catch (SecurityException e) {
+ throw new RuntimeException("java.sql has no " + sqlTypeName);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("java.sql has no " + sqlTypeName, e);
+ }
+ mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(ResultSet.class),
+ m.getName(), Type.getMethodDescriptor(m));
+ }
+
+ public void fieldSetBegin() {
+ pushEntity();
+ if (col.getParent() != null) {
+ appendGetField(col.getParent());
+ }
+ }
+
+ public void fieldSetEnd() {
+ final Type c = containerClass(col);
+ mv.visitFieldInsn(PUTFIELD, c.getInternalName(), col.getFieldName(),
+ toType(col).getDescriptor());
+ }
+
+ public void pushFieldValue() {
+ pushEntity();
+ appendGetField(col);
+ }
+
+ protected void appendGetField(final ColumnModel c) {
+ if (c.getParent() != null) {
+ appendGetField(c.getParent());
+ }
+ final Type t = containerClass(c);
+ mv.visitFieldInsn(GETFIELD, t.getInternalName(), c.getFieldName(),
+ toType(c).getDescriptor());
+ }
+
+ private Type containerClass(final ColumnModel c) {
+ if (c.getParent() == null) {
+ return entityType;
+ }
+ final String n = c.getParent().getNestedClassName();
+ return Type.getObjectType(n.replace('.', '/'));
+ }
+
+ static Type toType(final ColumnModel c) {
+ if (c.isSqlPrimitive()) {
+ return Type.getType(c.getPrimitiveType());
+ }
+ return Type.getObjectType(c.getNestedClassName().replace('.', '/'));
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/gen/GeneratedClassLoader.java b/src/com/google/gwtorm/jdbc/gen/GeneratedClassLoader.java
new file mode 100644
index 0000000..53643d3
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/gen/GeneratedClassLoader.java
@@ -0,0 +1,90 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc.gen;
+
+import com.google.gwtorm.client.OrmException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Hacked ClassLoader to inject generated code into the parent.
+ * <p>
+ * This ClassLoader allows our code generators to inject their generated classes
+ * into the parent ClassLoader, which should be the same ClassLoader that
+ * defined the application's Schema interface extension. This is necessary to
+ * ensure the generated classes can access protected and default-access fields
+ * within the entities.
+ */
+public class GeneratedClassLoader extends ClassLoader {
+ private static final boolean debugCodeGen;
+
+ private static final Method defineClass;
+
+ static {
+ debugCodeGen = "true".equals(System.getProperty("gwtorm.debugCodeGen"));
+
+ Method m;
+ try {
+ m =
+ ClassLoader.class.getDeclaredMethod("defineClass", String.class,
+ byte[].class, Integer.TYPE, Integer.TYPE);
+ m.setAccessible(true);
+ } catch (SecurityException e) {
+ throw new LinkageError("No defineClass in ClassLoader");
+ } catch (NoSuchMethodException e) {
+ throw new LinkageError("No defineClass in ClassLoader");
+ }
+ defineClass = m;
+ }
+
+ public GeneratedClassLoader(final ClassLoader parent) {
+ super(parent);
+ }
+
+ void defineClass(final String name, final byte[] code) throws OrmException {
+ if (debugCodeGen) {
+ final File outClassFile =
+ new File("generated_classes/" + name.replace('.', '/') + ".class");
+ outClassFile.getParentFile().mkdirs();
+ try {
+ final FileOutputStream out = new FileOutputStream(outClassFile);
+ try {
+ out.write(code);
+ } finally {
+ out.close();
+ }
+ } catch (IOException e) {
+ throw new OrmException("Cannot save debug class " + outClassFile, e);
+ }
+ }
+
+ try {
+ defineClass.invoke(getParent(), name, code, Integer.valueOf(0), Integer
+ .valueOf(code.length));
+ } catch (IllegalArgumentException e) {
+ throw new OrmException("Unable to inject class " + name, e);
+ } catch (SecurityException e) {
+ throw new OrmException("Unable to inject class " + name, e);
+ } catch (IllegalAccessException e) {
+ throw new OrmException("Unable to inject class " + name, e);
+ } catch (InvocationTargetException e) {
+ throw new OrmException("Unable to inject class " + name, e);
+ }
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/gen/SchemaFactoryGen.java b/src/com/google/gwtorm/jdbc/gen/SchemaFactoryGen.java
new file mode 100644
index 0000000..094888c
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/gen/SchemaFactoryGen.java
@@ -0,0 +1,114 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc.gen;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.jdbc.AbstractSchemaFactory;
+import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.schema.Util;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.sql.Connection;
+
+/** Generates a factory to efficiently create new Schema instances. */
+public class SchemaFactoryGen<T extends Schema> implements Opcodes {
+ private final GeneratedClassLoader classLoader;
+ private final SchemaGen schemaGen;
+ private ClassWriter cw;
+ private String superTypeName;
+ private String implClassName;
+ private String implTypeName;
+
+ public SchemaFactoryGen(final GeneratedClassLoader loader, final SchemaGen gen) {
+ classLoader = loader;
+ schemaGen = gen;
+ }
+
+ public void defineClass() throws OrmException {
+ init();
+ implementEmptyConstructor();
+ implementCreate();
+ cw.visitEnd();
+ classLoader.defineClass(implClassName, cw.toByteArray());
+ }
+
+ public AbstractSchemaFactory<T> create() throws OrmException {
+ defineClass();
+ try {
+ return cast(Class.forName(implClassName, true, classLoader).newInstance());
+ } catch (InstantiationException e) {
+ throw new OrmException("Cannot create schema factory", e);
+ } catch (IllegalAccessException e) {
+ throw new OrmException("Cannot create schema factory", e);
+ } catch (ClassNotFoundException e) {
+ throw new OrmException("Cannot create schema factory", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private AbstractSchemaFactory<T> cast(final Object newInstance) {
+ return (AbstractSchemaFactory<T>) newInstance;
+ }
+
+ private void init() {
+ superTypeName = Type.getInternalName(AbstractSchemaFactory.class);
+ implClassName =
+ schemaGen.getSchemaClassName() + "_Factory_" + Util.createRandomName();
+ implTypeName = implClassName.replace('.', '/');
+
+ cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ cw.visit(V1_3, ACC_PUBLIC | ACC_FINAL | ACC_SUPER, implTypeName, null,
+ superTypeName, null);
+ }
+
+ private void implementEmptyConstructor() {
+ final String consName = "<init>";
+ final String consDesc =
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {});
+ final MethodVisitor mv;
+ mv = cw.visitMethod(ACC_PUBLIC, consName, consDesc, null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, superTypeName, consName, consDesc);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementCreate() {
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "create", Type
+ .getMethodDescriptor(Type.getType(Schema.class), new Type[] {
+ Type.getType(Database.class), Type.getType(Connection.class)}),
+ null, null);
+ mv.visitCode();
+
+ mv.visitTypeInsn(NEW, schemaGen.getImplTypeName());
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKESPECIAL, schemaGen.getImplTypeName(), "<init>",
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
+ Type.getType(Database.class), Type.getType(Connection.class)}));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+}
diff --git a/src/com/google/gwtorm/jdbc/gen/SchemaGen.java b/src/com/google/gwtorm/jdbc/gen/SchemaGen.java
new file mode 100644
index 0000000..ee1bb89
--- /dev/null
+++ b/src/com/google/gwtorm/jdbc/gen/SchemaGen.java
@@ -0,0 +1,205 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.jdbc.gen;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.schema.RelationModel;
+import com.google.gwtorm.schema.SequenceModel;
+import com.google.gwtorm.schema.Util;
+import com.google.gwtorm.schema.java.JavaSchemaModel;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.sql.Connection;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Generates a concrete implementation of a {@link Schema} extension. */
+public class SchemaGen implements Opcodes {
+ private final GeneratedClassLoader classLoader;
+ private final JavaSchemaModel schema;
+ private final SqlDialect dialect;
+ private List<RelationGen> relations;
+ private ClassWriter cw;
+ private String superTypeName;
+ private String implClassName;
+ private String implTypeName;
+
+ public SchemaGen(final GeneratedClassLoader loader,
+ final JavaSchemaModel schemaModel, final SqlDialect sqlDialect) {
+ classLoader = loader;
+ schema = schemaModel;
+ dialect = sqlDialect;
+ }
+
+ public void defineClass() throws OrmException {
+ defineRelationClasses();
+
+ init();
+ implementRelationFields();
+ implementConstructor();
+ implementSequenceMethods();
+ implementRelationMethods();
+ cw.visitEnd();
+ classLoader.defineClass(getImplClassName(), cw.toByteArray());
+ }
+
+ String getSchemaClassName() {
+ return schema.getSchemaClassName();
+ }
+
+ String getImplClassName() {
+ return implClassName;
+ }
+
+ String getImplTypeName() {
+ return implTypeName;
+ }
+
+ private void defineRelationClasses() throws OrmException {
+ relations = new ArrayList<RelationGen>();
+ for (final RelationModel rel : schema.getRelations()) {
+ final RelationGen g = new RelationGen(rel);
+ relations.add(g);
+ new AccessGen(classLoader, g).defineClass();
+ }
+ }
+
+ private void init() {
+ superTypeName = Type.getInternalName(JdbcSchema.class);
+ implClassName = getSchemaClassName() + "_Schema_" + Util.createRandomName();
+ implTypeName = implClassName.replace('.', '/');
+
+ cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ cw.visit(V1_3, ACC_PUBLIC | ACC_FINAL | ACC_SUPER, implTypeName, null,
+ superTypeName, new String[] {getSchemaClassName().replace('.', '/')});
+ }
+
+ private void implementRelationFields() {
+ for (final RelationGen info : relations) {
+ info.implementField();
+ }
+ }
+
+ private void implementConstructor() {
+ final String consName = "<init>";
+ final String consDesc =
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
+ Type.getType(Database.class), Type.getType(Connection.class)});
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC, consName, consDesc, null, null);
+ mv.visitCode();
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKESPECIAL, superTypeName, consName, consDesc);
+
+ for (final RelationGen info : relations) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitTypeInsn(NEW, info.accessType.getInternalName());
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, info.accessType.getInternalName(),
+ consName, Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type
+ .getType(JdbcSchema.class)}));
+ mv.visitFieldInsn(PUTFIELD, implTypeName, info
+ .getAccessInstanceFieldName(), info.accessType.getDescriptor());
+ }
+
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ private void implementSequenceMethods() {
+ for (final SequenceModel seq : schema.getSequences()) {
+ final Type retType = Type.getType(seq.getResultType());
+ final MethodVisitor mv =
+ cw
+ .visitMethod(ACC_PUBLIC, seq.getMethodName(), Type
+ .getMethodDescriptor(retType, new Type[] {}), null,
+ new String[] {Type.getType(OrmException.class)
+ .getInternalName()});
+ mv.visitCode();
+
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitLdcInsn(dialect.getNextSequenceValueSql(seq.getSequenceName()));
+ mv.visitMethodInsn(INVOKEVIRTUAL, superTypeName, "nextLong", Type
+ .getMethodDescriptor(Type.getType(Long.TYPE), new Type[] {Type
+ .getType(String.class)}));
+ if (retType.getSize() == 1) {
+ mv.visitInsn(L2I);
+ mv.visitInsn(IRETURN);
+ } else {
+ mv.visitInsn(LRETURN);
+ }
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+ }
+
+ private void implementRelationMethods() {
+ for (final RelationGen info : relations) {
+ info.implementMethod();
+ }
+ }
+
+ class RelationGen {
+ final RelationModel model;
+ String accessClassName;
+ Type accessType;
+
+ RelationGen(final RelationModel model) {
+ this.model = model;
+ }
+
+ SqlDialect getDialect() {
+ return SchemaGen.this.dialect;
+ }
+
+ void implementField() {
+ accessType = Type.getObjectType(accessClassName.replace('.', '/'));
+ cw.visitField(ACC_PRIVATE | ACC_FINAL, getAccessInstanceFieldName(),
+ accessType.getDescriptor(), null, null).visitEnd();
+ }
+
+ String getAccessInstanceFieldName() {
+ return "access_" + model.getMethodName();
+ }
+
+ void implementMethod() {
+ final MethodVisitor mv =
+ cw.visitMethod(ACC_PUBLIC | ACC_FINAL, model.getMethodName(), Type
+ .getMethodDescriptor(Type.getObjectType(model
+ .getAccessInterfaceName().replace('.', '/')), new Type[] {}),
+ null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, implTypeName, getAccessInstanceFieldName(),
+ accessType.getDescriptor());
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+ }
+}
diff --git a/src/com/google/gwtorm/rebind/AccessCreator.java b/src/com/google/gwtorm/rebind/AccessCreator.java
new file mode 100644
index 0000000..240d748
--- /dev/null
+++ b/src/com/google/gwtorm/rebind/AccessCreator.java
@@ -0,0 +1,111 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.rebind;
+
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JPackage;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+
+import java.io.PrintWriter;
+
+class AccessCreator {
+ private static final String ACCESS_SUFFIX = "_DataAccess";
+ private JClassType svcInf;
+
+ AccessCreator(final JClassType remoteService) {
+ svcInf = remoteService;
+ }
+
+ String create(final TreeLogger logger, final GeneratorContext context)
+ throws UnableToCompleteException {
+ final TypeOracle typeOracle = context.getTypeOracle();
+
+ final SourceWriter srcWriter = getSourceWriter(logger, context);
+ if (srcWriter == null) {
+ return getProxyQualifiedName();
+ }
+
+ srcWriter.commit(logger);
+
+ return getProxyQualifiedName();
+ }
+
+ private SourceWriter getSourceWriter(final TreeLogger logger,
+ final GeneratorContext ctx) {
+ final JPackage servicePkg = svcInf.getPackage();
+ final String pkgName = servicePkg == null ? "" : servicePkg.getName();
+ final PrintWriter pw;
+ final ClassSourceFileComposerFactory cf;
+
+ pw = ctx.tryCreate(logger, pkgName, getProxySimpleName());
+ if (pw == null) {
+ return null;
+ }
+
+ cf = new ClassSourceFileComposerFactory(pkgName, getProxySimpleName());
+ cf.addImplementedInterface(svcInf.getErasedType().getQualifiedSourceName());
+ return cf.createSourceWriter(ctx, pw);
+ }
+
+ private String getProxyQualifiedName() {
+ final String[] name = synthesizeTopLevelClassName(svcInf, ACCESS_SUFFIX);
+ return name[0].length() == 0 ? name[1] : name[0] + "." + name[1];
+ }
+
+ private String getProxySimpleName() {
+ return synthesizeTopLevelClassName(svcInf, ACCESS_SUFFIX)[1];
+ }
+
+ static String[] synthesizeTopLevelClassName(JClassType type, String suffix) {
+ // Gets the basic name of the type. If it's a nested type, the type name
+ // will contains dots.
+ //
+ String className;
+ String packageName;
+
+ JType leafType = type.getLeafType();
+ if (leafType.isPrimitive() != null) {
+ className = leafType.getSimpleSourceName();
+ packageName = "";
+ } else {
+ JClassType classOrInterface = leafType.isClassOrInterface();
+ assert (classOrInterface != null);
+ className = classOrInterface.getName();
+ packageName = classOrInterface.getPackage().getName();
+ }
+
+ JArrayType isArray = type.isArray();
+ if (isArray != null) {
+ className += "_Array_Rank_" + isArray.getRank();
+ }
+
+ // Add the meaningful suffix.
+ //
+ className += suffix;
+
+ // Make it a top-level name.
+ //
+ className = className.replace('.', '_');
+
+ return new String[] {packageName, className};
+ }
+}
diff --git a/src/com/google/gwtorm/rebind/DataAccessGenerator.java b/src/com/google/gwtorm/rebind/DataAccessGenerator.java
new file mode 100644
index 0000000..31a9547
--- /dev/null
+++ b/src/com/google/gwtorm/rebind/DataAccessGenerator.java
@@ -0,0 +1,53 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.rebind;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+public class DataAccessGenerator extends Generator {
+ @Override
+ public String generate(final TreeLogger logger, final GeneratorContext ctx,
+ final String requestedClass) throws UnableToCompleteException {
+
+ final TypeOracle typeOracle = ctx.getTypeOracle();
+ assert (typeOracle != null);
+
+ final JClassType remoteService = typeOracle.findType(requestedClass);
+ if (remoteService == null) {
+ logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+ + requestedClass + "'", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (remoteService.isInterface() == null) {
+ logger.log(TreeLogger.ERROR, remoteService.getQualifiedSourceName()
+ + " is not an interface", null);
+ throw new UnableToCompleteException();
+ }
+
+ AccessCreator proxyCreator = new AccessCreator(remoteService);
+
+ TreeLogger proxyLogger =
+ logger.branch(TreeLogger.DEBUG, "Generating data access for '"
+ + remoteService.getQualifiedSourceName() + "'", null);
+
+ return proxyCreator.create(proxyLogger, ctx);
+ }
+}
diff --git a/src/com/google/gwtorm/schema/ColumnModel.java b/src/com/google/gwtorm/schema/ColumnModel.java
new file mode 100644
index 0000000..8122d52
--- /dev/null
+++ b/src/com/google/gwtorm/schema/ColumnModel.java
@@ -0,0 +1,133 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.OrmException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+public abstract class ColumnModel {
+ protected ColumnModel parent;
+ private String origName;
+ protected String columnName;
+ protected Column column;
+ protected Collection<ColumnModel> nestedColumns;
+ protected boolean inPrimaryKey;
+
+ protected ColumnModel() {
+ nestedColumns = Collections.<ColumnModel> emptyList();
+ }
+
+ protected void initName(final String fieldName, final Column col)
+ throws OrmException {
+ if (col == null) {
+ throw new OrmException("Field " + fieldName + " is missing "
+ + Column.class.getName() + " annotation");
+ }
+ column = col;
+ origName = Util.any(column.name(), fieldName);
+ columnName = origName;
+ }
+
+ protected void initNestedColumns(final Collection<? extends ColumnModel> col)
+ throws OrmException {
+ if (col == null || col.isEmpty()) {
+ throw new OrmException("Field " + getPathToFieldName()
+ + " has no nested members inside type " + getNestedClassName());
+ }
+
+ nestedColumns = new ArrayList<ColumnModel>(col);
+ recomputeColumnNames();
+ }
+
+ private void recomputeColumnNames() {
+ for (final ColumnModel c : nestedColumns) {
+ c.parent = this;
+ if (nestedColumns.size() == 1) {
+ c.columnName = columnName;
+ } else {
+ c.columnName = columnName + "_" + c.origName;
+ }
+ c.recomputeColumnNames();
+ }
+ }
+
+ public Collection<ColumnModel> getNestedColumns() {
+ return nestedColumns;
+ }
+
+ public Collection<ColumnModel> getAllLeafColumns() {
+ final ArrayList<ColumnModel> r = new ArrayList<ColumnModel>();
+ for (final ColumnModel c : nestedColumns) {
+ if (c.isNested()) {
+ r.addAll(c.getNestedColumns());
+ } else {
+ r.add(c);
+ }
+ }
+ return r;
+ }
+
+ public ColumnModel getParent() {
+ return parent;
+ }
+
+ public String getPathToFieldName() {
+ if (getParent() == null) {
+ return getFieldName();
+ }
+ return getParent().getPathToFieldName() + "." + getFieldName();
+ }
+
+ public String getColumnName() {
+ return columnName;
+ }
+
+ public boolean isSqlPrimitive() {
+ return getPrimitiveType() != null;
+ }
+
+ public boolean isNested() {
+ return getPrimitiveType() == null;
+ }
+
+ public Column getColumnAnnotation() {
+ return column;
+ }
+
+ public abstract String getFieldName();
+
+ public abstract Class<?> getPrimitiveType();
+
+ public abstract String getNestedClassName();
+
+ @Override
+ public String toString() {
+ final StringBuilder r = new StringBuilder();
+ r.append("Column[\n");
+ r.append(" field: " + getPathToFieldName() + "\n");
+ r.append(" column: " + getColumnName() + "\n");
+ if (isSqlPrimitive()) {
+ r.append(" type: " + getPrimitiveType().getName() + "\n");
+ } else {
+ r.append(" contains: " + getNestedClassName() + "\n");
+ }
+ r.append("]");
+ return r.toString();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/KeyModel.java b/src/com/google/gwtorm/schema/KeyModel.java
new file mode 100644
index 0000000..d298ffa
--- /dev/null
+++ b/src/com/google/gwtorm/schema/KeyModel.java
@@ -0,0 +1,58 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class KeyModel {
+ protected final String accessName;
+ protected final ColumnModel key;
+
+ public KeyModel(final String name, final ColumnModel keyField) {
+ accessName = name;
+ key = keyField;
+ }
+
+ public String getName() {
+ return accessName;
+ }
+
+ public ColumnModel getField() {
+ return key;
+ }
+
+ public Collection<ColumnModel> getAllLeafColumns() {
+ if (key.isNested()) {
+ return key.getAllLeafColumns();
+ }
+ return Collections.singleton(key);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder r = new StringBuilder();
+ r.append("Key[");
+ r.append(getName());
+ r.append(" / ");
+ r.append(key.getPathToFieldName());
+ r.append(":");
+ for (final ColumnModel c : getAllLeafColumns()) {
+ r.append(" " + c.getColumnName());
+ }
+ r.append("]");
+ return r.toString();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/Query.g b/src/com/google/gwtorm/schema/Query.g
new file mode 100644
index 0000000..e097764
--- /dev/null
+++ b/src/com/google/gwtorm/schema/Query.g
@@ -0,0 +1,215 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+grammar Query;
+options {
+ language = Java;
+ output = AST;
+}
+
+tokens {
+ WHERE;
+ ORDER;
+ BY;
+ AND;
+ LT;
+ LE;
+ GT;
+ GE;
+ EQ;
+ NE;
+ ID;
+ PLACEHOLDER;
+ COMMA;
+ ASC;
+ DESC;
+ LIMIT;
+ CONSTANT_INTEGER;
+ CONSTANT_STRING;
+ TRUE;
+ FALSE;
+}
+
+@header {
+package com.google.gwtorm.schema;
+}
+@members {
+ public static Tree parse(final RelationModel m, final String str)
+ throws QueryParseException {
+ try {
+ final QueryParser p = new QueryParser(
+ new TokenRewriteStream(
+ new QueryLexer(
+ new ANTLRStringStream(str)
+ )
+ )
+ );
+ p.relationModel = m;
+ return (Tree)p.query().getTree();
+ } catch (QueryParseInternalException e) {
+ throw new QueryParseException(e.getMessage());
+ } catch (RecognitionException e) {
+ throw new QueryParseException(e.getMessage());
+ }
+ }
+
+ public static class Column extends CommonTree {
+ private final ColumnModel field;
+
+ public Column(int ttype, Token t, final RelationModel relationModel) {
+ token = t;
+ field = relationModel.getField(t.getText());
+ if (field == null) {
+ throw new QueryParseInternalException("No field " + t.getText());
+ }
+ }
+
+ public Column(final Column o, final ColumnModel f) {
+ token = o.token;
+ field = f;
+ }
+
+ public ColumnModel getField() {
+ return field;
+ }
+
+ public Tree dupNode() {
+ return new Column(this, field);
+ }
+ }
+
+ private RelationModel relationModel;
+
+ public void displayRecognitionError(String[] tokenNames,
+ RecognitionException e) {
+ String hdr = getErrorHeader(e);
+ String msg = getErrorMessage(e, tokenNames);
+ throw new QueryParseInternalException(hdr + " " + msg);
+ }
+}
+
+@lexer::header {
+package com.google.gwtorm.schema;
+}
+@lexer::members {
+ public void displayRecognitionError(String[] tokenNames,
+ RecognitionException e) {
+ String hdr = getErrorHeader(e);
+ String msg = getErrorMessage(e, tokenNames);
+ throw new QueryParseInternalException(hdr + " " + msg);
+ }
+}
+
+
+query
+ : where? orderBy? limit?
+ ;
+
+where
+ : WHERE^ conditions
+ ;
+
+orderBy
+ : ORDER^ BY! fieldSort (COMMA! fieldSort)*
+ ;
+
+fieldSort
+ : field sortDirection^
+ | field -> ^(ASC field)
+ ;
+
+sortDirection
+ : ASC
+ | DESC
+ ;
+
+limit
+ : LIMIT^ limitArg
+ ;
+
+limitArg
+ : PLACEHOLDER
+ | CONSTANT_INTEGER
+ ;
+
+conditions
+ : condition AND^ condition (AND! condition)*
+ | condition
+ ;
+
+condition
+ : field compare_op^ conditionValue
+ ;
+
+compare_op
+ : LT
+ | LE
+ | GT
+ | GE
+ | EQ
+ | NE
+ ;
+
+field
+ : ID -> ID<Column>[$ID, relationModel]
+ ;
+
+conditionValue
+ : PLACEHOLDER
+ | CONSTANT_INTEGER
+ | constantBoolean
+ | CONSTANT_STRING
+ ;
+
+constantBoolean
+ : TRUE
+ | FALSE
+ ;
+
+WHERE: 'WHERE' ;
+ORDER: 'ORDER' ;
+BY: 'BY' ;
+AND: 'AND' ;
+ASC: 'ASC' ;
+DESC: 'DESC' ;
+LIMIT: 'LIMIT' ;
+TRUE: 'true' ;
+FALSE: 'false' ;
+
+LT : '<' ;
+LE : '<=' ;
+GT : '>' ;
+GE : '>=' ;
+EQ : '=' ;
+NE : '!=' ;
+
+PLACEHOLDER: '?' ;
+COMMA: ',' ;
+
+CONSTANT_INTEGER
+ : '0'
+ | '1'..'9' ('0'..'9')*
+ ;
+
+CONSTANT_STRING
+ : '\'' ( ~('\'') )* '\''
+ ;
+
+ID
+ : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
+ ;
+
+WS
+ : ( ' ' | '\r' | '\t' | '\n' ) { $channel=HIDDEN; }
+ ;
diff --git a/src/com/google/gwtorm/schema/QueryModel.java b/src/com/google/gwtorm/schema/QueryModel.java
new file mode 100644
index 0000000..c24bc2b
--- /dev/null
+++ b/src/com/google/gwtorm/schema/QueryModel.java
@@ -0,0 +1,319 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.schema.QueryParser.Column;
+import com.google.gwtorm.schema.sql.SqlBooleanTypeInfo;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+import org.antlr.runtime.CommonToken;
+import org.antlr.runtime.tree.CommonTree;
+import org.antlr.runtime.tree.Tree;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class QueryModel {
+ private final RelationModel model;
+ private final String name;
+ private final Query query;
+ private final Tree parsedQuery;
+
+ public QueryModel(final RelationModel rel, final String queryName,
+ final Query q) throws OrmException {
+ if (q == null) {
+ throw new OrmException("Query " + queryName + " is missing "
+ + Query.class.getName() + " annotation");
+ }
+
+ model = rel;
+ name = queryName;
+ query = q;
+
+ try {
+ parsedQuery = QueryParser.parse(model, q.value());
+ } catch (QueryParseException e) {
+ throw new OrmException("Cannot parse query " + q.value(), e);
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Tree getParseTree() {
+ return parsedQuery;
+ }
+
+ public List<ColumnModel> getParameters() {
+ final ArrayList<ColumnModel> r = new ArrayList<ColumnModel>();
+ if (parsedQuery != null) {
+ findParameters(r, parsedQuery);
+ }
+ return r;
+ }
+
+ private void findParameters(final List<ColumnModel> r, final Tree node) {
+ switch (node.getType()) {
+ case QueryParser.WHERE:
+ extractParameters(r, node);
+ break;
+
+ default:
+ for (int i = 0; i < node.getChildCount(); i++) {
+ findParameters(r, node.getChild(i));
+ }
+ break;
+ }
+ }
+
+ private void extractParameters(final List<ColumnModel> r, final Tree node) {
+ switch (node.getType()) {
+ case QueryParser.LT:
+ case QueryParser.LE:
+ case QueryParser.GT:
+ case QueryParser.GE:
+ case QueryParser.EQ:
+ case QueryParser.NE:
+ if (node.getChild(1).getType() == QueryParser.PLACEHOLDER) {
+ r.add(((QueryParser.Column) node.getChild(0)).getField());
+ }
+ break;
+
+ default:
+ for (int i = 0; i < node.getChildCount(); i++) {
+ extractParameters(r, node.getChild(i));
+ }
+ break;
+ }
+ }
+
+ public boolean hasLimit() {
+ return findLimit(parsedQuery) != null;
+ }
+
+ public boolean hasLimitParameter() {
+ final Tree limit = findLimit(parsedQuery);
+ return limit != null
+ && limit.getChild(0).getType() == QueryParser.PLACEHOLDER;
+ }
+
+ public int getStaticLimit() {
+ return Integer.parseInt(findLimit(parsedQuery).getChild(0).getText());
+ }
+
+ private Tree findLimit(final Tree node) {
+ if (node == null) {
+ return null;
+ }
+ switch (node.getType()) {
+ case QueryParser.LIMIT:
+ return node;
+ default:
+ for (int i = 0; i < node.getChildCount(); i++) {
+ final Tree r = findLimit(node.getChild(i));
+ if (r != null) {
+ return r;
+ }
+ }
+ return null;
+ }
+ }
+
+ public String getSelectSql(final SqlDialect dialect, final String tableAlias) {
+ final StringBuilder buf = new StringBuilder();
+ buf.append(model.getSelectSql(dialect, tableAlias));
+ if (parsedQuery != null) {
+ final FormatInfo fmt = new FormatInfo(buf, dialect, tableAlias);
+ final Tree t = expand(parsedQuery);
+ if (t.getType() == 0) {
+ formatChilden(fmt, t);
+ } else {
+ format(fmt, t);
+ }
+ }
+ return buf.toString();
+ }
+
+ private void formatChilden(final FormatInfo fmt, final Tree node) {
+ for (int i = 0; i < node.getChildCount(); i++) {
+ format(fmt, node.getChild(i));
+ }
+ }
+
+ private void format(final FormatInfo fmt, final Tree node) {
+ switch (node.getType()) {
+ case QueryParser.WHERE:
+ fmt.buf.append(" WHERE ");
+ formatChilden(fmt, node);
+ break;
+
+ case QueryParser.AND:
+ for (int i = 0; i < node.getChildCount(); i++) {
+ if (i > 0) {
+ fmt.buf.append(" AND ");
+ }
+ format(fmt, node.getChild(i));
+ }
+ break;
+
+ case QueryParser.LT:
+ case QueryParser.LE:
+ case QueryParser.GT:
+ case QueryParser.GE:
+ case QueryParser.EQ:
+ format(fmt, node.getChild(0));
+ fmt.buf.append(node.getText());
+ format(fmt, node.getChild(1));
+ break;
+ case QueryParser.NE:
+ format(fmt, node.getChild(0));
+ fmt.buf.append("<>");
+ format(fmt, node.getChild(1));
+ break;
+
+ case QueryParser.ID: {
+ final ColumnModel col = ((QueryParser.Column) node).getField();
+ if (!col.isSqlPrimitive()) {
+ throw new IllegalStateException("Unexpanded nested field");
+ }
+ fmt.buf.append(fmt.tableAlias);
+ fmt.buf.append('.');
+ fmt.buf.append(col.getColumnName());
+ break;
+ }
+
+ case QueryParser.PLACEHOLDER:
+ fmt.buf.append(fmt.dialect.getParameterPlaceHolder(fmt.nthParam++));
+ break;
+
+ case QueryParser.TRUE:
+ fmt.buf.append(((SqlBooleanTypeInfo) fmt.dialect
+ .getSqlTypeInfo(Boolean.TYPE)).getTrueLiteralValue());
+ break;
+
+ case QueryParser.FALSE:
+ fmt.buf.append(((SqlBooleanTypeInfo) fmt.dialect
+ .getSqlTypeInfo(Boolean.TYPE)).getFalseLiteralValue());
+ break;
+
+ case QueryParser.CONSTANT_INTEGER:
+ case QueryParser.CONSTANT_STRING:
+ fmt.buf.append(node.getText());
+ break;
+
+ case QueryParser.ORDER:
+ fmt.buf.append(" ORDER BY ");
+ for (int i = 0; i < node.getChildCount(); i++) {
+ final Tree sortOrder = node.getChild(i);
+ final Tree id = sortOrder.getChild(0);
+ if (i > 0) {
+ fmt.buf.append(',');
+ }
+ final ColumnModel col = ((QueryParser.Column) id).getField();
+ if (col.isNested()) {
+ for (final Iterator<ColumnModel> cItr =
+ col.getAllLeafColumns().iterator(); cItr.hasNext();) {
+ fmt.buf.append(fmt.tableAlias);
+ fmt.buf.append('.');
+ fmt.buf.append(cItr.next().getColumnName());
+ if (sortOrder.getType() == QueryParser.DESC) {
+ fmt.buf.append(" DESC");
+ }
+ if (cItr.hasNext()) {
+ fmt.buf.append(',');
+ }
+ }
+ } else {
+ fmt.buf.append(fmt.tableAlias);
+ fmt.buf.append('.');
+ fmt.buf.append(col.getColumnName());
+ if (sortOrder.getType() == QueryParser.DESC) {
+ fmt.buf.append(" DESC");
+ }
+ }
+ }
+ break;
+
+ case QueryParser.LIMIT:
+ if (fmt.dialect.selectHasLimit()) {
+ final Tree p = node.getChild(0);
+ if (p.getType() == QueryParser.CONSTANT_INTEGER) {
+ fmt.buf.append(" LIMIT ");
+ fmt.buf.append(p.getText());
+ }
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("Unsupported query token");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Query[" + name + " " + query.value() + "]";
+ }
+
+ private Tree expand(final Tree node) {
+ switch (node.getType()) {
+ case QueryParser.LT:
+ case QueryParser.LE:
+ case QueryParser.GT:
+ case QueryParser.GE:
+ case QueryParser.EQ:
+ case QueryParser.NE: {
+ final Column qpc = (QueryParser.Column) node.getChild(0);
+ final ColumnModel f = qpc.getField();
+ if (f.isNested()) {
+ final CommonTree join;
+
+ join = new CommonTree(new CommonToken(QueryParser.AND));
+ for (final ColumnModel c : f.getAllLeafColumns()) {
+ final Tree op;
+
+ op = node.dupNode();
+ op.addChild(new QueryParser.Column(qpc, c));
+ op.addChild(node.getChild(1).dupNode());
+ join.addChild(op);
+ }
+ return join;
+ }
+ }
+ }
+
+ final Tree r = node.dupNode();
+ for (int i = 0; i < node.getChildCount(); i++) {
+ r.addChild(expand(node.getChild(i)));
+ }
+ return r;
+ }
+
+ static class FormatInfo {
+ final StringBuilder buf;
+ final SqlDialect dialect;
+ final String tableAlias;
+ int nthParam = 1;
+
+ FormatInfo(StringBuilder r, SqlDialect dialect, String tableAlias) {
+ this.buf = r;
+ this.dialect = dialect;
+ this.tableAlias = tableAlias;
+ }
+ }
+}
diff --git a/src/com/google/gwtorm/schema/QueryParseException.java b/src/com/google/gwtorm/schema/QueryParseException.java
new file mode 100644
index 0000000..db71fa0
--- /dev/null
+++ b/src/com/google/gwtorm/schema/QueryParseException.java
@@ -0,0 +1,21 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+public class QueryParseException extends Exception {
+ public QueryParseException(final String message) {
+ super(message);
+ }
+}
diff --git a/src/com/google/gwtorm/schema/QueryParseInternalException.java b/src/com/google/gwtorm/schema/QueryParseInternalException.java
new file mode 100644
index 0000000..262f779
--- /dev/null
+++ b/src/com/google/gwtorm/schema/QueryParseInternalException.java
@@ -0,0 +1,21 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+class QueryParseInternalException extends RuntimeException {
+ QueryParseInternalException(String message) {
+ super(message);
+ }
+}
diff --git a/src/com/google/gwtorm/schema/RelationModel.java b/src/com/google/gwtorm/schema/RelationModel.java
new file mode 100644
index 0000000..6757dc6
--- /dev/null
+++ b/src/com/google/gwtorm/schema/RelationModel.java
@@ -0,0 +1,305 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+public abstract class RelationModel {
+ protected String methodName;
+ protected String relationName;
+ protected Relation relation;
+ protected final LinkedHashMap<String, ColumnModel> fieldsByFieldName;
+ protected final LinkedHashMap<String, ColumnModel> columnsByColumnName;
+ protected KeyModel primaryKey;
+ protected Collection<QueryModel> queries;
+
+ protected RelationModel() {
+ fieldsByFieldName = new LinkedHashMap<String, ColumnModel>();
+ columnsByColumnName = new LinkedHashMap<String, ColumnModel>();
+ queries = new ArrayList<QueryModel>();
+ }
+
+ protected void initName(final String method, final Relation rel)
+ throws OrmException {
+ if (rel == null) {
+ throw new OrmException("Method " + method + " is missing "
+ + Relation.class.getName() + " annotation");
+ }
+ relation = rel;
+ methodName = method;
+ relationName = Util.any(relation.name(), methodName);
+ }
+
+ protected void initColumns(final Collection<? extends ColumnModel> allFields)
+ throws OrmException {
+ for (final ColumnModel field : allFields) {
+ if (fieldsByFieldName.put(field.getFieldName(), field) != null) {
+ throw new OrmException("Duplicate fields " + field.getFieldName());
+ }
+
+ if (field.isNested()) {
+ for (final ColumnModel newCol : field.getAllLeafColumns()) {
+ registerColumn(newCol);
+ }
+ } else {
+ registerColumn(field);
+ }
+ }
+ }
+
+ private void registerColumn(final ColumnModel nc) throws OrmException {
+ final ColumnModel oc = columnsByColumnName.put(nc.getColumnName(), nc);
+ if (oc != null) {
+ throw new OrmException("Duplicate columns " + nc.getColumnName() + " in "
+ + getMethodName() + ":\n" + "prior " + oc.getPathToFieldName()
+ + "\n next " + nc.getPathToFieldName());
+ }
+ }
+
+ protected void initPrimaryKey(final String name, final PrimaryKey annotation)
+ throws OrmException {
+ if (primaryKey != null) {
+ throw new OrmException("Duplicate primary key definitions");
+ }
+
+ final ColumnModel field = getField(annotation.value());
+ if (field == null) {
+ throw new OrmException("Field " + annotation.value() + " not in "
+ + getEntityTypeClassName());
+ }
+
+ primaryKey = new KeyModel(name, field);
+ for (final ColumnModel c : primaryKey.getAllLeafColumns()) {
+ c.inPrimaryKey = true;
+ }
+ }
+
+ protected void addQuery(final QueryModel q) {
+ queries.add(q);
+ }
+
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public String getRelationName() {
+ return relationName;
+ }
+
+ public Collection<ColumnModel> getDependentFields() {
+ final ArrayList<ColumnModel> r = new ArrayList<ColumnModel>();
+ for (final ColumnModel c : fieldsByFieldName.values()) {
+ if (primaryKey == null || primaryKey.getField() != c) {
+ r.add(c);
+ }
+ }
+ return r;
+ }
+
+ public Collection<ColumnModel> getDependentColumns() {
+ final ArrayList<ColumnModel> r = new ArrayList<ColumnModel>();
+ for (final ColumnModel c : columnsByColumnName.values()) {
+ if (!c.inPrimaryKey) {
+ r.add(c);
+ }
+ }
+ return r;
+ }
+
+ public KeyModel getPrimaryKey() {
+ return primaryKey;
+ }
+
+ public Collection<ColumnModel> getPrimaryKeyColumns() {
+ if (getPrimaryKey() != null) {
+ return getPrimaryKey().getAllLeafColumns();
+ }
+ return Collections.<ColumnModel> emptyList();
+ }
+
+ public Collection<QueryModel> getQueries() {
+ return queries;
+ }
+
+ public Collection<ColumnModel> getColumns() {
+ final ArrayList<ColumnModel> r = new ArrayList<ColumnModel>();
+ r.addAll(getDependentColumns());
+ r.addAll(getPrimaryKeyColumns());
+ return r;
+ }
+
+ public Collection<ColumnModel> getFields() {
+ return fieldsByFieldName.values();
+ }
+
+ public ColumnModel getField(final String name) {
+ return fieldsByFieldName.get(name);
+ }
+
+ public String getCreateTableSql(final SqlDialect dialect) {
+ final StringBuilder r = new StringBuilder();
+ r.append("CREATE TABLE ");
+ r.append(relationName);
+ r.append(" (\n");
+
+ for (final Iterator<ColumnModel> i = getColumns().iterator(); i.hasNext();) {
+ final ColumnModel col = i.next();
+ r.append(col.getColumnName());
+ r.append(" ");
+ r.append(dialect.getSqlTypeInfo(col).getSqlType(col));
+ if (i.hasNext()) {
+ r.append(",");
+ } else if (!getPrimaryKeyColumns().isEmpty()) {
+ r.append(",");
+ }
+ r.append("\n");
+ }
+
+ if (!getPrimaryKeyColumns().isEmpty()) {
+ r.append("PRIMARY KEY(");
+ for (final Iterator<ColumnModel> i = getPrimaryKeyColumns().iterator(); i
+ .hasNext();) {
+ final ColumnModel col = i.next();
+ r.append(col.getColumnName());
+ if (i.hasNext()) {
+ r.append(",");
+ }
+ }
+ r.append(")\n");
+ }
+
+ r.append(")");
+ return r.toString();
+ }
+
+ public String getSelectSql(final SqlDialect dialect, final String tableAlias) {
+ final StringBuilder r = new StringBuilder();
+ r.append("SELECT ");
+ for (final Iterator<ColumnModel> i = getColumns().iterator(); i.hasNext();) {
+ final ColumnModel col = i.next();
+ r.append(tableAlias);
+ r.append('.');
+ r.append(col.getColumnName());
+ if (i.hasNext()) {
+ r.append(",");
+ }
+ }
+ r.append(" FROM ");
+ r.append(relationName);
+ r.append(' ');
+ r.append(tableAlias);
+ return r.toString();
+ }
+
+ public String getInsertOneSql(final SqlDialect dialect) {
+ final StringBuilder r = new StringBuilder();
+ r.append("INSERT INTO ");
+ r.append(relationName);
+ r.append("(");
+ for (final Iterator<ColumnModel> i = getColumns().iterator(); i.hasNext();) {
+ final ColumnModel col = i.next();
+ r.append(col.getColumnName());
+ if (i.hasNext()) {
+ r.append(",");
+ }
+ }
+ r.append(")VALUES(");
+ int nth = 1;
+ for (final Iterator<ColumnModel> i = getColumns().iterator(); i.hasNext();) {
+ i.next();
+ r.append(dialect.getParameterPlaceHolder(nth++));
+ if (i.hasNext()) {
+ r.append(",");
+ }
+ }
+ r.append(")");
+ return r.toString();
+ }
+
+ public String getUpdateOneSql(final SqlDialect dialect) {
+ final StringBuilder r = new StringBuilder();
+ r.append("UPDATE ");
+ r.append(relationName);
+ r.append(" SET ");
+ int nth = 1;
+ for (final Iterator<ColumnModel> i = getDependentColumns().iterator(); i
+ .hasNext();) {
+ final ColumnModel col = i.next();
+ r.append(col.getColumnName());
+ r.append("=");
+ r.append(dialect.getParameterPlaceHolder(nth++));
+ if (i.hasNext()) {
+ r.append(",");
+ }
+ }
+ r.append(" WHERE ");
+ for (final Iterator<ColumnModel> i = getPrimaryKeyColumns().iterator(); i
+ .hasNext();) {
+ final ColumnModel col = i.next();
+ r.append(col.getColumnName());
+ r.append("=");
+ r.append(dialect.getParameterPlaceHolder(nth++));
+ if (i.hasNext()) {
+ r.append(" AND ");
+ }
+ }
+ return r.toString();
+ }
+
+ public String getDeleteOneSql(final SqlDialect dialect) {
+ final StringBuilder r = new StringBuilder();
+ r.append("DELETE FROM ");
+ r.append(relationName);
+ int nth = 1;
+ r.append(" WHERE ");
+ for (final Iterator<ColumnModel> i = getPrimaryKeyColumns().iterator(); i
+ .hasNext();) {
+ final ColumnModel col = i.next();
+ r.append(col.getColumnName());
+ r.append("=");
+ r.append(dialect.getParameterPlaceHolder(nth++));
+ if (i.hasNext()) {
+ r.append(" AND ");
+ }
+ }
+ return r.toString();
+ }
+
+ public abstract String getAccessInterfaceName();
+
+ public abstract String getEntityTypeClassName();
+
+ @Override
+ public String toString() {
+ final StringBuilder r = new StringBuilder();
+ r.append("Relation[\n");
+ r.append(" method: " + getMethodName() + "\n");
+ r.append(" table: " + getRelationName() + "\n");
+ r.append(" access: " + getAccessInterfaceName() + "\n");
+ r.append(" entity: " + getEntityTypeClassName() + "\n");
+ r.append("]");
+ return r.toString();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/SchemaModel.java b/src/com/google/gwtorm/schema/SchemaModel.java
new file mode 100644
index 0000000..39146a7
--- /dev/null
+++ b/src/com/google/gwtorm/schema/SchemaModel.java
@@ -0,0 +1,95 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+public abstract class SchemaModel {
+ protected final Set<String> allNames;
+ protected final Map<String, RelationModel> relations;
+ protected final Map<String, SequenceModel> sequences;
+
+ protected SchemaModel() {
+ allNames = new HashSet<String>();
+ relations = new LinkedHashMap<String, RelationModel>();
+ sequences = new LinkedHashMap<String, SequenceModel>();
+ }
+
+ protected void add(final RelationModel r) throws OrmException {
+ final String n = r.getRelationName();
+ checkNotUsed(n);
+ if (relations.put(n, r) != null) {
+ throw new OrmException("Duplicate relations " + n);
+ }
+ allNames.add(n);
+ }
+
+ protected void add(final SequenceModel s) throws OrmException {
+ final String n = s.getSequenceName();
+ checkNotUsed(n);
+ if (sequences.put(n, s) != null) {
+ throw new OrmException("Duplicate sequences " + n);
+ }
+ allNames.add(n);
+ }
+
+ private void checkNotUsed(final String n) throws OrmException {
+ if (allNames.contains(n)) {
+ throw new OrmException("Name " + n + " already used");
+ }
+ }
+
+ public Collection<RelationModel> getRelations() {
+ return relations.values();
+ }
+
+ public Collection<SequenceModel> getSequences() {
+ return sequences.values();
+ }
+
+ public String getCreateDatabaseSql(final SqlDialect dialect) {
+ final StringBuffer r = new StringBuffer();
+
+ for (final SequenceModel seq : getSequences()) {
+ r.append(seq.getCreateSequenceSql(dialect));
+ r.append(";\n");
+ }
+ if (!getSequences().isEmpty()) {
+ r.append("\n");
+ }
+
+ for (final RelationModel rel : getRelations()) {
+ r.append(rel.getCreateTableSql(dialect));
+ r.append(";\n\n");
+ }
+
+ return r.toString();
+ }
+
+ public abstract String getSchemaClassName();
+
+ @Override
+ public String toString() {
+ return "Schema[" + getSchemaClassName() + "]";
+ }
+}
diff --git a/src/com/google/gwtorm/schema/SequenceModel.java b/src/com/google/gwtorm/schema/SequenceModel.java
new file mode 100644
index 0000000..aa9e389
--- /dev/null
+++ b/src/com/google/gwtorm/schema/SequenceModel.java
@@ -0,0 +1,80 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Sequence;
+import com.google.gwtorm.schema.sql.SqlDialect;
+
+public class SequenceModel {
+ protected String methodName;
+ protected String name;
+ protected Sequence sequence;
+ protected Class<?> returnType;
+
+ public SequenceModel(final String method, final Sequence seq,
+ final Class<?> type) throws OrmException {
+ if (seq == null) {
+ throw new OrmException("Method " + method + " is missing "
+ + Sequence.class.getName() + " annotation");
+ }
+ if (type != Integer.TYPE && type != Long.TYPE) {
+ throw new OrmException("Sequence method " + method
+ + " must return int or long");
+ }
+
+ sequence = seq;
+ methodName = method;
+
+ final String n;
+ if (methodName.startsWith("next")) {
+ n = methodName.substring(4).toLowerCase();
+ } else {
+ n = methodName;
+ }
+ name = Util.any(sequence.name(), n);
+ returnType = type;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public String getSequenceName() {
+ return name;
+ }
+
+ public Class<?> getResultType() {
+ return returnType;
+ }
+
+ public Sequence getSequence() {
+ return sequence;
+ }
+
+ public String getCreateSequenceSql(final SqlDialect dialect) {
+ return dialect.getCreateSequenceSql(this);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder r = new StringBuilder();
+ r.append("Sequence[\n");
+ r.append(" method: " + getMethodName() + "\n");
+ r.append(" name: " + getSequenceName() + "\n");
+ r.append("]");
+ return r.toString();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/Util.java b/src/com/google/gwtorm/schema/Util.java
new file mode 100644
index 0000000..2a90f28
--- /dev/null
+++ b/src/com/google/gwtorm/schema/Util.java
@@ -0,0 +1,59 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+
+public class Util {
+ private static int nameCounter;
+
+ public static synchronized String createRandomName() {
+ return "GwtOrm$$" + nameCounter++;
+ }
+
+ public static String any(final String a, final String b) {
+ if (a != null && a.length() > 0) {
+ return a;
+ }
+ return b;
+ }
+
+ public static boolean samePackage(final String aName, final String bName) {
+ return packageOf(aName).equals(packageOf(bName));
+ }
+
+ public static String packageOf(final String className) {
+ final int end = className.lastIndexOf('.');
+ if (end < 0) {
+ return "";
+ }
+ return className.substring(0, end);
+ }
+
+ public static boolean isSqlPrimitive(final Class<?> type) {
+ if (type == null || type == Void.TYPE) {
+ return false;
+ }
+ if (type.isPrimitive()) {
+ return true;
+ }
+ if (type == String.class) {
+ return true;
+ }
+ return false;
+ }
+
+ private Util() {
+ }
+}
diff --git a/src/com/google/gwtorm/schema/gwt/GwtColumnModel.java b/src/com/google/gwtorm/schema/gwt/GwtColumnModel.java
new file mode 100644
index 0000000..ddbf324
--- /dev/null
+++ b/src/com/google/gwtorm/schema/gwt/GwtColumnModel.java
@@ -0,0 +1,127 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.gwt;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.schema.ColumnModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+class GwtColumnModel extends ColumnModel {
+ static Class<?> toClass(final JType type) {
+ if (type.isPrimitive() == JPrimitiveType.BOOLEAN) {
+ return Boolean.TYPE;
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.BYTE) {
+ return Byte.TYPE;
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.SHORT) {
+ return Short.TYPE;
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.CHAR) {
+ return Character.TYPE;
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.INT) {
+ return Integer.TYPE;
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.LONG) {
+ return Long.TYPE;
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.FLOAT) {
+ return Float.TYPE;
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.DOUBLE) {
+ return Double.TYPE;
+ }
+
+ if (type.isClass() != null
+ && type.getQualifiedSourceName().startsWith("java.")) {
+ try {
+ return Class.forName(type.getQualifiedSourceName());
+ } catch (ClassNotFoundException err) {
+ // Never happen, but fall through anyway
+ }
+ }
+
+ return null;
+ }
+
+ private final JField field;
+ private final Class<?> primitiveType;
+
+ GwtColumnModel(final JField columnField) throws OrmException {
+ field = columnField;
+ initName(field.getName(), field.getAnnotation(Column.class));
+
+ if (field.isPrivate()) {
+ throw new OrmException("Field " + getFieldName() + " of "
+ + field.getEnclosingType().getQualifiedSourceName()
+ + " must not be private");
+ }
+ if (field.isFinal()) {
+ throw new OrmException("Field " + getFieldName() + " of "
+ + field.getEnclosingType().getQualifiedSourceName()
+ + " must not be final");
+ }
+
+ primitiveType = toClass(field.getType());
+
+ if (isNested()) {
+ final List<GwtColumnModel> col = new ArrayList<GwtColumnModel>();
+ JClassType in = field.getType().isClass();
+ while (in != null) {
+ for (final JField f : in.getFields()) {
+ if (f.getAnnotation(Column.class) != null) {
+ col.add(new GwtColumnModel(f));
+ }
+ }
+ in = in.getSuperclass();
+ }
+ initNestedColumns(col);
+ }
+ }
+
+ @Override
+ public String getFieldName() {
+ return field.getName();
+ }
+
+ @Override
+ public Class<?> getPrimitiveType() {
+ return primitiveType;
+ }
+
+ @Override
+ public String getNestedClassName() {
+ if (primitiveType == null) {
+ return field.getType().getQualifiedSourceName();
+ }
+ return null;
+ }
+}
diff --git a/src/com/google/gwtorm/schema/gwt/GwtRelationModel.java b/src/com/google/gwtorm/schema/gwt/GwtRelationModel.java
new file mode 100644
index 0000000..b0ef6ec
--- /dev/null
+++ b/src/com/google/gwtorm/schema/gwt/GwtRelationModel.java
@@ -0,0 +1,108 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.gwt;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.schema.QueryModel;
+import com.google.gwtorm.schema.RelationModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class GwtRelationModel extends RelationModel {
+ private final JMethod method;
+ private final JClassType accessType;
+ private final JClassType entityType;
+
+ GwtRelationModel(final JMethod m) throws OrmException {
+ method = m;
+ initName(method.getName(), m.getAnnotation(Relation.class));
+
+ accessType = method.getReturnType().isInterface();
+ if (accessType == null) {
+ throw new OrmException("Method " + method.getName() + " in "
+ + method.getEnclosingType().getQualifiedSourceName()
+ + " must return an extension of " + Access.class);
+ }
+
+ if (accessType.getImplementedInterfaces().length != 1
+ || !accessType.getImplementedInterfaces()[0].getQualifiedSourceName()
+ .equals(Access.class.getName())) {
+ throw new OrmException("Method " + method.getName() + " in "
+ + method.getEnclosingType().getQualifiedSourceName()
+ + " must return a direct extension of " + Access.class);
+ }
+
+ final JGenericType gt =
+ accessType.getImplementedInterfaces()[0].isGenericType();
+ if (gt == null) {
+ throw new OrmException(accessType.getQualifiedSourceName()
+ + " must specify entity type parameter for " + Access.class);
+ }
+ entityType = gt.getTypeParameters()[0];
+
+ initColumns();
+ initQueriesAndKeys();
+ }
+
+ private void initColumns() throws OrmException {
+ final List<GwtColumnModel> col = new ArrayList<GwtColumnModel>();
+ JClassType in = entityType;
+ while (in != null) {
+ for (final JField f : in.getFields()) {
+ if (f.getAnnotation(Column.class) != null) {
+ col.add(new GwtColumnModel(f));
+ }
+ }
+ in = in.getSuperclass();
+ }
+ initColumns(col);
+ }
+
+ private void initQueriesAndKeys() throws OrmException {
+ for (final JMethod m : accessType.getMethods()) {
+ if (m.getAnnotation(PrimaryKey.class) != null) {
+ if (!m.getReturnType().getQualifiedSourceName().equals(
+ entityType.getQualifiedSourceName())) {
+ throw new OrmException("PrimaryKey " + m.getName() + " must return "
+ + entityType.getName());
+ }
+ initPrimaryKey(m.getName(), m.getAnnotation(PrimaryKey.class));
+
+ } else if (m.getAnnotation(Query.class) != null) {
+ addQuery(new QueryModel(this, m.getName(), m.getAnnotation(Query.class)));
+ }
+ }
+ }
+
+ @Override
+ public String getAccessInterfaceName() {
+ return accessType.getQualifiedSourceName();
+ }
+
+ @Override
+ public String getEntityTypeClassName() {
+ return entityType.getQualifiedSourceName();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/gwt/GwtSchemaModel.java b/src/com/google/gwtorm/schema/gwt/GwtSchemaModel.java
new file mode 100644
index 0000000..e804083
--- /dev/null
+++ b/src/com/google/gwtorm/schema/gwt/GwtSchemaModel.java
@@ -0,0 +1,73 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.gwt;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.client.Sequence;
+import com.google.gwtorm.schema.SchemaModel;
+import com.google.gwtorm.schema.SequenceModel;
+
+public class GwtSchemaModel extends SchemaModel {
+ private final JClassType schema;
+
+ public GwtSchemaModel(final JClassType schemaInterface) throws OrmException {
+ schema = schemaInterface;
+
+ if (schema.isInterface() == null) {
+ throw new OrmException("Schema " + schema.getName()
+ + " must be an interface");
+ }
+
+ if (schema.getImplementedInterfaces().length != 1
+ || !schema.getImplementedInterfaces()[0].getQualifiedSourceName()
+ .equals(Schema.class.getName())) {
+ throw new OrmException("Schema " + schema.getName()
+ + " must only extend " + Schema.class.getName());
+ }
+
+ for (final JMethod m : schema.getMethods()) {
+ if (m.getAnnotation(Relation.class) != null) {
+ add(new GwtRelationModel(m));
+ continue;
+ }
+
+ final Sequence seq = m.getAnnotation(Sequence.class);
+ if (seq != null) {
+ final JType returnType = m.getReturnType();
+ final Class<?> r;
+ if (returnType == JPrimitiveType.INT) {
+ r = Integer.TYPE;
+ } else if (returnType == JPrimitiveType.LONG) {
+ r = Long.TYPE;
+ } else {
+ r = Object.class;
+ }
+ add(new SequenceModel(m.getName(), seq, r));
+ continue;
+ }
+ }
+ }
+
+ @Override
+ public String getSchemaClassName() {
+ return schema.getQualifiedSourceName();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/java/JavaColumnModel.java b/src/com/google/gwtorm/schema/java/JavaColumnModel.java
new file mode 100644
index 0000000..47c79f5
--- /dev/null
+++ b/src/com/google/gwtorm/schema/java/JavaColumnModel.java
@@ -0,0 +1,77 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.java;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.schema.ColumnModel;
+import com.google.gwtorm.schema.Util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+
+class JavaColumnModel extends ColumnModel {
+ private final Field field;
+
+ JavaColumnModel(final Field columnField) throws OrmException {
+ field = columnField;
+ initName(field.getName(), field.getAnnotation(Column.class));
+
+ if (Modifier.isPrivate(field.getModifiers())) {
+ throw new OrmException("Field " + field.getName() + " of "
+ + field.getDeclaringClass().getName() + " must not be private");
+ }
+ if (Modifier.isFinal(field.getModifiers())) {
+ throw new OrmException("Field " + field.getName() + " of "
+ + field.getDeclaringClass().getName() + " must not be final");
+ }
+
+ if (isNested()) {
+ final List<JavaColumnModel> col = new ArrayList<JavaColumnModel>();
+ Class<?> in = field.getType();
+ while (in != null) {
+ for (final Field f : in.getDeclaredFields()) {
+ if (f.getAnnotation(Column.class) != null) {
+ col.add(new JavaColumnModel(f));
+ }
+ }
+ in = in.getSuperclass();
+ }
+ initNestedColumns(col);
+ }
+ }
+
+ @Override
+ public String getFieldName() {
+ return field.getName();
+ }
+
+ @Override
+ public Class<?> getPrimitiveType() {
+ return isPrimitive() ? field.getType() : null;
+ }
+
+ @Override
+ public String getNestedClassName() {
+ return isPrimitive() ? null : field.getType().getName();
+ }
+
+ private boolean isPrimitive() {
+ return Util.isSqlPrimitive(field.getType());
+ }
+}
diff --git a/src/com/google/gwtorm/schema/java/JavaRelationModel.java b/src/com/google/gwtorm/schema/java/JavaRelationModel.java
new file mode 100644
index 0000000..f447095
--- /dev/null
+++ b/src/com/google/gwtorm/schema/java/JavaRelationModel.java
@@ -0,0 +1,109 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.java;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.schema.QueryModel;
+import com.google.gwtorm.schema.RelationModel;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+class JavaRelationModel extends RelationModel {
+ private final Method method;
+ private final Class<?> accessType;
+ private final Class<?> entityType;
+
+ JavaRelationModel(final Method m) throws OrmException {
+ method = m;
+ initName(method.getName(), m.getAnnotation(Relation.class));
+
+ accessType = method.getReturnType();
+ if (accessType.getInterfaces().length != 1
+ || accessType.getInterfaces()[0] != Access.class) {
+ throw new OrmException("Method " + method.getName() + " in "
+ + method.getDeclaringClass().getName()
+ + " must return a direct extension of " + Access.class);
+ }
+
+ final Type gt = accessType.getGenericInterfaces()[0];
+ if (!(gt instanceof ParameterizedType)) {
+ throw new OrmException(accessType.getName()
+ + " must specify entity type parameter for " + Access.class);
+ }
+
+ entityType =
+ (Class<?>) ((ParameterizedType) gt).getActualTypeArguments()[0];
+
+ initColumns();
+ initQueriesAndKeys();
+ }
+
+ private void initColumns() throws OrmException {
+ final List<JavaColumnModel> col = new ArrayList<JavaColumnModel>();
+ Class<?> in = entityType;
+ while (in != null) {
+ for (final Field f : in.getDeclaredFields()) {
+ if (f.getAnnotation(Column.class) != null) {
+ col.add(new JavaColumnModel(f));
+ }
+ }
+ in = in.getSuperclass();
+ }
+ initColumns(col);
+ }
+
+ private void initQueriesAndKeys() throws OrmException {
+ for (final Method m : accessType.getDeclaredMethods()) {
+ if (m.getAnnotation(PrimaryKey.class) != null) {
+ if (m.getReturnType() != entityType) {
+ throw new OrmException("PrimaryKey " + m.getName() + " must return "
+ + entityType.getName());
+ }
+ initPrimaryKey(m.getName(), m.getAnnotation(PrimaryKey.class));
+
+ } else if (m.getAnnotation(Query.class) != null) {
+ if (!ResultSet.class.isAssignableFrom(m.getReturnType())
+ || !(m.getGenericReturnType() instanceof ParameterizedType)
+ || ((ParameterizedType) m.getGenericReturnType())
+ .getActualTypeArguments()[0] != entityType) {
+ throw new OrmException("Query " + m.getName() + " must return"
+ + " ResultSet<" + entityType.getName() + ">");
+ }
+ addQuery(new QueryModel(this, m.getName(), m.getAnnotation(Query.class)));
+ }
+ }
+ }
+
+ @Override
+ public String getAccessInterfaceName() {
+ return accessType.getName();
+ }
+
+ @Override
+ public String getEntityTypeClassName() {
+ return entityType.getName();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/java/JavaSchemaModel.java b/src/com/google/gwtorm/schema/java/JavaSchemaModel.java
new file mode 100644
index 0000000..53eabc9
--- /dev/null
+++ b/src/com/google/gwtorm/schema/java/JavaSchemaModel.java
@@ -0,0 +1,62 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.java;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.client.Sequence;
+import com.google.gwtorm.schema.SchemaModel;
+import com.google.gwtorm.schema.SequenceModel;
+
+import java.lang.reflect.Method;
+
+
+public class JavaSchemaModel extends SchemaModel {
+ private final Class<?> schema;
+
+ public JavaSchemaModel(final Class<?> schemaInterface) throws OrmException {
+ schema = schemaInterface;
+
+ if (!schema.isInterface()) {
+ throw new OrmException("Schema " + schema.getName()
+ + " must be an interface");
+ }
+
+ if (schema.getInterfaces().length != 1
+ || schema.getInterfaces()[0] != Schema.class) {
+ throw new OrmException("Schema " + schema.getName()
+ + " must only extend " + Schema.class.getName());
+ }
+
+ for (final Method m : schema.getDeclaredMethods()) {
+ if (m.getAnnotation(Relation.class) != null) {
+ add(new JavaRelationModel(m));
+ continue;
+ }
+
+ final Sequence seq = m.getAnnotation(Sequence.class);
+ if (seq != null) {
+ add(new SequenceModel(m.getName(), seq, m.getReturnType()));
+ continue;
+ }
+ }
+ }
+
+ @Override
+ public String getSchemaClassName() {
+ return schema.getName();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/DialectH2.java b/src/com/google/gwtorm/schema/sql/DialectH2.java
new file mode 100644
index 0000000..adf6be2
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/DialectH2.java
@@ -0,0 +1,9 @@
+package com.google.gwtorm.schema.sql;
+
+/** Dialect for <a href="http://www.h2database.com/">H2</a> */
+public class DialectH2 extends SqlDialect {
+ @Override
+ public String getNextSequenceValueSql(final String seqname) {
+ return "SELECT NEXT VALUE FOR " + seqname;
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/DialectPostgreSQL.java b/src/com/google/gwtorm/schema/sql/DialectPostgreSQL.java
new file mode 100644
index 0000000..eed004b
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/DialectPostgreSQL.java
@@ -0,0 +1,9 @@
+package com.google.gwtorm.schema.sql;
+
+/** Dialect for <a href="http://www.postgresql.org/>PostgreSQL</a> */
+public class DialectPostgreSQL extends SqlDialect {
+ @Override
+ public String getNextSequenceValueSql(final String seqname) {
+ return "SELECT nextval('" + seqname + "')";
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/SqlBooleanTypeInfo.java b/src/com/google/gwtorm/schema/sql/SqlBooleanTypeInfo.java
new file mode 100644
index 0000000..fae2f1b
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/SqlBooleanTypeInfo.java
@@ -0,0 +1,91 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.jdbc.gen.CodeGenSupport;
+import com.google.gwtorm.schema.ColumnModel;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.sql.Types;
+
+public class SqlBooleanTypeInfo extends SqlTypeInfo {
+ @Override
+ public String getSqlType(final ColumnModel column) {
+ final String name = column.getColumnName();
+ final String t = getTrueLiteralValue();
+ final String f = getFalseLiteralValue();
+ return "CHAR(1) NOT NULL DEFAULT " + f + " CHECK (" + name + " IN (" + t
+ + "," + f + "))";
+ }
+
+ @Override
+ protected String getJavaSqlTypeAlias() {
+ return "String";
+ }
+
+ @Override
+ protected int getSqlTypeConstant() {
+ return Types.CHAR;
+ }
+
+ public String getTrueValue() {
+ return "Y";
+ }
+
+ public String getFalseValue() {
+ return "N";
+ }
+
+ public String getTrueLiteralValue() {
+ return "'" + getTrueValue() + "'";
+ }
+
+ public String getFalseLiteralValue() {
+ return "'" + getFalseValue() + "'";
+ }
+
+ @Override
+ public void generatePreparedStatementSet(final CodeGenSupport cgs) {
+ cgs.pushSqlHandle();
+ cgs.pushColumnIndex();
+ cgs.pushFieldValue();
+
+ final Label useNo = new Label();
+ final Label end = new Label();
+ cgs.mv.visitJumpInsn(Opcodes.IFEQ, useNo);
+ cgs.mv.visitLdcInsn(getTrueValue());
+ cgs.mv.visitJumpInsn(Opcodes.GOTO, end);
+ cgs.mv.visitLabel(useNo);
+ cgs.mv.visitLdcInsn(getFalseValue());
+ cgs.mv.visitLabel(end);
+ cgs.invokePreparedStatementSet(getJavaSqlTypeAlias());
+ }
+
+ @Override
+ public void generateResultSetGet(final CodeGenSupport cgs) {
+ cgs.fieldSetBegin();
+ cgs.mv.visitLdcInsn(getTrueValue());
+ cgs.pushSqlHandle();
+ cgs.pushColumnIndex();
+ cgs.invokeResultSetGet(getJavaSqlTypeAlias());
+ cgs.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type
+ .getInternalName(String.class), "equals", Type.getMethodDescriptor(
+ Type.BOOLEAN_TYPE, new Type[] {Type.getType(Object.class)}));
+ cgs.fieldSetEnd();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/SqlDialect.java b/src/com/google/gwtorm/schema/sql/SqlDialect.java
new file mode 100644
index 0000000..5d42304
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/SqlDialect.java
@@ -0,0 +1,72 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.client.Sequence;
+import com.google.gwtorm.schema.ColumnModel;
+import com.google.gwtorm.schema.SequenceModel;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class SqlDialect {
+ protected final Map<Class<?>, SqlTypeInfo> types;
+
+ protected SqlDialect() {
+ types = new HashMap<Class<?>, SqlTypeInfo>();
+ types.put(Boolean.TYPE, new SqlBooleanTypeInfo());
+ types.put(Integer.TYPE, new SqlIntTypeInfo());
+ types.put(Long.TYPE, new SqlLongTypeInfo());
+ types.put(String.class, new SqlStringTypeInfo());
+ types.put(java.sql.Timestamp.class, new SqlTimestampTypeInfo());
+ }
+
+ public SqlTypeInfo getSqlTypeInfo(final ColumnModel col) {
+ return getSqlTypeInfo(col.getPrimitiveType());
+ }
+
+ public SqlTypeInfo getSqlTypeInfo(final Class<?> t) {
+ return types.get(t);
+ }
+
+ public String getParameterPlaceHolder(final int nthParameter) {
+ return "?";
+ }
+
+ public boolean selectHasLimit() {
+ return true;
+ }
+
+ public String getCreateSequenceSql(final SequenceModel seq) {
+ final Sequence s = seq.getSequence();
+ final StringBuilder r = new StringBuilder();
+ r.append("CREATE SEQUENCE ");
+ r.append(seq.getSequenceName());
+
+ if (s.startsWith() > 0) {
+ r.append(" STARTS WITH ");
+ r.append(s.startsWith());
+ }
+
+ if (s.cache() > 0) {
+ r.append(" CACHE ");
+ r.append(s.cache());
+ }
+
+ return r.toString();
+ }
+
+ public abstract String getNextSequenceValueSql(String seqname);
+}
diff --git a/src/com/google/gwtorm/schema/sql/SqlIntTypeInfo.java b/src/com/google/gwtorm/schema/sql/SqlIntTypeInfo.java
new file mode 100644
index 0000000..554c1c4
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/SqlIntTypeInfo.java
@@ -0,0 +1,36 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.schema.ColumnModel;
+
+import java.sql.Types;
+
+public class SqlIntTypeInfo extends SqlTypeInfo {
+ @Override
+ protected String getJavaSqlTypeAlias() {
+ return "Int";
+ }
+
+ @Override
+ protected int getSqlTypeConstant() {
+ return Types.INTEGER;
+ }
+
+ @Override
+ public String getSqlType(final ColumnModel column) {
+ return "INT DEFAULT 0 NOT NULL";
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/SqlLongTypeInfo.java b/src/com/google/gwtorm/schema/sql/SqlLongTypeInfo.java
new file mode 100644
index 0000000..1b1f50c
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/SqlLongTypeInfo.java
@@ -0,0 +1,36 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.schema.ColumnModel;
+
+import java.sql.Types;
+
+public class SqlLongTypeInfo extends SqlTypeInfo {
+ @Override
+ protected String getJavaSqlTypeAlias() {
+ return "Long";
+ }
+
+ @Override
+ protected int getSqlTypeConstant() {
+ return Types.BIGINT;
+ }
+
+ @Override
+ public String getSqlType(final ColumnModel column) {
+ return "BIGINT DEFAULT 0 NOT NULL";
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/SqlStringTypeInfo.java b/src/com/google/gwtorm/schema/sql/SqlStringTypeInfo.java
new file mode 100644
index 0000000..57de3af
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/SqlStringTypeInfo.java
@@ -0,0 +1,51 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.schema.ColumnModel;
+
+import java.sql.Types;
+
+public class SqlStringTypeInfo extends SqlTypeInfo {
+ @Override
+ protected String getJavaSqlTypeAlias() {
+ return "String";
+ }
+
+ @Override
+ protected int getSqlTypeConstant() {
+ return Types.VARCHAR;
+ }
+
+ @Override
+ public String getSqlType(final ColumnModel col) {
+ final Column column = col.getColumnAnnotation();
+ final StringBuilder r = new StringBuilder();
+
+ if (column.length() <= 0) {
+ r.append("VARCHAR(255)");
+ } else if (column.length() < 255) {
+ r.append("VARCHAR(" + column.length() + ")");
+ }
+
+ if (column.notNull()) {
+ r.append(" DEFAULT ''");
+ r.append(" NOT NULL");
+ }
+
+ return r.toString();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/SqlTimestampTypeInfo.java b/src/com/google/gwtorm/schema/sql/SqlTimestampTypeInfo.java
new file mode 100644
index 0000000..7eed577
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/SqlTimestampTypeInfo.java
@@ -0,0 +1,44 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.schema.ColumnModel;
+
+import java.sql.Types;
+
+public class SqlTimestampTypeInfo extends SqlTypeInfo {
+ @Override
+ protected String getJavaSqlTypeAlias() {
+ return "Timestamp";
+ }
+
+ @Override
+ protected int getSqlTypeConstant() {
+ return Types.TIMESTAMP;
+ }
+
+ @Override
+ public String getSqlType(final ColumnModel col) {
+ final Column column = col.getColumnAnnotation();
+ final StringBuilder r = new StringBuilder();
+ r.append("TIMESTAMP");
+ if (column.notNull()) {
+ r.append(" DEFAULT '1900-01-01 00:00:00'");
+ r.append(" NOT NULL");
+ }
+ return r.toString();
+ }
+}
diff --git a/src/com/google/gwtorm/schema/sql/SqlTypeInfo.java b/src/com/google/gwtorm/schema/sql/SqlTypeInfo.java
new file mode 100644
index 0000000..75e656d
--- /dev/null
+++ b/src/com/google/gwtorm/schema/sql/SqlTypeInfo.java
@@ -0,0 +1,60 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.jdbc.gen.CodeGenSupport;
+import com.google.gwtorm.schema.ColumnModel;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.sql.PreparedStatement;
+
+
+public abstract class SqlTypeInfo {
+ protected SqlTypeInfo() {
+ }
+
+ public abstract String getSqlType(ColumnModel column);
+
+ protected abstract String getJavaSqlTypeAlias();
+
+ protected abstract int getSqlTypeConstant();
+
+ public void generatePreparedStatementSet(final CodeGenSupport cgs) {
+ cgs.pushSqlHandle();
+ cgs.pushColumnIndex();
+ cgs.pushFieldValue();
+ cgs.invokePreparedStatementSet(getJavaSqlTypeAlias());
+ }
+
+ public void generatePreparedStatementNull(final CodeGenSupport cgs) {
+ cgs.pushSqlHandle();
+ cgs.pushColumnIndex();
+ cgs.push(getSqlTypeConstant());
+ cgs.mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type
+ .getInternalName(PreparedStatement.class), "setNull", Type
+ .getMethodDescriptor(Type.VOID_TYPE, new Type[] {Type.INT_TYPE,
+ Type.INT_TYPE}));
+ }
+
+ public void generateResultSetGet(final CodeGenSupport cgs) {
+ cgs.fieldSetBegin();
+ cgs.pushSqlHandle();
+ cgs.pushColumnIndex();
+ cgs.invokeResultSetGet(getJavaSqlTypeAlias());
+ cgs.fieldSetEnd();
+ }
+}
diff --git a/test/com/google/gwtorm/client/IntKeyTestCase.java b/test/com/google/gwtorm/client/IntKeyTestCase.java
new file mode 100644
index 0000000..8147ca4
--- /dev/null
+++ b/test/com/google/gwtorm/client/IntKeyTestCase.java
@@ -0,0 +1,112 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import junit.framework.TestCase;
+
+
+public class IntKeyTestCase extends TestCase {
+ private abstract static class IntKeyImpl<T extends Key<?>> extends IntKey<T> {
+ @Column
+ int id;
+
+ public IntKeyImpl(int n) {
+ id = n;
+ }
+
+ @Override
+ public int get() {
+ return id;
+ }
+ }
+
+ private static class Parent extends IntKeyImpl<Key<?>> {
+ public Parent(int n) {
+ super(n);
+ }
+ }
+
+ private static class UnrelatedEntity extends IntKeyImpl<Key<?>> {
+ public UnrelatedEntity(int n) {
+ super(n);
+ }
+ }
+
+ private static class Child extends IntKeyImpl<Parent> {
+ private Parent parent;
+
+ public Child(Parent p, int n) {
+ super(n);
+ parent = p;
+ }
+
+ @Override
+ public Parent getParentKey() {
+ return parent;
+ }
+ }
+
+ public void testHashCodeWhenNull() {
+ final Parent p = new Parent(0);
+ assertEquals(0, p.hashCode());
+ }
+
+ public void testParentHashCode() {
+ final int id = 42;
+ final Parent p = new Parent(id);
+ assertEquals(id, p.hashCode());
+ }
+
+ public void testParentEquals() {
+ final int id = 42;
+ final Parent p1 = new Parent(id);
+ final Parent p2 = new Parent(id);
+ assertTrue(p1.equals(p1));
+ assertTrue(p1.equals(p2));
+ assertTrue(p2.equals(p1));
+ assertFalse(p1.equals(null));
+
+ final UnrelatedEntity u = new UnrelatedEntity(id);
+ assertFalse(p1.equals(u));
+ assertFalse(u.equals(p1));
+
+ final Parent p3 = new Parent(64);
+ assertFalse(p1.equals(p3));
+ }
+
+ public void testChildHashCode() {
+ final int pId = 2;
+ final int cId = 8;
+ final Parent p = new Parent(pId);
+ final Child c = new Child(p, cId);
+ assertSame(p, c.getParentKey());
+ assertTrue(cId != c.hashCode());
+ }
+
+ public void testChildEquals() {
+ final int pId = 2;
+ final int cId = 8;
+ final Child c1 = new Child(new Parent(pId), cId);
+ final Child c2 = new Child(new Parent(pId), cId);
+ assertTrue(c1.equals(c1));
+ assertTrue(c1.equals(c2));
+ assertTrue(c2.equals(c1));
+ assertFalse(c1.equals(null));
+
+ final UnrelatedEntity u = new UnrelatedEntity(cId);
+ assertFalse(c1.equals(u));
+ assertFalse(u.equals(c1));
+ }
+}
diff --git a/test/com/google/gwtorm/client/LongKeyTestCase.java b/test/com/google/gwtorm/client/LongKeyTestCase.java
new file mode 100644
index 0000000..34958cf
--- /dev/null
+++ b/test/com/google/gwtorm/client/LongKeyTestCase.java
@@ -0,0 +1,113 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import junit.framework.TestCase;
+
+
+public class LongKeyTestCase extends TestCase {
+ private abstract static class LongKeyImpl<T extends Key<?>> extends
+ LongKey<T> {
+ @Column
+ long id;
+
+ public LongKeyImpl(long n) {
+ id = n;
+ }
+
+ @Override
+ public long get() {
+ return id;
+ }
+ }
+
+ private static class Parent extends LongKeyImpl<Key<?>> {
+ public Parent(long n) {
+ super(n);
+ }
+ }
+
+ private static class UnrelatedEntity extends LongKeyImpl<Key<?>> {
+ public UnrelatedEntity(long n) {
+ super(n);
+ }
+ }
+
+ private static class Child extends LongKeyImpl<Parent> {
+ private Parent parent;
+
+ public Child(Parent p, long n) {
+ super(n);
+ parent = p;
+ }
+
+ @Override
+ public Parent getParentKey() {
+ return parent;
+ }
+ }
+
+ public void testHashCodeWhenNull() {
+ final Parent p = new Parent(0);
+ assertEquals(0, p.hashCode());
+ }
+
+ public void testParentHashCode() {
+ final long id = 21281821821821881L;
+ final Parent p = new Parent(id);
+ assertEquals((int) id, p.hashCode());
+ }
+
+ public void testParentEquals() {
+ final long id = 21281821821821881L;
+ final Parent p1 = new Parent(id);
+ final Parent p2 = new Parent(id);
+ assertTrue(p1.equals(p1));
+ assertTrue(p1.equals(p2));
+ assertTrue(p2.equals(p1));
+ assertFalse(p1.equals(null));
+
+ final UnrelatedEntity u = new UnrelatedEntity(id);
+ assertFalse(p1.equals(u));
+ assertFalse(u.equals(p1));
+
+ final Parent p3 = new Parent(64);
+ assertFalse(p1.equals(p3));
+ }
+
+ public void testChildHashCode() {
+ final long pId = 21281821821821881L;
+ final long cId = 8;
+ final Parent p = new Parent(pId);
+ final Child c = new Child(p, cId);
+ assertSame(p, c.getParentKey());
+ assertTrue(cId != c.hashCode());
+ }
+
+ public void testChildEquals() {
+ final long pId = 21281821821821881L;
+ final long cId = 8;
+ final Child c1 = new Child(new Parent(pId), cId);
+ final Child c2 = new Child(new Parent(pId), cId);
+ assertTrue(c1.equals(c1));
+ assertTrue(c1.equals(c2));
+ assertTrue(c2.equals(c1));
+ assertFalse(c1.equals(null));
+
+ final UnrelatedEntity u = new UnrelatedEntity(cId);
+ assertFalse(c1.equals(u));
+ assertFalse(u.equals(c1));
+ }
+}
diff --git a/test/com/google/gwtorm/client/StringKeyTestCase.java b/test/com/google/gwtorm/client/StringKeyTestCase.java
new file mode 100644
index 0000000..62dfbd0
--- /dev/null
+++ b/test/com/google/gwtorm/client/StringKeyTestCase.java
@@ -0,0 +1,113 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+import junit.framework.TestCase;
+
+
+public class StringKeyTestCase extends TestCase {
+ private abstract static class StringKeyImpl<T extends Key<?>> extends
+ StringKey<T> {
+ @Column
+ String name;
+
+ public StringKeyImpl(String n) {
+ name = n;
+ }
+
+ @Override
+ public String get() {
+ return name;
+ }
+ }
+
+ private static class Parent extends StringKeyImpl<Key<?>> {
+ public Parent(String n) {
+ super(n);
+ }
+ }
+
+ private static class UnrelatedEntity extends StringKeyImpl<Key<?>> {
+ public UnrelatedEntity(String n) {
+ super(n);
+ }
+ }
+
+ private static class Child extends StringKeyImpl<Parent> {
+ private Parent parent;
+
+ public Child(Parent p, String n) {
+ super(n);
+ parent = p;
+ }
+
+ @Override
+ public Parent getParentKey() {
+ return parent;
+ }
+ }
+
+ public void testHashCodeWhenNull() {
+ final Parent p = new Parent(null);
+ assertEquals(0, p.hashCode());
+ }
+
+ public void testParentHashCode() {
+ final String str = "foo";
+ final Parent p = new Parent(str);
+ assertEquals(str.hashCode(), p.hashCode());
+ }
+
+ public void testParentEquals() {
+ final String str = "foo";
+ final Parent p1 = new Parent(str);
+ final Parent p2 = new Parent(str);
+ assertTrue(p1.equals(p1));
+ assertTrue(p1.equals(p2));
+ assertTrue(p2.equals(p1));
+ assertFalse(p1.equals(null));
+
+ final UnrelatedEntity u = new UnrelatedEntity(str);
+ assertFalse(p1.equals(u));
+ assertFalse(u.equals(p1));
+
+ final Parent p3 = new Parent("bar");
+ assertFalse(p1.equals(p3));
+ }
+
+ public void testChildHashCode() {
+ final String pName = "foo";
+ final String cName = "bar";
+ final Parent p = new Parent(pName);
+ final Child c = new Child(p, cName);
+ assertSame(p, c.getParentKey());
+ assertTrue(cName.hashCode() != c.hashCode());
+ }
+
+ public void testChildEquals() {
+ final String pName = "foo";
+ final String cName = "bar";
+ final Child c1 = new Child(new Parent(pName), cName);
+ final Child c2 = new Child(new Parent(pName), cName);
+ assertTrue(c1.equals(c1));
+ assertTrue(c1.equals(c2));
+ assertTrue(c2.equals(c1));
+ assertFalse(c1.equals(null));
+
+ final UnrelatedEntity u = new UnrelatedEntity("bar");
+ assertFalse(c1.equals(u));
+ assertFalse(u.equals(c1));
+ }
+}
diff --git a/test/com/google/gwtorm/data/AddressAccess.java b/test/com/google/gwtorm/data/AddressAccess.java
new file mode 100644
index 0000000..ef98582
--- /dev/null
+++ b/test/com/google/gwtorm/data/AddressAccess.java
@@ -0,0 +1,29 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AddressAccess extends Access<TestAddress, TestAddress.Key> {
+ @PrimaryKey("city")
+ TestAddress byId(TestAddress.Key a) throws OrmException;
+
+ @Query()
+ ResultSet<TestAddress> all() throws OrmException;
+}
diff --git a/test/com/google/gwtorm/data/PersonAccess.java b/test/com/google/gwtorm/data/PersonAccess.java
new file mode 100644
index 0000000..7df107e
--- /dev/null
+++ b/test/com/google/gwtorm/data/PersonAccess.java
@@ -0,0 +1,48 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface PersonAccess extends Access<TestPerson, TestPerson.Key> {
+ @PrimaryKey("name")
+ TestPerson get(TestPerson.Key key) throws OrmException;
+
+ @Query
+ ResultSet<TestPerson> all() throws OrmException;
+
+ @Query("WHERE age > ? ORDER BY age")
+ ResultSet<TestPerson> olderThan(int age) throws OrmException;
+
+ @Query("WHERE name != ? AND age > ? ORDER BY name DESC")
+ ResultSet<TestPerson> notPerson(TestPerson.Key key, int age)
+ throws OrmException;
+
+ @Query("WHERE name = 'bob' LIMIT ?")
+ ResultSet<TestPerson> firstNBob(int n) throws OrmException;
+
+ @Query("WHERE registered = false ORDER BY name")
+ ResultSet<TestPerson> notRegistered() throws OrmException;
+
+ @Query("ORDER BY age LIMIT 1")
+ ResultSet<TestPerson> youngest() throws OrmException;
+
+ @Query("ORDER BY age LIMIT ?")
+ ResultSet<TestPerson> youngestN(int n) throws OrmException;
+}
diff --git a/test/com/google/gwtorm/data/PhoneBookDb.java b/test/com/google/gwtorm/data/PhoneBookDb.java
new file mode 100644
index 0000000..15439b8
--- /dev/null
+++ b/test/com/google/gwtorm/data/PhoneBookDb.java
@@ -0,0 +1,30 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.client.Sequence;
+
+public interface PhoneBookDb extends Schema {
+ @Relation
+ PersonAccess people();
+
+ @Relation
+ AddressAccess addresses();
+
+ @Sequence
+ int nextAddressId();
+}
diff --git a/test/com/google/gwtorm/data/TestAddress.java b/test/com/google/gwtorm/data/TestAddress.java
new file mode 100644
index 0000000..d6a135a
--- /dev/null
+++ b/test/com/google/gwtorm/data/TestAddress.java
@@ -0,0 +1,61 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+public class TestAddress {
+ public static class Key extends StringKey<TestPerson.Key> {
+ @Column
+ protected TestPerson.Key owner;
+
+ @Column
+ protected String name;
+
+ protected Key() {
+ owner = new TestPerson.Key();
+ }
+
+ public Key(final TestPerson.Key owner, final String name) {
+ this.owner = owner;
+ this.name = name;
+ }
+
+ @Override
+ public String get() {
+ return name;
+ }
+
+ @Override
+ public TestPerson.Key getParentKey() {
+ return owner;
+ }
+ }
+
+ @Column
+ protected Key city;
+
+ protected TestAddress() {
+ }
+
+ public TestAddress(final TestAddress.Key city) {
+ this.city = city;
+ }
+
+ public String city() {
+ return city.name;
+ }
+}
diff --git a/test/com/google/gwtorm/data/TestPerson.java b/test/com/google/gwtorm/data/TestPerson.java
new file mode 100644
index 0000000..7cce083
--- /dev/null
+++ b/test/com/google/gwtorm/data/TestPerson.java
@@ -0,0 +1,79 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+
+public class TestPerson {
+ public static class Key extends StringKey {
+ @Column(length = 20)
+ protected String name;
+
+ protected Key() {
+ }
+
+ public Key(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String get() {
+ return name;
+ }
+ }
+
+ @Column
+ protected Key name;
+
+ @Column
+ protected int age;
+
+ @Column
+ protected boolean registered;
+
+ protected TestPerson() {
+ }
+
+ public TestPerson(final Key key, final int age) {
+ this.name = key;
+ this.age = age;
+ }
+
+ public String name() {
+ return name.get();
+ }
+
+ public int age() {
+ return age;
+ }
+
+ public boolean isRegistered() {
+ return registered;
+ }
+
+ public void growOlder() {
+ age++;
+ }
+
+ public void register() {
+ registered = true;
+ }
+
+ public void unregister() {
+ registered = false;
+ }
+}
diff --git a/test/com/google/gwtorm/schema/QueryParserTest.java b/test/com/google/gwtorm/schema/QueryParserTest.java
new file mode 100644
index 0000000..3f5bd90
--- /dev/null
+++ b/test/com/google/gwtorm/schema/QueryParserTest.java
@@ -0,0 +1,178 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema;
+
+import com.google.gwtorm.client.OrmException;
+
+import junit.framework.TestCase;
+
+import org.antlr.runtime.tree.Tree;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class QueryParserTest extends TestCase {
+ private final class DummyColumn extends ColumnModel {
+ private String name;
+
+ DummyColumn(final String n) {
+ name = n;
+ columnName = n;
+ }
+
+ @Override
+ public String getFieldName() {
+ return name;
+ }
+
+ @Override
+ public String getNestedClassName() {
+ return null;
+ }
+
+ @Override
+ public Class<?> getPrimitiveType() {
+ return String.class;
+ }
+ }
+
+ protected Tree parse(final String str) throws QueryParseException {
+ final RelationModel dummy = new RelationModel() {
+ {
+ final Collection<ColumnModel> c = new ArrayList<ColumnModel>();
+ try {
+ c.add(new DummyColumn("name"));
+ c.add(new DummyColumn("a"));
+ c.add(new DummyColumn("b"));
+ c.add(new DummyColumn("c"));
+ initColumns(c);
+ } catch (OrmException e) {
+ throw new RuntimeException("init columns failure", e);
+ }
+ }
+
+ @Override
+ public String getAccessInterfaceName() {
+ return getClass().getName();
+ }
+
+ @Override
+ public String getEntityTypeClassName() {
+ return getClass().getName();
+ }
+ };
+ return QueryParser.parse(dummy, str);
+ }
+
+ protected static void assertGoodEQ(final Tree c, final String fieldName) {
+ assertEquals(QueryParser.EQ, c.getType());
+ assertEquals(2, c.getChildCount());
+ assertEquals(QueryParser.ID, c.getChild(0).getType());
+ assertTrue(c.getChild(0) instanceof QueryParser.Column);
+ assertEquals(fieldName, c.getChild(0).getText());
+ assertEquals(QueryParser.PLACEHOLDER, c.getChild(1).getType());
+ }
+
+ public void testEmptyQuery() throws QueryParseException {
+ assertNull(parse(""));
+ }
+
+ public void testWhereNameEq() throws QueryParseException {
+ final Tree t = parse("WHERE name = ?");
+ assertNotNull(t);
+ assertEquals(QueryParser.WHERE, t.getType());
+
+ assertEquals(1, t.getChildCount());
+ assertGoodEQ(t.getChild(0), "name");
+ }
+
+ public void testWhereAAndBAndC() throws QueryParseException {
+ final Tree t = parse("WHERE a = ? AND b = ? AND c = ?");
+ assertNotNull(t);
+ assertEquals(QueryParser.WHERE, t.getType());
+
+ assertEquals(1, t.getChildCount());
+ final Tree c = t.getChild(0);
+ assertEquals(QueryParser.AND, c.getType());
+ assertEquals(3, c.getChildCount());
+ assertGoodEQ(c.getChild(0), "a");
+ assertGoodEQ(c.getChild(1), "b");
+ assertGoodEQ(c.getChild(2), "c");
+ }
+
+ public void testOrderByA() throws QueryParseException {
+ final Tree t = parse("ORDER BY a");
+ assertNotNull(t);
+ assertEquals(QueryParser.ORDER, t.getType());
+ assertEquals(1, t.getChildCount());
+
+ final Tree a = t.getChild(0);
+ assertEquals(QueryParser.ASC, a.getType());
+ assertEquals(1, a.getChildCount());
+ assertEquals(QueryParser.ID, a.getChild(0).getType());
+ assertTrue(a.getChild(0) instanceof QueryParser.Column);
+ assertEquals("a", a.getChild(0).getText());
+ }
+
+ public void testOrderByAB() throws QueryParseException {
+ final Tree t = parse("ORDER BY a DESC, b ASC");
+ assertNotNull(t);
+ assertEquals(QueryParser.ORDER, t.getType());
+ assertEquals(2, t.getChildCount());
+ {
+ final Tree a = t.getChild(0);
+ assertEquals(QueryParser.DESC, a.getType());
+ assertEquals(1, a.getChildCount());
+ assertEquals(QueryParser.ID, a.getChild(0).getType());
+ assertTrue(a.getChild(0) instanceof QueryParser.Column);
+ assertEquals("a", a.getChild(0).getText());
+ }
+ {
+ final Tree b = t.getChild(1);
+ assertEquals(QueryParser.ASC, b.getType());
+ assertEquals(1, b.getChildCount());
+ assertEquals(QueryParser.ID, b.getChild(0).getType());
+ assertTrue(b.getChild(0) instanceof QueryParser.Column);
+ assertEquals("b", b.getChild(0).getText());
+ }
+ }
+
+ public void testWhereAOrderByA() throws QueryParseException {
+ final Tree t = parse("WHERE a = ? ORDER BY a");
+ assertNotNull(t);
+ assertEquals(0, t.getType());
+ assertEquals(2, t.getChildCount());
+ {
+ final Tree w = t.getChild(0);
+ assertEquals(QueryParser.WHERE, w.getType());
+ assertEquals(1, w.getChildCount());
+ assertGoodEQ(w.getChild(0), "a");
+ }
+ {
+ final Tree o = t.getChild(1);
+ assertEquals(QueryParser.ORDER, o.getType());
+ assertEquals(1, o.getChildCount());
+
+ final Tree a = o.getChild(0);
+ assertEquals(QueryParser.ASC, a.getType());
+ assertEquals(1, a.getChildCount());
+ final Tree aId = a.getChild(0);
+ assertEquals(QueryParser.ID, aId.getType());
+ assertTrue(aId instanceof QueryParser.Column);
+ assertEquals("a", aId.getText());
+ assertEquals("a", ((QueryParser.Column) aId).getField().getFieldName());
+ }
+ }
+}
diff --git a/test/com/google/gwtorm/server/PhoneBookDbTestCase.java b/test/com/google/gwtorm/server/PhoneBookDbTestCase.java
new file mode 100644
index 0000000..7252d85
--- /dev/null
+++ b/test/com/google/gwtorm/server/PhoneBookDbTestCase.java
@@ -0,0 +1,353 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.server;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.gwtorm.data.PersonAccess;
+import com.google.gwtorm.data.PhoneBookDb;
+import com.google.gwtorm.data.TestPerson;
+import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.jdbc.JdbcSchema;
+
+import junit.framework.TestCase;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+public class PhoneBookDbTestCase extends TestCase {
+ private static int runCount;
+ protected Database<PhoneBookDb> db;
+ private List<PhoneBookDb> openSchemas;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ final Properties p = new Properties();
+ p.setProperty("driver", org.h2.Driver.class.getName());
+ p.setProperty("url", "jdbc:h2:mem:PhoneBookDb" + (runCount++));
+ db = new Database<PhoneBookDb>(p, PhoneBookDb.class);
+ openSchemas = new ArrayList<PhoneBookDb>();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (openSchemas != null) {
+ for (PhoneBookDb schema : openSchemas) {
+ schema.close();
+ }
+ openSchemas = null;
+ }
+ super.tearDown();
+ }
+
+ protected PhoneBookDb open() throws OrmException {
+ final PhoneBookDb r = db.open();
+ if (r != null) {
+ openSchemas.add(r);
+ }
+ return r;
+ }
+
+ protected PhoneBookDb openAndCreate() throws OrmException {
+ final PhoneBookDb schema = open();
+ schema.createSchema();
+ return schema;
+ }
+
+ protected Statement statement(final PhoneBookDb schema) throws SQLException {
+ return ((JdbcSchema) schema).getConnection().createStatement();
+ }
+
+ public void testCreateDatabaseHandle() throws Exception {
+ assertNotNull(db);
+ }
+
+ public void testOpenSchema() throws Exception {
+ final PhoneBookDb schema1 = open();
+ assertNotNull(schema1);
+
+ final PhoneBookDb schema2 = open();
+ assertNotNull(schema2);
+ assertNotSame(schema1, schema2);
+ }
+
+ public void testGetPeopleAccess() throws Exception {
+ final PhoneBookDb schema = open();
+ assertNotNull(schema.people());
+ }
+
+ public void testGetAddressAccess() throws Exception {
+ final PhoneBookDb schema = open();
+ assertNotNull(schema.addresses());
+ }
+
+ public void testCreateSchema() throws Exception {
+ final PhoneBookDb schema = open();
+ schema.createSchema();
+ }
+
+ public void testNextAddressId() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final int a = schema.nextAddressId();
+ final int b = schema.nextAddressId();
+ assertTrue(a != b);
+ }
+
+ public void testPersonPrimaryKey() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson.Key key = new TestPerson.Key("Bob");
+ final TestPerson bob = new TestPerson(key, 18);
+ assertSame(key, schema.people().primaryKey(bob));
+ }
+
+ public void testInsertOnePerson() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson bob = new TestPerson(new TestPerson.Key("Bob"), 18);
+ schema.people().insert(Collections.singleton(bob));
+
+ final Statement st = statement(schema);
+ final ResultSet rs = st.executeQuery("SELECT name,age FROM people");
+ assertTrue(rs.next());
+ assertEquals(bob.name(), rs.getString(1));
+ assertEquals(bob.age(), rs.getInt(2));
+ assertFalse(rs.next());
+ rs.close();
+ st.close();
+ }
+
+ public void testGetOnePerson() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final PersonAccess sp = schema.people();
+ final TestPerson p1 = new TestPerson(new TestPerson.Key("Bob"), 18);
+ sp.insert(Collections.singleton(p1));
+
+ final TestPerson p2 = sp.get(sp.primaryKey(p1));
+ assertNotNull(p2);
+ assertNotSame(p1, p2);
+ assertEquals(sp.primaryKey(p1), sp.primaryKey(p2));
+ }
+
+ public void testGetOnePersonIterator() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final PersonAccess sp = schema.people();
+ final TestPerson p1 = new TestPerson(new TestPerson.Key("Bob"), 18);
+ sp.insert(Collections.singleton(p1));
+
+ final List<TestPerson> list =
+ sp.get(Collections.singleton(sp.primaryKey(p1))).toList();
+ assertNotNull(list);
+ assertEquals(1, list.size());
+
+ final TestPerson p2 = list.get(0);
+ assertNotNull(p2);
+ assertNotSame(p1, p2);
+ assertEquals(sp.primaryKey(p1), sp.primaryKey(p2));
+ }
+
+ public void testInsertManyPeople() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final ArrayList<TestPerson> all = new ArrayList<TestPerson>();
+ all.add(new TestPerson(new TestPerson.Key("Bob"), 18));
+ all.add(new TestPerson(new TestPerson.Key("Mary"), 22));
+ all.add(new TestPerson(new TestPerson.Key("Zak"), 33));
+ schema.people().insert(all);
+
+ final Statement st = statement(schema);
+ final ResultSet rs;
+ rs = st.executeQuery("SELECT name,age FROM people ORDER BY name");
+ for (int rowIdx = 0; rowIdx < all.size(); rowIdx++) {
+ assertTrue(rs.next());
+ assertEquals(all.get(rowIdx).name(), rs.getString(1));
+ assertEquals(all.get(rowIdx).age(), rs.getInt(2));
+ }
+ assertFalse(rs.next());
+ rs.close();
+ st.close();
+ }
+
+ public void testInsertManyPeopleByTransaction() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final Transaction txn = schema.beginTransaction();
+ final ArrayList<TestPerson> all = new ArrayList<TestPerson>();
+ all.add(new TestPerson(new TestPerson.Key("Bob"), 18));
+ all.add(new TestPerson(new TestPerson.Key("Mary"), 22));
+ all.add(new TestPerson(new TestPerson.Key("Zak"), 33));
+ schema.people().insert(all, txn);
+
+ final Statement st = statement(schema);
+ ResultSet rs;
+
+ rs = st.executeQuery("SELECT name,age FROM people ORDER BY name");
+ assertFalse(rs.next());
+ rs.close();
+
+ txn.commit();
+ rs = st.executeQuery("SELECT name,age FROM people ORDER BY name");
+ for (int rowIdx = 0; rowIdx < all.size(); rowIdx++) {
+ assertTrue(rs.next());
+ assertEquals(all.get(rowIdx).name(), rs.getString(1));
+ assertEquals(all.get(rowIdx).age(), rs.getInt(2));
+ }
+ assertFalse(rs.next());
+ rs.close();
+ st.close();
+ }
+
+ public void testDeleteOnePerson() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson bob = new TestPerson(new TestPerson.Key("Bob"), 18);
+ schema.people().insert(Collections.singleton(bob));
+ schema.people().delete(Collections.singleton(bob));
+
+ final Statement st = statement(schema);
+ final ResultSet rs = st.executeQuery("SELECT name,age FROM people");
+ assertFalse(rs.next());
+ rs.close();
+ st.close();
+ }
+
+ public void testUpdateOnePerson() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson bob = new TestPerson(new TestPerson.Key("Bob"), 18);
+ schema.people().insert(Collections.singleton(bob));
+ bob.growOlder();
+ schema.people().update(Collections.singleton(bob));
+
+ final Statement st = statement(schema);
+ final ResultSet rs = st.executeQuery("SELECT name,age FROM people");
+ assertTrue(rs.next());
+ assertEquals(bob.name(), rs.getString(1));
+ assertEquals(bob.age(), rs.getInt(2));
+ assertFalse(rs.next());
+ rs.close();
+ st.close();
+ }
+
+ public void testUpdateNoPerson() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson bob = new TestPerson(new TestPerson.Key("Bob"), 18);
+ try {
+ schema.people().update(Collections.singleton(bob));
+ fail("Update of missing person succeeded");
+ } catch (OrmException e) {
+ assertEquals("Update failure: people", e.getMessage());
+ assertTrue(e.getCause() instanceof SQLException);
+ assertEquals("Entity 1 not affected by update", e.getCause().getMessage());
+ }
+ }
+
+ public void testFetchOnePerson() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson bob = new TestPerson(new TestPerson.Key("Bob"), 18);
+ schema.people().insert(Collections.singleton(bob));
+
+ final List<TestPerson> all = schema.people().all().toList();
+ assertNotNull(all);
+ assertEquals(1, all.size());
+ assertNotSame(bob, all.get(0));
+ assertEquals(bob.name(), all.get(0).name());
+ assertEquals(bob.age(), all.get(0).age());
+ assertEquals(bob.isRegistered(), all.get(0).isRegistered());
+ }
+
+ public void testFetchOnePersonByName() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson bob1 = new TestPerson(new TestPerson.Key("Bob"), 18);
+ schema.people().insert(Collections.singleton(bob1));
+
+ final TestPerson bob2 =
+ schema.people().get(new TestPerson.Key(bob1.name()));
+ assertNotNull(bob2);
+ assertNotSame(bob1, bob2);
+ assertEquals(bob1.name(), bob2.name());
+ assertEquals(bob1.age(), bob2.age());
+ assertEquals(bob1.isRegistered(), bob2.isRegistered());
+ }
+
+ public void testFetchByAge() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final ArrayList<TestPerson> all = new ArrayList<TestPerson>();
+ all.add(new TestPerson(new TestPerson.Key("Bob"), 18));
+ all.add(new TestPerson(new TestPerson.Key("Mary"), 22));
+ all.add(new TestPerson(new TestPerson.Key("Zak"), 33));
+ schema.people().insert(all);
+
+ final List<TestPerson> r = schema.people().olderThan(20).toList();
+ assertEquals(2, r.size());
+ assertEquals(all.get(1).name(), r.get(0).name());
+ assertEquals(all.get(2).name(), r.get(1).name());
+ }
+
+ public void testFetchNotPerson() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final ArrayList<TestPerson> all = new ArrayList<TestPerson>();
+ all.add(new TestPerson(new TestPerson.Key("Bob"), 18));
+ all.add(new TestPerson(new TestPerson.Key("Mary"), 22));
+ all.add(new TestPerson(new TestPerson.Key("Zak"), 33));
+ schema.people().insert(all);
+
+ final List<TestPerson> r =
+ schema.people().notPerson(new TestPerson.Key("Mary"), 10).toList();
+ assertEquals(2, r.size());
+ assertEquals(all.get(2).name(), r.get(0).name());
+ assertEquals(all.get(0).name(), r.get(1).name());
+ }
+
+ public void testBooleanType() throws Exception {
+ final PhoneBookDb schema = openAndCreate();
+ final TestPerson bob = new TestPerson(new TestPerson.Key("Bob"), 18);
+ schema.people().insert(Collections.singleton(bob));
+
+ final Statement st = statement(schema);
+ ResultSet rs;
+
+ rs = st.executeQuery("SELECT registered FROM people");
+ assertTrue(rs.next());
+ assertEquals("N", rs.getString(1));
+ assertFalse(rs.next());
+ rs.close();
+ assertEquals(bob.isRegistered(), schema.people().all().toList().get(0)
+ .isRegistered());
+
+ bob.register();
+ schema.people().update(Collections.singleton(bob));
+ rs = st.executeQuery("SELECT registered FROM people");
+ assertTrue(rs.next());
+ assertEquals("Y", rs.getString(1));
+ assertFalse(rs.next());
+ rs.close();
+ assertEquals(bob.isRegistered(), schema.people().all().toList().get(0)
+ .isRegistered());
+
+ bob.unregister();
+ schema.people().update(Collections.singleton(bob));
+ rs = st.executeQuery("SELECT registered FROM people");
+ assertTrue(rs.next());
+ assertEquals("N", rs.getString(1));
+ assertFalse(rs.next());
+ rs.close();
+ assertEquals(bob.isRegistered(), schema.people().all().toList().get(0)
+ .isRegistered());
+
+ st.close();
+ }
+}
diff --git a/test/com/google/gwtorm/server/PrintCreateTablesTestCase.java b/test/com/google/gwtorm/server/PrintCreateTablesTestCase.java
new file mode 100644
index 0000000..544c185
--- /dev/null
+++ b/test/com/google/gwtorm/server/PrintCreateTablesTestCase.java
@@ -0,0 +1,28 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.server;
+
+import com.google.gwtorm.data.PhoneBookDb;
+import com.google.gwtorm.schema.java.JavaSchemaModel;
+import com.google.gwtorm.schema.sql.DialectH2;
+
+import junit.framework.TestCase;
+
+public class PrintCreateTablesTestCase extends TestCase {
+ public void testCreate() throws Exception {
+ final JavaSchemaModel m = new JavaSchemaModel(PhoneBookDb.class);
+ System.out.println(m.getCreateDatabaseSql(new DialectH2()));
+ }
+}