diff --git a/.settings/org.eclipse.wst.jsdt.core.prefs b/.settings/org.eclipse.wst.jsdt.core.prefs index 7b594a52b..0a9384361 100644 --- a/.settings/org.eclipse.wst.jsdt.core.prefs +++ b/.settings/org.eclipse.wst.jsdt.core.prefs @@ -186,6 +186,7 @@ org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_superinterfaces= org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_arguments=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_parameters=insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.wst.jsdt.core.formatter.insert_space_after_function_keyword=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert diff --git a/extensions/build.xml b/extensions/build.xml index 1b7fa19ad..0cd013394 100644 --- a/extensions/build.xml +++ b/extensions/build.xml @@ -13,6 +13,7 @@ + @@ -21,5 +22,6 @@ + diff --git a/extensions/database/.classpath b/extensions/database/.classpath new file mode 100644 index 000000000..4831eb720 --- /dev/null +++ b/extensions/database/.classpath @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/database/.project b/extensions/database/.project new file mode 100644 index 000000000..3b075b2a5 --- /dev/null +++ b/extensions/database/.project @@ -0,0 +1,29 @@ + + + refine-database-extension + + + + + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/extensions/database/.settings/.jsdtscope b/extensions/database/.settings/.jsdtscope new file mode 100644 index 000000000..b13d30357 --- /dev/null +++ b/extensions/database/.settings/.jsdtscope @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/extensions/database/.settings/org.eclipse.jdt.core.prefs b/extensions/database/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..d6721a52c --- /dev/null +++ b/extensions/database/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,280 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +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.6 +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_annotation=0 +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=0 +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=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +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=33 +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=1 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +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=1 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +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=false +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=true +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=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +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.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +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=false +org.eclipse.jdt.core.formatter.indentation.size=8 +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_label=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=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=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=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=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.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +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=120 +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=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/extensions/database/.settings/org.eclipse.jdt.ui.prefs b/extensions/database/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..e3ddcd8c4 --- /dev/null +++ b/extensions/database/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,4 @@ +#Mon Sep 27 15:02:46 PDT 2010 +eclipse.preferences.version=1 +formatter_profile=_Google Refine +formatter_settings_version=11 diff --git a/extensions/database/.settings/org.eclipse.wst.common.project.facet.core.xml b/extensions/database/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 000000000..8ca4d66e9 --- /dev/null +++ b/extensions/database/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.container b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 000000000..3bd5d0a48 --- /dev/null +++ b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.name b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 000000000..05bd71b6e --- /dev/null +++ b/extensions/database/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/extensions/database/README.md b/extensions/database/README.md new file mode 100644 index 000000000..216d4f6ab --- /dev/null +++ b/extensions/database/README.md @@ -0,0 +1,41 @@ +This project is an extension for Google Refine that provides a way to import database data using JDBC. + + +INSTALL + +1. Before installing this extension download Google Refine code from http://code.google.com/p/google-refine/source/checkout. + +2. Pull this extension's code into folder database under folder /extensions. +For more information on how to write a Google Refine extensions and where to put the files see http://code.google.com/p/google-refine/wiki/WriteAnExtension + +The folder structure should resemble this: +grefine-all/ +----------/extensions +--------------/database +------------------/module +------------------/src +------------------build.xml +------------------README (this file) + +3. Update build.xml in folder /extensions with build and clean ant tasks for database: + + + + + + + + + + + + + + + + + + + + +4. If using Eclipse, make sure that you build project with ant diff --git a/extensions/database/build.xml b/extensions/database/build.xml new file mode 100644 index 000000000..9e42b3843 --- /dev/null +++ b/extensions/database/build.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/database/licenses/jdbc-client.LICENSE.txt b/extensions/database/licenses/jdbc-client.LICENSE.txt new file mode 100644 index 000000000..a14b06b91 --- /dev/null +++ b/extensions/database/licenses/jdbc-client.LICENSE.txt @@ -0,0 +1,188 @@ +Copyright 2006 Google + + 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. + + 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. \ No newline at end of file diff --git a/extensions/database/module/MOD-INF/.gitignore b/extensions/database/module/MOD-INF/.gitignore new file mode 100644 index 000000000..840e7d312 --- /dev/null +++ b/extensions/database/module/MOD-INF/.gitignore @@ -0,0 +1 @@ +/classes/ diff --git a/extensions/database/module/MOD-INF/controller.js b/extensions/database/module/MOD-INF/controller.js new file mode 100644 index 000000000..4e90e2efd --- /dev/null +++ b/extensions/database/module/MOD-INF/controller.js @@ -0,0 +1,153 @@ +/* + +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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. + + */ + +/* + * Controller for JDBC Database extension. + * + * This is run in the Butterfly (ie Refine) server context using the Rhino + * Javascript interpreter. + */ + +var html = "text/html"; +var encoding = "UTF-8"; +var version = "0.1"; +var ClientSideResourceManager = Packages.com.google.refine.ClientSideResourceManager; + +var logger = Packages.org.slf4j.LoggerFactory.getLogger("database-extension"), +File = Packages.java.io.File, +refineServlet = Packages.com.google.refine.RefineServlet; + +/* + * Register our custom commands. + */ +function registerCommands() { + + logger.info("Registering Database Extension Commands......"); + var RS = Packages.com.google.refine.RefineServlet; + RS.registerCommand(module, "test-connect", Packages.com.google.refine.extension.database.cmd.TestConnectCommand()); + RS.registerCommand(module, "connect", Packages.com.google.refine.extension.database.cmd.ConnectCommand()); + RS.registerCommand(module, "saved-connection", Packages.com.google.refine.extension.database.cmd.SavedConnectionCommand()); + RS.registerCommand(module, "execute-query", Packages.com.google.refine.extension.database.cmd.ExecuteQueryCommand()); + RS.registerCommand(module, "test-query", Packages.com.google.refine.extension.database.cmd.TestQueryCommand()); + logger.info("Database Extension Command Registeration done!!"); +} + +function registerOperations() { + logger.info("Database Operations Registered successfully..."); +} + +function registerFunctions() { + logger.info("Database Functions Registered successfully..."); +} + + +/* + * Function invoked to initialize the extension. + */ +function init() { + + logger.info("Initializing OpenRefine Database..."); + logger.info("Database Extension Mount point " + module.getMountPoint()); + + registerCommands(); + registerOperations(); + registerFunctions(); + + + // Register importer and exporter + var IM = Packages.com.google.refine.importing.ImportingManager; + + IM.registerController( + module, + "database-import-controller", + new Packages.com.google.refine.extension.database.DatabaseImportController() + ); + + + // Script files to inject into /index page + ClientSideResourceManager.addPaths( + "index/scripts", + module, + [ + "scripts/index/jquery.contextMenu.min.js", + "scripts/index/jquery.ui.position.min.js", + "scripts/database-extension.js", + "scripts/index/database-import-controller.js", + "scripts/index/database-source-ui.js" + ] + ); + // Style files to inject into /index page + ClientSideResourceManager.addPaths( + "index/styles", + module, + [ + "styles/jquery.contextMenu.css", + "styles/pure.css", + "styles/bootstrap.css", + "styles/database-import.less" + + ] + ); + + // Script files to inject into /project page + ClientSideResourceManager.addPaths( + "project/scripts", + module, + [ + "scripts/database-extension.js", + "scripts/project/database-exporters.js" + ] + ); +} + +/* + * Function invoked to handle each request in a custom way. + */ +function process(path, request, response) { + + + var method = request.getMethod(); + + logger.info('receiving request for ' + path); + logger.info('receiving method for ' + method); + + if (path == "/" || path == "") { + var context = {}; + context.version = version; + send(request, response, "index.vt", context); + } +} + +function send(request, response, template, context) { + butterfly.sendTextFromTemplate(request, response, context, template, encoding, html); +} diff --git a/extensions/database/module/MOD-INF/dbextension.properties b/extensions/database/module/MOD-INF/dbextension.properties new file mode 100644 index 000000000..db482f687 --- /dev/null +++ b/extensions/database/module/MOD-INF/dbextension.properties @@ -0,0 +1,3 @@ +# Batch size for import data +preview.batchSize = 100 +create.batchSize = 1000 diff --git a/extensions/database/module/MOD-INF/lib/commons-collections-3.2.1.jar b/extensions/database/module/MOD-INF/lib/commons-collections-3.2.1.jar new file mode 100644 index 000000000..c35fa1fee Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/commons-collections-3.2.1.jar differ diff --git a/extensions/database/module/MOD-INF/lib/commons-io-1.4.jar b/extensions/database/module/MOD-INF/lib/commons-io-1.4.jar new file mode 100644 index 000000000..133dc6cb3 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/commons-io-1.4.jar differ diff --git a/extensions/database/module/MOD-INF/lib/google-http-client-1.20.0.jar b/extensions/database/module/MOD-INF/lib/google-http-client-1.20.0.jar new file mode 100644 index 000000000..82887fb3e Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/google-http-client-1.20.0.jar differ diff --git a/extensions/database/module/MOD-INF/lib/google-http-client-jackson2-1.20.0.jar b/extensions/database/module/MOD-INF/lib/google-http-client-jackson2-1.20.0.jar new file mode 100644 index 000000000..674aea920 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/google-http-client-jackson2-1.20.0.jar differ diff --git a/extensions/database/module/MOD-INF/lib/httpclient-4.0.1.jar b/extensions/database/module/MOD-INF/lib/httpclient-4.0.1.jar new file mode 100644 index 000000000..ad32285ab Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/httpclient-4.0.1.jar differ diff --git a/extensions/database/module/MOD-INF/lib/httpcore-4.0.1.jar b/extensions/database/module/MOD-INF/lib/httpcore-4.0.1.jar new file mode 100644 index 000000000..4aef35e2f Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/httpcore-4.0.1.jar differ diff --git a/extensions/database/module/MOD-INF/lib/jackson-core-asl-1.9.13.jar b/extensions/database/module/MOD-INF/lib/jackson-core-asl-1.9.13.jar new file mode 100644 index 000000000..bb4fe1da1 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/jackson-core-asl-1.9.13.jar differ diff --git a/extensions/database/module/MOD-INF/lib/jackson-mapper-asl-1.9.13.jar b/extensions/database/module/MOD-INF/lib/jackson-mapper-asl-1.9.13.jar new file mode 100644 index 000000000..0f2073fc7 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/jackson-mapper-asl-1.9.13.jar differ diff --git a/extensions/database/module/MOD-INF/lib/jasypt-1.9.2.jar b/extensions/database/module/MOD-INF/lib/jasypt-1.9.2.jar new file mode 100644 index 000000000..c22a7e6d5 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/jasypt-1.9.2.jar differ diff --git a/extensions/database/module/MOD-INF/lib/json-simple-1.1.1.jar b/extensions/database/module/MOD-INF/lib/json-simple-1.1.1.jar new file mode 100644 index 000000000..dfd5856d0 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/json-simple-1.1.1.jar differ diff --git a/extensions/database/module/MOD-INF/lib/mariadb-java-client-2.2.0.jar b/extensions/database/module/MOD-INF/lib/mariadb-java-client-2.2.0.jar new file mode 100644 index 000000000..7575fec64 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/mariadb-java-client-2.2.0.jar differ diff --git a/extensions/database/module/MOD-INF/lib/mockito-all-1.10.19.jar b/extensions/database/module/MOD-INF/lib/mockito-all-1.10.19.jar new file mode 100644 index 000000000..c831489cd Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/mockito-all-1.10.19.jar differ diff --git a/extensions/database/module/MOD-INF/lib/mysql-connector-java-5.1.44-bin.jar b/extensions/database/module/MOD-INF/lib/mysql-connector-java-5.1.44-bin.jar new file mode 100644 index 000000000..2f2e32d51 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/mysql-connector-java-5.1.44-bin.jar differ diff --git a/extensions/database/module/MOD-INF/lib/postgresql-42.1.4.jar b/extensions/database/module/MOD-INF/lib/postgresql-42.1.4.jar new file mode 100644 index 000000000..08a54b105 Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/postgresql-42.1.4.jar differ diff --git a/extensions/database/module/MOD-INF/lib/powermock-api-mockito-1.7.3.jar b/extensions/database/module/MOD-INF/lib/powermock-api-mockito-1.7.3.jar new file mode 100644 index 000000000..c8e1bba0e Binary files /dev/null and b/extensions/database/module/MOD-INF/lib/powermock-api-mockito-1.7.3.jar differ diff --git a/extensions/database/module/MOD-INF/module.properties b/extensions/database/module/MOD-INF/module.properties new file mode 100644 index 000000000..2b0cef961 --- /dev/null +++ b/extensions/database/module/MOD-INF/module.properties @@ -0,0 +1,7 @@ +name = database +description = Database importer/exporter for OpenRefine +templating.macros = macros.vm +requires = core + +# Use our custom class for a module implementation. +module-impl = com.google.refine.extension.database.DatabaseModuleImpl diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.eot b/extensions/database/module/images/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..b93a4953f Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.eot differ diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.svg b/extensions/database/module/images/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/extensions/database/module/images/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.ttf b/extensions/database/module/images/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..1413fc609 Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.ttf differ diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..9e612858f Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff differ diff --git a/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff2 b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000..64539b54c Binary files /dev/null and b/extensions/database/module/images/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/extensions/database/module/images/more-option-horiz-16.png b/extensions/database/module/images/more-option-horiz-16.png new file mode 100644 index 000000000..0620d0a57 Binary files /dev/null and b/extensions/database/module/images/more-option-horiz-16.png differ diff --git a/extensions/database/module/images/more_option-vert-16.png b/extensions/database/module/images/more_option-vert-16.png new file mode 100644 index 000000000..fa41b4f3b Binary files /dev/null and b/extensions/database/module/images/more_option-vert-16.png differ diff --git a/extensions/database/module/index.vt b/extensions/database/module/index.vt new file mode 100644 index 000000000..850f0f387 --- /dev/null +++ b/extensions/database/module/index.vt @@ -0,0 +1,20 @@ +#* + * Access this page at the URL /extension/database/ + *# + + + Google Refine Database Extension v$version + + +

Google Refine DATABASE Extension v$version

+

by Tony O

+

The JDBC extension allows OpenRefine to read + (and write, soon) data from JDBC compatible databases(MySQL, PostgreSQL, Oracle). +

+

Known Limitations

+
    +
  • No write support
  • + +
+ + \ No newline at end of file diff --git a/extensions/database/module/langs/translation-en.json b/extensions/database/module/langs/translation-en.json new file mode 100644 index 000000000..c29b6930d --- /dev/null +++ b/extensions/database/module/langs/translation-en.json @@ -0,0 +1,58 @@ +{ + "database-import": { + "title": "Database Servers", + "preparing": "Preparing Result ...", + "checking": "Validating Query ...", + "creating": "Creating project ..." + }, + "database-source": { + "alert-host": "You must specify a Database Host", + "alert-port": "You must specify a Database Port", + "alert-user": "You must specify a Database User", + "alert-password": "You must specify a Database Password", + "alert-connection-name": "You must specify a Connection Name", + "alert-initial-database": "You must specify an Initial Database", + "alert-query": "You must specify a valid Query", + "alert-invalid-query-keyword": "Query cannot contain Data Manipulation keyword:", + "alert-invalid-query-select": "Query must start with SELECT Keyword", + "form-validation-failure" : "New Connection From is Invalid!", + "alert-connection-edit": "Connection edited successfully", + "connectionNameLabel": "Name:", + "databaseTypeLabel": "Type:", + "databaseHostLabel": "Host:", + "databasePortLabel": "Port:", + "databaseUserLabel": "User:", + "databasePasswordLabel": "Password:", + "databaseNameLabel": "Database:", + "databaseSchemaLabel": "Schema:", + "databaseTestButton": "Test", + "databaseSaveButton": "Save", + "databaseConnectButton": "Connect", + "newConnectionButtonDiv": "New Connection", + "savedConnectionSpan": "Saved Connections" + + + }, + "database-parsing": { + "start-over": "« Start Over", + "conf-pars": "Configure Parsing Options", + "proj-name": "Project name", + "create-proj": "Create Project »", + "updating-preview": "Updating preview ...", + "worksheet": "Worksheets", + "option": "Options", + "preview-button": "Update Preview", + "ignore-first": "Ignore first", + "ignore": "line(s) at beginning of file", + "parse-next": "Parse next", + "parse": "line(s) as column headers", + "discard-next": "Discard initial", + "discard": "row(s) of data", + "limit-next": "Load at most", + "limit": "row(s) of data", + "store-row": "Store blank rows", + "store-cell": "Store blank cells as nulls" + } + +} + \ No newline at end of file diff --git a/extensions/database/module/langs/translation-fr.json b/extensions/database/module/langs/translation-fr.json new file mode 100644 index 000000000..deaa69b84 --- /dev/null +++ b/extensions/database/module/langs/translation-fr.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/extensions/database/module/langs/translation-he.json b/extensions/database/module/langs/translation-he.json new file mode 100644 index 000000000..deaa69b84 --- /dev/null +++ b/extensions/database/module/langs/translation-he.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/extensions/database/module/langs/translation-it.json b/extensions/database/module/langs/translation-it.json new file mode 100644 index 000000000..deaa69b84 --- /dev/null +++ b/extensions/database/module/langs/translation-it.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/extensions/database/module/macros.vm b/extensions/database/module/macros.vm new file mode 100644 index 000000000..18a83bfce --- /dev/null +++ b/extensions/database/module/macros.vm @@ -0,0 +1,14 @@ +#* + This file contains common velocity macros used in all .vt files. + For Velocity documentation, see: + + http://velocity.apache.org/engine/releases/velocity-1.5/user-guide.html +*# + +#macro( makeAList $list ) +
    + #foreach($item in $list) +
  • $item
  • + #end +
+#end \ No newline at end of file diff --git a/extensions/database/module/scripts/database-extension.js b/extensions/database/module/scripts/database-extension.js new file mode 100644 index 000000000..0f7535342 --- /dev/null +++ b/extensions/database/module/scripts/database-extension.js @@ -0,0 +1,206 @@ +/* + +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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. + +*/ +$(function(){ + $.contextMenu({ + selector: '.context-menu-one', + trigger: 'left', + build: function($trigger, e) { + + return { + callback: function(key, options) { + var m = "clicked: " + key; + DatabaseExtension.handleSavedConnectionClicked(key, $(this).text()); + + }, + + items: { + "edit": {name: " Edit "}, + "sep0": "", + "delete": {name: " Delete "}, + "sep1": "---------", + "connect": {name: " Connect "}, + "dummy": {name: "", icon: ""} + } + }; + } + }); +}); + +var DatabaseExtension = {}; + +DatabaseExtension.currentConnection = {}; + +DatabaseExtension.handleSavedConnectionClicked = function(menuKey, connectionName) { + var jdbcConnectionInfo = {}; + jdbcConnectionInfo.connectionName = connectionName; + + if(menuKey == "edit"){ + DatabaseExtension.handleEditConnectionClicked(connectionName); + + }else if(menuKey == "delete"){ + DatabaseExtension.handleDeleteConnectionClicked(connectionName); + + }else if(menuKey == "connect"){ + DatabaseExtension.handleConnectClicked(connectionName); + } + +}; + + +DatabaseExtension.handleConnectClicked = function(connectionName) { + + $.get( + "command/database/saved-connection" + '?' + $.param({"connectionName": connectionName}), + null, + + function(savedDatabaseConfig) { + + if(savedDatabaseConfig){ + + var savedConfig = savedDatabaseConfig.savedConnections[0]; + var databaseConfig = {}; + databaseConfig.connectionName = savedConfig.connectionName; + databaseConfig.databaseType = savedConfig.databaseType; + databaseConfig.databaseServer = savedConfig.databaseHost; + databaseConfig.databasePort = savedConfig.databasePort; + databaseConfig.databaseUser = savedConfig.databaseUser; + databaseConfig.databasePassword = savedConfig.databasePassword; + databaseConfig.initialDatabase = savedConfig.databaseName; + databaseConfig.initialSchema = savedConfig.databaseSchema; + + $.post( + "command/database/connect", + databaseConfig, + + function(databaseInfo) { + + if(databaseInfo){ + DatabaseExtension.currentConnection.databaseInfo; + $( "#currentConnectionNameInput" ).val(databaseConfig.connectionName); + $( "#currentDatabaseTypeInput" ).val(databaseConfig.databaseType); + $( "#currentDatabaseUserInput" ).val(databaseConfig.databaseUser); + $( "#currentDatabasePasswordInput" ).val(databaseConfig.databasePassword); + $( "#currentDatabaseHostInput" ).val(databaseConfig.databaseServer); + $( "#currentDatabasePortInput" ).val(databaseConfig.databasePort); + $( "#currentInitialDatabaseInput" ).val(databaseConfig.initialDatabase); + + var connectionParam = "Connection[" + databaseConfig.connectionName + "] :: " + + "jdbc:" + + databaseConfig.databaseType + "://" + + databaseConfig.databaseServer + ":" + + databaseConfig.databasePort + "/" + + databaseConfig.initialDatabase; + + $( "#connectionParameterSpan" ).text(connectionParam); + $( "#newConnectionDiv" ).hide(); + $( "#sqlEditorDiv" ).show(); + + }else{ + window.alert("Unable to establish connection to database"); + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + + } + + }, + "json" + ); +}; + +DatabaseExtension.handleDeleteConnectionClicked = function(connectionName) { + $.ajax({ + url: 'command/database/saved-connection' + '?' + $.param({"connectionName": connectionName}), + type: 'DELETE', + contentType:'application/json', + data: null, + success: function(settings) { + if(settings){ + + $( "#menuListUl" ).empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ + +// items.push('
  • ' +// + savedConnection.connectionName +// + '
  • '); + + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + }) + + $( "#menuListUl" ).append(items.join('')); + } + } + }).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); +} + +DatabaseExtension.handleEditConnectionClicked = function(connectionName) { + + $.get( + "command/database/saved-connection" + '?' + $.param({"connectionName": connectionName}), + null, + function(savedDatabaseConfig) { + + if(savedDatabaseConfig){ + + var savedConfig = savedDatabaseConfig.savedConnections[0]; + $( "#connectionName" ).val(savedConfig.connectionName); + $( "#databaseTypeSelect" ).val(savedConfig.databaseType); + $( "#databaseHost" ).val(savedConfig.databaseHost); + $( "#databasePort" ).val(savedConfig.databasePort); + $( "#databaseUser" ).val(savedConfig.databaseUser); + $( "#databasePassword" ).val(savedConfig.databasePassword); + $( "#initialDatabase" ).val(savedConfig.databaseName); + $( "#initialSchema" ).val(savedConfig.databaseSchema); + $( "#newConnectionControlDiv" ).hide(); + $( "#editConnectionControlDiv" ).show(); + $( "#newConnectionDiv" ).show(); + $('#sqlEditorDiv').hide(); + $("#connectionName").attr('readonly', 'readonly'); + + } + + }, + "json" + ); + +} + diff --git a/extensions/database/module/scripts/index/database-import-controller.js b/extensions/database/module/scripts/index/database-import-controller.js new file mode 100644 index 000000000..9193a234e --- /dev/null +++ b/extensions/database/module/scripts/index/database-import-controller.js @@ -0,0 +1,375 @@ +/* + +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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. + + */ + +//Internationalization init +var lang = navigator.language.split("-")[0] + || navigator.userLanguage.split("-")[0]; +var dictionary = ""; +$.ajax({ + url : "command/core/load-language?", + type : "POST", + async : false, + data : { + module : "database", + }, + success : function(data) { + dictionary = data; + } +}); +$.i18n.setDictionary(dictionary); +// End internationalization + +Refine.DatabaseImportController = function(createProjectUI) { + this._createProjectUI = createProjectUI; + + this._parsingPanel = createProjectUI.addCustomPanel(); + + createProjectUI.addSourceSelectionUI({ + label: "Database", + id: "database-source", + ui: new Refine.DatabaseSourceUI(this) + }); + +}; +Refine.CreateProjectUI.controllers.push(Refine.DatabaseImportController); + +Refine.DatabaseImportController.prototype.startImportingDocument = function(queryInfo) { + var dismiss = DialogSystem.showBusy($.i18n._('database-import')["preparing"]); + //alert(queryInfo.query); + var self = this; + + $.post( + "command/core/create-importing-job", + null, + function(data) { + $.post( + "command/core/importing-controller?" + $.param({ + "controller": "database/database-import-controller", + "subCommand": "initialize-parser-ui" + }), + queryInfo, + + function(data2) { + dismiss(); + + if (data2.status == 'ok') { + self._queryInfo = queryInfo; + self._jobID = data.jobID; + self._options = data2.options; + + self._showParsingPanel(); + + } else { + alert(data2.message); + } + }, + "json" + ); + }, + "json" + ); +}; + +Refine.DatabaseImportController.prototype.getOptions = function() { + var options = { + + }; + + var parseIntDefault = function(s, def) { + try { + var n = parseInt(s); + if (!isNaN(n)) { + return n; + } + } catch (e) { + // Ignore + } + return def; + }; + + + if (this._parsingPanelElmts.skipCheckbox[0].checked) { + options.skipDataLines = parseIntDefault(this._parsingPanelElmts.skipInput[0].value, 0); + } else { + options.skipDataLines = 0; + } + if (this._parsingPanelElmts.limitCheckbox[0].checked) { + options.limit = parseIntDefault(this._parsingPanelElmts.limitInput[0].value, -1); + } else { + options.limit = -1; + } + options.storeBlankRows = this._parsingPanelElmts.storeBlankRowsCheckbox[0].checked; + options.storeBlankCellsAsNulls = this._parsingPanelElmts.storeBlankCellsAsNullsCheckbox[0].checked; + + return options; +}; + +Refine.DatabaseImportController.prototype._showParsingPanel = function() { + var self = this; + + this._parsingPanel.unbind().empty().html( + DOM.loadHTML("database",'scripts/index/database-parsing-panel.html')); + + this._parsingPanelElmts = DOM.bind(this._parsingPanel); + + this._parsingPanelElmts.startOverButton.html($.i18n._('database-parsing')["start-over"]); + this._parsingPanelElmts.database_conf_pars.html($.i18n._('database-parsing')["conf-pars"]); + this._parsingPanelElmts.database_proj_name.html($.i18n._('database-parsing')["proj-name"]); + this._parsingPanelElmts.createProjectButton.html($.i18n._('database-parsing')["create-proj"]); + this._parsingPanelElmts.database_options.html($.i18n._('database-parsing')["option"]); + this._parsingPanelElmts.previewButton.html($.i18n._('database-parsing')["preview-button"]); + this._parsingPanelElmts.database_updating.html($.i18n._('database-parsing')["updating-preview"]); + this._parsingPanelElmts.database_discard_next.html($.i18n._('database-parsing')["discard-next"]); + this._parsingPanelElmts.database_discard.html($.i18n._('database-parsing')["discard"]); + this._parsingPanelElmts.database_limit_next.html($.i18n._('database-parsing')["limit-next"]); + this._parsingPanelElmts.database_limit.html($.i18n._('database-parsing')["limit"]); + this._parsingPanelElmts.database_store_row.html($.i18n._('database-parsing')["store-row"]); + this._parsingPanelElmts.database_store_cell.html($.i18n._('database-parsing')["store-cell"]); + + if (this._parsingPanelResizer) { + $(window).unbind('resize', this._parsingPanelResizer); + } + + this._parsingPanelResizer = function() { + var elmts = self._parsingPanelElmts; + var width = self._parsingPanel.width(); + var height = self._parsingPanel.height(); + var headerHeight = elmts.wizardHeader.outerHeight(true); + var controlPanelHeight = 250; + + elmts.dataPanel + .css("left", "0px") + .css("top", headerHeight + "px") + .css("width", (width - DOM.getHPaddings(elmts.dataPanel)) + "px") + .css("height", (height - headerHeight - controlPanelHeight - DOM.getVPaddings(elmts.dataPanel)) + "px"); + elmts.progressPanel + .css("left", "0px") + .css("top", headerHeight + "px") + .css("width", (width - DOM.getHPaddings(elmts.progressPanel)) + "px") + .css("height", (height - headerHeight - controlPanelHeight - DOM.getVPaddings(elmts.progressPanel)) + "px"); + + elmts.controlPanel + .css("left", "0px") + .css("top", (height - controlPanelHeight) + "px") + .css("width", (width - DOM.getHPaddings(elmts.controlPanel)) + "px") + .css("height", (controlPanelHeight - DOM.getVPaddings(elmts.controlPanel)) + "px"); + }; + + $(window).resize(this._parsingPanelResizer); + this._parsingPanelResizer(); + + this._parsingPanelElmts.startOverButton.click(function() { + // explicitly cancel the import job + Refine.CreateProjectUI.cancelImportingJob(self._jobID); + + delete self._jobID; + delete self._options; + + self._createProjectUI.showSourceSelectionPanel(); + }); + + this._parsingPanelElmts.createProjectButton.click(function() { self._createProject(); }); + this._parsingPanelElmts.previewButton.click(function() { self._updatePreview(); }); + //alert("datetime::" + $.now()); + //this._parsingPanelElmts.projectNameInput[0].value = this._queryInfo.connectionName + "_" + this._queryInfo.databaseUser + "_" + $.now(); + this._parsingPanelElmts.projectNameInput[0].value = this._queryInfo.databaseServer + "_" + this._queryInfo.initialDatabase + "_" + $.now(); + + + if (this._options.limit > 0) { + this._parsingPanelElmts.limitCheckbox.prop("checked", true); + this._parsingPanelElmts.limitInput[0].value = this._options.limit.toString(); + } + if (this._options.skipDataLines > 0) { + this._parsingPanelElmts.skipCheckbox.prop("checked", true); + this._parsingPanelElmts.skipInput.value[0].value = this._options.skipDataLines.toString(); + } + if (this._options.storeBlankRows) { + this._parsingPanelElmts.storeBlankRowsCheckbox.prop("checked", true); + } + if (this._options.storeBlankCellsAsNulls) { + this._parsingPanelElmts.storeBlankCellsAsNullsCheckbox.prop("checked", true); + } + + var onChange = function() { + self._scheduleUpdatePreview(); + }; + this._parsingPanel.find("input").bind("change", onChange); + this._parsingPanel.find("select").bind("change", onChange); + + this._createProjectUI.showCustomPanel(this._parsingPanel); + this._updatePreview(); +}; + +Refine.DatabaseImportController.prototype._scheduleUpdatePreview = function() { + if (this._timerID != null) { + window.clearTimeout(this._timerID); + this._timerID = null; + } + + var self = this; + this._timerID = window.setTimeout(function() { + self._timerID = null; + self._updatePreview(); + }, 500); // 0.5 second + }; + +Refine.DatabaseImportController.prototype._updatePreview = function() { + var self = this; + // alert("query::" + this._queryInfo.query); + this._parsingPanelElmts.dataPanel.hide(); + this._parsingPanelElmts.progressPanel.show(); + this._queryInfo.options = JSON.stringify(this.getOptions()); + //alert("options:" + this._queryInfo.options); + + $.post( + "command/core/importing-controller?" + $.param({ + "controller": "database/database-import-controller", + "jobID": this._jobID, + "subCommand": "parse-preview" + }), + + this._queryInfo, + + function(result) { + if (result.status == "ok") { + self._getPreviewData(function(projectData) { + self._parsingPanelElmts.progressPanel.hide(); + self._parsingPanelElmts.dataPanel.show(); + + new Refine.PreviewTable(projectData, self._parsingPanelElmts.dataPanel.unbind().empty()); + }); + } else { + alert(result.message); + //self._parsingPanelElmts.progressPanel.hide(); + alert('Errors:\n' + + (result.message) ? result.message : Refine.CreateProjectUI.composeErrorMessage(job)); + + } + }, + "json" + ); + }; + +Refine.DatabaseImportController.prototype._getPreviewData = function(callback, numRows) { + var self = this; + var result = {}; + + $.post( + "command/core/get-models?" + $.param({ "importingJobID" : this._jobID }), + null, + function(data) { + for (var n in data) { + if (data.hasOwnProperty(n)) { + result[n] = data[n]; + } + } + + $.post( + "command/core/get-rows?" + $.param({ + "importingJobID" : self._jobID, + "start" : 0, + "limit" : numRows || 100 // More than we parse for preview anyway + }), + null, + function(data) { + result.rowModel = data; + callback(result); + }, + "jsonp" + ); + }, + "json" + ); +}; + +Refine.DatabaseImportController.prototype._createProject = function() { + var projectName = $.trim(this._parsingPanelElmts.projectNameInput[0].value); + if (projectName.length == 0) { + window.alert("Please name the project."); + this._parsingPanelElmts.projectNameInput.focus(); + return; + } + + var self = this; + var options = this.getOptions(); + options.projectName = projectName; + + this._queryInfo.options = JSON.stringify(options); + $.post( + "command/core/importing-controller?" + $.param({ + "controller": "database/database-import-controller", + "jobID": this._jobID, + "subCommand": "create-project" + }), + this._queryInfo, + function(o) { + if (o.status == 'error') { + alert(o.message); + } else { + var start = new Date(); + var timerID = window.setInterval( + function() { + self._createProjectUI.pollImportJob( + start, + self._jobID, + timerID, + function(job) { + return "projectID" in job.config; + }, + function(jobID, job) { + //alert("jobID::" + jobID + " job :" + job); + window.clearInterval(timerID); + Refine.CreateProjectUI.cancelImportingJob(jobID); + document.location = "project?project=" + job.config.projectID; + }, + function(job) { + alert(Refine.CreateProjectUI.composeErrorMessage(job)); + } + ); + }, + 1000 + ); + self._createProjectUI.showImportProgressPanel($.i18n._('database-import')["creating"], function() { + // stop the timed polling + window.clearInterval(timerID); + + // explicitly cancel the import job + Refine.CreateProjectUI.cancelImportingJob(jobID); + + self._createProjectUI.showSourceSelectionPanel(); + }); + } + }, + "json" + ); +}; diff --git a/extensions/database/module/scripts/index/database-import-form.html b/extensions/database/module/scripts/index/database-import-form.html new file mode 100644 index 000000000..de1f96f08 --- /dev/null +++ b/extensions/database/module/scripts/index/database-import-form.html @@ -0,0 +1,144 @@ +
    + +
    + +
    +
    +
    +
    +
    + +
    + Saved Connections + +
    + +
    +
    + +
    + +
    + + +
    +
    + + + New Connection Editor + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + + +
    + + + +
    + + + +
    +
    + + + + + +
    + + + +
    + +
    + +
    + + +
    + \ No newline at end of file diff --git a/extensions/database/module/scripts/index/database-parsing-panel.html b/extensions/database/module/scripts/index/database-parsing-panel.html new file mode 100644 index 000000000..f0f359aa0 --- /dev/null +++ b/extensions/database/module/scripts/index/database-parsing-panel.html @@ -0,0 +1,53 @@ +
    +
    + + + + + + + + +
    +
    +
    +
    +
    + + +
    +
    +
    + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/extensions/database/module/scripts/index/database-source-ui.js b/extensions/database/module/scripts/index/database-source-ui.js new file mode 100644 index 000000000..e2ca10a56 --- /dev/null +++ b/extensions/database/module/scripts/index/database-source-ui.js @@ -0,0 +1,483 @@ +/* + +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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. + + */ + +Refine.DatabaseSourceUI = function(controller) { + this._controller = controller; +}; + +Refine.DatabaseSourceUI.prototype.attachUI = function(body) { + this._body = body; + + this._body.html(DOM.loadHTML("database", "scripts/index/database-import-form.html")); + this._elmts = DOM.bind(this._body); + var self = this; + + $('#database-title').text($.i18n._("database-import")["title"]); + $('#connectionNameLabel').html($.i18n._("database-source")["connectionNameLabel"]); + $('#databaseTypeLabel').html($.i18n._("database-source")["databaseTypeLabel"]); + $('#databaseHostLabel').text($.i18n._("database-source")["databaseHostLabel"]); + $('#databasePortLabel').text($.i18n._("database-source")["databasePortLabel"]); + $('#databaseUserLabel').text($.i18n._("database-source")["databaseUserLabel"]); + $('#databasePasswordLabel').text($.i18n._("database-source")["databasePasswordLabel"]); + $('#databaseNameLabel').text($.i18n._("database-source")["databaseNameLabel"]); + $('#databaseSchemaLabel').text($.i18n._("database-source")["databaseSchemaLabel"]); + $('#databaseTestButton').text($.i18n._("database-source")["databaseTestButton"]); + $('#databaseSaveButton').text($.i18n._("database-source")["databaseSaveButton"]); + $('#databaseConnectButton').text($.i18n._("database-source")["databaseConnectButton"]); + $('#newConnectionButtonDiv').text($.i18n._("database-source")["newConnectionButtonDiv"]); + $('#savedConnectionSpan').text($.i18n._("database-source")["savedConnectionSpan"]); + + + this._elmts.newConnectionButton.click(function(evt) { + self._resetDatabaseImportForm(); + $( "#newConnectionDiv" ).show(); + $( "#sqlEditorDiv" ).hide(); + // self._body.find('.newConnectionDiv').show(); + // self._body.find('.sqlEditorDiv').hide(); + + }); + + + this._elmts.databaseTypeSelect.change(function(event) { + var type = $( "#databaseTypeSelect" ).val(); + if(type === "postgresql"){ + $( "#databaseUser" ).val("postgres"); + $( "#databasePort" ).val("5432"); + }else if(type === "mysql"){ + $( "#databaseUser" ).val("root"); + $( "#databasePort" ).val("3306"); + }else if(type === "mariadb"){ + $( "#databaseUser" ).val("root"); + $( "#databasePort" ).val("3306"); + }else{ + $( "#databaseUser" ).val("root"); + $( "#databasePort" ).val("3306"); + } + }); + + this._elmts.testDatabaseButton.click(function(evt) { + + if(self._validateNewConnectionForm() == true){ + self._testDatabaseConnect(self._getConnectionInfo()); + } + + }); + + this._elmts.databaseConnectButton.click(function(evt) { + + if(self._validateNewConnectionForm() == true){ + self._connect(self._getConnectionInfo()); + } + + + }); + + this._elmts.saveConnectionButton.click(function(evt) { + + if(self._validateNewConnectionForm() == true){ + var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); + if (connectionNameInput.length === 0) { + window.alert($.i18n._('database-source')["alert-connection-name"]); + } else{ + self._saveConnection(self._getConnectionInfo()); + } + + } + + }); + + this._elmts.executeQueryButton.click(function(evt) { + var jdbcQueryInfo = {}; + jdbcQueryInfo.connectionName = $( "#currentConnectionNameInput" ).val(); + jdbcQueryInfo.databaseType = $( "#currentDatabaseTypeInput" ).val(); + jdbcQueryInfo.databaseServer = $( "#currentDatabaseHostInput" ).val(); + jdbcQueryInfo.databasePort = $( "#currentDatabasePortInput" ).val(); + jdbcQueryInfo.databaseUser = $( "#currentDatabaseUserInput" ).val(); + jdbcQueryInfo.databasePassword = $( "#currentDatabasePasswordInput" ).val(); + jdbcQueryInfo.initialDatabase = $( "#currentInitialDatabaseInput" ).val(); + jdbcQueryInfo.query = $.trim($( "#queryTextArea" ).val()); + +// if(jdbcQueryInfo.query && jdbcQueryInfo.query.length > 0 ) { +// self._executeQuery(jdbcQueryInfo); +// }else{ +// window.alert($.i18n._('database-source')["alert-query"]); +// } + + if(self.validateQuery(jdbcQueryInfo.query)) { + self._executeQuery(jdbcQueryInfo); + } + + + + + }); + + + + this._elmts.editConnectionButton.click(function(evt) { + + if(self._validateNewConnectionForm() == true){ + var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); + if (connectionNameInput.length === 0) { + window.alert($.i18n._('database-source')["alert-connection-name"]); + } else{ + self._editConnection(self._getConnectionInfo()); + } + + } + + }); + + this._elmts.cancelEditConnectionButton.click(function(evt) { + self._resetDatabaseImportForm(); + + }); + + //load saved connections from settings file in user home directory + self._loadSavedConnections(); + +};//end Refine.createUI + + +Refine.DatabaseSourceUI.prototype.focus = function() { +}; + + +Refine.DatabaseSourceUI.prototype.validateQuery = function(query) { + //alert("query::" + query); + if(!query || query.length <= 0 ) { + window.alert($.i18n._('database-source')["alert-query"]); + return false; + } + + var allCapsQuery = query.toUpperCase(); + + if(allCapsQuery.indexOf('DROP') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " DROP"); + return false; + }else if(allCapsQuery.indexOf('TRUNCATE') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " TRUNCATE"); + return false; + }else if(allCapsQuery.indexOf('DELETE') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " DELETE"); + return false; + }else if(allCapsQuery.indexOf('ROLLBACK') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " ROLLBACK"); + return false; + }else if(allCapsQuery.indexOf('SHUTDOWN') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " SHUTDOWN"); + return false; + }else if(allCapsQuery.indexOf('INSERT') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " INSERT"); + return false; + }else if(allCapsQuery.indexOf('ALTER') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " ALTER"); + return false; + }else if(allCapsQuery.indexOf('UPDATE') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " UPDATE"); + return false; + }else if(allCapsQuery.indexOf('LIMIT') > -1){ + window.alert($.i18n._('database-source')["alert-invalid-query-keyword"] + " LIMIT"); + return false; + } + +// if ((allCapsQuery.indexOf('DROP') > -1) || (allCapsQuery.indexOf('TRUNCATE') > -1) || +// (allCapsQuery.indexOf('DELETE') > -1) || (allCapsQuery.indexOf('ROLLBACK') > -1) +// || (allCapsQuery.indexOf('SHUTDOWN') > -1) || (allCapsQuery.indexOf('INSERT') > -1) +// || (allCapsQuery.indexOf('ALTER') > -1) || (allCapsQuery.indexOf('UPDATE') > -1)) +// { +// window.alert($.i18n._('database-source')["alert-invalid-query-keyword"]); +// return false; +// } + + if(!allCapsQuery.startsWith('SELECT')) { + window.alert($.i18n._('database-source')["alert-invalid-query-select"]); + return false; + } + + return true; +}; + +Refine.DatabaseSourceUI.prototype._editConnection = function(connectionInfo) { + //alert("database user:" + connectionInfo.databaseUser); + var self = this; + $.ajax({ + url: 'command/database/saved-connection', + type: 'PUT', + contentType:'application/x-www-form-urlencoded', + data: connectionInfo, + success: function(settings) { + if(settings){ + $( "#menuListUl" ).empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ +// items.push('' +// + '' + savedConnection.connectionName + '' +// + ' '); + + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + }) + + $( "#menuListUl" ).append(items.join('')); + window.alert($.i18n._('database-source')["alert-connection-edit"]); + } + } + }).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + +}; + +Refine.DatabaseSourceUI.prototype._executeQuery = function(jdbcQueryInfo) { + var self = this; + //remove start line + + var dismiss = DialogSystem.showBusy($.i18n._('database-import')["checking"]); + //$("#executeQueryBtn").text('Please wait ...').attr('disabled','disabled'); + + $.post( + "command/database/test-query", + jdbcQueryInfo, + function(jdbcConnectionResult) { + // $("#executeQueryBtn").text('Preview Query Result').removeAttr('disabled'); + dismiss(); + self._controller.startImportingDocument(jdbcQueryInfo); + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + //$("#executeQueryBtn").text('Preview Query Result').removeAttr('disabled'); + dismiss(); + alert( textStatus + ':' + errorThrown ); + }); + //remove end line + + //self._controller.startImportingDocument(jdbcQueryInfo); +} + +Refine.DatabaseSourceUI.prototype._saveConnection = function(jdbcConnectionInfo) { + var self = this; + $.post( + "command/database/saved-connection", + jdbcConnectionInfo, + function(settings) { + if(settings){ + +// self._elmts.scListGroupDiv.empty(); + self._elmts.menuListUl.empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ + +// items.push('' +// + '' + savedConnection.connectionName + '' +// + ' '); + + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + }) + + self._elmts.menuListUl.append(items.join('')); + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + +}; + +Refine.DatabaseSourceUI.prototype._loadSavedConnections = function() { + var self = this; + $.get( + "command/database/saved-connection", + null, + function(settings) { + if(settings){ + + self._elmts.menuListUl.empty(); + //self._elmts.scListGroupDiv.empty(); + var items = []; + $.each(settings.savedConnections,function(index,savedConnection){ + +// items.push('' +// + '' + savedConnection.connectionName + '' +// + ' '); + + items.push('
  • ' + + '' + savedConnection.connectionName + '' + + '
  • '); + + }) + + self._elmts.menuListUl.append(items.join('')); + // self._elmts.scListGroupDiv.append(items.join('')); + } + + }, + "json" + ); + +}; + +Refine.DatabaseSourceUI.prototype._testDatabaseConnect = function(jdbcConnectionInfo) { + + var self = this; + $.post( + "command/database/test-connect", + jdbcConnectionInfo, + function(jdbcConnectionResult) { + if(jdbcConnectionResult && jdbcConnectionResult.connectionResult == true){ + window.alert("Test Connection Succeeded!"); + }else{ + window.alert("Unable to establish connection to database"); + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); +}; + +Refine.DatabaseSourceUI.prototype._connect = function(jdbcConnectionInfo) { + + var self = this; + $.post( + "command/database/connect", + jdbcConnectionInfo, + function(databaseInfo) { + + if(databaseInfo){ + $( "#currentConnectionNameInput" ).val(jdbcConnectionInfo.connectionName); + $( "#currentDatabaseTypeInput" ).val(jdbcConnectionInfo.databaseType); + $( "#currentDatabaseUserInput" ).val(jdbcConnectionInfo.databaseUser); + $( "#currentDatabasePasswordInput" ).val(jdbcConnectionInfo.databasePassword); + $( "#currentDatabaseHostInput" ).val(jdbcConnectionInfo.databaseServer); + $( "#currentDatabasePortInput" ).val(jdbcConnectionInfo.databasePort); + $( "#currentInitialDatabaseInput" ).val(jdbcConnectionInfo.initialDatabase); + + var connectionParam = "Connection :: " + + "jdbc:" + + jdbcConnectionInfo.databaseType + "://" + + jdbcConnectionInfo.databaseServer + ":" + + jdbcConnectionInfo.databasePort + "/" + + jdbcConnectionInfo.initialDatabase; + + //alert("connectionParam::" + connectionParam); + $( "#connectionParameterSpan" ).text(connectionParam); + // self._body.find('.newConnectionDiv').hide(); + // self._body.find('.sqlEditorDiv').show(); + $( "#newConnectionDiv" ).hide(); + $( "#sqlEditorDiv" ).show(); + + }else{ + window.alert("Unable to establish connection to database"); + return; + } + + }, + "json" + ).fail(function( jqXhr, textStatus, errorThrown ){ + alert( textStatus + ':' + errorThrown ); + }); + +}; + +Refine.DatabaseSourceUI.prototype._getConnectionInfo = function() { + var self = this; + var jdbcConnectionInfo = {}; + jdbcConnectionInfo.connectionName = $.trim(self._elmts.connectionNameInput[0].value); + jdbcConnectionInfo.databaseType = $.trim(self._elmts.databaseTypeSelect[0].value); + jdbcConnectionInfo.databaseServer = $.trim(self._elmts.databaseHostInput[0].value); + jdbcConnectionInfo.databasePort = $.trim(self._elmts.databasePortInput[0].value); + jdbcConnectionInfo.databaseUser = $.trim(self._elmts.databaseUserInput[0].value); + jdbcConnectionInfo.databasePassword = $.trim(self._elmts.databasePasswordInput[0].value); + jdbcConnectionInfo.initialDatabase = $.trim(self._elmts.initialDatabaseInput[0].value); + jdbcConnectionInfo.initialSchema = $.trim(self._elmts.initialSchemaInput[0].value); + return jdbcConnectionInfo; + +} + +Refine.DatabaseSourceUI.prototype._validateNewConnectionForm = function() { + + var self = this; + var connectionNameInput = $.trim(self._elmts.connectionNameInput[0].value); + var databaseTypeSelect = $.trim(self._elmts.databaseTypeSelect[0].value); + var databaseHostInput = $.trim(self._elmts.databaseHostInput[0].value); + var databasePortInput = $.trim(self._elmts.databasePortInput[0].value); + var databaseUserInput = $.trim(self._elmts.databaseUserInput[0].value); + var databasePasswordInput = $.trim(self._elmts.databasePasswordInput[0].value); + var initialDatabaseInput = $.trim(self._elmts.initialDatabaseInput[0].value); + var initialSchemaInput = $.trim(self._elmts.initialSchemaInput[0].value); + + if (databaseHostInput.length === 0) { + window.alert($.i18n._('database-source')["alert-server"]); + return false; + }else if(databasePortInput.length === 0){ + window.alert($.i18n._('database-source')["alert-port"]); + return false; + }else if(databaseUserInput.length === 0){ + window.alert($.i18n._('database-source')["alert-user"]); + return false; + }else if(initialDatabaseInput.length === 0){ + window.alert($.i18n._('database-source')["alert-initial-database"]); + return false; + } + else{ + return true; + + } + + return true; +}; + +Refine.DatabaseSourceUI.prototype._resetDatabaseImportForm = function() { + var self = this; + $( "#connectionName" ).val("127.0.0.1"); + $( "#databaseTypeSelect" ).val("postgresql"); + $( "#databaseHost" ).val("127.0.0.1"); + $( "#databasePort" ).val("5432"); + $( "#databaseUser" ).val("postgres"); + $( "#databasePassword" ).val(""); + $( "#initialDatabase" ).val(""); + $( "#initialSchema" ).val(""); + + $( "#editConnectionControlDiv" ).hide(); + $( "#newConnectionControlDiv" ).show(); + $('#connectionName').removeAttr('readonly'); + +}; diff --git a/extensions/database/module/scripts/index/jquery.contextMenu.min.js b/extensions/database/module/scripts/index/jquery.contextMenu.min.js new file mode 100644 index 000000000..bf6b6169f --- /dev/null +++ b/extensions/database/module/scripts/index/jquery.contextMenu.min.js @@ -0,0 +1,2 @@ +!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){"use strict";function t(e){for(var t,n=e.split(/\s+/),a=[],o=0;t=n[o];o++)t=t.charAt(0).toUpperCase(),a.push(t);return a}function n(t){return t.id&&e('label[for="'+t.id+'"]').val()||t.name}function a(t,o,s){return s||(s=0),o.each(function(){var o,i,c=e(this),l=this,r=this.nodeName.toLowerCase();switch("label"===r&&c.find("input, textarea, select").length&&(o=c.text(),r=(l=(c=c.children().first()).get(0)).nodeName.toLowerCase()),r){case"menu":i={name:c.attr("label"),items:{}},s=a(i.items,c.children(),s);break;case"a":case"button":i={name:c.text(),disabled:!!c.attr("disabled"),callback:function(){c.get(0).click()}};break;case"menuitem":case"command":switch(c.attr("type")){case void 0:case"command":case"menuitem":i={name:c.attr("label"),disabled:!!c.attr("disabled"),icon:c.attr("icon"),callback:function(){c.get(0).click()}};break;case"checkbox":i={type:"checkbox",disabled:!!c.attr("disabled"),name:c.attr("label"),selected:!!c.attr("checked")};break;case"radio":i={type:"radio",disabled:!!c.attr("disabled"),name:c.attr("label"),radio:c.attr("radiogroup"),value:c.attr("id"),selected:!!c.attr("checked")};break;default:i=void 0}break;case"hr":i="-------";break;case"input":switch(c.attr("type")){case"text":i={type:"text",name:o||n(l),disabled:!!c.attr("disabled"),value:c.val()};break;case"checkbox":i={type:"checkbox",name:o||n(l),disabled:!!c.attr("disabled"),selected:!!c.attr("checked")};break;case"radio":i={type:"radio",name:o||n(l),disabled:!!c.attr("disabled"),radio:!!c.attr("name"),value:c.val(),selected:!!c.attr("checked")};break;default:i=void 0}break;case"select":i={type:"select",name:o||n(l),disabled:!!c.attr("disabled"),selected:c.val(),options:{}},c.children().each(function(){i.options[this.value]=e(this).text()});break;case"textarea":i={type:"textarea",name:o||n(l),disabled:!!c.attr("disabled"),value:c.val()};break;case"label":break;default:i={type:"html",html:c.clone(!0)}}i&&(t["key"+ ++s]=i)}),s}e.support.htmlMenuitem="HTMLMenuItemElement"in window,e.support.htmlCommand="HTMLCommandElement"in window,e.support.eventSelectstart="onselectstart"in document.documentElement,e.ui&&e.widget||(e.cleanData=function(t){return function(n){var a,o,s;for(s=0;null!=n[s];s++){o=n[s];try{(a=e._data(o,"events"))&&a.remove&&e(o).triggerHandler("remove")}catch(e){}}t(n)}}(e.cleanData));var o=null,s=!1,i=e(window),c=0,l={},r={},u={},d={selector:null,appendTo:null,trigger:"right",autoHide:!1,delay:200,reposition:!0,hideOnSecondTrigger:!1,selectableSubMenu:!1,classNames:{hover:"context-menu-hover",disabled:"context-menu-disabled",visible:"context-menu-visible",notSelectable:"context-menu-not-selectable",icon:"context-menu-icon",iconEdit:"context-menu-icon-edit",iconCut:"context-menu-icon-cut",iconCopy:"context-menu-icon-copy",iconPaste:"context-menu-icon-paste",iconDelete:"context-menu-icon-delete",iconAdd:"context-menu-icon-add",iconQuit:"context-menu-icon-quit",iconLoadingClass:"context-menu-icon-loading"},determinePosition:function(t){if(e.ui&&e.ui.position)t.css("display","block").position({my:"center top",at:"center bottom",of:this,offset:"0 5",collision:"fit"}).css("display","none");else{var n=this.offset();n.top+=this.outerHeight(),n.left+=this.outerWidth()/2-t.outerWidth()/2,t.css(n)}},position:function(e,t,n){var a;if(t||n){if("maintain"===t&&"maintain"===n)a=e.$menu.position();else{var o=e.$menu.offsetParent().offset();a={top:n-o.top,left:t-o.left}}var s=i.scrollTop()+i.height(),c=i.scrollLeft()+i.width(),l=e.$menu.outerHeight(),r=e.$menu.outerWidth();a.top+l>s&&(a.top-=l),a.top<0&&(a.top=0),a.left+r>c&&(a.left-=r),a.left<0&&(a.left=0),e.$menu.css(a)}else e.determinePosition.call(this,e.$menu)},positionSubmenu:function(t){if(void 0!==t)if(e.ui&&e.ui.position)t.css("display","block").position({my:"left top-5",at:"right top",of:this,collision:"flipfit fit"}).css("display","");else{var n={top:-9,left:this.outerWidth()-5};t.css(n)}},zIndex:1,animation:{duration:50,show:"slideDown",hide:"slideUp"},events:{show:e.noop,hide:e.noop,activated:e.noop},callback:null,items:{}},m={timer:null,pageX:null,pageY:null},p=function(e){for(var t=0,n=e;;)if(t=Math.max(t,parseInt(n.css("z-index"),10)||0),!(n=n.parent())||!n.length||"html body".indexOf(n.prop("nodeName").toLowerCase())>-1)break;return t},f={abortevent:function(e){e.preventDefault(),e.stopImmediatePropagation()},contextmenu:function(t){var n=e(this);if("right"===t.data.trigger&&(t.preventDefault(),t.stopImmediatePropagation()),!("right"!==t.data.trigger&&"demand"!==t.data.trigger&&t.originalEvent||!(void 0===t.mouseButton||!t.data||"left"===t.data.trigger&&0===t.mouseButton||"right"===t.data.trigger&&2===t.mouseButton)||n.hasClass("context-menu-active")||n.hasClass("context-menu-disabled"))){if(o=n,t.data.build){var a=t.data.build(o,t);if(!1===a)return;if(t.data=e.extend(!0,{},d,t.data,a||{}),!t.data.items||e.isEmptyObject(t.data.items))throw window.console&&(console.error||console.log).call(console,"No items specified to show in contextMenu"),new Error("No Items specified");t.data.$trigger=o,h.create(t.data)}var s=!1;for(var i in t.data.items)if(t.data.items.hasOwnProperty(i)){(e.isFunction(t.data.items[i].visible)?t.data.items[i].visible.call(e(t.currentTarget),i,t.data):void 0===t.data.items[i]||!t.data.items[i].visible||!0===t.data.items[i].visible)&&(s=!0)}s&&h.show.call(n,t.data,t.pageX,t.pageY)}},click:function(t){t.preventDefault(),t.stopImmediatePropagation(),e(this).trigger(e.Event("contextmenu",{data:t.data,pageX:t.pageX,pageY:t.pageY}))},mousedown:function(t){var n=e(this);o&&o.length&&!o.is(n)&&o.data("contextMenu").$menu.trigger("contextmenu:hide"),2===t.button&&(o=n.data("contextMenuActive",!0))},mouseup:function(t){var n=e(this);n.data("contextMenuActive")&&o&&o.length&&o.is(n)&&!n.hasClass("context-menu-disabled")&&(t.preventDefault(),t.stopImmediatePropagation(),o=n,n.trigger(e.Event("contextmenu",{data:t.data,pageX:t.pageX,pageY:t.pageY}))),n.removeData("contextMenuActive")},mouseenter:function(t){var n=e(this),a=e(t.relatedTarget),s=e(document);a.is(".context-menu-list")||a.closest(".context-menu-list").length||o&&o.length||(m.pageX=t.pageX,m.pageY=t.pageY,m.data=t.data,s.on("mousemove.contextMenuShow",f.mousemove),m.timer=setTimeout(function(){m.timer=null,s.off("mousemove.contextMenuShow"),o=n,n.trigger(e.Event("contextmenu",{data:m.data,pageX:m.pageX,pageY:m.pageY}))},t.data.delay))},mousemove:function(e){m.pageX=e.pageX,m.pageY=e.pageY},mouseleave:function(t){var n=e(t.relatedTarget);if(!n.is(".context-menu-list")&&!n.closest(".context-menu-list").length){try{clearTimeout(m.timer)}catch(t){}m.timer=null}},layerClick:function(t){var n,a,o=e(this).data("contextMenuRoot"),s=t.button,c=t.pageX,l=t.pageY;t.preventDefault(),setTimeout(function(){var r,u="left"===o.trigger&&0===s||"right"===o.trigger&&2===s;if(document.elementFromPoint&&o.$layer){if(o.$layer.hide(),(n=document.elementFromPoint(c-i.scrollLeft(),l-i.scrollTop())).isContentEditable){var d=document.createRange(),m=window.getSelection();d.selectNode(n),d.collapse(!0),m.removeAllRanges(),m.addRange(d)}e(n).trigger(t),o.$layer.show()}if(o.hideOnSecondTrigger&&u&&null!==o.$menu&&void 0!==o.$menu)o.$menu.trigger("contextmenu:hide");else{if(o.reposition&&u)if(document.elementFromPoint){if(o.$trigger.is(n))return void o.position.call(o.$trigger,o,c,l)}else if(a=o.$trigger.offset(),r=e(window),a.top+=r.scrollTop(),a.top<=t.pageY&&(a.left+=r.scrollLeft(),a.left<=t.pageX&&(a.bottom=a.top+o.$trigger.outerHeight(),a.bottom>=t.pageY&&(a.right=a.left+o.$trigger.outerWidth(),a.right>=t.pageX))))return void o.position.call(o.$trigger,o,c,l);n&&u&&o.$trigger.one("contextmenu:hidden",function(){e(n).contextMenu({x:c,y:l,button:s})}),null!==o&&void 0!==o&&null!==o.$menu&&void 0!==o.$menu&&o.$menu.trigger("contextmenu:hide")}},50)},keyStop:function(e,t){t.isInput||e.preventDefault(),e.stopPropagation()},key:function(e){var t={};o&&(t=o.data("contextMenu")||{}),void 0===t.zIndex&&(t.zIndex=0);var n=0,a=function(e){""!==e.style.zIndex?n=e.style.zIndex:null!==e.offsetParent&&void 0!==e.offsetParent?a(e.offsetParent):null!==e.parentElement&&void 0!==e.parentElement&&a(e.parentElement)};if(a(e.target),!(t.$menu&&parseInt(n,10)>parseInt(t.$menu.css("zIndex"),10))){switch(e.keyCode){case 9:case 38:if(f.keyStop(e,t),t.isInput){if(9===e.keyCode&&e.shiftKey)return e.preventDefault(),t.$selected&&t.$selected.find("input, textarea, select").blur(),void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("prevcommand"));if(38===e.keyCode&&"checkbox"===t.$selected.find("input, textarea, select").prop("type"))return void e.preventDefault()}else if(9!==e.keyCode||e.shiftKey)return void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("prevcommand"));break;case 40:if(f.keyStop(e,t),!t.isInput)return void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("nextcommand"));if(9===e.keyCode)return e.preventDefault(),t.$selected&&t.$selected.find("input, textarea, select").blur(),void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("nextcommand"));if(40===e.keyCode&&"checkbox"===t.$selected.find("input, textarea, select").prop("type"))return void e.preventDefault();break;case 37:if(f.keyStop(e,t),t.isInput||!t.$selected||!t.$selected.length)break;if(!t.$selected.parent().hasClass("context-menu-root")){var s=t.$selected.parent().parent();return t.$selected.trigger("contextmenu:blur"),void(t.$selected=s)}break;case 39:if(f.keyStop(e,t),t.isInput||!t.$selected||!t.$selected.length)break;var i=t.$selected.data("contextMenu")||{};if(i.$menu&&t.$selected.hasClass("context-menu-submenu"))return t.$selected=null,i.$selected=null,void i.$menu.trigger("nextcommand");break;case 35:case 36:return t.$selected&&t.$selected.find("input, textarea, select").length?void 0:((t.$selected&&t.$selected.parent()||t.$menu).children(":not(."+t.classNames.disabled+", ."+t.classNames.notSelectable+")")[36===e.keyCode?"first":"last"]().trigger("contextmenu:focus"),void e.preventDefault());case 13:if(f.keyStop(e,t),t.isInput){if(t.$selected&&!t.$selected.is("textarea, select"))return void e.preventDefault();break}return void(void 0!==t.$selected&&null!==t.$selected&&t.$selected.trigger("mouseup"));case 32:case 33:case 34:return void f.keyStop(e,t);case 27:return f.keyStop(e,t),void(null!==t.$menu&&void 0!==t.$menu&&t.$menu.trigger("contextmenu:hide"));default:var c=String.fromCharCode(e.keyCode).toUpperCase();if(t.accesskeys&&t.accesskeys[c])return void t.accesskeys[c].$node.trigger(t.accesskeys[c].$menu?"contextmenu:focus":"mouseup")}e.stopPropagation(),void 0!==t.$selected&&null!==t.$selected&&t.$selected.trigger(e)}},prevItem:function(t){t.stopPropagation();var n=e(this).data("contextMenu")||{},a=e(this).data("contextMenuRoot")||{};if(n.$selected){var o=n.$selected;(n=n.$selected.parent().data("contextMenu")||{}).$selected=o}for(var s=n.$menu.children(),i=n.$selected&&n.$selected.prev().length?n.$selected.prev():s.last(),c=i;i.hasClass(a.classNames.disabled)||i.hasClass(a.classNames.notSelectable)||i.is(":hidden");)if((i=i.prev().length?i.prev():s.last()).is(c))return;n.$selected&&f.itemMouseleave.call(n.$selected.get(0),t),f.itemMouseenter.call(i.get(0),t);var l=i.find("input, textarea, select");l.length&&l.focus()},nextItem:function(t){t.stopPropagation();var n=e(this).data("contextMenu")||{},a=e(this).data("contextMenuRoot")||{};if(n.$selected){var o=n.$selected;(n=n.$selected.parent().data("contextMenu")||{}).$selected=o}for(var s=n.$menu.children(),i=n.$selected&&n.$selected.next().length?n.$selected.next():s.first(),c=i;i.hasClass(a.classNames.disabled)||i.hasClass(a.classNames.notSelectable)||i.is(":hidden");)if((i=i.next().length?i.next():s.first()).is(c))return;n.$selected&&f.itemMouseleave.call(n.$selected.get(0),t),f.itemMouseenter.call(i.get(0),t);var l=i.find("input, textarea, select");l.length&&l.focus()},focusInput:function(){var t=e(this).closest(".context-menu-item"),n=t.data(),a=n.contextMenu,o=n.contextMenuRoot;o.$selected=a.$selected=t,o.isInput=a.isInput=!0},blurInput:function(){var t=e(this).closest(".context-menu-item").data(),n=t.contextMenu;t.contextMenuRoot.isInput=n.isInput=!1},menuMouseenter:function(){e(this).data().contextMenuRoot.hovering=!0},menuMouseleave:function(t){var n=e(this).data().contextMenuRoot;n.$layer&&n.$layer.is(t.relatedTarget)&&(n.hovering=!1)},itemMouseenter:function(t){var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;s.hovering=!0,t&&s.$layer&&s.$layer.is(t.relatedTarget)&&(t.preventDefault(),t.stopImmediatePropagation()),(o.$menu?o:s).$menu.children("."+s.classNames.hover).trigger("contextmenu:blur").children(".hover").trigger("contextmenu:blur"),n.hasClass(s.classNames.disabled)||n.hasClass(s.classNames.notSelectable)?o.$selected=null:n.trigger("contextmenu:focus")},itemMouseleave:function(t){var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;if(s!==o&&s.$layer&&s.$layer.is(t.relatedTarget))return void 0!==s.$selected&&null!==s.$selected&&s.$selected.trigger("contextmenu:blur"),t.preventDefault(),t.stopImmediatePropagation(),void(s.$selected=o.$selected=o.$node);o&&o.$menu&&o.$menu.hasClass("context-menu-visible")||n.trigger("contextmenu:blur")},itemClick:function(t){var n,a=e(this),o=a.data(),s=o.contextMenu,i=o.contextMenuRoot,c=o.contextMenuKey;if(!(!s.items[c]||a.is("."+i.classNames.disabled+", .context-menu-separator, ."+i.classNames.notSelectable)||a.is(".context-menu-submenu")&&!1===i.selectableSubMenu)){if(t.preventDefault(),t.stopImmediatePropagation(),e.isFunction(s.callbacks[c])&&Object.prototype.hasOwnProperty.call(s.callbacks,c))n=s.callbacks[c];else{if(!e.isFunction(i.callback))return;n=i.callback}!1!==n.call(i.$trigger,c,i,t)?i.$menu.trigger("contextmenu:hide"):i.$menu.parent().length&&h.update.call(i.$trigger,i)}},inputClick:function(e){e.stopImmediatePropagation()},hideMenu:function(t,n){var a=e(this).data("contextMenuRoot");h.hide.call(a.$trigger,a,n&&n.force)},focusItem:function(t){t.stopPropagation();var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;n.hasClass(s.classNames.disabled)||n.hasClass(s.classNames.notSelectable)||(n.addClass([s.classNames.hover,s.classNames.visible].join(" ")).parent().find(".context-menu-item").not(n).removeClass(s.classNames.visible).filter("."+s.classNames.hover).trigger("contextmenu:blur"),o.$selected=s.$selected=n,o&&o.$node&&o.$node.hasClass("context-menu-submenu")&&o.$node.addClass(s.classNames.hover),o.$node&&s.positionSubmenu.call(o.$node,o.$menu))},blurItem:function(t){t.stopPropagation();var n=e(this),a=n.data(),o=a.contextMenu,s=a.contextMenuRoot;o.autoHide&&n.removeClass(s.classNames.visible),n.removeClass(s.classNames.hover),o.$selected=null}},h={show:function(t,n,a){var s=e(this),i={};if(e("#context-menu-layer").trigger("mousedown"),t.$trigger=s,!1!==t.events.show.call(s,t)){if(h.update.call(s,t),t.position.call(s,t,n,a),t.zIndex){var c=t.zIndex;"function"==typeof t.zIndex&&(c=t.zIndex.call(s,t)),i.zIndex=p(s)+c}h.layer.call(t.$menu,t,i.zIndex),t.$menu.find("ul").css("zIndex",i.zIndex+1),t.$menu.css(i)[t.animation.show](t.animation.duration,function(){s.trigger("contextmenu:visible"),h.activated(t),t.events.activated()}),s.data("contextMenu",t).addClass("context-menu-active"),e(document).off("keydown.contextMenu").on("keydown.contextMenu",f.key),t.autoHide&&e(document).on("mousemove.contextMenuAutoHide",function(e){var n=s.offset();n.right=n.left+s.outerWidth(),n.bottom=n.top+s.outerHeight(),!t.$layer||t.hovering||e.pageX>=n.left&&e.pageX<=n.right&&e.pageY>=n.top&&e.pageY<=n.bottom||setTimeout(function(){t.hovering||null===t.$menu||void 0===t.$menu||t.$menu.trigger("contextmenu:hide")},50)})}else o=null},hide:function(t,n){var a=e(this);if(t||(t=a.data("contextMenu")||{}),n||!t.events||!1!==t.events.hide.call(a,t)){if(a.removeData("contextMenu").removeClass("context-menu-active"),t.$layer){setTimeout(function(e){return function(){e.remove()}}(t.$layer),10);try{delete t.$layer}catch(e){t.$layer=null}}o=null,t.$menu.find("."+t.classNames.hover).trigger("contextmenu:blur"),t.$selected=null,t.$menu.find("."+t.classNames.visible).removeClass(t.classNames.visible),e(document).off(".contextMenuAutoHide").off("keydown.contextMenu"),t.$menu&&t.$menu[t.animation.hide](t.animation.duration,function(){t.build&&(t.$menu.remove(),e.each(t,function(e){switch(e){case"ns":case"selector":case"build":case"trigger":return!0;default:t[e]=void 0;try{delete t[e]}catch(e){}return!0}})),setTimeout(function(){a.trigger("contextmenu:hidden")},10)})}},create:function(n,a){function o(t){var n=e("");if(t._accesskey)t._beforeAccesskey&&n.append(document.createTextNode(t._beforeAccesskey)),e("").addClass("context-menu-accesskey").text(t._accesskey).appendTo(n),t._afterAccesskey&&n.append(document.createTextNode(t._afterAccesskey));else if(t.isHtmlName){if(void 0!==t.accesskey)throw new Error("accesskeys are not compatible with HTML names and cannot be used together in the same item");n.html(t.name)}else n.text(t.name);return n}void 0===a&&(a=n),n.$menu=e('
      ').addClass(n.className||"").data({contextMenu:n,contextMenuRoot:a}),e.each(["callbacks","commands","inputs"],function(e,t){n[t]={},a[t]||(a[t]={})}),a.accesskeys||(a.accesskeys={}),e.each(n.items,function(s,i){var c=e('
    • ').addClass(i.className||""),l=null,r=null;if(c.on("click",e.noop),"string"!=typeof i&&"cm_separator"!==i.type||(i={type:"cm_seperator"}),i.$node=c.data({contextMenu:n,contextMenuRoot:a,contextMenuKey:s}),void 0!==i.accesskey)for(var d,m=t(i.accesskey),p=0;d=m[p];p++)if(!a.accesskeys[d]){a.accesskeys[d]=i;var v=i.name.match(new RegExp("^(.*?)("+d+")(.*)$","i"));v&&(i._beforeAccesskey=v[1],i._accesskey=v[2],i._afterAccesskey=v[3]);break}if(i.type&&u[i.type])u[i.type].call(c,i,n,a),e.each([n,a],function(t,a){a.commands[s]=i,!e.isFunction(i.callback)||void 0!==a.callbacks[s]&&void 0!==n.type||(a.callbacks[s]=i.callback)});else{switch("cm_seperator"===i.type?c.addClass("context-menu-separator "+a.classNames.notSelectable):"html"===i.type?c.addClass("context-menu-html "+a.classNames.notSelectable):"sub"===i.type||(i.type?(l=e("").appendTo(c),o(i).appendTo(l),c.addClass("context-menu-input"),n.hasTypes=!0,e.each([n,a],function(e,t){t.commands[s]=i,t.inputs[s]=i})):i.items&&(i.type="sub")),i.type){case"cm_seperator":break;case"text":r=e('').attr("name","context-menu-input-"+s).val(i.value||"").appendTo(l);break;case"textarea":r=e('').attr("name","context-menu-input-"+s).val(i.value||"").appendTo(l),i.height&&r.height(i.height);break;case"checkbox":r=e('').attr("name","context-menu-input-"+s).val(i.value||"").prop("checked",!!i.selected).prependTo(l);break;case"radio":r=e('').attr("name","context-menu-input-"+i.radio).val(i.value||"").prop("checked",!!i.selected).prependTo(l);break;case"select":r=e('').attr("name","context-menu-input-"+s).appendTo(l),i.options&&(e.each(i.options,function(t,n){e("").val(t).text(n).appendTo(r)}),r.val(i.selected));break;case"sub":o(i).appendTo(c),i.appendTo=i.$node,c.data("contextMenu",i).addClass("context-menu-submenu"),i.callback=null,"function"==typeof i.items.then?h.processPromises(i,a,i.items):h.create(i,a);break;case"html":e(i.html).appendTo(c);break;default:e.each([n,a],function(t,a){a.commands[s]=i,!e.isFunction(i.callback)||void 0!==a.callbacks[s]&&void 0!==n.type||(a.callbacks[s]=i.callback)}),o(i).appendTo(c)}i.type&&"sub"!==i.type&&"html"!==i.type&&"cm_seperator"!==i.type&&(r.on("focus",f.focusInput).on("blur",f.blurInput),i.events&&r.on(i.events,n)),i.icon&&(e.isFunction(i.icon)?i._icon=i.icon.call(this,this,c,s,i):"string"==typeof i.icon&&"fa-"===i.icon.substring(0,3)?i._icon=a.classNames.icon+" "+a.classNames.icon+"--fa fa "+i.icon:i._icon=a.classNames.icon+" "+a.classNames.icon+"-"+i.icon,c.addClass(i._icon))}i.$input=r,i.$label=l,c.appendTo(n.$menu),!n.hasTypes&&e.support.eventSelectstart&&c.on("selectstart.disableTextSelect",f.abortevent)}),n.$node||n.$menu.css("display","none").addClass("context-menu-root"),n.$menu.appendTo(n.appendTo||document.body)},resize:function(t,n){var a;t.css({position:"absolute",display:"block"}),t.data("width",(a=t.get(0)).getBoundingClientRect?Math.ceil(a.getBoundingClientRect().width):t.outerWidth()+1),t.css({position:"static",minWidth:"0px",maxWidth:"100000px"}),t.find("> li > ul").each(function(){h.resize(e(this),!0)}),n||t.find("ul").addBack().css({position:"",display:"",minWidth:"",maxWidth:""}).outerWidth(function(){return e(this).data("width")})},update:function(t,n){var a=this;void 0===n&&(n=t,h.resize(t.$menu)),t.$menu.children().each(function(){var o,s=e(this),i=s.data("contextMenuKey"),c=t.items[i],l=e.isFunction(c.disabled)&&c.disabled.call(a,i,n)||!0===c.disabled;if(o=e.isFunction(c.visible)?c.visible.call(a,i,n):void 0===c.visible||!0===c.visible,s[o?"show":"hide"](),s[l?"addClass":"removeClass"](n.classNames.disabled),e.isFunction(c.icon)&&(s.removeClass(c._icon),c._icon=c.icon.call(this,a,s,i,c),s.addClass(c._icon)),c.type)switch(s.find("input, select, textarea").prop("disabled",l),c.type){case"text":case"textarea":c.$input.val(c.value||"");break;case"checkbox":case"radio":c.$input.val(c.value||"").prop("checked",!!c.selected);break;case"select":c.$input.val((0===c.selected?"0":c.selected)||"")}c.$menu&&h.update.call(a,c,n)})},layer:function(t,n){var a=t.$layer=e('
      ').css({height:i.height(),width:i.width(),display:"block",position:"fixed","z-index":n,top:0,left:0,opacity:0,filter:"alpha(opacity=0)","background-color":"#000"}).data("contextMenuRoot",t).insertBefore(this).on("contextmenu",f.abortevent).on("mousedown",f.layerClick);return void 0===document.body.style.maxWidth&&a.css({position:"absolute",height:e(document).height()}),a},processPromises:function(e,t,n){function a(e,t,n){void 0===n?(n={error:{name:"No items and no error item",icon:"context-menu-icon context-menu-icon-quit"}},window.console&&(console.error||console.log).call(console,'When you reject a promise, provide an "items" object, equal to normal sub-menu items')):"string"==typeof n&&(n={error:{name:n}}),o(e,t,n)}function o(e,t,n){void 0!==t.$menu&&t.$menu.is(":visible")&&(e.$node.removeClass(t.classNames.iconLoadingClass),e.items=n,h.create(e,t,!0),h.update(e,t),t.positionSubmenu.call(e.$node,e.$menu))}e.$node.addClass(t.classNames.iconLoadingClass),n.then(function(e,t,n){void 0===n&&a(void 0),o(e,t,n)}.bind(this,e,t),a.bind(this,e,t))},activated:function(t){var n=t.$menu,a=n.offset(),o=e(window).height(),s=e(window).scrollTop(),i=n.height();i>o?n.css({height:o+"px","overflow-x":"hidden","overflow-y":"auto",top:s+"px"}):(a.tops+o)&&n.css({top:"0px"})}};e.fn.contextMenu=function(t){var n=this,a=t;if(this.length>0)if(void 0===t)this.first().trigger("contextmenu");else if(void 0!==t.x&&void 0!==t.y)this.first().trigger(e.Event("contextmenu",{pageX:t.x,pageY:t.y,mouseButton:t.button}));else if("hide"===t){var o=this.first().data("contextMenu")?this.first().data("contextMenu").$menu:null;o&&o.trigger("contextmenu:hide")}else"destroy"===t?e.contextMenu("destroy",{context:this}):e.isPlainObject(t)?(t.context=this,e.contextMenu("create",t)):t?this.removeClass("context-menu-disabled"):t||this.addClass("context-menu-disabled");else e.each(r,function(){this.selector===n.selector&&(a.data=this,e.extend(a.data,{trigger:"demand"}))}),f.contextmenu.call(a.target,a);return this},e.contextMenu=function(t,n){"string"!=typeof t&&(n=t,t="create"),"string"==typeof n?n={selector:n}:void 0===n&&(n={});var a=e.extend(!0,{},d,n||{}),o=e(document),i=o,u=!1;switch(a.context&&a.context.length?(i=e(a.context).first(),a.context=i.get(0),u=!e(a.context).is(document)):a.context=document,t){case"update":if(u)h.update(i);else for(var m in r)r.hasOwnProperty(m)&&h.update(r[m]);break;case"create":if(!a.selector)throw new Error("No selector specified");if(a.selector.match(/.context-menu-(list|item|input)($|\s)/))throw new Error('Cannot bind to selector "'+a.selector+'" as it contains a reserved className');if(!a.build&&(!a.items||e.isEmptyObject(a.items)))throw new Error("No Items specified");if(c++,a.ns=".contextMenu"+c,u||(l[a.selector]=a.ns),r[a.ns]=a,a.trigger||(a.trigger="right"),!s){var p="click"===a.itemClickEvent?"click.contextMenu":"mouseup.contextMenu",v={"contextmenu:focus.contextMenu":f.focusItem,"contextmenu:blur.contextMenu":f.blurItem,"contextmenu.contextMenu":f.abortevent,"mouseenter.contextMenu":f.itemMouseenter,"mouseleave.contextMenu":f.itemMouseleave};v[p]=f.itemClick,o.on({"contextmenu:hide.contextMenu":f.hideMenu,"prevcommand.contextMenu":f.prevItem,"nextcommand.contextMenu":f.nextItem,"contextmenu.contextMenu":f.abortevent,"mouseenter.contextMenu":f.menuMouseenter,"mouseleave.contextMenu":f.menuMouseleave},".context-menu-list").on("mouseup.contextMenu",".context-menu-input",f.inputClick).on(v,".context-menu-item"),s=!0}switch(i.on("contextmenu"+a.ns,a.selector,a,f.contextmenu),u&&i.on("remove"+a.ns,function(){e(this).contextMenu("destroy")}),a.trigger){case"hover":i.on("mouseenter"+a.ns,a.selector,a,f.mouseenter).on("mouseleave"+a.ns,a.selector,a,f.mouseleave);break;case"left":i.on("click"+a.ns,a.selector,a,f.click);break;case"touchstart":i.on("touchstart"+a.ns,a.selector,a,f.click)}a.build||h.create(a);break;case"destroy":var x;if(u){var g=a.context;e.each(r,function(t,n){if(!n)return!0;if(!e(g).is(n.selector))return!0;(x=e(".context-menu-list").filter(":visible")).length&&x.data().contextMenuRoot.$trigger.is(e(n.context).find(n.selector))&&x.trigger("contextmenu:hide",{force:!0});try{r[n.ns].$menu&&r[n.ns].$menu.remove(),delete r[n.ns]}catch(e){r[n.ns]=null}return e(n.context).off(n.ns),!0})}else if(a.selector){if(l[a.selector]){(x=e(".context-menu-list").filter(":visible")).length&&x.data().contextMenuRoot.$trigger.is(a.selector)&&x.trigger("contextmenu:hide",{force:!0});try{r[l[a.selector]].$menu&&r[l[a.selector]].$menu.remove(),delete r[l[a.selector]]}catch(e){r[l[a.selector]]=null}o.off(l[a.selector])}}else o.off(".contextMenu .contextMenuAutoHide"),e.each(r,function(t,n){e(n.context).off(n.ns)}),l={},r={},c=0,s=!1,e("#context-menu-layer, .context-menu-list").remove();break;case"html5":(!e.support.htmlCommand&&!e.support.htmlMenuitem||"boolean"==typeof n&&n)&&e('menu[type="context"]').each(function(){this.id&&e.contextMenu({selector:"[contextmenu="+this.id+"]",items:e.contextMenu.fromMenu(this)})}).css("display","none");break;default:throw new Error('Unknown operation "'+t+'"')}return this},e.contextMenu.setInputValues=function(t,n){void 0===n&&(n={}),e.each(t.inputs,function(e,t){switch(t.type){case"text":case"textarea":t.value=n[e]||"";break;case"checkbox":t.selected=!!n[e];break;case"radio":t.selected=(n[t.radio]||"")===t.value;break;case"select":t.selected=n[e]||""}})},e.contextMenu.getInputValues=function(t,n){return void 0===n&&(n={}),e.each(t.inputs,function(e,t){switch(t.type){case"text":case"textarea":case"select":n[e]=t.$input.val();break;case"checkbox":n[e]=t.$input.prop("checked");break;case"radio":t.$input.prop("checked")&&(n[t.radio]=t.value)}}),n},e.contextMenu.fromMenu=function(t){var n={};return a(n,e(t).children()),n},e.contextMenu.defaults=d,e.contextMenu.types=u,e.contextMenu.handle=f,e.contextMenu.op=h,e.contextMenu.menus=r}); +//# sourceMappingURL=jquery.contextMenu.min.js.map diff --git a/extensions/database/module/scripts/index/jquery.ui.position.min.js b/extensions/database/module/scripts/index/jquery.ui.position.min.js new file mode 100644 index 000000000..48d6a99b4 --- /dev/null +++ b/extensions/database/module/scripts/index/jquery.ui.position.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.12.1 - 2016-09-16 + * http://jqueryui.com + * Includes: position.js + * Copyright jQuery Foundation and other contributors; Licensed MIT */ + +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1",function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
      "),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position}); \ No newline at end of file diff --git a/extensions/database/module/scripts/project/database-exporters.js b/extensions/database/module/scripts/project/database-exporters.js new file mode 100644 index 000000000..2e99bbc84 --- /dev/null +++ b/extensions/database/module/scripts/project/database-exporters.js @@ -0,0 +1,52 @@ +/* + +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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. + + */ + +var dictionary = ""; +$.ajax({ + url : "command/core/load-language?", + type : "POST", + async : false, + data : { + module : "database", + + }, + success : function(data) { + dictionary = data; + } +}); +$.i18n.setDictionary(dictionary); +// End internationalization + +(function() { + +})(); diff --git a/extensions/database/module/styles/bootstrap.css b/extensions/database/module/styles/bootstrap.css new file mode 100644 index 000000000..39e2a1597 --- /dev/null +++ b/extensions/database/module/styles/bootstrap.css @@ -0,0 +1,1701 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2017 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! + * Generated using the Bootstrap Customizer (http://getbootstrap.com/docs/3.3/customize/?id=3426e25f732fc7882717e871fd08de17) + * Config saved to config.json and https://gist.github.com/3426e25f732fc7882717e871fd08de17 + */ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #dddddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-left: 15px; + padding-right: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #dddddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + border: 0; + margin-bottom: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #dddddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dddddd; +} +.panel-default { + border-color: #dddddd; +} +.panel-default > .panel-heading { + color: #333333; + background-color: #f5f5f5; + border-color: #dddddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #dddddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #dddddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.clearfix:before, +.clearfix:after, +.nav:before, +.nav:after, +.panel-body:before, +.panel-body:after { + content: " "; + display: table; +} +.clearfix:after, +.nav:after, +.panel-body:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #3c763d; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #31708f; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + background-color: #fcf8e3; + border-color: #faebcc; + color: #8a6d3b; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + background-color: #f2dede; + border-color: #ebccd1; + color: #a94442; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} + +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('../images/fonts/glyphicons-halflings-regular.eot'); + src: url('../images/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../images/fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../images/fonts/glyphicons-halflings-regular.woff') format('woff'), url('../images/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../images/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} + +.nav { + margin-bottom: 0; + padding-left: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #777777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777777; + text-decoration: none; + background-color: transparent; + cursor: not-allowed; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #dddddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid #dddddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #dddddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #dddddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +/* +* List Group +*/ +.list-group { + margin-bottom: 20px; + padding-left: 0; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #ffffff; + border: 1px solid #dddddd; +} +.list-group-item:first-child { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + text-decoration: none; + color: #555555; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + background-color: #eeeeee; + color: #777777; + cursor: not-allowed; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #ffffff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} diff --git a/extensions/database/module/styles/database-import.less b/extensions/database/module/styles/database-import.less new file mode 100644 index 000000000..be383bbe1 --- /dev/null +++ b/extensions/database/module/styles/database-import.less @@ -0,0 +1,152 @@ +/* + +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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. + +*/ + +@import-less url("theme.less"); + +.database-container { +// margin-left: 200px; + // width: 50%; +// min-height: 50%; +} + +.cleared { + clear: both; +} + +.custom-restricted-width { + /* To limit the menu width to the content of the menu: */ + display: inline-block; + /* Or set the width explicitly: */ + /* width: 10em; */ +} + + +.scrollable { + height: 400px; + overflow: auto; +} + +.database-importing-wizard-header { + font-size: 1.3em; + background: @chrome_primary; + padding: @padding_tight; + } + +.database-importing-parsing-data-panel { + font-size: 1.1em; + position: absolute; + overflow: auto; + } + +.database-importing-progress-data-panel { + position: absolute; + overflow: auto; + font-size: 200%; + padding: 3em; + background: rgba(255, 255, 255, 0.7); + text-align: center; + } + +.database-importing-parsing-control-panel { + font-size: 1.3em; + position: absolute; + overflow: auto; + border-top: 5px solid @chrome_primary; + background: white; + padding: @padding_looser; + } + +.context-menu-text { + padding: 4px 4px 2px 2px; +} + + +.custom-restricted { + height: 300px; + width: 200px; + border: 1px solid #bce8f1; + border-radius: 4px; + margin-right:10px; + padding-right:10px; + margin-bottom:10px; +} +.custom-restricted-width { + /* To limit the menu width to the content of the menu: */ + display: inline-block; + /* Or set the width explicitly: */ + /* width: 10em; */ + margin-right:10px; + padding-right:10px; + margin-bottom:10px; +} +.no-resize { + resize: none; +} +.layout-div { + width : 100%; + +} +.layout-div .connection-div-layout { + min-width: 400px; +} +.new-connection-legend { + padding-left: 2px; + margin-left: 2px; + border-color: #bce8f1; +} + +.sql-editor-div { + margin-left:40px; + padding-left:20px; +} +.new-connection-div { + margin-left:40px; + padding-left:20px; +} +.new-connection-fieldset { + border: 1px solid gray; +} +.sc-list { + border-bottom: 1px dotted #bce8f1; +} + +.sc-context-more-vert:after { + content: ""; + display: inline-block; + background: url("../images/more-option-horiz-16.png") no-repeat top right; + // line-height: 1; + width: 16px; + height: 16px; + -webkit-font-smoothing: antialiased; + +} diff --git a/extensions/database/module/styles/jquery.contextMenu.css b/extensions/database/module/styles/jquery.contextMenu.css new file mode 100644 index 000000000..0253e6e1b --- /dev/null +++ b/extensions/database/module/styles/jquery.contextMenu.css @@ -0,0 +1,292 @@ +@charset "UTF-8"; +/*! + * jQuery contextMenu - Plugin for simple contextMenu handling + * + * Version: v2.6.3 + * + * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://swisnl.github.io/jQuery-contextMenu/ + * + * Copyright (c) 2011-2017 SWIS BV and contributors + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * + * Date: 2017-10-30T19:03:13.936Z + */ +@-webkit-keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} +@-o-keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + -o-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + -o-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} +@keyframes cm-spin { + 0% { + -webkit-transform: translateY(-50%) rotate(0deg); + -o-transform: translateY(-50%) rotate(0deg); + transform: translateY(-50%) rotate(0deg); + } + 100% { + -webkit-transform: translateY(-50%) rotate(359deg); + -o-transform: translateY(-50%) rotate(359deg); + transform: translateY(-50%) rotate(359deg); + } +} + +@font-face { + font-family: "context-menu-icons"; + font-style: normal; + font-weight: normal; + + src: url("font/context-menu-icons.eot?2wp27"); + src: url("font/context-menu-icons.eot?2wp27#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2wp27") format("woff2"), url("font/context-menu-icons.woff?2wp27") format("woff"), url("font/context-menu-icons.ttf?2wp27") format("truetype"); +} + +.context-menu-icon-add:before { + content: "\EA01"; +} + +.context-menu-icon-copy:before { + content: "\EA02"; +} + +.context-menu-icon-cut:before { + content: "\EA03"; +} + +.context-menu-icon-delete:before { + content: "\EA04"; +} + +.context-menu-icon-edit:before { + content: "\EA05"; +} + +.context-menu-icon-loading:before { + content: "\EA06"; +} + +.context-menu-icon-paste:before { + content: "\EA07"; +} + +.context-menu-icon-quit:before { + content: "\EA08"; +} + +.context-menu-icon::before { + position: absolute; + top: 50%; + left: 0; + width: 2em; + font-family: "context-menu-icons"; + font-size: 1em; + font-style: normal; + font-weight: normal; + line-height: 1; + color: #2980b9; + text-align: center; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.context-menu-icon.context-menu-hover:before { + color: #fff; +} + +.context-menu-icon.context-menu-disabled::before { + color: #bbb; +} + +.context-menu-icon.context-menu-icon-loading:before { + -webkit-animation: cm-spin 2s infinite; + -o-animation: cm-spin 2s infinite; + animation: cm-spin 2s infinite; +} + +.context-menu-icon.context-menu-icon--fa { + display: list-item; + font-family: inherit; +} +.context-menu-icon.context-menu-icon--fa::before { + position: absolute; + top: 50%; + left: 0; + width: 2em; + font-family: FontAwesome; + font-size: 1em; + font-style: normal; + font-weight: normal; + line-height: 1; + color: #2980b9; + text-align: center; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.context-menu-icon.context-menu-icon--fa.context-menu-hover:before { + color: #fff; +} +.context-menu-icon.context-menu-icon--fa.context-menu-disabled::before { + color: #bbb; +} + +.context-menu-list { + position: absolute; + display: inline-block; + min-width: 13em; + max-width: 26em; + padding: .25em 0; + margin: .3em; + font-family: inherit; + font-size: inherit; + list-style-type: none; + background: #fff; + border: 1px solid #bebebe; + border-radius: .2em; + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5); + box-shadow: 0 2px 5px rgba(0, 0, 0, .5); +} + +.context-menu-item { + position: relative; + padding: .2em 2em; + color: #2f2f2f; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: #fff; + font: 15px "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.context-menu-separator { + padding: 0; + margin: .35em 0; + border-bottom: 1px solid #e6e6e6; +} + +.context-menu-item > label > input, +.context-menu-item > label > textarea { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.context-menu-item.context-menu-hover { + color: #fff; + cursor: pointer; + background-color: #2980b9; +} + +.context-menu-item.context-menu-disabled { + color: #bbb; + cursor: default; + background-color: #fff; +} + +.context-menu-input.context-menu-hover { + color: #2f2f2f; + cursor: default; +} + +.context-menu-submenu:after { + position: absolute; + top: 50%; + right: .5em; + z-index: 1; + width: 0; + height: 0; + content: ''; + border-color: transparent transparent transparent #2f2f2f; + border-style: solid; + border-width: .25em 0 .25em .25em; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); +} + +/** + * Inputs + */ +.context-menu-item.context-menu-input { + padding: .3em .6em; +} + +/* vertically align inside labels */ +.context-menu-input > label > * { + vertical-align: top; +} + +/* position checkboxes and radios as icons */ +.context-menu-input > label > input[type="checkbox"], +.context-menu-input > label > input[type="radio"] { + position: relative; + top: .12em; + margin-right: .4em; +} + +.context-menu-input > label { + margin: 0; +} + +.context-menu-input > label, +.context-menu-input > label > input[type="text"], +.context-menu-input > label > textarea, +.context-menu-input > label > select { + display: block; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.context-menu-input > label > textarea { + height: 7em; +} + +.context-menu-item > .context-menu-list { + top: .3em; + /* re-positioned by js */ + right: -.3em; + display: none; +} + +.context-menu-item.context-menu-visible > .context-menu-list { + display: block; +} + +.context-menu-accesskey { + text-decoration: underline; +} +/*Custom to display icons*/ + + + diff --git a/extensions/database/module/styles/pure.css b/extensions/database/module/styles/pure.css new file mode 100644 index 000000000..607e48aff --- /dev/null +++ b/extensions/database/module/styles/pure.css @@ -0,0 +1,1549 @@ +/*! +Pure v1.0.0 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/yahoo/pure/blob/master/LICENSE.md +*/ +/*! +normalize.css v^3.0 | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS and IE text size adjust after device orientation change, + * without disabling user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability of focused elements when they are also in an + * active/hover state. + */ + +a:active, +a:hover { + outline: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + box-sizing: content-box; /* 2 */ +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} + +/*csslint important:false*/ + +/* ========================================================================== + Pure Base Extras + ========================================================================== */ + +/** + * Extra rules that Pure adds on top of Normalize.css + */ + +/** + * Always hide an element when it has the `hidden` HTML attribute. + */ + +.hidden, +[hidden] { + display: none !important; +} + +/** + * Add this class to an image to make it fit within it's fluid parent wrapper while maintaining + * aspect ratio. + */ +.pure-img { + max-width: 100%; + height: auto; + display: block; +} + +/*csslint regex-selectors:false, known-properties:false, duplicate-properties:false*/ + +.pure-g { + letter-spacing: -0.31em; /* Webkit: collapse white-space between units */ + *letter-spacing: normal; /* reset IE < 8 */ + *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */ + text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */ + + /* + Sets the font stack to fonts known to work properly with the above letter + and word spacings. See: https://github.com/yahoo/pure/issues/41/ + + The following font stack makes Pure Grids work on all known environments. + + * FreeSans: Ships with many Linux distros, including Ubuntu + + * Arimo: Ships with Chrome OS. Arimo has to be defined before Helvetica and + Arial to get picked up by the browser, even though neither is available + in Chrome OS. + + * Droid Sans: Ships with all versions of Android. + + * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows. + */ + font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif; + + /* Use flexbox when possible to avoid `letter-spacing` side-effects. */ + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + + /* Prevents distributing space between rows */ + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start; +} + +/* IE10 display: -ms-flexbox (and display: flex in IE 11) does not work inside a table; fall back to block and rely on font hack */ +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + table .pure-g { + display: block; + } +} + +/* Opera as of 12 on Windows needs word-spacing. + The ".opera-only" selector is used to prevent actual prefocus styling + and is not required in markup. +*/ +.opera-only :-o-prefocus, +.pure-g { + word-spacing: -0.43em; +} + +.pure-u { + display: inline-block; + *display: inline; /* IE < 8: fake inline-block */ + zoom: 1; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} + +/* +Resets the font family back to the OS/browser's default sans-serif font, +this the same font stack that Normalize.css sets for the `body`. +*/ +.pure-g [class *= "pure-u"] { + font-family: sans-serif; +} + +.pure-u-1, +.pure-u-1-1, +.pure-u-1-2, +.pure-u-1-3, +.pure-u-2-3, +.pure-u-1-4, +.pure-u-3-4, +.pure-u-1-5, +.pure-u-2-5, +.pure-u-3-5, +.pure-u-4-5, +.pure-u-5-5, +.pure-u-1-6, +.pure-u-5-6, +.pure-u-1-8, +.pure-u-3-8, +.pure-u-5-8, +.pure-u-7-8, +.pure-u-1-12, +.pure-u-5-12, +.pure-u-7-12, +.pure-u-11-12, +.pure-u-1-24, +.pure-u-2-24, +.pure-u-3-24, +.pure-u-4-24, +.pure-u-5-24, +.pure-u-6-24, +.pure-u-7-24, +.pure-u-8-24, +.pure-u-9-24, +.pure-u-10-24, +.pure-u-11-24, +.pure-u-12-24, +.pure-u-13-24, +.pure-u-14-24, +.pure-u-15-24, +.pure-u-16-24, +.pure-u-17-24, +.pure-u-18-24, +.pure-u-19-24, +.pure-u-20-24, +.pure-u-21-24, +.pure-u-22-24, +.pure-u-23-24, +.pure-u-24-24 { + display: inline-block; + *display: inline; + zoom: 1; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} + +.pure-u-1-24 { + width: 4.1667%; + *width: 4.1357%; +} + +.pure-u-1-12, +.pure-u-2-24 { + width: 8.3333%; + *width: 8.3023%; +} + +.pure-u-1-8, +.pure-u-3-24 { + width: 12.5000%; + *width: 12.4690%; +} + +.pure-u-1-6, +.pure-u-4-24 { + width: 16.6667%; + *width: 16.6357%; +} + +.pure-u-1-5 { + width: 20%; + *width: 19.9690%; +} + +.pure-u-5-24 { + width: 20.8333%; + *width: 20.8023%; +} + +.pure-u-1-4, +.pure-u-6-24 { + width: 25%; + *width: 24.9690%; +} + +.pure-u-7-24 { + width: 29.1667%; + *width: 29.1357%; +} + +.pure-u-1-3, +.pure-u-8-24 { + width: 33.3333%; + *width: 33.3023%; +} + +.pure-u-3-8, +.pure-u-9-24 { + width: 37.5000%; + *width: 37.4690%; +} + +.pure-u-2-5 { + width: 40%; + *width: 39.9690%; +} + +.pure-u-5-12, +.pure-u-10-24 { + width: 41.6667%; + *width: 41.6357%; +} + +.pure-u-11-24 { + width: 45.8333%; + *width: 45.8023%; +} + +.pure-u-1-2, +.pure-u-12-24 { + width: 50%; + *width: 49.9690%; +} + +.pure-u-13-24 { + width: 54.1667%; + *width: 54.1357%; +} + +.pure-u-7-12, +.pure-u-14-24 { + width: 58.3333%; + *width: 58.3023%; +} + +.pure-u-3-5 { + width: 60%; + *width: 59.9690%; +} + +.pure-u-5-8, +.pure-u-15-24 { + width: 62.5000%; + *width: 62.4690%; +} + +.pure-u-2-3, +.pure-u-16-24 { + width: 66.6667%; + *width: 66.6357%; +} + +.pure-u-17-24 { + width: 70.8333%; + *width: 70.8023%; +} + +.pure-u-3-4, +.pure-u-18-24 { + width: 75%; + *width: 74.9690%; +} + +.pure-u-19-24 { + width: 79.1667%; + *width: 79.1357%; +} + +.pure-u-4-5 { + width: 80%; + *width: 79.9690%; +} + +.pure-u-5-6, +.pure-u-20-24 { + width: 83.3333%; + *width: 83.3023%; +} + +.pure-u-7-8, +.pure-u-21-24 { + width: 87.5000%; + *width: 87.4690%; +} + +.pure-u-11-12, +.pure-u-22-24 { + width: 91.6667%; + *width: 91.6357%; +} + +.pure-u-23-24 { + width: 95.8333%; + *width: 95.8023%; +} + +.pure-u-1, +.pure-u-1-1, +.pure-u-5-5, +.pure-u-24-24 { + width: 100%; +} +.pure-button { + /* Structure */ + display: inline-block; + zoom: 1; + line-height: normal; + white-space: nowrap; + vertical-align: middle; + text-align: center; + cursor: pointer; + -webkit-user-drag: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + box-sizing: border-box; +} + +/* Firefox: Get rid of the inner focus border */ +.pure-button::-moz-focus-inner { + padding: 0; + border: 0; +} + +/* Inherit .pure-g styles */ +.pure-button-group { + letter-spacing: -0.31em; /* Webkit: collapse white-space between units */ + *letter-spacing: normal; /* reset IE < 8 */ + *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */ + text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */ +} + +.opera-only :-o-prefocus, +.pure-button-group { + word-spacing: -0.43em; +} + +.pure-button-group .pure-button { + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} + +/*csslint outline-none:false*/ + +.pure-button { + font-family: inherit; + font-size: 100%; + padding: 0.5em 1em; + color: #444; /* rgba not supported (IE 8) */ + color: rgba(0, 0, 0, 0.80); /* rgba supported */ + border: 1px solid #999; /*IE 6/7/8*/ + border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/ + background-color: #E6E6E6; + text-decoration: none; + border-radius: 2px; +} + +.pure-button-hover, +.pure-button:hover, +.pure-button:focus { + /* csslint ignore:start */ + filter: alpha(opacity=90); + /* csslint ignore:end */ + background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); + background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); +} +.pure-button:focus { + outline: 0; +} +.pure-button-active, +.pure-button:active { + box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset; + border-color: #000\9; +} + +.pure-button[disabled], +.pure-button-disabled, +.pure-button-disabled:hover, +.pure-button-disabled:focus, +.pure-button-disabled:active { + border: none; + background-image: none; + /* csslint ignore:start */ + filter: alpha(opacity=40); + /* csslint ignore:end */ + opacity: 0.40; + cursor: not-allowed; + box-shadow: none; + pointer-events: none; +} + +.pure-button-hidden { + display: none; +} + +.pure-button-primary, +.pure-button-selected, +a.pure-button-primary, +a.pure-button-selected { + background-color: rgb(0, 120, 231); + color: #fff; +} + +/* Button Groups */ +.pure-button-group .pure-button { + margin: 0; + border-radius: 0; + border-right: 1px solid #111; /* fallback color for rgba() for IE7/8 */ + border-right: 1px solid rgba(0, 0, 0, 0.2); + +} + +.pure-button-group .pure-button:first-child { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} +.pure-button-group .pure-button:last-child { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-right: none; +} + +/*csslint box-model:false*/ +/* +Box-model set to false because we're setting a height on select elements, which +also have border and padding. This is done because some browsers don't render +the padding. We explicitly set the box-model for select elements to border-box, +so we can ignore the csslint warning. +*/ + +.pure-form input[type="text"], +.pure-form input[type="password"], +.pure-form input[type="email"], +.pure-form input[type="url"], +.pure-form input[type="date"], +.pure-form input[type="month"], +.pure-form input[type="time"], +.pure-form input[type="datetime"], +.pure-form input[type="datetime-local"], +.pure-form input[type="week"], +.pure-form input[type="number"], +.pure-form input[type="search"], +.pure-form input[type="tel"], +.pure-form input[type="color"], +.pure-form select, +.pure-form textarea { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + vertical-align: middle; + box-sizing: border-box; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type]) { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + box-sizing: border-box; +} + + +/* Chrome (as of v.32/34 on OS X) needs additional room for color to display. */ +/* May be able to remove this tweak as color inputs become more standardized across browsers. */ +.pure-form input[type="color"] { + padding: 0.2em 0.5em; +} + + +.pure-form input[type="text"]:focus, +.pure-form input[type="password"]:focus, +.pure-form input[type="email"]:focus, +.pure-form input[type="url"]:focus, +.pure-form input[type="date"]:focus, +.pure-form input[type="month"]:focus, +.pure-form input[type="time"]:focus, +.pure-form input[type="datetime"]:focus, +.pure-form input[type="datetime-local"]:focus, +.pure-form input[type="week"]:focus, +.pure-form input[type="number"]:focus, +.pure-form input[type="search"]:focus, +.pure-form input[type="tel"]:focus, +.pure-form input[type="color"]:focus, +.pure-form select:focus, +.pure-form textarea:focus { + outline: 0; + border-color: #129FEA; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type]):focus { + outline: 0; + border-color: #129FEA; +} + +.pure-form input[type="file"]:focus, +.pure-form input[type="radio"]:focus, +.pure-form input[type="checkbox"]:focus { + outline: thin solid #129FEA; + outline: 1px auto #129FEA; +} +.pure-form .pure-checkbox, +.pure-form .pure-radio { + margin: 0.5em 0; + display: block; +} + +.pure-form input[type="text"][disabled], +.pure-form input[type="password"][disabled], +.pure-form input[type="email"][disabled], +.pure-form input[type="url"][disabled], +.pure-form input[type="date"][disabled], +.pure-form input[type="month"][disabled], +.pure-form input[type="time"][disabled], +.pure-form input[type="datetime"][disabled], +.pure-form input[type="datetime-local"][disabled], +.pure-form input[type="week"][disabled], +.pure-form input[type="number"][disabled], +.pure-form input[type="search"][disabled], +.pure-form input[type="tel"][disabled], +.pure-form input[type="color"][disabled], +.pure-form select[disabled], +.pure-form textarea[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type])[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; +} +.pure-form input[readonly], +.pure-form select[readonly], +.pure-form textarea[readonly] { + background-color: #eee; /* menu hover bg color */ + color: #777; /* menu text color */ + border-color: #ccc; +} + +.pure-form input:focus:invalid, +.pure-form textarea:focus:invalid, +.pure-form select:focus:invalid { + color: #b94a48; + border-color: #e9322d; +} +.pure-form input[type="file"]:focus:invalid:focus, +.pure-form input[type="radio"]:focus:invalid:focus, +.pure-form input[type="checkbox"]:focus:invalid:focus { + outline-color: #e9322d; +} +.pure-form select { + /* Normalizes the height; padding is not sufficient. */ + height: 2.25em; + border: 1px solid #ccc; + background-color: white; +} +.pure-form select[multiple] { + height: auto; +} +.pure-form label { + margin: 0.5em 0 0.2em; +} +.pure-form fieldset { + margin: 0; + padding: 0.35em 0 0.75em; + border: 0; +} +.pure-form legend { + display: block; + width: 50%; + padding: 0.3em 0; + margin-bottom: 0.3em; + margin-left: 5px; + color: #333; + /* border-bottom: 1px solid #e5e5e5; */ + border-bottom: 1px solid #bce8f1; +} + +.pure-form-stacked input[type="text"], +.pure-form-stacked input[type="password"], +.pure-form-stacked input[type="email"], +.pure-form-stacked input[type="url"], +.pure-form-stacked input[type="date"], +.pure-form-stacked input[type="month"], +.pure-form-stacked input[type="time"], +.pure-form-stacked input[type="datetime"], +.pure-form-stacked input[type="datetime-local"], +.pure-form-stacked input[type="week"], +.pure-form-stacked input[type="number"], +.pure-form-stacked input[type="search"], +.pure-form-stacked input[type="tel"], +.pure-form-stacked input[type="color"], +.pure-form-stacked input[type="file"], +.pure-form-stacked select, +.pure-form-stacked label, +.pure-form-stacked textarea { + display: block; + margin: 0.25em 0; +} + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form-stacked input:not([type]) { + display: block; + margin: 0.25em 0; +} +.pure-form-aligned input, +.pure-form-aligned textarea, +.pure-form-aligned select, +/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ +.pure-form-aligned .pure-help-inline, +.pure-form-message-inline { + display: inline-block; + *display: inline; + *zoom: 1; + vertical-align: middle; +} +.pure-form-aligned textarea { + vertical-align: top; +} + +/* Aligned Forms */ +.pure-form-aligned .pure-control-group { + margin-bottom: 0.5em; +} +.pure-form-aligned .pure-control-group label { + text-align: right; + display: inline-block; + vertical-align: middle; + width: 10em; + margin: 0 1em 0 0; +} +.pure-form-aligned .pure-controls { + margin: 1.5em 0 0 11em; +} + +/* Rounded Inputs */ +.pure-form input.pure-input-rounded, +.pure-form .pure-input-rounded { + border-radius: 2em; + padding: 0.5em 1em; +} + +/* Grouped Inputs */ +.pure-form .pure-group fieldset { + margin-bottom: 10px; +} +.pure-form .pure-group input, +.pure-form .pure-group textarea { + display: block; + padding: 10px; + margin: 0 0 -1px; + border-radius: 0; + position: relative; + top: -1px; +} +.pure-form .pure-group input:focus, +.pure-form .pure-group textarea:focus { + z-index: 3; +} +.pure-form .pure-group input:first-child, +.pure-form .pure-group textarea:first-child { + top: 1px; + border-radius: 4px 4px 0 0; + margin: 0; +} +.pure-form .pure-group input:first-child:last-child, +.pure-form .pure-group textarea:first-child:last-child { + top: 1px; + border-radius: 4px; + margin: 0; +} +.pure-form .pure-group input:last-child, +.pure-form .pure-group textarea:last-child { + top: -2px; + border-radius: 0 0 4px 4px; + margin: 0; +} +.pure-form .pure-group button { + margin: 0.35em 0; +} + +.pure-form .pure-input-1 { + width: 100%; +} +.pure-form .pure-input-3-4 { + width: 75%; +} +.pure-form .pure-input-2-3 { + width: 66%; +} +.pure-form .pure-input-1-2 { + width: 50%; +} +.pure-form .pure-input-1-3 { + width: 33%; +} +.pure-form .pure-input-1-4 { + width: 25%; +} + +/* Inline help for forms */ +/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ +.pure-form .pure-help-inline, +.pure-form-message-inline { + display: inline-block; + padding-left: 0.3em; + color: #666; + vertical-align: middle; + font-size: 0.875em; +} + +/* Block help for forms */ +.pure-form-message { + display: block; + color: #666; + font-size: 0.875em; +} + +@media only screen and (max-width : 480px) { + .pure-form button[type="submit"] { + margin: 0.7em 0 0; + } + + .pure-form input:not([type]), + .pure-form input[type="text"], + .pure-form input[type="password"], + .pure-form input[type="email"], + .pure-form input[type="url"], + .pure-form input[type="date"], + .pure-form input[type="month"], + .pure-form input[type="time"], + .pure-form input[type="datetime"], + .pure-form input[type="datetime-local"], + .pure-form input[type="week"], + .pure-form input[type="number"], + .pure-form input[type="search"], + .pure-form input[type="tel"], + .pure-form input[type="color"], + .pure-form label { + margin-bottom: 0.3em; + display: block; + } + + .pure-group input:not([type]), + .pure-group input[type="text"], + .pure-group input[type="password"], + .pure-group input[type="email"], + .pure-group input[type="url"], + .pure-group input[type="date"], + .pure-group input[type="month"], + .pure-group input[type="time"], + .pure-group input[type="datetime"], + .pure-group input[type="datetime-local"], + .pure-group input[type="week"], + .pure-group input[type="number"], + .pure-group input[type="search"], + .pure-group input[type="tel"], + .pure-group input[type="color"] { + margin-bottom: 0; + } + + .pure-form-aligned .pure-control-group label { + margin-bottom: 0.3em; + text-align: left; + display: block; + width: 100%; + } + + .pure-form-aligned .pure-controls { + margin: 1.5em 0 0 0; + } + + /* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ + .pure-form .pure-help-inline, + .pure-form-message-inline, + .pure-form-message { + display: block; + font-size: 0.75em; + /* Increased bottom padding to make it group with its related input element. */ + padding: 0.2em 0 0.8em; + } +} + +/*csslint adjoining-classes: false, box-model:false*/ +.pure-menu { + box-sizing: border-box; +} + +.pure-menu-fixed { + position: fixed; + left: 0; + top: 0; + z-index: 3; +} + +.pure-menu-list, +.pure-menu-item { + position: relative; +} + +.pure-menu-list { + list-style: none; + margin: 0; + padding: 0; +} + +.pure-menu-item { + padding: 0; + margin: 0; + height: 100%; +} + +.pure-menu-link, +.pure-menu-heading { + display: block; + text-decoration: none; + white-space: nowrap; +} + +/* HORIZONTAL MENU */ +.pure-menu-horizontal { + width: 100%; + white-space: nowrap; +} + +.pure-menu-horizontal .pure-menu-list { + display: inline-block; +} + +/* Initial menus should be inline-block so that they are horizontal */ +.pure-menu-horizontal .pure-menu-item, +.pure-menu-horizontal .pure-menu-heading, +.pure-menu-horizontal .pure-menu-separator { + display: inline-block; + *display: inline; + zoom: 1; + vertical-align: middle; +} + +/* Submenus should still be display: block; */ +.pure-menu-item .pure-menu-item { + display: block; +} + +.pure-menu-children { + display: none; + position: absolute; + left: 100%; + top: 0; + margin: 0; + padding: 0; + z-index: 3; +} + +.pure-menu-horizontal .pure-menu-children { + left: 0; + top: auto; + width: inherit; +} + +.pure-menu-allow-hover:hover > .pure-menu-children, +.pure-menu-active > .pure-menu-children { + display: block; + position: absolute; +} + +/* Vertical Menus - show the dropdown arrow */ +.pure-menu-has-children > .pure-menu-link:after { + padding-left: 0.5em; + content: "\25B8"; + font-size: small; +} + +/* Horizontal Menus - show the dropdown arrow */ +.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after { + content: "\25BE"; +} + +/* scrollable menus */ +.pure-menu-scrollable { + overflow-y: scroll; + overflow-x: scroll; +} + +.pure-menu-scrollable .pure-menu-list { + display: block; +} + +.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list { + display: inline-block; +} + +.pure-menu-horizontal.pure-menu-scrollable { + white-space: nowrap; + overflow-y: hidden; + overflow-x: auto; + -ms-overflow-style: none; + -webkit-overflow-scrolling: touch; + /* a little extra padding for this style to allow for scrollbars */ + padding: .5em 0; +} + +.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar { + display: none; +} + +/* misc default styling */ + +.pure-menu-separator, +.pure-menu-horizontal .pure-menu-children .pure-menu-separator { + background-color: #ccc; + height: 1px; + margin: .3em 0; +} + +.pure-menu-horizontal .pure-menu-separator { + width: 1px; + height: 1.3em; + margin: 0 .3em ; +} + +/* Need to reset the separator since submenu is vertical */ +.pure-menu-horizontal .pure-menu-children .pure-menu-separator { + display: block; + width: auto; +} + +.pure-menu-heading { + text-transform: uppercase; + color: #565d64; +} + +.pure-menu-link { + /* color: #777; */ +} + +.pure-menu-children { + background-color: #fff; +} + +.pure-menu-link, +.pure-menu-disabled, +.pure-menu-heading { + padding: .5em 1em; +} + +.pure-menu-disabled { + opacity: .5; +} + +.pure-menu-disabled .pure-menu-link:hover { + background-color: transparent; +} + +.pure-menu-active > .pure-menu-link, +.pure-menu-link:hover, +.pure-menu-link:focus { + background-color: #eee; +} + +.pure-menu-selected .pure-menu-link, +.pure-menu-selected .pure-menu-link:visited { + color: #000; +} + +.pure-table { + /* Remove spacing between table cells (from Normalize.css) */ + border-collapse: collapse; + border-spacing: 0; + empty-cells: show; + border: 1px solid #cbcbcb; +} + +.pure-table caption { + color: #000; + font: italic 85%/1 arial, sans-serif; + padding: 1em 0; + text-align: center; +} + +.pure-table td, +.pure-table th { + border-left: 1px solid #cbcbcb;/* inner column border */ + border-width: 0 0 0 1px; + font-size: inherit; + margin: 0; + overflow: visible; /*to make ths where the title is really long work*/ + padding: 0.5em 1em; /* cell padding */ +} + +/* Consider removing this next declaration block, as it causes problems when +there's a rowspan on the first cell. Case added to the tests. issue#432 */ +.pure-table td:first-child, +.pure-table th:first-child { + border-left-width: 0; +} + +.pure-table thead { + background-color: #e0e0e0; + color: #000; + text-align: left; + vertical-align: bottom; +} + +/* +striping: + even - #fff (white) + odd - #f2f2f2 (light gray) +*/ +.pure-table td { + background-color: transparent; +} +.pure-table-odd td { + background-color: #f2f2f2; +} + +/* nth-child selector for modern browsers */ +.pure-table-striped tr:nth-child(2n-1) td { + background-color: #f2f2f2; +} + +/* BORDERED TABLES */ +.pure-table-bordered td { + border-bottom: 1px solid #cbcbcb; +} +.pure-table-bordered tbody > tr:last-child > td { + border-bottom-width: 0; +} + + +/* HORIZONTAL BORDERED TABLES */ + +.pure-table-horizontal td, +.pure-table-horizontal th { + border-width: 0 0 1px 0; + border-bottom: 1px solid #cbcbcb; +} +.pure-table-horizontal tbody > tr:last-child > td { + border-bottom-width: 0; +} + + +/* + * -- HELPER STYLES -- + * Over-riding some of the .pure-button styles to make my buttons look unique + */ +.button-success, +.button-error, +.button-warning, +.button-secondary { + color: white; + border-radius: 4px; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); +} + +.button-success { + background: rgb(28, 184, 65); /* this is a green */ +} + +.button-error { + background: rgb(202, 60, 60); /* this is a maroon */ +} + +.button-warning { + background: rgb(223, 117, 20); /* this is an orange */ +} + +.button-secondary { + background: rgb(66, 184, 221); /* this is a light blue */ +} + + +.custom-restricted { + height: 160px; + width: 150px; + border: 1px solid gray; + border-radius: 4px; +} + diff --git a/extensions/database/module/styles/theme.less b/extensions/database/module/styles/theme.less new file mode 100644 index 000000000..d34f87ab7 --- /dev/null +++ b/extensions/database/module/styles/theme.less @@ -0,0 +1,34 @@ +/* + +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google Inc. 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. + +*/ + +@import-less url("../../../../main/webapp/modules/core/styles/theme.less"); diff --git a/extensions/database/src/com/google/refine/extension/database/DBQueryResultImportReader.java b/extensions/database/src/com/google/refine/extension/database/DBQueryResultImportReader.java new file mode 100644 index 000000000..78935db07 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DBQueryResultImportReader.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseQueryInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.importers.TabularImportingParserBase.TableDataReader; +import com.google.refine.importing.ImportingJob; + + +public class DBQueryResultImportReader implements TableDataReader { + + static final Logger logger = LoggerFactory.getLogger("DBQueryResultImportReader"); + + private final ImportingJob job; + private final String querySource; + private List dbColumns; + private final int batchSize; + + private int nextRow = 0; // 0-based + private int batchRowStart = 0; // 0-based + private boolean end = false; + private List> rowsOfCells = null; + private boolean usedHeaders = false; + private DatabaseService databaseService; + private DatabaseQueryInfo dbQueryInfo; + private int processedRows = 0; + private static int progress = 0; + + + public DBQueryResultImportReader( + ImportingJob job, + DatabaseService databaseService, + String querySource, + List columns, + DatabaseQueryInfo dbQueryInfo, + int batchSize) { + + this.job = job; + this.querySource = querySource; + this.batchSize = batchSize; + this.dbColumns = columns; + this.databaseService = databaseService; + this.dbQueryInfo = dbQueryInfo; + logger.info("DBQueryResultImportReader::batchSize:" + batchSize); + + } + + @Override + public List getNextRowOfCells() throws IOException { + + try { + + if (!usedHeaders) { + List row = new ArrayList(dbColumns.size()); + for (DatabaseColumn cd : dbColumns) { + row.add(cd.getName()); + } + usedHeaders = true; + //logger.info("Exit::getNextRowOfCells return header::row:" + row); + return row; + } + + if (rowsOfCells == null || (nextRow >= batchRowStart + rowsOfCells.size() && !end)) { + int newBatchRowStart = batchRowStart + (rowsOfCells == null ? 0 : rowsOfCells.size()); + rowsOfCells = getRowsOfCells(newBatchRowStart); + processedRows = processedRows + rowsOfCells.size(); + batchRowStart = newBatchRowStart; + setProgress(job, querySource, -1 /* batchRowStart * 100 / totalRows */); + } + + if (rowsOfCells != null && nextRow - batchRowStart < rowsOfCells.size()) { + List result = rowsOfCells.get(nextRow++ - batchRowStart); + if(nextRow >= batchSize) { + rowsOfCells = getRowsOfCells(processedRows); + processedRows = processedRows + rowsOfCells.size(); + + if(logger.isDebugEnabled()) { + logger.debug("[[ Returning last row in batch:nextRow::{}, processedRows:{} ]]", nextRow, processedRows); + } + + nextRow = 0; + if(processedRows % 100 == 0) { + setProgress(job, querySource, progress++); + } + if(processedRows % 10000 == 0) { + logger.info("[[ {} rows processed... ]]",processedRows); + } + } + return result; + } else { + logger.info("[[processedRows:{} ]]", processedRows); + return null; + } + + + }catch(DatabaseServiceException e) { + logger.error("DatabaseServiceException::{}", e); + throw new IOException(e); + + } + + + } + + /** + * @param startRow + * @return + * @throws IOException + * @throws DatabaseServiceException + */ + private List> getRowsOfCells(int startRow) throws IOException, DatabaseServiceException { + //logger.info("Entry getRowsOfCells::startRow:" + startRow); + + List> rowsOfCells = new ArrayList>(batchSize); + + String query = databaseService.buildLimitQuery(batchSize, startRow, dbQueryInfo.getQuery()); + //logger.info("batchSize::" + batchSize + " startRow::" + startRow + " query::" + query ); + + List dbRows = databaseService.getRows(dbQueryInfo.getDbConfig(), query); + + if(dbRows != null && !dbRows.isEmpty() && dbRows.size() > 0) { + + for(DatabaseRow dbRow: dbRows) { + List row = dbRow.getValues(); + List rowOfCells = new ArrayList(row.size()); + + for (int j = 0; j < row.size() && j < dbColumns.size(); j++) { + + String text = row.get(j); + if (text == null || text.isEmpty()) { + rowOfCells.add(null); + }else { + DatabaseColumn col = dbColumns.get(j); + if(col.getType() == DatabaseColumnType.NUMBER) { + try { + rowOfCells.add(Long.parseLong(text)); + continue; + } catch (NumberFormatException e) {} + + try { + double d = Double.parseDouble(text); + if (!Double.isInfinite(d) && !Double.isNaN(d)) { + rowOfCells.add(d); + continue; + } + } catch (NumberFormatException e) {} + + } + + rowOfCells.add(text); + } + + } + + rowsOfCells.add(rowOfCells); + + } + + } + end = dbRows.size() < batchSize + 1; + //logger.info("Exit::getRowsOfCells::rowsOfCells:{}", rowsOfCells); + return rowsOfCells; + + } + + private static void setProgress(ImportingJob job, String querySource, int percent) { + job.setProgress(percent, "Reading " + querySource); + } + + public List getColumns() { + return dbColumns; + } + + + public void setColumns(List columns) { + this.dbColumns = columns; + } + + + public int getNextRow() { + return nextRow; + } + + + public void setNextRow(int nextRow) { + this.nextRow = nextRow; + } + + + public int getBatchRowStart() { + return batchRowStart; + } + + + public void setBatchRowStart(int batchRowStart) { + this.batchRowStart = batchRowStart; + } + + + public boolean isEnd() { + return end; + } + + + public void setEnd(boolean end) { + this.end = end; + } + + + public List> getRowsOfCells() { + return rowsOfCells; + } + + + public void setRowsOfCells(List> rowsOfCells) { + this.rowsOfCells = rowsOfCells; + } + + + public boolean isUsedHeaders() { + return usedHeaders; + } + + + public void setUsedHeaders(boolean usedHeaders) { + this.usedHeaders = usedHeaders; + } + + + public ImportingJob getJob() { + return job; + } + + + public String getQuerySource() { + return querySource; + } + + + public int getBatchSize() { + return batchSize; + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DBQueryResultPreviewReader.java b/extensions/database/src/com/google/refine/extension/database/DBQueryResultPreviewReader.java new file mode 100644 index 000000000..06c1a6241 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DBQueryResultPreviewReader.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseQueryInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.importers.TabularImportingParserBase.TableDataReader; +import com.google.refine.importing.ImportingJob; + + +public class DBQueryResultPreviewReader implements TableDataReader { + + static final Logger logger = LoggerFactory.getLogger("DBQueryResultPreviewReader"); + + private final ImportingJob job; + private final String querySource; + private List dbColumns; + private final int batchSize; + + private int nextRow = 0; // 0-based + private int batchRowStart = 0; // 0-based + private boolean end = false; + private List> rowsOfCells = null; + private boolean usedHeaders = false; + private DatabaseService databaseService; + private DatabaseQueryInfo dbQueryInfo; + + + public DBQueryResultPreviewReader( + ImportingJob job, + DatabaseService databaseService, + String querySource, + List columns, + DatabaseQueryInfo dbQueryInfo, + int batchSize) { + + this.job = job; + this.querySource = querySource; + this.batchSize = batchSize; + this.dbColumns = columns; + this.databaseService = databaseService; + this.dbQueryInfo = dbQueryInfo; + logger.info("DBQueryResultPreviewReader::batchSize:" + batchSize); + + } + + @Override + public List getNextRowOfCells() throws IOException { + + // logger.info("Entry::getNextRowOfCells"); + + try { + + if (!usedHeaders) { + List row = new ArrayList(dbColumns.size()); + for (DatabaseColumn cd : dbColumns) { + row.add(cd.getName()); + } + usedHeaders = true; + logger.info("Exit::getNextRowOfCells return header::row:" + row); + return row; + } + + if (rowsOfCells == null || (nextRow >= batchRowStart + rowsOfCells.size() && !end)) { + int newBatchRowStart = batchRowStart + (rowsOfCells == null ? 0 : rowsOfCells.size()); + rowsOfCells = getRowsOfCells(newBatchRowStart); + batchRowStart = newBatchRowStart; + setProgress(job, querySource, -1 /* batchRowStart * 100 / totalRows */); + // logger.info("getNextRowOfCells:: rowsOfCellsIsNull::rowsOfCells size:" + rowsOfCells.size() + ":batchRowStart:" + batchRowStart + " ::nextRow:" + nextRow); + } + + if (rowsOfCells != null && nextRow - batchRowStart < rowsOfCells.size()) { + //logger.info("Exit::getNextRowOfCells :rowsOfCellsNotNull::rowsOfCells size:" + rowsOfCells.size() + ":batchRowStart:" + batchRowStart + " ::nextRow:" + nextRow); + return rowsOfCells.get(nextRow++ - batchRowStart); + } else { + logger.info("nextRow:{}, batchRowStart:{}", nextRow, batchRowStart); +// +// rowsOfCells = getRowsOfCells(batchRowStart); +// if(rowsOfCells != null) { +// return rowsOfCells.get(nextRow++ - batchRowStart); +// } + return null; + } + + + }catch(DatabaseServiceException e) { + logger.error("DatabaseServiceException::{}", e); + throw new IOException(e); + + } + + + } + + /** + * + * @param startRow + * @return + * @throws IOException + * @throws DatabaseServiceException + */ + private List> getRowsOfCells(int startRow) throws IOException, DatabaseServiceException { + //logger.info("Entry getRowsOfCells::startRow:" + startRow); + + List> rowsOfCells = new ArrayList>(batchSize); + + String query = databaseService.buildLimitQuery(batchSize, startRow, dbQueryInfo.getQuery()); + logger.info("batchSize::" + batchSize + " startRow::" + startRow + " query::" + query ); + + List dbRows = databaseService.getRows(dbQueryInfo.getDbConfig(), query); + + if(dbRows != null && !dbRows.isEmpty() && dbRows.size() > 0) { + + for(DatabaseRow dbRow: dbRows) { + List row = dbRow.getValues(); + List rowOfCells = new ArrayList(row.size()); + + for (int j = 0; j < row.size() && j < dbColumns.size(); j++) { + + String text = row.get(j); + if (text == null || text.isEmpty()) { + rowOfCells.add(null); + }else { + DatabaseColumn col = dbColumns.get(j); + if(col.getType() == DatabaseColumnType.NUMBER) { + try { + rowOfCells.add(Long.parseLong(text)); + continue; + } catch (NumberFormatException e) { + // ignore + } + + try { + double d = Double.parseDouble(text); + if (!Double.isInfinite(d) && !Double.isNaN(d)) { + rowOfCells.add(d); + continue; + } + } catch (NumberFormatException e) { + // ignore + } + } + + rowOfCells.add(text); + } + + } + rowsOfCells.add(rowOfCells); + + } + + } + end = dbRows.size() < batchSize + 1; + //logger.info("Exit::getRowsOfCells::rowsOfCells:{}", rowsOfCells); + return rowsOfCells; + + } + + private static void setProgress(ImportingJob job, String querySource, int percent) { + job.setProgress(percent, "Reading " + querySource); + } + + public List getColumns() { + return dbColumns; + } + + + public void setColumns(List columns) { + this.dbColumns = columns; + } + + + public int getNextRow() { + return nextRow; + } + + + public void setNextRow(int nextRow) { + this.nextRow = nextRow; + } + + + public int getBatchRowStart() { + return batchRowStart; + } + + + public void setBatchRowStart(int batchRowStart) { + this.batchRowStart = batchRowStart; + } + + + public boolean isEnd() { + return end; + } + + + public void setEnd(boolean end) { + this.end = end; + } + + + public List> getRowsOfCells() { + return rowsOfCells; + } + + + public void setRowsOfCells(List> rowsOfCells) { + this.rowsOfCells = rowsOfCells; + } + + + public boolean isUsedHeaders() { + return usedHeaders; + } + + + public void setUsedHeaders(boolean usedHeaders) { + this.usedHeaders = usedHeaders; + } + + + public ImportingJob getJob() { + return job; + } + + + public String getQuerySource() { + return querySource; + } + + + public int getBatchSize() { + return batchSize; + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseColumnType.java b/extensions/database/src/com/google/refine/extension/database/DatabaseColumnType.java new file mode 100644 index 000000000..a6c0cddda --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseColumnType.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + + +public enum DatabaseColumnType { + + STRING, + NUMBER, + DATETIME, + LOCATION + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java b/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java new file mode 100644 index 000000000..85c4cd6e8 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + + +public class DatabaseConfiguration { + + private String connectionName; + private String databaseType; + private String databaseHost; + private int databasePort; + private String databaseUser; + private String databasePassword; + private String databaseName; + private String databaseSchema; + + //optional parameters + private boolean useSSL; + + + public String getConnectionName() { + return connectionName; + } + + public void setConnectionName(String connectionName) { + this.connectionName = connectionName; + } + + public String getDatabaseType() { + return databaseType; + } + + public void setDatabaseType(String databaseType) { + this.databaseType = databaseType; + } + + public String getDatabaseHost() { + return databaseHost; + } + + public void setDatabaseHost(String databaseServer) { + this.databaseHost = databaseServer; + } + + public int getDatabasePort() { + return databasePort; + } + + public void setDatabasePort(int databasePort) { + this.databasePort = databasePort; + } + + public String getDatabaseUser() { + return databaseUser; + } + + public void setDatabaseUser(String databaseUser) { + this.databaseUser = databaseUser; + } + + public String getDatabasePassword() { + return databasePassword; + } + + public void setDatabasePassword(String databasePassword) { + this.databasePassword = databasePassword; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String initialDatabase) { + this.databaseName = initialDatabase; + } + + public String getDatabaseSchema() { + return databaseSchema; + } + + public void setDatabaseSchema(String initialSchema) { + this.databaseSchema = initialSchema; + } + + + + public boolean isUseSSL() { + return useSSL; + } + + public void setUseSSL(boolean useSSL) { + this.useSSL = useSSL; + } + + @Override + public String toString() { + return "DatabaseConfiguration [connectionName=" + connectionName + ", databaseType=" + databaseType + + ", databaseHost=" + databaseHost + ", databasePort=" + databasePort + ", databaseUser=" + databaseUser + + ", databaseName=" + databaseName + ", databaseSchema=" + + databaseSchema + ", useSSL=" + useSSL + "]"; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseImportController.java b/extensions/database/src/com/google/refine/extension/database/DatabaseImportController.java new file mode 100644 index 000000000..0246ee619 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseImportController.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ + +package com.google.refine.extension.database; + +import java.io.IOException; +import java.io.Writer; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.ProjectManager; +import com.google.refine.ProjectMetadata; +import com.google.refine.RefineServlet; +import com.google.refine.commands.HttpUtilities; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseQueryInfo; +import com.google.refine.importers.TabularImportingParserBase; +import com.google.refine.importing.DefaultImportingController; +import com.google.refine.importing.ImportingController; +import com.google.refine.importing.ImportingJob; +import com.google.refine.importing.ImportingManager; +import com.google.refine.model.Project; +import com.google.refine.util.JSONUtilities; +import com.google.refine.util.ParsingUtilities; + + +public class DatabaseImportController implements ImportingController { + + final static Logger logger = LoggerFactory.getLogger("DatabaseImportController"); + protected RefineServlet servlet; + public static int DEFAULT_PREVIEW_LIMIT = 100; + public static String OPTIONS_KEY = "options"; + + @Override + public void init(RefineServlet servlet) { + this.servlet = servlet; + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + HttpUtilities.respond(response, "error", "GET not implemented"); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + logger.info("DatabaseImportController::doPost::databaseName::{}", request.getParameter("databaseName")); + + response.setCharacterEncoding("UTF-8"); + Properties parameters = ParsingUtilities.parseUrlParameters(request); + + String subCommand = parameters.getProperty("subCommand"); + + logger.info("DatabaseImportController::doPost::subCommand::{}", subCommand); + + if ("initialize-parser-ui".equals(subCommand)) { + doInitializeParserUI(request, response, parameters); + } else if ("parse-preview".equals(subCommand)) { + try { + doParsePreview(request, response, parameters); + } catch (DatabaseServiceException e) { + logger.error("DatabaseImportController::doPost::DatabaseServiceException::{}", e); + HttpUtilities.respond(response, "error", e.getMessage()); + } + } else if ("create-project".equals(subCommand)) { + doCreateProject(request, response, parameters); + } else { + HttpUtilities.respond(response, "error", "No such sub command"); + } + + } + + /** + * + * @param request + * @param response + * @param parameters + * @throws ServletException + * @throws IOException + */ + private void doInitializeParserUI(HttpServletRequest request, HttpServletResponse response, Properties parameters) + throws ServletException, IOException { + logger.info("::doInitializeParserUI::"); + + JSONObject result = new JSONObject(); + JSONObject options = new JSONObject(); + JSONUtilities.safePut(result, "status", "ok"); + JSONUtilities.safePut(result, OPTIONS_KEY, options); + + JSONUtilities.safePut(options, "skipDataLines", 0); + JSONUtilities.safePut(options, "storeBlankRows", true); + JSONUtilities.safePut(options, "storeBlankCellsAsNulls", true); + + logger.info("doInitializeParserUI:::{}", result.toString()); + HttpUtilities.respond(response, result.toString()); + + } + + + /** + * doParsePreview + * @param request + * @param response + * @param parameters + * @throws ServletException + * @throws IOException + * @throws DatabaseServiceException + */ + private void doParsePreview( + HttpServletRequest request, HttpServletResponse response, Properties parameters) + throws ServletException, IOException, DatabaseServiceException { + logger.info("DatabaseImportController::doParsePreview::JobID::{}", parameters.getProperty("jobID")); + + long jobID = Long.parseLong(parameters.getProperty("jobID")); + ImportingJob job = ImportingManager.getJob(jobID); + if (job == null) { + HttpUtilities.respond(response, "error", "No such import job"); + return; + } + + DatabaseQueryInfo databaseQueryInfo = getQueryInfo(request); + if(databaseQueryInfo == null) { + HttpUtilities.respond(response, "error", "Invalid or missing Query Info"); + } + + job.updating = true; + try { + JSONObject optionObj = ParsingUtilities.evaluateJsonStringToObject( + request.getParameter("options")); + + List exceptions = new LinkedList(); + + job.prepareNewProject(); + + parsePreview( + databaseQueryInfo, + job.project, + job.metadata, + job, + DEFAULT_PREVIEW_LIMIT , + optionObj, + exceptions + ); + + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + try { + writer.object(); + if (exceptions.size() == 0) { + job.project.update(); // update all internal models, indexes, caches, etc. + writer.key("status"); writer.value("ok"); + } else { + writer.key("status"); writer.value("error"); + writer.key("errors"); + writer.array(); + DefaultImportingController.writeErrors(writer, exceptions); + writer.endArray(); + } + writer.endObject(); + } catch (JSONException e) { + throw new ServletException(e); + } finally { + w.flush(); + w.close(); + } + + } catch (JSONException e) { + throw new ServletException(e); + } finally { + job.touch(); + job.updating = false; + } + } + + /** + * + * @param dbQueryInfo + * @param project + * @param metadata + * @param job + * @param limit + * @param options + * @param exceptions + * @throws DatabaseServiceException + */ + private static void parsePreview( + DatabaseQueryInfo dbQueryInfo, + Project project, + ProjectMetadata metadata, + final ImportingJob job, + int limit, + JSONObject options, + List exceptions) throws DatabaseServiceException{ + + DatabaseService databaseService = DatabaseService.get(dbQueryInfo.getDbConfig().getDatabaseType()); + String querySource = getQuerySource(dbQueryInfo); + + List columns = databaseService.getColumns(dbQueryInfo.getDbConfig(), dbQueryInfo.getQuery()); + + + setProgress(job, querySource, -1); + + JSONUtilities.safePut(options, "ignoreLines", 0); // number of blank lines at the beginning to ignore + JSONUtilities.safePut(options, "headerLines", 1); // number of header lines + + + TabularImportingParserBase.readTable( + project, + metadata, + job, + new DBQueryResultPreviewReader(job, databaseService, querySource, columns, dbQueryInfo, 100), + querySource, + limit, + options, + exceptions + ); + + setProgress(job, querySource, 100); + + } + + + /** + * doCreateProject + * @param request + * @param response + * @param parameters + */ + private void doCreateProject(HttpServletRequest request, HttpServletResponse response, Properties parameters) + throws ServletException, IOException{ + logger.info("DatabaseImportController::doCreateProject:::{}", parameters.getProperty("jobID")); + + long jobID = Long.parseLong(parameters.getProperty("jobID")); + final ImportingJob job = ImportingManager.getJob(jobID); + if (job == null) { + HttpUtilities.respond(response, "error", "No such import job"); + return; + } + + final DatabaseQueryInfo databaseQueryInfo = getQueryInfo(request); + if(databaseQueryInfo == null) { + HttpUtilities.respond(response, "error", "Invalid or missing Query Info"); + } + + job.updating = true; + try { + final JSONObject optionObj = ParsingUtilities.evaluateJsonStringToObject( + request.getParameter("options")); + + final List exceptions = new LinkedList(); + + job.setState("creating-project"); + + final Project project = new Project(); + + new Thread() { + @Override + public void run() { + ProjectMetadata pm = new ProjectMetadata(); + pm.setName(JSONUtilities.getString(optionObj, "projectName", "Untitled")); + pm.setEncoding(JSONUtilities.getString(optionObj, "encoding", "UTF-8")); + + try { + parseCreate( + databaseQueryInfo, + project, + pm, + job, + -1, + optionObj, + exceptions + ); + } catch (DatabaseServiceException e) { + logger.info("DatabaseImportController::doCreateProject:::run{}", e); + // throw new RuntimeException("DatabaseServiceException::", e); + } + + if (!job.canceled) { + if (exceptions.size() > 0) { + job.setError(exceptions); + } else { + project.update(); // update all internal models, indexes, caches, etc. + ProjectManager.singleton.registerProject(project, pm); + job.setState("created-project"); + job.setProjectID(project.id); + // logger.info("DatabaseImportController::doCreateProject:::run::projectID :{}", project.id); + } + + job.touch(); + job.updating = false; + } + } + }.start(); + + HttpUtilities.respond(response, "ok", "done"); + } catch (JSONException e) { + throw new ServletException(e); + } + } + + + /** + * @param dbQueryInfo + * @param project + * @param metadata + * @param job + * @param limit + * @param options + * @param exceptions + * @throws DatabaseServiceException + */ + private static void parseCreate( + DatabaseQueryInfo dbQueryInfo, + Project project, + ProjectMetadata metadata, + final ImportingJob job, + int limit, + JSONObject options, + List exceptions) throws DatabaseServiceException{ + + DatabaseService databaseService = DatabaseService.get(dbQueryInfo.getDbConfig().getDatabaseType()); + String querySource = getQuerySource(dbQueryInfo); + + List columns = databaseService.getColumns(dbQueryInfo.getDbConfig(), dbQueryInfo.getQuery()); + + setProgress(job, querySource, -1); + + JSONUtilities.safePut(options, "ignoreLines", 0); // number of blank lines at the beginning to ignore + JSONUtilities.safePut(options, "headerLines", 1); // number of header lines + + long startTime = System.currentTimeMillis() ; + + TabularImportingParserBase.readTable( + project, + metadata, + job, + new DBQueryResultImportReader(job, databaseService, querySource, columns, dbQueryInfo, getCreateBatchSize()), + querySource, + limit, + options, + exceptions + ); + + long endTime = System.currentTimeMillis() ; + + logger.info("Execution Time: {}", endTime - startTime); + + setProgress(job, querySource, 100); + + } + + private static int getCreateBatchSize() { + String propBatchSize = DatabaseModuleImpl.getImportCreateBatchSize(); + int batchSize = 100; + if(propBatchSize != null && !propBatchSize.isEmpty()) { + try { + batchSize = Integer.parseInt(propBatchSize); + }catch(NumberFormatException nfe) { + + } + } + return batchSize; + } + + /** + * @param request + * @return + */ + private DatabaseQueryInfo getQueryInfo(HttpServletRequest request) { + DatabaseConfiguration jdbcConfig = new DatabaseConfiguration(); + jdbcConfig.setConnectionName(request.getParameter("connectionName")); + jdbcConfig.setDatabaseType(request.getParameter("databaseType")); + jdbcConfig.setDatabaseHost(request.getParameter("databaseServer")); + jdbcConfig.setDatabasePort(Integer.parseInt(request.getParameter("databasePort"))); + jdbcConfig.setDatabaseUser(request.getParameter("databaseUser")); + jdbcConfig.setDatabasePassword(request.getParameter("databasePassword")); + jdbcConfig.setDatabaseName(request.getParameter("initialDatabase")); + jdbcConfig.setDatabaseSchema(request.getParameter("initialSchema")); + + String query = request.getParameter("query"); + + if (jdbcConfig.getDatabaseHost() == null || jdbcConfig.getDatabaseName() == null + || jdbcConfig.getDatabasePassword() == null || jdbcConfig.getDatabaseType() == null + || jdbcConfig.getDatabaseUser() == null || query == null) { + + logger.info("Missing Database Configuration::{}", jdbcConfig); + return null; + } + + return new DatabaseQueryInfo(jdbcConfig, query); + } + + + private static String getQuerySource(DatabaseQueryInfo dbQueryInfo) { + String dbType = dbQueryInfo.getDbConfig().getDatabaseType(); + return DatabaseService.get(dbType).getDatabaseUrl(dbQueryInfo.getDbConfig()); + } + + + private static void setProgress(ImportingJob job, String querySource, int percent) { + job.setProgress(percent, "Reading " + querySource); + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseModuleImpl.java b/extensions/database/src/com/google/refine/extension/database/DatabaseModuleImpl.java new file mode 100644 index 000000000..605c00bbb --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseModuleImpl.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; + +import javax.servlet.ServletConfig; + +import org.json.JSONException; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.Jsonizable; + +import edu.mit.simile.butterfly.ButterflyModuleImpl; + + +public class DatabaseModuleImpl extends ButterflyModuleImpl implements Jsonizable { + + final static Logger logger = LoggerFactory.getLogger("DatabaseModuleImpl"); + + public static DatabaseModuleImpl instance; + + public static Properties extensionProperties; + + + @Override + public void init(ServletConfig config) + throws Exception { + // TODO Auto-generated method stub + super.init(config); + + logger.info("init called in DatabaseModuleImpl: {}", config); + readModuleProperty(); + + // Set the singleton. + instance = this; + + + } + + public static String getImportCreateBatchSize() { + + return extensionProperties.getProperty("create.batchSize", "100"); + } + + public static String getImportPreviewBatchSize() { + + return extensionProperties.getProperty("preview.batchSize", "100"); + } + + private void readModuleProperty() { + // The module path + File f = getPath(); + logger.debug("Module getPath(): {}", f.getPath()); + + // Load our custom properties. + File modFile = new File(f,"MOD-INF"); + logger.debug("Module File: {}", modFile.getPath()); + + if (modFile.exists()) { + + extensionProperties = loadProperties (new File(modFile,"dbextension.properties")); + + } + + } + + private Properties loadProperties(File propFile) { + Properties ps = new Properties(); + try { + if (propFile.exists()) { + logger.debug("Loading Extension properties ({})", propFile); + BufferedInputStream stream = null; + try { + ps = new Properties(); + stream = new BufferedInputStream(new FileInputStream(propFile)); + ps.load(stream); + + } finally { + // Close the stream. + if (stream != null) stream.close(); + } + + } + } catch (Exception e) { + logger.error("Error loading Database properties", e); + } + return ps; + } + + @Override + public void write(JSONWriter writer, Properties options) + throws JSONException { + // TODO Auto-generated method stub + + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseService.java b/extensions/database/src/com/google/refine/extension/database/DatabaseService.java new file mode 100644 index 000000000..0de55650e --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseService.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import java.sql.Connection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.mariadb.MariaDBDatabaseService; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.extension.database.mysql.MySQLDatabaseService; +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + +public abstract class DatabaseService { + + static final Logger logger = LoggerFactory.getLogger("DatabaseService"); + + + public static class DBType { + private static Map databaseServiceMap = new HashMap(); + + static { + try { + + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + DatabaseService.DBType.registerDatabase(PgSQLDatabaseService.DB_NAME, PgSQLDatabaseService.getInstance()); + DatabaseService.DBType.registerDatabase(MariaDBDatabaseService.DB_NAME, MariaDBDatabaseService.getInstance()); + + } catch (Exception e) { + logger.error("Exception occurred while trying to prepare databases!", e); + } + } + + public static void registerDatabase(String name, DatabaseService db) { + + if (!databaseServiceMap.containsKey(name)) { + //throw new DatabaseServiceException(name + " cannot be registered. Database Type already exists"); + databaseServiceMap.put(name, db); + logger.info(String.format("Registered %s Database", name)); + }else { + logger.info(name + " Database Type already exists"); + } + + } + + public static DatabaseService getJdbcServiceFromType(String name) { + return databaseServiceMap.get(name); + } + + } + + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); + } + + /** + * get Database + * @param dbType + * @return + */ + public static DatabaseService get(String dbType) { + logger.info("get called on DatabaseService with, {}", dbType); + DatabaseService databaseService = DatabaseService.DBType.getJdbcServiceFromType(dbType.toLowerCase()); + + logger.debug("DatabaseService found: {}", databaseService.getClass()); + return databaseService; + + } + + + //Database Service APIs + public abstract Connection getConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException; + + public abstract boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException; + + public abstract DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException; + + public abstract DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + + public abstract DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + + public abstract String buildLimitQuery(Integer limit, Integer offset, String query); + + public abstract List getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + + public abstract List getRows(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException; + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseServiceException.java b/extensions/database/src/com/google/refine/extension/database/DatabaseServiceException.java new file mode 100644 index 000000000..cb37f9040 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseServiceException.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import java.sql.SQLException; + +public class DatabaseServiceException extends Exception { + + + private static final long serialVersionUID = 1L; + + private boolean sqlException; + private String sqlState; + private int sqlCode; + + + public DatabaseServiceException(String exception) { + super(exception); + } + + + public DatabaseServiceException(boolean sqlException, String sqlState, int sqlCode, String message) { + super(message); + this.sqlException = sqlException; + this.sqlState = sqlState; + this.sqlCode = sqlCode; + + } + + + public boolean isSqlException() { + return sqlException; + } + + + public void setSqlException(boolean sqlException) { + this.sqlException = sqlException; + } + + + public String getSqlState() { + return sqlState; + } + + + public void setSqlState(String sqlState) { + this.sqlState = sqlState; + } + + + public int getSqlCode() { + return sqlCode; + } + + + public void setSqlCode(int sqlCode) { + this.sqlCode = sqlCode; + } + + public DatabaseServiceException(String string, SQLException e) { + super(string, e); + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java b/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java new file mode 100644 index 000000000..d450b7691 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/DatabaseUtils.java @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.ProjectManager; +import com.google.refine.io.FileProjectManager; + +public class DatabaseUtils { + + final static Logger logger = LoggerFactory.getLogger("DatabaseUtils"); + + + private final static String DATABASE_EXTENSION_DIR = "dbextension"; + private final static String SETTINGS_FILE_NAME = ".saved-db-connections.json"; + public final static String SAVED_CONNECTION_KEY = "savedConnections"; + + private static SimpleTextEncryptor textEncryptor = new SimpleTextEncryptor("Aa1Gb@tY7_Y"); + + /** + * GET saved connections + * @return + */ + public static List getSavedConnections() { + ObjectMapper mapper = new ObjectMapper(); + try { + String filename = getExtensionFilePath(); + + File file = new File(filename); + if (!file.exists()) { + logger.info("saved connections file not found, creating new: {}", filename); + + String dirPath = getExtensionFolder(); + File dirFile = new File(dirPath); + boolean dirExists = true; + if(!dirFile.exists()) { + dirExists = dirFile.mkdir(); + } + + if(dirExists) { + + SavedConnectionContainer sc = new SavedConnectionContainer(new ArrayList()); + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(filename), sc); + return sc.getSavedConnections(); + //return decryptAll(sc.getSavedConnections()); + + } + + } + logger.info("saved connections file found {}", filename); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(filename), SavedConnectionContainer.class); + //return decryptAll(savedConnectionContainer.getSavedConnections()); + return savedConnectionContainer.getSavedConnections(); + + } catch (JsonParseException e) { + logger.error("JsonParseException: {}", e); + } catch (JsonMappingException e) { + logger.error("JsonMappingException: {}", e); + } catch (IOException e) { + logger.error("IOException: {}", e); + } + return null; + } + + + + + + /** + * GET one saved connection + * @param connectionName + * @return + */ + public static DatabaseConfiguration getSavedConnection(String connectionName) { + logger.info("get saved connection called with connectionName: {}", connectionName); + List savedConfigurations = getSavedConnections(); + + for (DatabaseConfiguration dc : savedConfigurations) { + logger.info("Saved Connection : {}", dc.getConnectionName()); + if (dc.getConnectionName().equalsIgnoreCase(connectionName.trim())) { + logger.info("Saved Connection Found : {}", dc); + //dc.setDatabasePassword(decrypt(dc.getDatabasePassword())); + return dc; + } + } + + return null; + } + + public static String encrypt(String plainPassword) { + return textEncryptor.encrypt(plainPassword); + } + + public static String decrypt(String encodedPassword) { + return textEncryptor.decrypt(encodedPassword); + } + + public static List decryptAll(List savedConnections) { + List dbConfigs = new ArrayList(savedConnections.size()); + + for(DatabaseConfiguration d: savedConnections) { + d.setDatabasePassword(decrypt(d.getDatabasePassword())); + dbConfigs.add(d); + + } + return dbConfigs; + } + + + /** + * ADD to saved connections + * @param dbConfig + */ + public static void addToSavedConnections(DatabaseConfiguration dbConfig){ + + try { + +// String encPassword = encrypt(dbConfig.getDatabasePassword()); +// dbConfig.setDatabasePassword(encPassword); + + ObjectMapper mapper = new ObjectMapper(); + String savedConnectionFile = getExtensionFilePath(); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(savedConnectionFile), SavedConnectionContainer.class); + savedConnectionContainer.getSavedConnections().add(dbConfig); + + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(savedConnectionFile), savedConnectionContainer); + + } catch (JsonGenerationException e1) { + logger.error("JsonGenerationException: {}", e1); + e1.printStackTrace(); + } catch (JsonMappingException e1) { + logger.error("JsonMappingException: {}", e1); + e1.printStackTrace(); + } catch (IOException e1) { + logger.error("IOException: {}", e1); + e1.printStackTrace(); + } + } + + /** + * DELETE saved connections + * @param connectionName + */ + public static void deleteSavedConnections(String connectionName) { + + logger.info("deleteSavedConnections called with: {}", connectionName); + + try { + + List savedConnections = getSavedConnections(); + logger.info("Size before delete SavedConnections :: {}", savedConnections.size()); + + ArrayList newSavedConns = new ArrayList(); + for(DatabaseConfiguration dc: savedConnections) { + if(!dc.getConnectionName().equalsIgnoreCase(connectionName.trim())) { + newSavedConns.add(dc); + } + + } + + ObjectMapper mapper = new ObjectMapper(); + String savedConnectionFile = getExtensionFilePath(); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(savedConnectionFile), SavedConnectionContainer.class); + savedConnectionContainer.setSavedConnections(newSavedConns); + + logger.info("Size after delete SavedConnections :: {}", savedConnectionContainer.getSavedConnections().size()); + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(savedConnectionFile), savedConnectionContainer); + + } catch (JsonGenerationException e1) { + logger.error("JsonGenerationException: {}", e1); + e1.printStackTrace(); + } catch (JsonMappingException e1) { + logger.error("JsonMappingException: {}", e1); + e1.printStackTrace(); + } catch (IOException e1) { + logger.error("IOException: {}", e1); + e1.printStackTrace(); + } + } + + /** + * EDIT saved connections + * @param jdbcConfig + */ + public static void editSavedConnections(DatabaseConfiguration jdbcConfig) { + logger.info("Edit SavedConnections called with: {}", jdbcConfig); + + + try { + ObjectMapper mapper = new ObjectMapper(); + String savedConnectionFile = getExtensionFilePath(); + SavedConnectionContainer savedConnectionContainer = mapper.readValue(new File(savedConnectionFile), SavedConnectionContainer.class); + + List savedConnections = savedConnectionContainer.getSavedConnections(); + + ListIterator savedConnArrayIter = (ListIterator) savedConnections.listIterator(); + + while (savedConnArrayIter.hasNext()) { + DatabaseConfiguration sc = (DatabaseConfiguration) savedConnArrayIter.next(); + + if (sc.getConnectionName().equals(jdbcConfig.getConnectionName())) { + savedConnArrayIter.remove(); + } + + } + + savedConnections.add(jdbcConfig); + savedConnectionContainer.setSavedConnections(savedConnections); + + mapper.writerWithDefaultPrettyPrinter().writeValue(new File(savedConnectionFile), savedConnectionContainer); + + } catch (JsonGenerationException e1) { + logger.error("JsonGenerationException: {}", e1); + e1.printStackTrace(); + } catch (JsonMappingException e1) { + logger.error("JsonMappingException: {}", e1); + e1.printStackTrace(); + } catch (IOException e1) { + logger.error("IOException: {}", e1); + e1.printStackTrace(); + } + } + + public static String getExtensionFilePath(){ + File dir = ((FileProjectManager) ProjectManager.singleton).getWorkspaceDir(); + String fileSep = System.getProperty("file.separator"); + String filename = dir.getPath() + fileSep + DATABASE_EXTENSION_DIR + fileSep + SETTINGS_FILE_NAME; + return filename; + } + public static String getExtensionFolder(){ + File dir = ((FileProjectManager) ProjectManager.singleton).getWorkspaceDir(); + String fileSep = System.getProperty("file.separator"); + String filename = dir.getPath() + fileSep + DATABASE_EXTENSION_DIR; + return filename; + } + + public static DatabaseColumnType getDbColumnType(int dbType) { + + switch (dbType) { + case java.sql.Types.BIGINT: + return DatabaseColumnType.NUMBER; + case java.sql.Types.FLOAT: + return DatabaseColumnType.STRING; + case java.sql.Types.REAL: + return DatabaseColumnType.STRING; + case java.sql.Types.DOUBLE: + return DatabaseColumnType.STRING; + case java.sql.Types.NUMERIC: + return DatabaseColumnType.NUMBER; + case java.sql.Types.DECIMAL: + return DatabaseColumnType.STRING; + case java.sql.Types.CHAR: + return DatabaseColumnType.STRING; + case java.sql.Types.VARCHAR: + return DatabaseColumnType.STRING; + case java.sql.Types.LONGVARCHAR: + return DatabaseColumnType.STRING; + case java.sql.Types.DATE: + return DatabaseColumnType.DATETIME; + case java.sql.Types.TIME: + return DatabaseColumnType.DATETIME; + case java.sql.Types.TIMESTAMP: + return DatabaseColumnType.DATETIME; + case java.sql.Types.BINARY: + return DatabaseColumnType.STRING; + case java.sql.Types.VARBINARY: + return DatabaseColumnType.STRING; + case java.sql.Types.LONGVARBINARY: + return DatabaseColumnType.STRING; + case java.sql.Types.NULL: + return DatabaseColumnType.STRING; + case java.sql.Types.OTHER: + return DatabaseColumnType.STRING; + case java.sql.Types.JAVA_OBJECT: + return DatabaseColumnType.STRING; + case java.sql.Types.DISTINCT: + return DatabaseColumnType.STRING; + case java.sql.Types.STRUCT: + return DatabaseColumnType.STRING; + case java.sql.Types.ARRAY: + return DatabaseColumnType.STRING; + case java.sql.Types.BLOB: + return DatabaseColumnType.STRING; + case java.sql.Types.CLOB: + return DatabaseColumnType.STRING; + case java.sql.Types.REF: + return DatabaseColumnType.STRING; + default: + return DatabaseColumnType.STRING; + } + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/SQLType.java b/extensions/database/src/com/google/refine/extension/database/SQLType.java new file mode 100644 index 000000000..2871b6abd --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/SQLType.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + + + +import java.util.HashMap; +import java.util.Map; + + +public final class SQLType { + + private static final Map jdbcDriverRegistry = new HashMap(); + private final DriverContainer driverContainer; + + private SQLType(DriverContainer container) { + this.driverContainer = container; + } + + public static SQLType forName(String name) { + for (SQLType sqlType : jdbcDriverRegistry.values()) { + if (sqlType.getIdentifier().equalsIgnoreCase(name)) { + return sqlType; + } + } + return null; + } + + public static SQLType registerSQLDriver(String identifier, String classpath) { + return registerSQLDriver(identifier, classpath, true); + } + + public static SQLType registerSQLDriver(String identifier, String classpath, boolean useJDBCManager) { + DriverContainer driverContainer = new DriverContainer(identifier, classpath, useJDBCManager); + if (!jdbcDriverRegistry.containsKey(driverContainer)) { + SQLType newType = new SQLType(driverContainer); + jdbcDriverRegistry.put(driverContainer, newType); + return newType; + } + return null; + } + + + public String getClassPath() { + return this.driverContainer.classpath; + } + + public String getIdentifier() { + return this.driverContainer.identifier; + } + + public boolean usesJDBCManager() { + return this.driverContainer.useJDBCManager; + } + + + private static class DriverContainer { + + public final String classpath; + public final String identifier; + public final boolean useJDBCManager; + + private DriverContainer(String identifier, String classpath, boolean useJDBCManager) { + this.classpath = classpath; + this.identifier = identifier; + this.useJDBCManager = useJDBCManager; + } + + public final boolean equals(Object obj) { + return obj instanceof DriverContainer && ((DriverContainer) obj).classpath.equals(this.classpath) + && ((DriverContainer) obj).identifier.equals(this.identifier) + && ((DriverContainer) obj).useJDBCManager == this.useJDBCManager; + } + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/SavedConnectionContainer.java b/extensions/database/src/com/google/refine/extension/database/SavedConnectionContainer.java new file mode 100644 index 000000000..c63e4247e --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/SavedConnectionContainer.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import java.util.List; + +public class SavedConnectionContainer { + + public List getSavedConnections() { + return savedConnections; + } + + + public void setSavedConnections(List savedConnections) { + this.savedConnections = savedConnections; + } + + List savedConnections; + + public SavedConnectionContainer(List savedConnections) { + super(); + this.savedConnections = savedConnections; + } + + + public SavedConnectionContainer() { + + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/SimpleTextEncryptor.java b/extensions/database/src/com/google/refine/extension/database/SimpleTextEncryptor.java new file mode 100644 index 000000000..ad5f20d95 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/SimpleTextEncryptor.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database; + +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.util.text.TextEncryptor; + + +public class SimpleTextEncryptor implements TextEncryptor { + + private final StandardPBEStringEncryptor encryptor; + + @Override + public String decrypt(String encryptedMessage) { + return this.encryptor.decrypt(encryptedMessage); + } + + @Override + public String encrypt(String message) { + return this.encryptor.encrypt(message); + } + + public SimpleTextEncryptor(String passwordChars) { + super(); + + this.encryptor = new StandardPBEStringEncryptor(); + this.encryptor.setAlgorithm("PBEWithMD5AndDES"); + this.encryptor.setPasswordCharArray(passwordChars.toCharArray()); + } + + + + public void setPassword(final String password) { + this.encryptor.setPassword(password); + } + + + /** + * Sets a password, as a char[] + * + * @since 1.8 + * @param password the password to be set. + */ + public void setPasswordCharArray(final char[] password) { + this.encryptor.setPasswordCharArray(password); + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java new file mode 100644 index 000000000..9ba5ddc2e --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.codehaus.jackson.map.ObjectMapper; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//import com.google.refine.ProjectManager; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseInfo; + + +public class ConnectCommand extends DatabaseCommand { + + static final Logger logger = LoggerFactory.getLogger("ConnectCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + logger.info("ConnectCommand::Post"); + DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request); + + logger.info("ConnectCommand::Post::{}", databaseConfiguration); + + // ProjectManager.singleton.setBusy(true); + try { + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + ObjectMapper mapperObj = new ObjectMapper(); + + try { + DatabaseInfo databaseInfo = DatabaseService.get(databaseConfiguration.getDatabaseType()) + .connect(databaseConfiguration); + response.setStatus(HttpStatus.SC_OK); + writer.object(); + writer.key("code"); + writer.value("ok"); + String databaseInfoString = mapperObj.writeValueAsString(databaseInfo); + writer.key("databaseInfo"); + writer.value(databaseInfoString); + + writer.endObject(); + + } catch (DatabaseServiceException e) { + logger.error("ConnectCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_UNAUTHORIZED,response, writer, e); + }catch (Exception e) { + logger.error("ConnectCommand::Post::Exception::{}", e); + sendError(HttpStatus.SC_UNAUTHORIZED,response, writer, e); + } finally { + // w.flush(); + w.close(); + } + } catch (Exception e) { + logger.error("ConnectCommand::Post::Exception::{}", e); + throw new ServletException(e); + } finally { + // ProjectManager.singleton.setBusy(false); + } + + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/DatabaseCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/DatabaseCommand.java new file mode 100644 index 000000000..87cdd3674 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/DatabaseCommand.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.commands.Command; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; + +public abstract class DatabaseCommand extends Command { + + final static protected Logger logger = LoggerFactory.getLogger("jdbcCommand"); + /** + * + * @param request + * @return + */ + protected DatabaseConfiguration getJdbcConfiguration(HttpServletRequest request) { + DatabaseConfiguration jdbcConfig = new DatabaseConfiguration(); + + jdbcConfig.setConnectionName(request.getParameter("connectionName")); + jdbcConfig.setDatabaseType(request.getParameter("databaseType")); + jdbcConfig.setDatabaseHost(request.getParameter("databaseServer")); + jdbcConfig.setDatabasePort(Integer.parseInt(request.getParameter("databasePort"))); + jdbcConfig.setDatabaseUser(request.getParameter("databaseUser")); + jdbcConfig.setDatabasePassword(request.getParameter("databasePassword")); + jdbcConfig.setDatabaseName(request.getParameter("initialDatabase")); + jdbcConfig.setDatabaseSchema(request.getParameter("initialSchema")); + + return jdbcConfig; + } + /** + * + * @param status + * @param response + * @param writer + * @param e + * @throws IOException + */ + protected void sendError(int status, HttpServletResponse response, JSONWriter writer, Exception e) + throws IOException { + + //logger.info("sendError::{}", writer); + response.sendError(status, e.getMessage()); + + } + /** + * + * @param status + * @param response + * @param writer + * @param e + * @throws IOException + */ + protected void sendError(int status, HttpServletResponse response, JSONWriter writer, DatabaseServiceException e) + throws IOException { + + String message = ""; + + if(e.getSqlState() != null) { + + message = message + "SqlCode:" + e.getSqlCode() + "SqlState" + e.getSqlState(); + } + + message = message + e.getMessage(); + + response.sendError(status, e.getMessage()); + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java new file mode 100644 index 000000000..79b831f44 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.codehaus.jackson.map.ObjectMapper; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//import com.google.refine.ProjectManager; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseInfo; + + +public class ExecuteQueryCommand extends DatabaseCommand { + + static final Logger logger = LoggerFactory.getLogger("QueryCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + logger.info("QueryCommand::Post"); + + DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request); + String query = request.getParameter("queryString"); + logger.info("QueryCommand::Post::DatabaseConfiguration::{}::Query::{} " ,databaseConfiguration, query); + + //ProjectManager.singleton.setBusy(true); + try { + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + + try { + DatabaseInfo databaseInfo = DatabaseService.get(databaseConfiguration.getDatabaseType()) + .executeQuery(databaseConfiguration, query); + ObjectMapper mapperObj = new ObjectMapper(); + + response.setStatus(HttpStatus.SC_OK); + String jsonStr = mapperObj.writeValueAsString(databaseInfo); + logger.info("QueryCommand::Post::Result::{} " ,jsonStr); + + writer.object(); + writer.key("code"); writer.value("ok"); + writer.key("QueryResult"); + writer.value(jsonStr); + writer.endObject(); + + + } catch (DatabaseServiceException e) { + logger.error("QueryCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST, response, writer, e); + + } catch (Exception e) { + logger.error("QueryCommand::Post::Exception::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST,response, writer, e); + } finally { + w.close(); + } + } catch (Exception e) { + logger.error("QueryCommand::Post::Exception::{}", e); + throw new ServletException(e); + } finally { + // ProjectManager.singleton.setBusy(false); + } + + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java new file mode 100644 index 000000000..1c64d563b --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.json.JSONException; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseUtils; + + +public class SavedConnectionCommand extends DatabaseCommand { + + static final Logger logger = LoggerFactory.getLogger("SavedConnectionCommand"); + + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + logger.info("SavedConnectionCommand::Get::connectionName::{}", request.getParameter("connectionName")); + + String connectionName = request.getParameter("connectionName"); + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + if(connectionName == null || connectionName.isEmpty()) { + writeSavedConnectionResponse(response); + }else { + + DatabaseConfiguration savedConnection = DatabaseUtils.getSavedConnection(connectionName); + writeSavedConnectionResponse(response, savedConnection); + + } + + } catch (Exception e) { + logger.error("Exception while loading settings {}", e); + } + } + + + @Override + public void doDelete(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + logger.info("SavedConnectionCommand::Delete Connection: {}", request.getParameter("connectionName")); + + String connectionName = request.getParameter("connectionName"); + + DatabaseConfiguration savedConn = DatabaseUtils.getSavedConnection(connectionName); + if(savedConn == null) { + logger.error("Connection With name:: {} does not exist!", request.getParameter("connectionName")); + response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection with name " + connectionName + " does not exists!"); + response.flushBuffer(); + return; + } + + try { + + DatabaseUtils.deleteSavedConnections(connectionName); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + writeSavedConnectionResponse(response); + + } catch (Exception e) { + logger.error("Exception while Deleting Connection with name: {}, error:{}",connectionName, e); + } + } + + /** + * + * @param response + * @param savedConnection + * @throws IOException + * @throws JSONException + */ + private void writeSavedConnectionResponse(HttpServletResponse response, DatabaseConfiguration savedConnection) throws IOException, JSONException { + Writer w = response.getWriter(); + try { + JSONWriter writer = new JSONWriter(w); + + writer.object(); + writer.key(DatabaseUtils.SAVED_CONNECTION_KEY); + writer.array(); + + writer.object(); + writer.key("connectionName"); + writer.value(savedConnection.getConnectionName()); + + writer.key("databaseType"); + writer.value(savedConnection.getDatabaseType()); + + writer.key("databaseHost"); + writer.value(savedConnection.getDatabaseHost()); + + writer.key("databasePort"); + writer.value(savedConnection.getDatabasePort()); + + writer.key("databaseName"); + writer.value(savedConnection.getDatabaseName()); + + writer.key("databasePassword"); + + // + String dbPasswd = savedConnection.getDatabasePassword(); + if(dbPasswd != null && !dbPasswd.isEmpty()) { + dbPasswd = DatabaseUtils.decrypt(savedConnection.getDatabasePassword()); + //logger.info("Decrypted Password::" + dbPasswd); + } + writer.value(dbPasswd); + // + + // writer.value(savedConnection.getDatabasePassword()); + + writer.key("databaseSchema"); + writer.value(savedConnection.getDatabaseSchema()); + + writer.key("databaseUser"); + writer.value(savedConnection.getDatabaseUser()); + + writer.endObject(); + writer.endArray(); + + writer.endObject(); + + }finally { + w.flush(); + w.close(); + } + + } + /** + * + * @param response + * @throws IOException + * @throws JSONException + */ + private void writeSavedConnectionResponse(HttpServletResponse response) throws IOException, JSONException { + Writer w = response.getWriter(); + try { + + List savedConnections = DatabaseUtils.getSavedConnections(); + JSONWriter writer = new JSONWriter(w); + + writer.object(); + writer.key(DatabaseUtils.SAVED_CONNECTION_KEY); + writer.array(); + + int size = savedConnections.size(); + + for (int i = 0; i < size; i++) { + + writer.object(); + DatabaseConfiguration dbConfig = (DatabaseConfiguration) savedConnections.get(i); + + writer.key("connectionName"); + writer.value(dbConfig.getConnectionName()); + + writer.key("databaseType"); + writer.value(dbConfig.getDatabaseType()); + + writer.key("databaseHost"); + writer.value(dbConfig.getDatabaseHost()); + + writer.key("databasePort"); + writer.value(dbConfig.getDatabasePort()); + + writer.key("databaseName"); + writer.value(dbConfig.getDatabaseName()); + + writer.key("databasePassword"); + + String dbPasswd = dbConfig.getDatabasePassword(); + if(dbPasswd != null && !dbPasswd.isEmpty()) { + dbPasswd = DatabaseUtils.decrypt(dbConfig.getDatabasePassword()); + } + // writer.value(dbConfig.getDatabasePassword()); + writer.value(dbPasswd); + + writer.key("databaseSchema"); + writer.value(dbConfig.getDatabaseSchema()); + + writer.key("databaseUser"); + writer.value(dbConfig.getDatabaseUser()); + + writer.endObject(); + + } + writer.endArray(); + writer.endObject(); + logger.info("Saved Connection Get Response sent"); + } finally { + w.flush(); + w.close(); + } + } + + /** + * Add a new Saved JDBC connection configuration + */ + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + // TODO Auto-generated method stub + logger.info("SavedConnectionCommand::Post"); + + DatabaseConfiguration jdbcConfig = getJdbcConfiguration(request); + + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + if(jdbcConfig.getConnectionName() == null) { + response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection Name is Required!"); + response.flushBuffer(); + return; + } + + DatabaseConfiguration savedConn = DatabaseUtils.getSavedConnection(jdbcConfig.getConnectionName()); + if(savedConn != null) { + response.sendError(HttpStatus.SC_BAD_REQUEST, "Connection with name " + jdbcConfig.getConnectionName() + " already exists!"); + response.flushBuffer(); + return; + } + + + if(jdbcConfig.getDatabasePassword() != null) { + logger.info("SavedConnectionCommand::Post::password::{}", jdbcConfig.getDatabasePassword()); + jdbcConfig.setDatabasePassword(DatabaseUtils.encrypt(jdbcConfig.getDatabasePassword())); + } + + DatabaseUtils.addToSavedConnections(jdbcConfig); + + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + writeSavedConnectionResponse(response); + } catch (Exception e) { + logger.error("Exception while loading settings {}", e); + } + + } + + + + @Override + public void doPut(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + logger.info("SavedConnectionCommand::Put::databaseType " + request.getParameter("databaseType")); + + DatabaseConfiguration jdbcConfig = getJdbcConfiguration(request); + StringBuilder sb = new StringBuilder(); + boolean error = false; + if(jdbcConfig.getConnectionName() == null) { + sb.append("Connection Name, "); + error = true; + } + if(jdbcConfig.getDatabaseHost() == null) { + sb.append("Database Host, "); + error = true; + } + if(jdbcConfig.getDatabaseUser() == null) { + sb.append("Database User, "); + error = true; + } + if(jdbcConfig.getDatabaseName() == null) { + sb.append("Database Name, "); + error = true; + } + if(error) { + sb.append(" is missing"); + logger.info("Connection Parameter errors::{}", sb.toString()); + response.sendError(HttpStatus.SC_BAD_REQUEST, sb.toString()); + } + + + logger.info("SavedConnectionCommand::PUT Connection: {}", jdbcConfig.getConnectionName()); + + if(jdbcConfig.getDatabasePassword() != null) { + jdbcConfig.setDatabasePassword(DatabaseUtils.encrypt(jdbcConfig.getDatabasePassword())); + } + + DatabaseUtils.editSavedConnections(jdbcConfig); + + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + writeSavedConnectionResponse(response); + + } catch (Exception e) { + logger.error("Exception while loading settings {}", e); + } + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java new file mode 100644 index 000000000..a83af22e3 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + + + +public class TestConnectCommand extends DatabaseCommand { + + static final Logger logger = LoggerFactory.getLogger("TestConnectCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + logger.info("TestConnectCommand::Post"); + DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request); + logger.info("TestConnectCommand::Post::{}", databaseConfiguration); + + //ProjectManager.singleton.setBusy(true); + try { + + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + + try { + + boolean connectionTestResult = DatabaseService.get(databaseConfiguration.getDatabaseType()) + .testConnection(databaseConfiguration); + + response.setStatus(HttpStatus.SC_OK); + writer.object(); + + writer.key("connectionResult"); + writer.value(connectionTestResult); + writer.key("code"); + writer.value("ok"); + writer.endObject(); + + } catch (DatabaseServiceException e) { + logger.error("TestConnectCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_UNAUTHORIZED,response, writer, e); + } finally { + // writer.endObject(); + // w.flush(); + w.close(); + } + } catch (Exception e) { + logger.error("TestConnectCommand::Post::Exception::{}", e); + throw new ServletException(e); + } finally { + //ProjectManager.singleton.setBusy(false); + } + + + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java new file mode 100644 index 000000000..588758744 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.codehaus.jackson.map.ObjectMapper; +import org.json.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//import com.google.refine.ProjectManager; +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.model.DatabaseInfo; + + +public class TestQueryCommand extends DatabaseCommand { + + static final Logger logger = LoggerFactory.getLogger("TestQueryCommand"); + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + logger.info("TestQueryCommand::Post"); + + DatabaseConfiguration dbConfig = getJdbcConfiguration(request); + String query = request.getParameter("query"); + logger.info("TestQueryCommand::Post::DatabaseConfiguration::{}::Query::{} " ,dbConfig, query); + + //ProjectManager.singleton.setBusy(true); + try { + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); + Writer w = response.getWriter(); + JSONWriter writer = new JSONWriter(w); + + try { + DatabaseInfo databaseInfo = DatabaseService.get(dbConfig.getDatabaseType()) + .testQuery(dbConfig, query); + ObjectMapper mapperObj = new ObjectMapper(); + + response.setStatus(HttpStatus.SC_OK); + String jsonStr = mapperObj.writeValueAsString(databaseInfo); + logger.info("TestQueryCommand::Post::Result::{} " ,jsonStr); + + writer.object(); + writer.key("code"); + writer.value("ok"); + writer.key("QueryResult"); + writer.value(jsonStr); + writer.endObject(); + + + } catch (DatabaseServiceException e) { + logger.error("TestQueryCommand::Post::DatabaseServiceException::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST, response, writer, e); + + } catch (Exception e) { + logger.error("TestQueryCommand::Post::Exception::{}", e); + sendError(HttpStatus.SC_BAD_REQUEST,response, writer, e); + } finally { + w.close(); + } + } catch (Exception e) { + logger.error("TestQueryCommand::Post::Exception::{}", e); + throw new ServletException(e); + } finally { + // ProjectManager.singleton.setBusy(false); + } + + + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java new file mode 100644 index 000000000..ef3228ac3 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.mariadb; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.SQLType; + + + +public class MariaDBConnectionManager { + + static final Logger logger = LoggerFactory.getLogger("MariaDBConnectionManager"); + private Connection connection; + private SQLType type; + + private static MariaDBConnectionManager instance; + + /** + * + * @param type + * @param databaseConfiguration + * @throws SQLException + */ + private MariaDBConnectionManager() { + type = SQLType.forName(MariaDBDatabaseService.DB_NAME); + + } + + + + + /** + * Create a new instance of this connection manager. + * + * @return an instance of the manager + * + * @throws DatabaseServiceException + */ + public static MariaDBConnectionManager getInstance() throws DatabaseServiceException { + if (instance == null) { + logger.info("::Creating new MariaDB Connection Manager ::"); + instance = new MariaDBConnectionManager(); + + } + return instance; + } + + + /** + * Get the SQL Database type. + * + * @return the type + */ + public SQLType getType() { + return this.type; + } + + /** + * testConnection + * @param databaseConfiguration + * @return + */ + public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException{ + + try { + boolean connResult = false; + + Connection conn = getConnection(databaseConfiguration, true); + if(conn != null) { + connResult = true; + conn.close(); + } + + return connResult; + + } + catch (SQLException e) { + logger.error("Test connection Failed!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + /** + * Get a connection form the connection pool. + * + * @return connection from the pool + */ + public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection) throws DatabaseServiceException{ + try { + + logger.info("connection::{}, forceNewConnection: {}", connection, forceNewConnection); + + if (connection != null && !forceNewConnection) { + logger.info("connection closed::{}", connection.isClosed()); + if (!connection.isClosed()) { + logger.info("Returning existing connection::{}", connection); + return connection; + } + } + + Class.forName(type.getClassPath()); + DriverManager.setLoginTimeout(10); + String dbURL = getDatabaseUrl(databaseConfiguration); + connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), + databaseConfiguration.getDatabasePassword()); + + logger.info("*** Acquired New connection for ::{} **** ", dbURL); + + return connection; + + + } catch (ClassNotFoundException e) { + logger.error("Jdbc Driver not found", e); + throw new DatabaseServiceException(e.getMessage()); + } catch (SQLException e) { + logger.error("SQLException::Couldn't get a Connection!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + + public void shutdown() { + + if (connection != null) { + try { + connection.close(); + } + catch (SQLException e) { + logger.warn("Non-Managed connection could not be closed. Whoops!", e); + } + } + + } + + + + private static String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); + + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBDatabaseService.java b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBDatabaseService.java new file mode 100644 index 000000000..72ff30c1f --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBDatabaseService.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.mariadb; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.mariadb.jdbc.MariaDbResultSetMetaData; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.DatabaseUtils; +import com.google.refine.extension.database.SQLType; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.extension.database.mysql.MySQLConnectionManager; + + + +public class MariaDBDatabaseService extends DatabaseService { + + static final Logger logger = LoggerFactory.getLogger("MariaDBDatabaseService"); + + public static final String DB_NAME = "mariadb"; + public static final String DB_DRIVER = "org.mariadb.jdbc.Driver"; + + private static MariaDBDatabaseService instance; + + private MariaDBDatabaseService() { + } + + public static MariaDBDatabaseService getInstance() { + if (instance == null) { + SQLType.registerSQLDriver(DB_NAME, DB_DRIVER); + instance = new MariaDBDatabaseService(); + logger.debug("MariaDBDatabaseService Instance: {}", instance); + } + return instance; + } + + @Override + public boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return MariaDBConnectionManager.getInstance().testConnection(dbConfig); + + } + + @Override + public DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return getMetadata(dbConfig); + } + + + @Override + public DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + + try { + Connection connection = MariaDBConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + MariaDbResultSetMetaData metadata = (MariaDbResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn( + metadata.getColumnName(i), + metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), + metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + DatabaseInfo dbInfo = new DatabaseInfo(); + dbInfo.setColumns(columns); + dbInfo.setRows(rows); + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + MariaDBConnectionManager.getInstance().shutdown(); + } + } + + /** + * + * @param connectionInfo + * @return + * @throws DatabaseServiceException + */ + private DatabaseInfo getMetadata(DatabaseConfiguration connectionInfo) throws DatabaseServiceException { + + try { + Connection connection = MariaDBConnectionManager.getInstance().getConnection(connectionInfo, true); + if(connection != null) { + java.sql.DatabaseMetaData metadata; + + metadata = connection.getMetaData(); + + int dbMajorVersion = metadata.getDatabaseMajorVersion(); + int dbMinorVersion = metadata.getDatabaseMinorVersion(); + String dbProductVersion = metadata.getDatabaseProductVersion(); + String dbProductName = metadata.getDatabaseProductName(); + DatabaseInfo dbInfo = new DatabaseInfo(); + + dbInfo.setDatabaseMajorVersion(dbMajorVersion); + dbInfo.setDatabaseMinorVersion(dbMinorVersion); + dbInfo.setDatabaseProductVersion(dbProductVersion); + dbInfo.setDatabaseProductName(dbProductName); + return dbInfo; + } + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + return null; + + } + + @Override + public String buildLimitQuery(Integer limit, Integer offset, String query) { + StringBuilder sb = new StringBuilder(); + sb.append(query); + + if(limit != null) { + sb.append(" LIMIT" + " " + limit); + } + + if(offset != null) { + sb.append(" OFFSET" + " " + offset); + } + + return sb.toString(); + } + + @Override + public ArrayList getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + try { + + Connection connection = MariaDBConnectionManager.getInstance().getConnection(dbConfig, true); + Statement statement = connection.createStatement(); + + ResultSet queryResult = statement.executeQuery(query); + MariaDbResultSetMetaData metadata = (MariaDbResultSetMetaData) queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn(metadata.getColumnName(i), metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + + return columns; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + @Override + public List getRows(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + + + try { + Connection connection = MariaDBConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + MariaDbResultSetMetaData metadata = (MariaDbResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + return rows; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + @Override + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } + + @Override + public Connection getConnection(DatabaseConfiguration dbConfig) + throws DatabaseServiceException { + // TODO Auto-generated method stub + return MariaDBConnectionManager.getInstance().getConnection(dbConfig, true); + } + + @Override + public DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + Statement statement = null; + ResultSet queryResult = null; + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + statement = connection.createStatement(); + queryResult = statement.executeQuery(query); + + DatabaseInfo dbInfo = new DatabaseInfo(); + + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + + } + if (statement != null) { + statement.close(); + + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + MySQLConnectionManager.getInstance().shutdown(); + } + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseColumn.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseColumn.java new file mode 100644 index 000000000..bf8972c60 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseColumn.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.model; + +import com.google.refine.extension.database.DatabaseColumnType; + +public class DatabaseColumn { + + + private String name; + private int size; + private DatabaseColumnType type; + + public DatabaseColumnType getType() { + return type; + } + + + + public void setType(DatabaseColumnType type) { + this.type = type; + } + + + + public String getLabel() { + return label; + } + + + + public void setLabel(String label) { + this.label = label; + } + + + + + private String label; + + public DatabaseColumn(String name, int size, DatabaseColumnType type) { + super(); + this.name = name; + this.size = size; + this.type = type; + } + + + public DatabaseColumn(String name, String label, DatabaseColumnType type, int size) { + this.name = name; + this.label = label; + this.size = size; + this.type = type; + } + + + public String getName() { + return name; + } + + + public void setName(String name) { + this.name = name; + } + + + public int getSize() { + return size; + } + + + public void setSize(int size) { + this.size = size; + } + + + + + @Override + public String toString() { + return "DatabaseColumn [name=" + name + ", size=" + size + ", type=" + type + ", label=" + label + "]"; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseInfo.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseInfo.java new file mode 100644 index 000000000..c5755e9a7 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseInfo.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.model; + +import java.util.ArrayList; +import java.util.List; + +public class DatabaseInfo { + + private List tables; + private int dbMajorVersion; + private int dbMinorVersion; + private String dbProductVersion; + private String dbProductName; + + private ArrayList columns; + private List rows; + + public DatabaseInfo() { + // TODO Auto-generated constructor stub + } + + public List getTables() { + return tables; + } + + public void setTables(List tables) { + this.tables = tables; + } + + public void setDatabaseMajorVersion(int dbMajorVersion) { + this.dbMajorVersion = dbMajorVersion; + + } + + public void setDatabaseMinorVersion(int dbMinorVersion) { + this.dbMinorVersion = dbMinorVersion; + + } + + public void setDatabaseProductVersion(String dbProductVersion) { + this.dbProductVersion = dbProductVersion; + + } + + public void setDatabaseProductName(String dbProductName) { + this.dbProductName = dbProductName; + + } + + public int getDbMajorVersion() { + return dbMajorVersion; + } + + public void setDbMajorVersion(int dbMajorVersion) { + this.dbMajorVersion = dbMajorVersion; + } + + public int getDbMinorVersion() { + return dbMinorVersion; + } + + public void setDbMinorVersion(int dbMinorVersion) { + this.dbMinorVersion = dbMinorVersion; + } + + public String getDbProductVersion() { + return dbProductVersion; + } + + public void setDbProductVersion(String dbProductVersion) { + this.dbProductVersion = dbProductVersion; + } + + public String getDbProductName() { + return dbProductName; + } + + public void setDbProductName(String dbProductName) { + this.dbProductName = dbProductName; + } + + public void setColumns(ArrayList columns) { + this.columns = columns; + + } + + public void setRows(List rows) { + this.rows = rows; + + } + + public ArrayList getColumns() { + return columns; + } + + + public List getRows() { + return rows; + } + + + @Override + public String toString() { + return "DatabaseInfo [tables=" + tables + ", dbMajorVersion=" + dbMajorVersion + ", dbMinorVersion=" + + dbMinorVersion + ", dbProductVersion=" + dbProductVersion + ", dbProductName=" + dbProductName + "]"; + } + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseQueryInfo.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseQueryInfo.java new file mode 100644 index 000000000..e8f1fb8c8 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseQueryInfo.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.model; + +import com.google.refine.extension.database.DatabaseConfiguration; + +public class DatabaseQueryInfo { + + private DatabaseConfiguration dbConfig; + + private String query; + + public DatabaseQueryInfo(DatabaseConfiguration databaseConfig, String query) { + super(); + this.dbConfig = databaseConfig; + this.query = query; + } + + + public DatabaseConfiguration getDbConfig() { + return dbConfig; + } + + + public void setDbConfig(DatabaseConfiguration databaseConfig) { + this.dbConfig = databaseConfig; + } + + + public String getQuery() { + return query; + } + + + public void setQuery(String query) { + this.query = query; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseRow.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseRow.java new file mode 100644 index 000000000..3fd6b9979 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseRow.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.model; + +import java.util.List; + +public class DatabaseRow { + + private int index; + + private List values; + + + public int getIndex() { + return index; + } + + + public void setIndex(int index) { + this.index = index; + } + + + public List getValues() { + return values; + } + + + public void setValues(List values) { + this.values = values; + } + + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/model/DatabaseTable.java b/extensions/database/src/com/google/refine/extension/database/model/DatabaseTable.java new file mode 100644 index 000000000..c7758de27 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/model/DatabaseTable.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.model; + +import java.util.ArrayList; +import java.util.List; + +public class DatabaseTable { + + + private List columns = new ArrayList(); + + private String name; + + + public DatabaseTable(String name) { + this.name = name; + } + + + public List getColumns() { + return columns; + } + + + public void setColumns(List columns) { + this.columns = columns; + } + + + public String getName() { + return name; + } + + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "DatabaseTable [columns=" + columns + ", name=" + name + "]"; + } + + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java new file mode 100644 index 000000000..f3986a895 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.mysql; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.SQLType; + + +public class MySQLConnectionManager { + + static final Logger logger = LoggerFactory.getLogger("MySQLConnectionManager"); + private Connection connection; + private SQLType type; + + private static MySQLConnectionManager instance; + + /** + * + * @param type + * @param databaseConfiguration + * @throws SQLException + */ + private MySQLConnectionManager() { + type = SQLType.forName(MySQLDatabaseService.DB_NAME); + + } + + + + + /** + * Create a new instance of this connection manager. + * + * @return an instance of the manager + * + * @throws DatabaseServiceException + */ + public static MySQLConnectionManager getInstance() throws DatabaseServiceException { + if (instance == null) { + logger.info("::Creating new MySQLConnectionManager ::"); + instance = new MySQLConnectionManager(); + + } + return instance; + } + + + /** + * Get the SQL Database type. + * + * @return the type + */ + public SQLType getType() { + return this.type; + } + + /** + * testConnection + * @param databaseConfiguration + * @return + */ + public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException{ + + try { + boolean connResult = false; + + Connection conn = getConnection(databaseConfiguration, true); + if(conn != null) { + connResult = true; + conn.close(); + } + + return connResult; + + } + catch (SQLException e) { + logger.error("Test connection Failed!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + /** + * Get a connection form the connection pool. + * + * @return connection from the pool + */ + public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection) throws DatabaseServiceException{ + try { + + // logger.info("connection::{}, forceNewConnection: {}", connection, forceNewConnection); + + if (connection != null && !forceNewConnection) { + //logger.info("connection closed::{}", connection.isClosed()); + if (!connection.isClosed()) { + if(logger.isDebugEnabled()){ + logger.debug("Returning existing connection::{}", connection); + } + + return connection; + } + } + + Class.forName(type.getClassPath()); + DriverManager.setLoginTimeout(10); + String dbURL = getDatabaseUrl(databaseConfiguration); + connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), + databaseConfiguration.getDatabasePassword()); + + logger.info("*** Acquired New connection for ::{} **** ", dbURL); + + return connection; + + + } catch (ClassNotFoundException e) { + logger.error("Jdbc Driver not found", e); + throw new DatabaseServiceException(e.getMessage()); + } catch (SQLException e) { + logger.error("SQLException::Couldn't get a Connection!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + + public void shutdown() { + + if (connection != null) { + try { + connection.close(); + } + catch (SQLException e) { + logger.warn("Non-Managed connection could not be closed. Whoops!", e); + } + } + + } + + + private String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/mysql/MySQLDatabaseService.java b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLDatabaseService.java new file mode 100644 index 000000000..b577a63b6 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/mysql/MySQLDatabaseService.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.mysql; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.DatabaseUtils; +import com.google.refine.extension.database.SQLType; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.mysql.jdbc.ResultSetMetaData; + +public class MySQLDatabaseService extends DatabaseService { + + + private final static Logger logger = LoggerFactory.getLogger("MySQLDatabaseService"); + + public static final String DB_NAME = "mysql"; + public static final String DB_DRIVER = "com.mysql.jdbc.Driver"; + + private static MySQLDatabaseService instance; + + private MySQLDatabaseService() { + } + + public static MySQLDatabaseService getInstance() { + if (instance == null) { + SQLType.registerSQLDriver(DB_NAME, DB_DRIVER, false); + instance = new MySQLDatabaseService(); + logger.debug("MySQLDatabaseService Instance: {}", instance); + } + return instance; + } + + @Override + public boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return MySQLConnectionManager.getInstance().testConnection(dbConfig); + + } + + @Override + public DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return getMetadata(dbConfig); + } + + + @Override + public DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + ResultSetMetaData metadata = (ResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn( + metadata.getColumnName(i), + metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), + metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + DatabaseInfo dbInfo = new DatabaseInfo(); + dbInfo.setColumns(columns); + dbInfo.setRows(rows); + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + }finally { + MySQLConnectionManager.getInstance().shutdown(); + } + } + + /** + * + * @param connectionInfo + * @return + * @throws DatabaseServiceException + */ + private DatabaseInfo getMetadata(DatabaseConfiguration connectionInfo) throws DatabaseServiceException { + + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(connectionInfo, true); + if(connection != null) { + java.sql.DatabaseMetaData metadata; + + metadata = connection.getMetaData(); + + int dbMajorVersion = metadata.getDatabaseMajorVersion(); + int dbMinorVersion = metadata.getDatabaseMinorVersion(); + String dbProductVersion = metadata.getDatabaseProductVersion(); + String dbProductName = metadata.getDatabaseProductName(); + DatabaseInfo dbInfo = new DatabaseInfo(); + + dbInfo.setDatabaseMajorVersion(dbMajorVersion); + dbInfo.setDatabaseMinorVersion(dbMinorVersion); + dbInfo.setDatabaseProductVersion(dbProductVersion); + dbInfo.setDatabaseProductName(dbProductName); + return dbInfo; + } + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + + return null; + + } + + @Override + public String buildLimitQuery(Integer limit, Integer offset, String query) { + StringBuilder sb = new StringBuilder(); + sb.append(query); + + if(limit != null) { + sb.append(" LIMIT" + " " + limit); + } + + if(offset != null) { + sb.append(" OFFSET" + " " + offset); + } + + return sb.toString(); + } + + @Override + public ArrayList getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + Statement statement = connection.createStatement(); + + ResultSet queryResult = statement.executeQuery(query); + ResultSetMetaData metadata = (ResultSetMetaData) queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn(metadata.getColumnName(i), metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + + return columns; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + + } + + @Override + public List getRows(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, false); + + Statement statement = connection.createStatement(); + statement.setFetchSize(10); + ResultSet queryResult = statement.executeQuery(query); + ResultSetMetaData metadata = (ResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + return rows; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + @Override + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } + + + @Override + public Connection getConnection(DatabaseConfiguration dbConfig) + throws DatabaseServiceException { + // TODO Auto-generated method stub + return MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + } + + @Override + public DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + Statement statement = null; + ResultSet queryResult = null; + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + statement = connection.createStatement(); + queryResult = statement.executeQuery(query); + + DatabaseInfo dbInfo = new DatabaseInfo(); + + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + + } + if (statement != null) { + statement.close(); + + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + MySQLConnectionManager.getInstance().shutdown(); + } + } + + +} diff --git a/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java new file mode 100644 index 000000000..cda24dc45 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.pgsql; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.SQLType; + + + +public class PgSQLConnectionManager { + + static final Logger logger = LoggerFactory.getLogger("PgSQLConnectionManager"); + private Connection connection; + private SQLType type; + + private static PgSQLConnectionManager instance; + + + + /** + * + * @param type + * @param databaseConfiguration + * @throws SQLException + */ + private PgSQLConnectionManager() { + type = SQLType.forName(PgSQLDatabaseService.DB_NAME); + + } + + + + + /** + * Create a new instance of this connection manager. + * + * @return an instance of the manager + * + * @throws DatabaseServiceException + */ + public static PgSQLConnectionManager getInstance() throws DatabaseServiceException { + if (instance == null) { + logger.info("::Creating new PgSQL ConnectionManager ::"); + instance = new PgSQLConnectionManager(); + + } + return instance; + } + + + /** + * Get the SQL Database type. + * + * @return the type + */ + public SQLType getType() { + return this.type; + } + + /** + * testConnection + * @param databaseConfiguration + * @return + */ + public boolean testConnection(DatabaseConfiguration databaseConfiguration) throws DatabaseServiceException{ + + try { + boolean connResult = false; + + Connection conn = getConnection(databaseConfiguration, true); + if(conn != null) { + connResult = true; + conn.close(); + } + + return connResult; + + } + catch (SQLException e) { + logger.error("Test connection Failed!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + } + + /** + * Get a connection form the connection pool. + * + * @return connection from the pool + */ + public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection) throws DatabaseServiceException{ + try { + + // logger.info("connection::{}, forceNewConnection: {}", connection, forceNewConnection); + + if (connection != null && !forceNewConnection) { + // logger.info("connection closed::{}", connection.isClosed()); + if (!connection.isClosed()) { + if(logger.isDebugEnabled()){ + logger.debug("Returning existing connection::{}", connection); + } + return connection; + } + } + + Class.forName(type.getClassPath()); + DriverManager.setLoginTimeout(10); + String dbURL = getDatabaseUrl(databaseConfiguration); + connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), + databaseConfiguration.getDatabasePassword()); + + logger.info("*** Acquired New connection for ::{} **** ", dbURL); + + return connection; + + + } catch (ClassNotFoundException e) { + logger.error("Jdbc Driver not found", e); + throw new DatabaseServiceException(e.getMessage()); + } catch (SQLException e) { + logger.error("SQLException::Couldn't get a Connection!", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + + public void shutdown() { + + if (connection != null) { + try { + connection.close(); + } + catch (SQLException e) { + logger.warn("Non-Managed connection could not be closed. Whoops!", e); + } + } + + } + + + private static String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); + + } +} diff --git a/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLDatabaseService.java b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLDatabaseService.java new file mode 100644 index 000000000..14a2fcad7 --- /dev/null +++ b/extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLDatabaseService.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2017, Tony Opara + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - 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. + * + * Neither the name of Google 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 HOLDER 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. + */ +package com.google.refine.extension.database.pgsql; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.postgresql.jdbc.PgResultSetMetaData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; +import com.google.refine.extension.database.DatabaseUtils; +import com.google.refine.extension.database.SQLType; +import com.google.refine.extension.database.model.DatabaseColumn; +import com.google.refine.extension.database.model.DatabaseInfo; +import com.google.refine.extension.database.model.DatabaseRow; +import com.google.refine.extension.database.mysql.MySQLConnectionManager; + +public class PgSQLDatabaseService extends DatabaseService { + + private final static Logger logger = LoggerFactory.getLogger("PgSQLDatabaseService"); + + public static final String DB_NAME = "postgresql"; + public static final String DB_DRIVER = "org.postgresql.Driver"; + + private static PgSQLDatabaseService instance; + + private PgSQLDatabaseService() { + } + + public static PgSQLDatabaseService getInstance() { + if (instance == null) { + SQLType.registerSQLDriver(DB_NAME, DB_DRIVER); + instance = new PgSQLDatabaseService(); + logger.debug("PgSQLDatabaseService Instance: {}", instance); + } + return instance; + } + + + @Override + public boolean testConnection(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return PgSQLConnectionManager.getInstance().testConnection(dbConfig); + + } + + @Override + public DatabaseInfo connect(DatabaseConfiguration dbConfig) throws DatabaseServiceException{ + return getMetadata(dbConfig); + } + + + @Override + public DatabaseInfo executeQuery(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + ResultSet queryResult = statement.executeQuery(query); + PgResultSetMetaData metadata = (PgResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn( + metadata.getColumnName(i), + metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), + metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + + values.add(queryResult.getString(i)); + + } + row.setValues(values); + rows.add(row); + index++; + + } + + DatabaseInfo dbInfo = new DatabaseInfo(); + dbInfo.setColumns(columns); + dbInfo.setRows(rows); + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + }finally { + PgSQLConnectionManager.getInstance().shutdown(); + } + } + + /** + * + * @param connectionInfo + * @return + * @throws DatabaseServiceException + */ + private DatabaseInfo getMetadata(DatabaseConfiguration connectionInfo) throws DatabaseServiceException { + + + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(connectionInfo, true); + if(connection != null) { + java.sql.DatabaseMetaData metadata; + + metadata = connection.getMetaData(); + + int dbMajorVersion = metadata.getDatabaseMajorVersion(); + int dbMinorVersion = metadata.getDatabaseMinorVersion(); + String dbProductVersion = metadata.getDatabaseProductVersion(); + String dbProductName = metadata.getDatabaseProductName(); + DatabaseInfo dbInfo = new DatabaseInfo(); + + dbInfo.setDatabaseMajorVersion(dbMajorVersion); + dbInfo.setDatabaseMinorVersion(dbMinorVersion); + dbInfo.setDatabaseProductVersion(dbProductVersion); + dbInfo.setDatabaseProductName(dbProductName); + return dbInfo; + } + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + return null; + } + + @Override + public String buildLimitQuery(Integer limit, Integer offset, String query) { + StringBuilder sb = new StringBuilder(); + sb.append(query); + + if(limit != null) { + sb.append(" LIMIT" + " " + limit); + } + + if(offset != null) { + sb.append(" OFFSET" + " " + offset); + } + + return sb.toString(); + } + + @Override + public ArrayList getColumns(DatabaseConfiguration dbConfig, String query) throws DatabaseServiceException{ + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(dbConfig, true); + Statement statement = connection.createStatement(); + + ResultSet queryResult = statement.executeQuery(query); + PgResultSetMetaData metadata = (PgResultSetMetaData) queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + ArrayList columns = new ArrayList(columnCount); + + for (int i = 1; i <= columnCount; i++) { + DatabaseColumn dc = new DatabaseColumn(metadata.getColumnName(i), metadata.getColumnLabel(i), + DatabaseUtils.getDbColumnType(metadata.getColumnType(i)), metadata.getColumnDisplaySize(i)); + columns.add(dc); + } + + return columns; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + + + } + + @Override + public List getRows(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + + try { + Connection connection = PgSQLConnectionManager.getInstance().getConnection(dbConfig, false); + Statement statement = connection.createStatement(); + statement.setFetchSize(10); + ResultSet queryResult = statement.executeQuery(query); + PgResultSetMetaData metadata = (PgResultSetMetaData)queryResult.getMetaData(); + int columnCount = metadata.getColumnCount(); + + int index = 0; + List rows = new ArrayList(); + + while (queryResult.next()) { + DatabaseRow row = new DatabaseRow(); + row.setIndex(index); + List values = new ArrayList(columnCount); + for (int i = 1; i <= columnCount; i++) { + values.add(queryResult.getString(i)); + } + row.setValues(values); + rows.add(row); + index++; + + } + + return rows; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } + } + + @Override + protected String getDatabaseUrl(DatabaseConfiguration dbConfig) { + + int port = dbConfig.getDatabasePort(); + return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() + + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); + + } + + @Override + public Connection getConnection(DatabaseConfiguration dbConfig) + throws DatabaseServiceException { + return PgSQLConnectionManager.getInstance().getConnection(dbConfig, true); + } + + @Override + public DatabaseInfo testQuery(DatabaseConfiguration dbConfig, String query) + throws DatabaseServiceException { + Statement statement = null; + ResultSet queryResult = null; + try { + Connection connection = MySQLConnectionManager.getInstance().getConnection(dbConfig, true); + statement = connection.createStatement(); + queryResult = statement.executeQuery(query); + + DatabaseInfo dbInfo = new DatabaseInfo(); + + return dbInfo; + + } catch (SQLException e) { + logger.error("SQLException::", e); + throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage()); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + + } + if (statement != null) { + statement.close(); + + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //MySQLConnectionManager.getInstance().shutdown(); + } + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DBExtensionTestUtils.java b/extensions/database/test/com/google/refine/extension/database/DBExtensionTestUtils.java new file mode 100644 index 000000000..a75c37882 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DBExtensionTestUtils.java @@ -0,0 +1,245 @@ +package com.google.refine.extension.database; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +public class DBExtensionTestUtils { + + private static final String DB_TYPE_MYSQL = "mysql"; + private static final String DB_HOST_MYSQL = "127.0.0.1"; + private static final int DB_PORT_MYSQL = 3306; + private static final String DB_USER_MYSQL = "root"; + private static final String DB_PASSWORD_MYSQL = "secret"; + private static final String DB_NAME_MYSQL = "rxhub"; + + private static final String DB_TYPE_PGSQL = "postgresql"; + private static final String DB_HOST_PGSQL = "127.0.0.1"; + private static final int DB_PORT_PGSQL = 5432; + private static final String DB_USER_PGSQL = "postgres"; + private static final String DB_PASSWORD_PGSQL = ""; + private static final String DB_NAME_PGSQL = "openrefine"; + + + private static final int SAMPLE_SIZE = 500000; + private static final int BATCH_SIZE = 1000; + + private static Random rand = new Random(); + + private Map mncMap; + private Map mccMap; + + + public void generateMySQLTestData() { + mncMap = new HashMap(); + mccMap = new HashMap(); + mccMap.put(0, 302); + mccMap.put(1, 311); + mccMap.put(2, 730); + mccMap.put(1, 622); + + mncMap.put(0, 006); + mncMap.put(1, 140); + mncMap.put(2, 380); + mncMap.put(3, 710); + + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost(DB_HOST_MYSQL); + dc.setDatabaseName(DB_NAME_MYSQL); + dc.setDatabasePassword(DB_PASSWORD_MYSQL); + dc.setDatabasePort(DB_PORT_MYSQL); + dc.setDatabaseType(DB_TYPE_MYSQL); + dc.setDatabaseUser(DB_USER_MYSQL); + dc.setUseSSL(false); + + String truncateTableSQL = "TRUNCATE test_data"; + + String insertTableSQL = "INSERT INTO test_data(" + + "id, ue_id, start_time, end_date, bytes_upload, bytes_download, cell_id, mcc, mnc, lac, imei)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + + Connection conn; + try { + conn = DatabaseService.get(DB_TYPE_MYSQL).getConnection(dc); + + Statement truncateStmt = conn.createStatement(); + int result = truncateStmt.executeUpdate(truncateTableSQL); + System.out.println("Truncate Table Result::" + result); + truncateStmt.close(); + + + conn.setAutoCommit(false); + + PreparedStatement stmt = conn.prepareStatement(insertTableSQL); + + int counter=1; + for (int i = 0; i < SAMPLE_SIZE; i++) { + stmt.setLong(1, i); + stmt.setString(2, getNextUeId(i)); + stmt.setDate(3, getNextStartDate(i)); + stmt.setDate(4, getNextEndDate(i)); + stmt.setInt(5, rand.nextInt()); + stmt.setInt(6, rand.nextInt()); + stmt.setInt(7, rand.nextInt(10)); + stmt.setInt(8, getMCC(i)); + stmt.setInt(9, getMNC(i)); + stmt.setInt(10, rand.nextInt(100)); + stmt.setString(11, getNextIMEI(i)); + + stmt.addBatch(); + + //Execute batch of 1000 records + if(i%BATCH_SIZE==0){ + stmt.executeBatch(); + conn.commit(); + System.out.println("Batch "+(counter++)+" executed successfully"); + } + } + //execute final batch + stmt.executeBatch(); + System.out.println("Final Batch Executed "+(counter++)+" executed successfully"); + conn.commit(); + conn.close(); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + + public void generatePgSQLTestData() { + mncMap = new HashMap(); + mccMap = new HashMap(); + mccMap.put(0, 302); + mccMap.put(1, 311); + mccMap.put(2, 730); + mccMap.put(1, 622); + + mncMap.put(0, 006); + mncMap.put(1, 140); + mncMap.put(2, 380); + mncMap.put(3, 710); + + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost(DB_HOST_PGSQL); + dc.setDatabaseName(DB_NAME_PGSQL); + dc.setDatabasePassword(DB_PASSWORD_PGSQL); + dc.setDatabasePort(DB_PORT_PGSQL); + dc.setDatabaseType(DB_TYPE_PGSQL); + dc.setDatabaseUser(DB_USER_PGSQL); + dc.setUseSSL(false); + + String truncateTableSQL = "TRUNCATE public.test_data"; + + String insertTableSQL = "INSERT INTO public.test_data(" + + "id, ue_id, start_time, end_date, bytes_upload, bytes_download, cell_id, mcc, mnc, lac, imei)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + + Connection conn; + try { + conn = DatabaseService.get(DB_TYPE_PGSQL).getConnection(dc); + + Statement truncateStmt = conn.createStatement(); + int result = truncateStmt.executeUpdate(truncateTableSQL); + System.out.println("Truncate Table Result::" + result); + truncateStmt.close(); + + + conn.setAutoCommit(false); + + PreparedStatement stmt = conn.prepareStatement(insertTableSQL); + + int counter=1; + for (int i = 0; i < SAMPLE_SIZE; i++) { + stmt.setLong(1, i); + stmt.setString(2, getNextUeId(i)); + stmt.setDate(3, getNextStartDate(i)); + stmt.setDate(4, getNextEndDate(i)); + stmt.setInt(5, rand.nextInt()); + stmt.setInt(6, rand.nextInt()); + stmt.setInt(7, rand.nextInt(10)); + stmt.setInt(8, getMCC(i)); + stmt.setInt(9, getMNC(i)); + stmt.setInt(10, rand.nextInt(100)); + stmt.setString(11, getNextIMEI(i)); + + stmt.addBatch(); + + //Execute batch of 1000 records + if(i%BATCH_SIZE==0){ + stmt.executeBatch(); + conn.commit(); + System.out.println("Batch "+(counter++)+" executed successfully"); + } + } + //execute final batch + stmt.executeBatch(); + System.out.println("Final Batch Executed "+(counter++)+" executed successfully"); + conn.commit(); + conn.close(); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + private String getNextIMEI(int i) { + +// byte[] array = new byte[16]; // length is bounded by 7 +// new Random().nextBytes(array); +// String generatedString = new String(array, Charset.forName("UTF-8")); + + int n = 1000000000 + rand.nextInt(900000000); + return "" + n; + } + + + + private int getMNC(int i) { + // TODO Auto-generated method stub + return mncMap.get(rand.nextInt(3)); + } + + private int getMCC(int i) { + // System.out.println(rand.nextInt(3)); + return mccMap.get(rand.nextInt(3)); + } + + private Date getNextEndDate(int i) { + // TODO Auto-generated method stub + return new Date(System.currentTimeMillis() + 1); + } + + private Date getNextStartDate(int i) { + // TODO Auto-generated method stub + return new Date(System.currentTimeMillis()); + } + + private String getNextUeId(int i) { + int n = 300000000 + rand.nextInt(900000000); + + return "" + n; + } + + public static void main(String[] args) { + DBExtensionTestUtils testUtil = new DBExtensionTestUtils(); + // testUtil.generatePgSQLTestData(); + testUtil.generateMySQLTestData(); + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DBQueryResultImportReaderTest.java b/extensions/database/test/com/google/refine/extension/database/DBQueryResultImportReaderTest.java new file mode 100644 index 000000000..32500fa27 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DBQueryResultImportReaderTest.java @@ -0,0 +1,17 @@ +package com.google.refine.extension.database; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +public class DBQueryResultImportReaderTest { + + @BeforeTest + public void beforeTest() { + } + + @Test + public void testGetNextRowOfCells() { + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DBQueryResultPreviewReaderTest.java b/extensions/database/test/com/google/refine/extension/database/DBQueryResultPreviewReaderTest.java new file mode 100644 index 000000000..b9baf7c62 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DBQueryResultPreviewReaderTest.java @@ -0,0 +1,17 @@ +package com.google.refine.extension.database; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +public class DBQueryResultPreviewReaderTest { + + @BeforeTest + public void beforeTest() { + } + + @Test + public void testGetNextRowOfCells() { + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DatabaseImportControllerTest.java b/extensions/database/test/com/google/refine/extension/database/DatabaseImportControllerTest.java new file mode 100644 index 000000000..80302da4e --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DatabaseImportControllerTest.java @@ -0,0 +1,27 @@ +package com.google.refine.extension.database; + + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + + +public class DatabaseImportControllerTest { + + @BeforeTest + public void beforeTest() { + } + + + @Test + public void testDoGet() { + + } + + @Test + public void testDoPost() { + + } + + + +} diff --git a/extensions/database/test/com/google/refine/extension/database/DatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/DatabaseServiceTest.java new file mode 100644 index 000000000..7d2ff0b01 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/DatabaseServiceTest.java @@ -0,0 +1,59 @@ +package com.google.refine.extension.database; + + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + + +public class DatabaseServiceTest { + + @BeforeTest + public void beforeTest() { + } + + @Test + public void testGetDatabaseUrl() { + + } + + @Test + public void testGet() { + + } + + @Test + public void testGetConnection() { + + } + + @Test + public void testTestConnection() { + + } + + @Test + public void testConnect() { + + } + + @Test + public void testExecuteQuery() { + + } + + @Test + public void testBuildLimitQuery() { + + } + + @Test + public void testGetColumns() { + + } + + @Test + public void testGetRows() { + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/ConnectCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/ConnectCommandTest.java new file mode 100644 index 000000000..7b5501764 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/ConnectCommandTest.java @@ -0,0 +1,81 @@ + +package com.google.refine.extension.database.cmd; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + +import static org.mockito.Mockito.when; + + +public class ConnectCommandTest { + + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + @BeforeTest + public void beforeTest() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDoPost() { + + when(request.getParameter("databaseType")).thenReturn(PgSQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn("127.0.0.1"); + when(request.getParameter("databasePort")).thenReturn("5432"); + when(request.getParameter("databaseUser")).thenReturn("postgres"); + when(request.getParameter("databasePassword")).thenReturn(""); + when(request.getParameter("initialDatabase")).thenReturn("postgres"); + + + StringWriter sw = new StringWriter(); + + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + ConnectCommand connectCommand = new ConnectCommand(); + + connectCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ServletException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/QueryCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/QueryCommandTest.java new file mode 100644 index 000000000..62f677e1a --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/QueryCommandTest.java @@ -0,0 +1,79 @@ +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + + +public class QueryCommandTest { + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + @BeforeTest + public void beforeTest() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDoPost() { + + when(request.getParameter("databaseType")).thenReturn(PgSQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn("127.0.0.1"); + when(request.getParameter("databasePort")).thenReturn("5432"); + when(request.getParameter("databaseUser")).thenReturn("postgres"); + when(request.getParameter("databasePassword")).thenReturn(""); + when(request.getParameter("initialDatabase")).thenReturn("openrefine"); + when(request.getParameter("queryString")).thenReturn("SELECT count(*) FROM call_data"); + + + StringWriter sw = new StringWriter(); + + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + ExecuteQueryCommand connectCommand = new ExecuteQueryCommand(); + + connectCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ServletException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java new file mode 100644 index 000000000..f6a5539e7 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java @@ -0,0 +1,92 @@ +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + +public class SavedConnectionCommandTest { + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + @BeforeTest + public void beforeTest() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDoPost() { + + when(request.getParameter("connectionName")).thenReturn("test-db-name"); + when(request.getParameter("databaseType")).thenReturn(PgSQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn("127.0.0.1"); + when(request.getParameter("databasePort")).thenReturn("5432"); + when(request.getParameter("databaseUser")).thenReturn("postgres"); + when(request.getParameter("databasePassword")).thenReturn(""); + when(request.getParameter("initialDatabase")).thenReturn("openrefine"); + when(request.getParameter("queryString")).thenReturn("SELECT count(*) FROM call_data"); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + SavedConnectionCommand scCommand = new SavedConnectionCommand(); + + scCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + System.out.println("result:" + result); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ServletException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + @Test + public void testDoGet() { + + } + + @Test + public void testDoPut() { + + } + + @Test + public void testDoDelete() { + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/cmd/TestConnectCommandTest.java b/extensions/database/test/com/google/refine/extension/database/cmd/TestConnectCommandTest.java new file mode 100644 index 000000000..6c7313569 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/cmd/TestConnectCommandTest.java @@ -0,0 +1,79 @@ +package com.google.refine.extension.database.cmd; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.pgsql.PgSQLDatabaseService; + + + +public class TestConnectCommandTest { + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + @BeforeTest + public void beforeTest() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDoPost() { + + when(request.getParameter("databaseType")).thenReturn(PgSQLDatabaseService.DB_NAME); + when(request.getParameter("databaseServer")).thenReturn("127.0.0.1"); + when(request.getParameter("databasePort")).thenReturn("5432"); + when(request.getParameter("databaseUser")).thenReturn("postgres"); + when(request.getParameter("databasePassword")).thenReturn(""); + when(request.getParameter("initialDatabase")).thenReturn("postgres"); + + + StringWriter sw = new StringWriter(); + + PrintWriter pw = new PrintWriter(sw); + + try { + when(response.getWriter()).thenReturn(pw); + TestConnectCommand connectCommand = new TestConnectCommand(); + + connectCommand.doPost(request, response); + + String result = sw.getBuffer().toString().trim(); + JSONObject json = new JSONObject(result); + + String code = json.getString("code"); + Assert.assertEquals(code, "ok"); + + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ServletException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBConnectionManagerTest.java b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBConnectionManagerTest.java new file mode 100644 index 000000000..1e204cf34 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBConnectionManagerTest.java @@ -0,0 +1,96 @@ +package com.google.refine.extension.database.mariadb; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + +public class MariaDBConnectionManagerTest { + + + private static final int DATABASE_PORT = 3306; + + @BeforeTest + public void beforeTest() { + DatabaseService.DBType.registerDatabase(MariaDBDatabaseService.DB_NAME, MariaDBDatabaseService.getInstance()); + } + + + @Test + public void testTestConnection() { + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("test"); + dc.setDatabasePassword("secret"); + dc.setDatabasePort(DATABASE_PORT); + dc.setDatabaseType(MariaDBDatabaseService.DB_NAME); + dc.setDatabaseUser("root"); + dc.setUseSSL(false); + try { + boolean conn = MariaDBConnectionManager.getInstance().testConnection(dc); + Assert.assertEquals(conn, true); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testGetConnection() { + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("test"); + dc.setDatabasePassword("secret"); + dc.setDatabasePort(DATABASE_PORT); + dc.setDatabaseType(MariaDBDatabaseService.DB_NAME); + dc.setDatabaseUser("root"); + dc.setUseSSL(false); + try { + Connection conn = MariaDBConnectionManager.getInstance().getConnection(dc, true); + Assert.assertNotNull(conn); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testShutdown() { + + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("test"); + dc.setDatabasePassword("secret"); + dc.setDatabasePort(DATABASE_PORT); + dc.setDatabaseType(MariaDBDatabaseService.DB_NAME); + dc.setDatabaseUser("root"); + dc.setUseSSL(false); + try { + Connection conn = MariaDBConnectionManager.getInstance().getConnection(dc, true); + Assert.assertNotNull(conn); + + MariaDBConnectionManager.getInstance().shutdown(); + + if(conn != null) { + Assert.assertEquals(conn.isClosed(), true); + } + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBDatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBDatabaseServiceTest.java new file mode 100644 index 000000000..3fdced8df --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mariadb/MariaDBDatabaseServiceTest.java @@ -0,0 +1,57 @@ +package com.google.refine.extension.database.mariadb; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +public class MariaDBDatabaseServiceTest { + + @BeforeTest + public void beforeTest() { + } + + @Test + public void testGetDatabaseUrl() { + + } + + @Test + public void testGetConnection() { + + } + + @Test + public void testTestConnection() { + + } + + @Test + public void testConnect() { + + } + + @Test + public void testExecuteQuery() { + + } + + @Test + public void testBuildLimitQuery() { + + } + + @Test + public void testGetRows() { + + } + + @Test + public void testGetInstance() { + + } + + @Test + public void testGetColumnsDatabaseConfigurationString() { + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mysql/MySQLConnectionManagerTest.java b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLConnectionManagerTest.java new file mode 100644 index 000000000..77f885ae5 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLConnectionManagerTest.java @@ -0,0 +1,96 @@ +package com.google.refine.extension.database.mysql; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + +public class MySQLConnectionManagerTest { + + + private static final int DATABASE_PORT = 3306; + + @BeforeTest + public void beforeTest() { + DatabaseService.DBType.registerDatabase(MySQLDatabaseService.DB_NAME, MySQLDatabaseService.getInstance()); + } + + + @Test + public void testTestConnection() { + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("mysql"); + dc.setDatabasePassword("secret"); + dc.setDatabasePort(DATABASE_PORT); + dc.setDatabaseType(MySQLDatabaseService.DB_NAME); + dc.setDatabaseUser("root"); + dc.setUseSSL(false); + try { + boolean conn = MySQLConnectionManager.getInstance().testConnection(dc); + Assert.assertEquals(conn, true); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testGetConnection() { + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("mysql"); + dc.setDatabasePassword("secret"); + dc.setDatabasePort(DATABASE_PORT); + dc.setDatabaseType(MySQLDatabaseService.DB_NAME); + dc.setDatabaseUser("root"); + dc.setUseSSL(false); + try { + Connection conn = MySQLConnectionManager.getInstance().getConnection(dc, true); + Assert.assertNotNull(conn); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testShutdown() { + + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("mysql"); + dc.setDatabasePassword("secret"); + dc.setDatabasePort(DATABASE_PORT); + dc.setDatabaseType(MySQLDatabaseService.DB_NAME); + dc.setDatabaseUser("root"); + dc.setUseSSL(false); + try { + Connection conn = MySQLConnectionManager.getInstance().getConnection(dc, true); + Assert.assertNotNull(conn); + + MySQLConnectionManager.getInstance().shutdown(); + + if(conn != null) { + Assert.assertEquals(conn.isClosed(), true); + } + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/mysql/MySQLDatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLDatabaseServiceTest.java new file mode 100644 index 000000000..d874603e0 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/mysql/MySQLDatabaseServiceTest.java @@ -0,0 +1,57 @@ +package com.google.refine.extension.database.mysql; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +public class MySQLDatabaseServiceTest { + + @BeforeTest + public void beforeTest() { + } + + @Test + public void testGetDatabaseUrl() { + + } + + @Test + public void testGetConnection() { + + } + + @Test + public void testTestConnection() { + + } + + @Test + public void testConnect() { + + } + + @Test + public void testExecuteQuery() { + + } + + @Test + public void testBuildLimitQuery() { + + } + + @Test + public void testGetRows() { + + } + + @Test + public void testGetInstance() { + + } + + @Test + public void testGetColumnsDatabaseConfigurationString() { + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLConnectionManagerTest.java b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLConnectionManagerTest.java new file mode 100644 index 000000000..a393db95b --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLConnectionManagerTest.java @@ -0,0 +1,95 @@ +package com.google.refine.extension.database.pgsql; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; +import com.google.refine.extension.database.DatabaseServiceException; + + + +public class PgSQLConnectionManagerTest { + + + @BeforeTest + public void beforeTest() { + DatabaseService.DBType.registerDatabase(PgSQLDatabaseService.DB_NAME, PgSQLDatabaseService.getInstance()); + } + + + @Test + public void testTestConnection() { + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("enmsso"); + dc.setDatabasePort(5432); + dc.setDatabaseType(PgSQLDatabaseService.DB_NAME); + dc.setDatabaseUser("postgres"); + dc.setUseSSL(false); + try { + boolean conn = PgSQLConnectionManager.getInstance().testConnection(dc); + Assert.assertEquals(conn, true); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testGetConnection() { + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("enmsso"); + //dc.setDatabasePassword("secret"); + dc.setDatabasePort(5432); + dc.setDatabaseType(PgSQLDatabaseService.DB_NAME); + dc.setDatabaseUser("postgres"); + dc.setUseSSL(false); + try { + Connection conn = PgSQLConnectionManager.getInstance().getConnection(dc, true); + Assert.assertNotNull(conn); + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test + public void testShutdown() { + + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName("enmsso"); + //dc.setDatabasePassword("secret"); + dc.setDatabasePort(5432); + dc.setDatabaseType(PgSQLDatabaseService.DB_NAME); + dc.setDatabaseUser("postgres"); + dc.setUseSSL(false); + try { + Connection conn = PgSQLConnectionManager.getInstance().getConnection(dc, true); + Assert.assertNotNull(conn); + + PgSQLConnectionManager.getInstance().shutdown(); + + if(conn != null) { + Assert.assertEquals(conn.isClosed(), true); + } + + } catch (DatabaseServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLDatabaseServiceTest.java b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLDatabaseServiceTest.java new file mode 100644 index 000000000..8a0d4c4d1 --- /dev/null +++ b/extensions/database/test/com/google/refine/extension/database/pgsql/PgSQLDatabaseServiceTest.java @@ -0,0 +1,85 @@ +package com.google.refine.extension.database.pgsql; + +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import com.google.refine.extension.database.DatabaseConfiguration; +import com.google.refine.extension.database.DatabaseService; + +public class PgSQLDatabaseServiceTest { + + + @BeforeTest + public void beforeTest() { + DatabaseService.DBType.registerDatabase(PgSQLDatabaseService.DB_NAME, PgSQLDatabaseService.getInstance()); + } + + @Test + @Parameters("dbName") + public void testGetDatabaseUrl(@Optional("postgres") String dbName) { + DatabaseConfiguration dc = new DatabaseConfiguration(); + dc.setDatabaseHost("127.0.0.1"); + dc.setDatabaseName(dbName); + dc.setDatabasePort(5432); + dc.setDatabaseType(PgSQLDatabaseService.DB_NAME); + dc.setDatabaseUser("postgres"); + dc.setUseSSL(false); + + String dbURL = PgSQLDatabaseService.getInstance().getDatabaseUrl(dc); + Assert.assertEquals(dbURL, "jdbc:postgresql://127.0.0.1:5432/" + dbName + "?useSSL=false"); + + } + + @Test + @Parameters("dbName") + public void testGetConnection(@Optional("postgres") String dbName) { + System.out.println("dbName::" + dbName); + } + + @Test + @Parameters("dbName") + public void testTestConnection(@Optional("postgres") String dbName) { + + } + + @Test + @Parameters("dbName") + public void testConnect(@Optional("postgres") String dbName) { + + } + + @Test + @Parameters("dbName") + public void testExecuteQuery(@Optional("postgres") String dbName) { + + } + + @Test + public void testBuildLimitQuery() { + String query = PgSQLDatabaseService.getInstance().buildLimitQuery(100, 10, "SELECT * FROM TEST"); + Assert.assertEquals(query, "SELECT * FROM TEST LIMIT 100 OFFSET 10"); + + } + + @Test + @Parameters("dbName") + public void testGetRows(@Optional("postgres") String dbName) { + + } + + @Test + @Parameters("dbName") + public void testGetInstance(@Optional("postgres") String dbName) { + + } + + @Test + @Parameters("dbName") + public void testGetColumns(@Optional("postgres") String dbName) { + + } + +} diff --git a/extensions/database/test/testng.xml b/extensions/database/test/testng.xml new file mode 100644 index 000000000..e31b4f176 --- /dev/null +++ b/extensions/database/test/testng.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file